From 62f3fda2711b774333fa5e2632b93c54832e6f03 Mon Sep 17 00:00:00 2001 From: kgotlinux <60880393+kgotlinux@users.noreply.github.com> Date: Tue, 16 Jun 2020 22:15:41 +0200 Subject: Added neccessary headers for FreeBSD 1. Changed order of sys/sysctl.h, because sysctl.h needs types.h 2. Threads.h is neccessary for once_flag 3. absl/base/call_once.h is neccessary for LowLevelCallOnce --- absl/base/internal/unscaledcycleclock.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index f1e7bbef..83e2134e 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -24,8 +24,10 @@ #ifdef __GLIBC__ #include #elif defined(__FreeBSD__) -#include +#include "absl/base/call_once.h" #include +#include +#include #endif #endif -- cgit v1.2.3 From a09b5de0d57d7b2179210989ab63361c3c1894f5 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 24 Mar 2021 18:51:51 -0700 Subject: Export of internal Abseil changes -- 3de7250f2988e360764479fa590d299a649987c0 by Derek Mauro : Make the SOVERSION fit into 16 bits to make the MacOS linker happy PiperOrigin-RevId: 364939757 -- dead27aa0734a89ccb25da807e08e61000a47f8f by Abseil Team : Update `ABSL_ATTRIBUTE_UNUSED`'s documentation that `[[maybe_unused]]` in C++17 and up is now the preferred usage solution. Also document why we can't update `ABSL_ATTRIBUTE_UNUSED` to use `[[maybe_unused]]` (differences in positioning requirements). PiperOrigin-RevId: 364900016 -- 0baf1b01dc9a2b5f9869ff5a52a1cf7a032055a3 by Abseil Team : Slightly weaken the spec for `absl::string_view::compare` to match C++17. The Abseil-specific implementation provides a stronger guarantee than required by `std::string_view`, returning +1, 0, or -1. When `absl::string_view` is an alias for `std::string_view`, these are only guaranteed to be positive, zero, and negative. Portable code should not depend on the stronger guarantee. PiperOrigin-RevId: 364846419 GitOrigin-RevId: 3de7250f2988e360764479fa590d299a649987c0 Change-Id: I7c74004fc38c9f5eaad5b104a993b79518497c5b --- absl/base/attributes.h | 7 +++++++ absl/strings/string_view.h | 10 ++++------ create_lts.py | 15 ++++++++++----- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index cf2cb550..4a8581e1 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -524,6 +524,13 @@ // ABSL_ATTRIBUTE_UNUSED // // Prevents the compiler from complaining about variables that appear unused. +// +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard '[[maybe_unused]]' directly over this macro. +// +// Due to differences in positioning requirements between the old, compiler +// specific __attribute__ syntax and the now standard [[maybe_unused]], this +// macro does not attempt to take advantage of '[[maybe_unused]]'. #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) #undef ABSL_ATTRIBUTE_UNUSED #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 5260b5b7..b276bdba 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -398,12 +398,10 @@ class string_view { // string_view::compare() // - // Performs a lexicographical comparison between the `string_view` and - // another `absl::string_view`, returning -1 if `this` is less than, 0 if - // `this` is equal to, and 1 if `this` is greater than the passed string - // view. Note that in the case of data equality, a further comparison is made - // on the respective sizes of the two `string_view`s to determine which is - // smaller, equal, or greater. + // Performs a lexicographical comparison between this `string_view` and + // another `string_view` `x`, returning a negative value if `*this` is less + // than `x`, 0 if `*this` is equal to `x`, and a positive value if `*this` + // is greater than `x`. constexpr int compare(string_view x) const noexcept { return CompareImpl(length_, x.length_, Min(length_, x.length_) == 0 diff --git a/create_lts.py b/create_lts.py index a98c76b4..302812ad 100755 --- a/create_lts.py +++ b/create_lts.py @@ -108,11 +108,16 @@ def main(argv): 'project(absl LANGUAGES CXX)': 'project(absl LANGUAGES CXX VERSION {})'.format(datestamp) }) - # Set the SOVERSION to YYYYMMDD.0.0 - The first 0 means we only have - # ABI compatible changes, and the second 0 means we can increment it - # to mark changes as ABI-compatible, for patch releases. - ReplaceStringsInFile('CMake/AbseilHelpers.cmake', - {'SOVERSION 0': 'SOVERSION "{}.0.0"'.format(datestamp)}) + # Set the SOVERSION to YYMM.0.0 - The first 0 means we only have ABI + # compatible changes, and the second 0 means we can increment it to + # mark changes as ABI-compatible, for patch releases. Note that we + # only use the last two digits of the year and the month because the + # MacOS linker requires the first part of the SOVERSION to fit into + # 16 bits. + # https://www.sicpers.info/2013/03/how-to-version-a-mach-o-library/ + ReplaceStringsInFile( + 'CMake/AbseilHelpers.cmake', + {'SOVERSION 0': 'SOVERSION "{}.0.0"'.format(datestamp[2:6])}) StripContentBetweenTags('CMakeLists.txt', '# absl:lts-remove-begin', '# absl:lts-remove-end') -- cgit v1.2.3 From 9fe35195495da41ab8f307abb61ed4169afa396e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 29 Mar 2021 15:46:35 -0700 Subject: Export of internal Abseil changes -- 6b5be2524a088d0f4e8475794dc71232a24e94d8 by Abseil Team : 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 : Add IsFlat() evaluation to GetFlatAux for RingBuffer PiperOrigin-RevId: 365666501 -- c064eb686a3c036e093e71126c45f97d3a921569 by Abseil Team : Implement C++11 compatible std::remove_cvref added in C++20 PiperOrigin-RevId: 365606639 -- af2e7e055172da914e63c05308aedb68e197661e by Abseil Team : Add IsFlat() support to CordRepRing PiperOrigin-RevId: 365562090 -- 2cfeff9280f4967c4f828812bfe153b4e9cbabb7 by Abseil Team : Make unit test for TryFlat on 'substring of rep' explicit PiperOrigin-RevId: 365081382 GitOrigin-RevId: 6b5be2524a088d0f4e8475794dc71232a24e94d8 Change-Id: Ibb577748176217ce237614a6fe77c05375a97003 --- absl/base/attributes.h | 6 ++-- absl/meta/type_traits.h | 21 +++++++++++ absl/meta/type_traits_test.cc | 28 +++++++++++++++ absl/strings/cord.cc | 5 +++ absl/strings/cord_ring_test.cc | 65 +++++++++++++++++++++++++++++++++++ absl/strings/cord_test.cc | 50 ++++++++++++++++++++++++--- absl/strings/internal/cord_rep_ring.h | 31 +++++++++++++++++ 7 files changed, 199 insertions(+), 7 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 4a8581e1..325e2f8d 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.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 diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index d5cb5f3b..b5427a43 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -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 +using remove_cvref = std::remove_cvref; + +template +using remove_cvref_t = typename std::remove_cvref::type; +#else +// remove_cvref() +// +// C++11 compatible implementation of std::remove_cvref which was added in +// C++20. +template +struct remove_cvref { + using type = + typename std::remove_cv::type>::type; +}; + +template +using remove_cvref_t = typename remove_cvref::type; +#endif + namespace type_traits_internal { // is_trivially_copyable() // diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc index 1aafd0d4..0ef5b665 100644 --- a/absl/meta/type_traits_test.cc +++ b/absl/meta/type_traits_test.cc @@ -942,6 +942,34 @@ TEST(TypeTraitsTest, TestTriviallyCopyable) { absl::type_traits_internal::is_trivially_copyable::value); } +TEST(TypeTraitsTest, TestRemoveCVRef) { + EXPECT_TRUE( + (std::is_same::type, int>::value)); + EXPECT_TRUE( + (std::is_same::type, int>::value)); + EXPECT_TRUE( + (std::is_same::type, int>::value)); + EXPECT_TRUE(( + std::is_same::type, int>::value)); + EXPECT_TRUE( + (std::is_same::type, int*>::value)); + // Does not remove const in this case. + EXPECT_TRUE((std::is_same::type, + const int*>::value)); + EXPECT_TRUE((std::is_same::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same::type, + int[2]>::value)); +} + #define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...) \ EXPECT_TRUE((std::is_same::type, \ absl::trait_name##_t<__VA_ARGS__>>::value)) 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(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__ -- cgit v1.2.3 From 2d418976ef0a1765798afd8e69538308d7b15242 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 30 Mar 2021 18:44:11 +0200 Subject: Allowing to change the MSVC runtime (#921) * Allowing to change the MSVC runtime with the CMake option CMAKE_MSVC_RUNTIME_LIBRARY introduced in 3.15 * Update CMakeLists.txt Capitalize MSVC Co-authored-by: Derek Mauro <761129+derekmauro@users.noreply.github.com> --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e09e335c..d0c6e608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,11 @@ if (POLICY CMP0077) cmake_policy(SET CMP0077 NEW) endif (POLICY CMP0077) +# Allow the user to specify the MSVC runtime +if (POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) +endif (POLICY CMP0091) + # Set BUILD_TESTING to OFF by default. # This must come before the project() and include(CTest) lines. OPTION(BUILD_TESTING "Build tests" OFF) -- cgit v1.2.3 From 9fde5a6eb081ea080f5aa895102a9154c3a2d09f Mon Sep 17 00:00:00 2001 From: Christian Blichmann Date: Tue, 30 Mar 2021 18:44:33 +0200 Subject: Add missing `add_subdirectory()` call for "cleanup" (#925) Since `absl::Cleanup` is now public, it should also be included in the `absl/CMakeLists.txt` file. Signed-off-by: Christian Blichmann --- absl/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt index fbfa7822..a41e1eeb 100644 --- a/absl/CMakeLists.txt +++ b/absl/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(base) add_subdirectory(algorithm) +add_subdirectory(cleanup) add_subdirectory(container) add_subdirectory(debugging) add_subdirectory(flags) -- cgit v1.2.3 From d9a31a2d440f33aa40d88b3b8a98f4c19ffaa182 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 30 Mar 2021 10:56:54 -0700 Subject: Export of internal Abseil changes -- 45c3adace0020bb705e04d254f7b3f914d2ba2e6 by Abseil Team : Cleanup CordRepRing unit tests PiperOrigin-RevId: 365848947 -- 3d16aae1a4a20666151d8a5ebfd48a2777053b75 by Derek Mauro : Internal change PiperOrigin-RevId: 365830995 -- 298f2de7061f21fb94b4bc715bbadef37732d1fa by Derek Mauro : Internal change PiperOrigin-RevId: 365830862 -- 540c6f4b8f4443d3bb969e566761760813b17545 by Derek Mauro : Upgrade MacOS CI to use Bazel 3.7.0 PiperOrigin-RevId: 365825990 -- 8b09ef04a1061038a982c3ff29dc7c8a28364f5f by Abseil Team : internal change PiperOrigin-RevId: 365819242 GitOrigin-RevId: 45c3adace0020bb705e04d254f7b3f914d2ba2e6 Change-Id: I3b2bebd212a41d0c0dda56fefb455240408cc461 --- absl/status/status_test.cc | 8 +- absl/strings/cord_ring_test.cc | 197 ++--------------------------------------- ci/macos_xcode_bazel.sh | 2 +- 3 files changed, 16 insertions(+), 191 deletions(-) diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc index 0e1a43ce..1b038f6d 100644 --- a/absl/status/status_test.cc +++ b/absl/status/status_test.cc @@ -36,7 +36,9 @@ TEST(StatusCode, InsertionOperator) { // its creator, and its classifier. struct ErrorTest { absl::StatusCode code; - using Creator = absl::Status (*)(absl::string_view); + using Creator = absl::Status (*)( + absl::string_view + ); using Classifier = bool (*)(const absl::Status&); Creator creator; Classifier classifier; @@ -78,7 +80,9 @@ TEST(Status, CreateAndClassify) { // expected error code and message. std::string message = absl::StrCat("error code ", test.code, " test message"); - absl::Status status = test.creator(message); + absl::Status status = test.creator( + message + ); EXPECT_EQ(test.code, status.code()); EXPECT_EQ(message, status.message()); diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc index 3838dfe4..2943cf38 100644 --- a/absl/strings/cord_ring_test.cc +++ b/absl/strings/cord_ring_test.cc @@ -31,9 +31,6 @@ extern thread_local bool cord_ring; -// TOOD(b/177688959): weird things happened with the original test -#define ASAN_BUG_177688959_FIXED false - namespace absl { ABSL_NAMESPACE_BEGIN namespace { @@ -340,19 +337,15 @@ std::string TestParamToString(const testing::TestParamInfo& info) { class CordRingTest : public testing::Test { public: ~CordRingTest() override { -#if ASAN_BUG_177688959_FIXED for (CordRep* rep : unrefs_) { CordRep::Unref(rep); } -#endif } template CordRepType* NeedsUnref(CordRepType* rep) { assert(rep); -#if ASAN_BUG_177688959_FIXED unrefs_.push_back(rep); -#endif return rep; } @@ -362,26 +355,16 @@ class CordRingTest : public testing::Test { return NeedsUnref(rep); } - void Unref(CordRep* rep) { -#if !ASAN_BUG_177688959_FIXED - CordRep::Unref(rep); -#endif - } - private: -#if ASAN_BUG_177688959_FIXED std::vector unrefs_; -#endif }; class CordRingTestWithParam : public testing::TestWithParam { public: ~CordRingTestWithParam() override { -#if ASAN_BUG_177688959_FIXED for (CordRep* rep : unrefs_) { CordRep::Unref(rep); } -#endif } CordRepRing* CreateWithCapacity(CordRep* child, size_t extra_capacity) { @@ -400,9 +383,7 @@ class CordRingTestWithParam : public testing::TestWithParam { template CordRepType* NeedsUnref(CordRepType* rep) { assert(rep); -#if ASAN_BUG_177688959_FIXED unrefs_.push_back(rep); -#endif return rep; } @@ -412,43 +393,23 @@ class CordRingTestWithParam : public testing::TestWithParam { return NeedsUnref(rep); } - void Unref(CordRep* rep) { -#if !ASAN_BUG_177688959_FIXED - CordRep::Unref(rep); -#endif - } - template CordRepType* RefIfShared(CordRepType* rep) { return Shared() ? Ref(rep) : rep; } - void UnrefIfShared(CordRep* rep) { - if (Shared()) Unref(rep); - } - template CordRepType* RefIfInputShared(CordRepType* rep) { return InputShared() ? Ref(rep) : rep; } - void UnrefIfInputShared(CordRep* rep) { - if (InputShared()) Unref(rep); - } - template CordRepType* RefIfInputSharedIndirect(CordRepType* rep) { return InputSharedIndirect() ? Ref(rep) : rep; } - void UnrefIfInputSharedIndirect(CordRep* rep) { - if (InputSharedIndirect()) Unref(rep); - } - private: -#if ASAN_BUG_177688959_FIXED std::vector unrefs_; -#endif }; class CordRingCreateTest : public CordRingTestWithParam { @@ -520,26 +481,26 @@ class CordRingBuildInputTest : public CordRingTestWithParam { } }; -INSTANTIATE_TEST_CASE_P(WithParam, CordRingSubTest, - testing::ValuesIn(CordRingSubTest::CreateTestParams()), - TestParamToString); +INSTANTIATE_TEST_SUITE_P(WithParam, CordRingSubTest, + testing::ValuesIn(CordRingSubTest::CreateTestParams()), + TestParamToString); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( WithParam, CordRingCreateTest, testing::ValuesIn(CordRingCreateTest::CreateTestParams()), TestParamToString); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( WithParam, CordRingCreateFromTreeTest, testing::ValuesIn(CordRingCreateFromTreeTest::CreateTestParams()), TestParamToString); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( WithParam, CordRingBuildTest, testing::ValuesIn(CordRingBuildTest::CreateTestParams()), TestParamToString); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( WithParam, CordRingBuildInputTest, testing::ValuesIn(CordRingBuildInputTest::CreateTestParams()), TestParamToString); @@ -550,7 +511,6 @@ TEST_P(CordRingCreateTest, CreateFromFlat) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(str1.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str1)); - Unref(result); } TEST_P(CordRingCreateTest, CreateFromRing) { @@ -559,8 +519,6 @@ TEST_P(CordRingCreateTest, CreateFromRing) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringRing) { @@ -570,23 +528,20 @@ TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringRing) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfInputPrivate(GetParam(), ring)); EXPECT_THAT(ToString(result), string_view(kFox).substr(2, 11)); - UnrefIfInputSharedIndirect(ring); - UnrefIfInputShared(sub); - Unref(result); } TEST_F(CordRingTest, CreateWithIllegalExtraCapacity) { - CordRep* flat = NeedsUnref(MakeFlat("Hello world")); #if defined(ABSL_HAVE_EXCEPTIONS) + CordRep* flat = NeedsUnref(MakeFlat("Hello world")); try { CordRepRing::Create(flat, CordRepRing::kMaxCapacity); GTEST_FAIL() << "expected std::length_error exception"; } catch (const std::length_error&) { } #elif defined(GTEST_HAS_DEATH_TEST) + CordRep* flat = NeedsUnref(MakeFlat("Hello world")); EXPECT_DEATH(CordRepRing::Create(flat, CordRepRing::kMaxCapacity), ".*"); #endif - Unref(flat); } TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfFlat) { @@ -597,9 +552,6 @@ TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfFlat) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(20)); EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(4, 20))); - Unref(result); - UnrefIfInputShared(flat); - UnrefIfInputSharedIndirect(child); } TEST_P(CordRingCreateTest, CreateFromExternal) { @@ -609,8 +561,6 @@ TEST_P(CordRingCreateTest, CreateFromExternal) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(str1.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str1)); - Unref(result); - UnrefIfInputShared(child); } TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfExternal) { @@ -621,9 +571,6 @@ TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfExternal) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(24)); EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(1, 24))); - Unref(result); - UnrefIfInputShared(external); - UnrefIfInputSharedIndirect(child); } TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) { @@ -637,9 +584,6 @@ TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(str.size())); EXPECT_THAT(ToRawFlats(result), ElementsAre(str)); - Unref(result); - UnrefIfInputShared(external); - UnrefIfInputSharedIndirect(child); } TEST_P(CordRingBuildInputTest, CreateFromConcat) { @@ -652,10 +596,6 @@ TEST_P(CordRingBuildInputTest, CreateFromConcat) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(26)); EXPECT_THAT(ToString(result), Eq(kAlphabet)); - UnrefIfInputSharedIndirect(flats[0]); - UnrefIfInputSharedIndirect(flats[3]); - UnrefIfInputShared(concat); - Unref(result); } TEST_P(CordRingBuildInputTest, CreateFromSubstringConcat) { @@ -671,10 +611,6 @@ TEST_P(CordRingBuildInputTest, CreateFromSubstringConcat) { ASSERT_THAT(result, IsValidRingBuffer()); ASSERT_THAT(result->length, Eq(len)); ASSERT_THAT(ToString(result), string_view(kAlphabet).substr(off, len)); - UnrefIfInputSharedIndirect(flats[0]); - UnrefIfInputSharedIndirect(flats[3]); - UnrefIfInputShared(child); - Unref(result); } } } @@ -689,7 +625,6 @@ TEST_P(CordRingCreateTest, Properties) { EXPECT_THAT(result->capacity(), Le(2 * 120 + 1)); EXPECT_THAT(result->entries(), Eq(1)); EXPECT_THAT(result->begin_pos(), Eq(0)); - Unref(result); } TEST_P(CordRingCreateTest, EntryForNewFlat) { @@ -700,7 +635,6 @@ TEST_P(CordRingCreateTest, EntryForNewFlat) { EXPECT_THAT(result->entry_child(0), Eq(child)); EXPECT_THAT(result->entry_end_pos(0), Eq(str1.length())); EXPECT_THAT(result->entry_data_offset(0), Eq(0)); - Unref(result); } TEST_P(CordRingCreateTest, EntryForNewFlatSubstring) { @@ -712,7 +646,6 @@ TEST_P(CordRingCreateTest, EntryForNewFlatSubstring) { EXPECT_THAT(result->entry_child(0), Eq(child)); EXPECT_THAT(result->entry_end_pos(0), Eq(26)); EXPECT_THAT(result->entry_data_offset(0), Eq(10)); - Unref(result); } TEST_P(CordRingBuildTest, AppendFlat) { @@ -724,8 +657,6 @@ TEST_P(CordRingBuildTest, AppendFlat) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, PrependFlat) { @@ -737,8 +668,6 @@ TEST_P(CordRingBuildTest, PrependFlat) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendString) { @@ -750,8 +679,6 @@ TEST_P(CordRingBuildTest, AppendString) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendStringHavingExtra) { @@ -762,8 +689,6 @@ TEST_P(CordRingBuildTest, AppendStringHavingExtra) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) { @@ -790,8 +715,6 @@ TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) { } else { EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); } - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) { @@ -808,8 +731,6 @@ TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) { } else { EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2)); } - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) { @@ -839,8 +760,6 @@ TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(result->length, Eq(4 + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2)); - UnrefIfShared(ring); - Unref(result); CordRep::Unref(shared_type == 1 ? flat1 : flat); } @@ -857,8 +776,6 @@ TEST_P(CordRingBuildTest, AppendStringWithExtra) { EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size())); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre(str1, StrCat(str2, str3))); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, PrependString) { @@ -875,8 +792,6 @@ TEST_P(CordRingBuildTest, PrependString) { } EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, PrependStringHavingExtra) { @@ -893,8 +808,6 @@ TEST_P(CordRingBuildTest, PrependStringHavingExtra) { } else { EXPECT_THAT(ToFlats(result), ElementsAre(str2, "1234")); } - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) { @@ -921,8 +834,6 @@ TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) { EXPECT_THAT(result->length, Eq(str1a.size() + str2.size())); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a)); - UnrefIfShared(ring); - Unref(result); CordRep::Unref(shared_type == 1 ? flat1 : flat); } } @@ -938,8 +849,6 @@ TEST_P(CordRingBuildTest, PrependStringWithExtra) { EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size())); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str3, str2), str1)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendPrependStringMix) { @@ -950,12 +859,10 @@ TEST_P(CordRingBuildTest, AppendPrependStringMix) { result = CordRepRing::Prepend(result, flats[4 - i]); result = CordRepRing::Append(result, flats[4 + i]); } - UnrefIfShared(ring); NeedsUnref(result); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToString(result), kFox); - Unref(result); } TEST_P(CordRingBuildTest, AppendPrependStringMixWithExtra) { @@ -976,8 +883,6 @@ TEST_P(CordRingBuildTest, AppendPrependStringMixWithExtra) { EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ", "over the lazy dog")); } - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendPrependStringMixWithPrependedExtra) { @@ -998,8 +903,6 @@ TEST_P(CordRingBuildTest, AppendPrependStringMixWithPrependedExtra) { EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ", "over the lazy dog")); } - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingSubTest, SubRing) { @@ -1011,7 +914,6 @@ TEST_P(CordRingSubTest, SubRing) { CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); CordRepRing* result = CordRepRing::SubRing(ring, offset, 0); EXPECT_THAT(result, nullptr); - UnrefIfShared(ring); for (size_t len = 1; len < all.size() - offset; ++len) { ring = RefIfShared(FromFlats(flats, composition)); @@ -1019,8 +921,6 @@ TEST_P(CordRingSubTest, SubRing) { ASSERT_THAT(result, IsValidRingBuffer()); ASSERT_THAT(result, EqIfPrivate(GetParam(), ring)); ASSERT_THAT(ToString(result), Eq(all.substr(offset, len))); - UnrefIfShared(ring); - Unref(result); } } } @@ -1039,7 +939,6 @@ TEST_P(CordRingSubTest, SubRingFromLargeExternal) { CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); CordRepRing* result = CordRepRing::SubRing(ring, offset, 0); EXPECT_THAT(result, nullptr); - UnrefIfShared(ring); for (size_t len = all.size() - 30; len < all.size() - offset; ++len) { ring = RefIfShared(FromFlats(flats, composition)); @@ -1049,8 +948,6 @@ TEST_P(CordRingSubTest, SubRingFromLargeExternal) { auto str = ToString(result); ASSERT_THAT(str, SizeIs(len)); ASSERT_THAT(str, Eq(all.substr(offset, len))); - UnrefIfShared(ring); - Unref(result); } } } @@ -1063,7 +960,6 @@ TEST_P(CordRingSubTest, RemovePrefix) { CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); CordRepRing* result = CordRepRing::RemovePrefix(ring, all.size()); EXPECT_THAT(result, nullptr); - UnrefIfShared(ring); for (size_t len = 1; len < all.size(); ++len) { ring = RefIfShared(FromFlats(flats, composition)); @@ -1071,8 +967,6 @@ TEST_P(CordRingSubTest, RemovePrefix) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); EXPECT_THAT(ToString(result), Eq(all.substr(len))); - UnrefIfShared(ring); - Unref(result); } } @@ -1087,7 +981,6 @@ TEST_P(CordRingSubTest, RemovePrefixFromLargeExternal) { ElementsAre( not_a_string_view(external1->base, 1 << 20).remove_prefix(1 << 16), not_a_string_view(external2->base, 1 << 20))); - Unref(result); } TEST_P(CordRingSubTest, RemoveSuffix) { @@ -1098,7 +991,6 @@ TEST_P(CordRingSubTest, RemoveSuffix) { CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); CordRepRing* result = CordRepRing::RemoveSuffix(ring, all.size()); EXPECT_THAT(result, nullptr); - UnrefIfShared(ring); for (size_t len = 1; len < all.size(); ++len) { ring = RefIfShared(FromFlats(flats, composition)); @@ -1106,8 +998,6 @@ TEST_P(CordRingSubTest, RemoveSuffix) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); EXPECT_THAT(ToString(result), Eq(all.substr(0, all.size() - len))); - UnrefIfShared(ring); - Unref(result); } } @@ -1121,8 +1011,6 @@ TEST_P(CordRingSubTest, AppendRing) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) { @@ -1137,9 +1025,6 @@ TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ", "over ", "the ", "lazy ", "dog")); - UnrefIfInputSharedIndirect(child); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) { @@ -1154,9 +1039,6 @@ TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog")); - UnrefIfInputSharedIndirect(child); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) { @@ -1171,9 +1053,6 @@ TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ", "fox ", "jumps ", "over ", "the ")); - UnrefIfInputSharedIndirect(child); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) { @@ -1188,9 +1067,6 @@ TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ", "fox ", "jumps ", "ov")); - UnrefIfInputSharedIndirect(child); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendRingMiddlePiece) { @@ -1205,9 +1081,6 @@ TEST_P(CordRingBuildTest, AppendRingMiddlePiece) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "ck ", "brown ", "fox ", "jum")); - UnrefIfInputSharedIndirect(child); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildTest, AppendRingSinglePiece) { @@ -1221,10 +1094,6 @@ TEST_P(CordRingBuildTest, AppendRingSinglePiece) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row")); - UnrefIfInputSharedIndirect(child); - UnrefIfInputShared(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) { @@ -1242,10 +1111,6 @@ TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row")); - UnrefIfInputSharedIndirect(child); - UnrefIfInputShared(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRing) { @@ -1259,9 +1124,6 @@ TEST_P(CordRingBuildInputTest, PrependRing) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); - UnrefIfInputShared(child); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) { @@ -1276,10 +1138,6 @@ TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ", "the ", "lazy ", "dog", "Tail")); - UnrefIfInputShared(child); - UnrefIfInputSharedIndirect(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) { @@ -1293,10 +1151,6 @@ TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail")); - UnrefIfInputShared(child); - UnrefIfInputSharedIndirect(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) { @@ -1311,10 +1165,6 @@ TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ", "jumps ", "over ", "the ", "Tail")); - UnrefIfShared(ring); - UnrefIfInputShared(child); - UnrefIfInputSharedIndirect(stripped); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) { @@ -1329,10 +1179,6 @@ TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ", "jumps ", "ov", "Tail")); - UnrefIfInputShared(child); - UnrefIfInputSharedIndirect(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) { @@ -1348,10 +1194,6 @@ TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) { EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("ck ", "brown ", "fox ", "jum", "Tail")); - UnrefIfInputShared(child); - UnrefIfInputSharedIndirect(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) { @@ -1365,10 +1207,6 @@ TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail")); - UnrefIfInputShared(child); - UnrefIfInputSharedIndirect(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) { @@ -1385,10 +1223,6 @@ TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail")); - UnrefIfInputShared(child); - UnrefIfInputSharedIndirect(stripped); - UnrefIfShared(ring); - Unref(result); } TEST_F(CordRingTest, Find) { @@ -1406,7 +1240,6 @@ TEST_F(CordRingTest, Find) { ASSERT_THAT(found.offset, Lt(data.length())); ASSERT_THAT(data[found.offset], Eq(value[i])); } - Unref(ring); } TEST_F(CordRingTest, FindWithHint) { @@ -1442,7 +1275,6 @@ TEST_F(CordRingTest, FindWithHint) { ++flat_pos; flat_offset += flat.length(); } - Unref(ring); } TEST_F(CordRingTest, FindInLargeRing) { @@ -1464,7 +1296,6 @@ TEST_F(CordRingTest, FindInLargeRing) { ASSERT_THAT(pos.offset, Lt(data.length())); ASSERT_THAT(data[pos.offset], Eq(value[i])); } - Unref(ring); } TEST_F(CordRingTest, FindTail) { @@ -1483,7 +1314,6 @@ TEST_F(CordRingTest, FindTail) { ASSERT_THAT(pos.offset, Lt(data.length())); ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i])); } - Unref(ring); } TEST_F(CordRingTest, FindTailWithHint) { @@ -1510,7 +1340,6 @@ TEST_F(CordRingTest, FindTailWithHint) { ASSERT_THAT(pos.offset, Lt(data.length())); ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i])); } - Unref(ring); } TEST_F(CordRingTest, FindTailInLargeRing) { @@ -1532,7 +1361,6 @@ TEST_F(CordRingTest, FindTailInLargeRing) { ASSERT_THAT(pos.offset, Lt(data.length())); ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i])); } - Unref(ring); } TEST_F(CordRingTest, GetCharacter) { @@ -1544,7 +1372,6 @@ TEST_F(CordRingTest, GetCharacter) { for (int i = 0; i < value.length(); ++i) { ASSERT_THAT(result->GetCharacter(i), Eq(value[i])); } - Unref(result); } TEST_F(CordRingTest, GetCharacterWithSubstring) { @@ -1556,7 +1383,6 @@ TEST_F(CordRingTest, GetCharacterWithSubstring) { for (int i = 0; i < value.length(); ++i) { ASSERT_THAT(result->GetCharacter(i), Eq(value[i])); } - Unref(result); } TEST_F(CordRingTest, IsFlatSingleFlat) { @@ -1581,8 +1407,6 @@ TEST_F(CordRingTest, IsFlatSingleFlat) { EXPECT_THAT(fragment, Eq("ello")); EXPECT_TRUE(ring->IsFlat(6, 5, &fragment)); EXPECT_THAT(fragment, Eq("world")); - - Unref(ring); } } @@ -1619,8 +1443,6 @@ TEST_F(CordRingTest, IsFlatMultiFlat) { EXPECT_FALSE(ring->IsFlat(1, 18, &fragment)); EXPECT_FALSE(ring->IsFlat(10, 2, &fragment)); EXPECT_THAT(fragment, Eq("Don't touch this")); - - Unref(ring); } } @@ -1629,7 +1451,6 @@ TEST_F(CordRingTest, Dump) { auto flats = MakeSpan(kFoxFlats); CordRepRing* ring = NeedsUnref(FromFlats(flats, kPrepend)); ss << *ring; - Unref(ring); } } // namespace diff --git a/ci/macos_xcode_bazel.sh b/ci/macos_xcode_bazel.sh index 738adf94..9e14e660 100755 --- a/ci/macos_xcode_bazel.sh +++ b/ci/macos_xcode_bazel.sh @@ -24,7 +24,7 @@ if [[ -z ${ABSEIL_ROOT:-} ]]; then fi # If we are running on Kokoro, check for a versioned Bazel binary. -KOKORO_GFILE_BAZEL_BIN="bazel-2.0.0-darwin-x86_64" +KOKORO_GFILE_BAZEL_BIN="bazel-3.7.0-darwin-x86_64" if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}" chmod +x ${BAZEL_BIN} -- cgit v1.2.3 From 2faed9dd2a3722eda068ad88df3b4071f5c5588d Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 31 Mar 2021 20:42:07 -0700 Subject: Export of internal Abseil changes -- 6cfac39ea1266f01f195de5eb4c9a6fc9ea9b20a by Matt Kulukundis : Fix a typo PiperOrigin-RevId: 366174890 -- 6ee8c58647aef171d394e59fa06f9bf8cd0306ec by Derek Mauro : Adds `ABSL_ATTRIBUTE_LIFETIME_BOUND` and applies it to the `const std::string&` constructor of `absl::string_view`. Compilers that support this attribute will emit a warning if the parameter does not have sufficient lifetime. PiperOrigin-RevId: 366027738 -- b944427d96e4b436b8fa0fe396c2a1118dbbbd13 by Abseil Team : Calls to `ResetToEmpty()` from `ClearSlow` use ~3% of the time in assignment. However, `ClearSlow()` is only used in contexts where `data_` is immediately reassigned. Rename `ClearSlow()` into `UnrefTree()` and remove `data_` resetting. PiperOrigin-RevId: 365977213 -- 7428b3147a5672c8bb55649efa3a1cfe19b52a8b by Abseil Team : Fix CordRepRing diabolical growth The 'Mutable' function in CordRepRing was over-eager in doubling capacity, which lead to 'ludicrous' growth in the diabolical test case as added to cord_test. This CL fixes the doubling for growing shared reps, and tempers CordRepRing growth for non shared capacity to 1.5 instead of 2, which is more inline with a conservative growth we also have in tree cord. After this change, CordRepRing no longer swamps the heap into the shadow realm, and is in effect reducing the memory used compared to the tree implementation. With a diabolical 5000 bytes growth pattern: Tree cord: 1523520 bytes Ring cord: 274232 bytes PiperOrigin-RevId: 365915857 -- f24d4aee48b03c5a7980664df903f947cbb198e8 by Andy Getzendanner : Import of CCTZ from GitHub. PiperOrigin-RevId: 365873932 GitOrigin-RevId: 6cfac39ea1266f01f195de5eb4c9a6fc9ea9b20a Change-Id: I9737aa215ac732c9785a1d0032c77aba62330f12 --- absl/base/attributes.h | 22 ++++++++++++++++++++++ absl/hash/internal/wyhash.h | 2 +- absl/strings/cord.cc | 5 ++--- absl/strings/cord.h | 6 +++--- absl/strings/cord_test.cc | 20 ++++++++++++++++++++ absl/strings/internal/cord_rep_ring.cc | 5 +++-- absl/strings/string_view.h | 5 +++-- absl/time/internal/cctz/src/time_zone_fixed.cc | 2 +- .../internal/cctz/src/time_zone_lookup_test.cc | 12 ++++++++++++ 9 files changed, 67 insertions(+), 12 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 325e2f8d..294e5d0c 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -706,4 +706,26 @@ #define ABSL_ATTRIBUTE_PURE_FUNCTION #endif +// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function +// parameter or implicit object parameter is retained by the return value of the +// annotated function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +#elif ABSL_HAVE_ATTRIBUTE(lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +#else +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + #endif // ABSL_BASE_ATTRIBUTES_H_ diff --git a/absl/hash/internal/wyhash.h b/absl/hash/internal/wyhash.h index 4aff4e93..2b534b47 100644 --- a/absl/hash/internal/wyhash.h +++ b/absl/hash/internal/wyhash.h @@ -36,7 +36,7 @@ namespace hash_internal { // integers are hashed into the result. // // To allow all hashable types (including string_view and Span) to depend on -// this algoritm, we keep the API low-level, with as few dependencies as +// this algorithm, we keep the API low-level, with as few dependencies as // possible. uint64_t Wyhash(const void* data, size_t len, uint64_t seed, const uint64_t salt[5]); 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() 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(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 #include +#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 string_view( // NOLINT(runtime/explicit) - const std::basic_string, Allocator>& - str) noexcept + const std::basic_string, 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()) {} diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc index 303c0244..f2b3294e 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.cc +++ b/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -53,7 +53,7 @@ int Parse02d(const char* p) { } // namespace bool FixedOffsetFromName(const std::string& name, seconds* offset) { - if (name.compare(0, std::string::npos, "UTC", 3) == 0) { + if (name == "UTC" || name == "UTC0") { *offset = seconds::zero(); return true; } diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 9a1a8d6e..6948c3ea 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -717,6 +717,18 @@ TEST(TimeZones, LoadZonesConcurrently) { } #endif +TEST(TimeZone, UTC) { + const time_zone utc = utc_time_zone(); + + time_zone loaded_utc; + EXPECT_TRUE(load_time_zone("UTC", &loaded_utc)); + EXPECT_EQ(loaded_utc, utc); + + time_zone loaded_utc0; + EXPECT_TRUE(load_time_zone("UTC0", &loaded_utc0)); + EXPECT_EQ(loaded_utc0, utc); +} + TEST(TimeZone, NamedTimeZones) { const time_zone utc = utc_time_zone(); EXPECT_EQ("UTC", utc.name()); -- cgit v1.2.3 From 354030bec37f8d90092245e07323628da50c6996 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 1 Apr 2021 08:22:23 -0700 Subject: Export of internal Abseil changes -- c207f164477b5c7f0cb1c5c8bfdc1430b457da17 by Martijn Vels : Add a 'node()' property to CordRepRingReader. PiperOrigin-RevId: 366254778 GitOrigin-RevId: c207f164477b5c7f0cb1c5c8bfdc1430b457da17 Change-Id: I90478d151c2ab5d2ceed6de9fb4946a6a4b48f32 --- absl/strings/cord_ring_reader_test.cc | 13 ++++++++++--- absl/strings/internal/cord_rep_ring_reader.h | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/absl/strings/cord_ring_reader_test.cc b/absl/strings/cord_ring_reader_test.cc index 585616f3..d9a9a76d 100644 --- a/absl/strings/cord_ring_reader_test.cc +++ b/absl/strings/cord_ring_reader_test.cc @@ -78,6 +78,7 @@ TEST(CordRingReaderTest, Reset) { EXPECT_TRUE(static_cast(reader)); EXPECT_THAT(reader.ring(), Eq(ring)); EXPECT_THAT(reader.index(), Eq(ring->head())); + EXPECT_THAT(reader.node(), Eq(ring->entry_child(ring->head()))); EXPECT_THAT(reader.length(), Eq(ring->length)); EXPECT_THAT(reader.consumed(), Eq(flats[0].length())); EXPECT_THAT(reader.remaining(), Eq(ring->length - reader.consumed())); @@ -99,11 +100,13 @@ TEST(CordRingReaderTest, Next) { size_t consumed = reader.consumed(); size_t remaining = reader.remaining(); for (int i = 1; i < flats.size(); ++i) { + CordRepRing::index_type index = ring->advance(head, i); consumed += flats[i].length(); remaining -= flats[i].length(); absl::string_view next = reader.Next(); ASSERT_THAT(next, Eq(flats[i])); - ASSERT_THAT(reader.index(), Eq(ring->advance(head, i))); + ASSERT_THAT(reader.index(), Eq(index)); + ASSERT_THAT(reader.node(), Eq(ring->entry_child(index))); ASSERT_THAT(reader.consumed(), Eq(consumed)); ASSERT_THAT(reader.remaining(), Eq(remaining)); } @@ -125,13 +128,15 @@ TEST(CordRingReaderTest, SeekForward) { size_t consumed = 0; size_t remaining = ring->length;; for (int i = 0; i < flats.size(); ++i) { + CordRepRing::index_type index = ring->advance(head, i); size_t offset = consumed; consumed += flats[i].length(); remaining -= flats[i].length(); for (int off = 0; off < flats[i].length(); ++off) { absl::string_view chunk = reader.Seek(offset + off); ASSERT_THAT(chunk, Eq(flats[i].substr(off))); - ASSERT_THAT(reader.index(), Eq(ring->advance(head, i))); + ASSERT_THAT(reader.index(), Eq(index)); + ASSERT_THAT(reader.node(), Eq(ring->entry_child(index))); ASSERT_THAT(reader.consumed(), Eq(consumed)); ASSERT_THAT(reader.remaining(), Eq(remaining)); } @@ -150,11 +155,13 @@ TEST(CordRingReaderTest, SeekBackward) { size_t consumed = ring->length; size_t remaining = 0; for (int i = flats.size() - 1; i >= 0; --i) { + CordRepRing::index_type index = ring->advance(head, i); size_t offset = consumed - flats[i].length(); for (int off = 0; off < flats[i].length(); ++off) { absl::string_view chunk = reader.Seek(offset + off); ASSERT_THAT(chunk, Eq(flats[i].substr(off))); - ASSERT_THAT(reader.index(), Eq(ring->advance(head, i))); + ASSERT_THAT(reader.index(), Eq(index)); + ASSERT_THAT(reader.node(), Eq(ring->entry_child(index))); ASSERT_THAT(reader.consumed(), Eq(consumed)); ASSERT_THAT(reader.remaining(), Eq(remaining)); } diff --git a/absl/strings/internal/cord_rep_ring_reader.h b/absl/strings/internal/cord_rep_ring_reader.h index 396c0e2c..7ceeaa00 100644 --- a/absl/strings/internal/cord_rep_ring_reader.h +++ b/absl/strings/internal/cord_rep_ring_reader.h @@ -40,6 +40,10 @@ class CordRepRingReader { // The returned value is undefined if this instance is empty. CordRepRing::index_type index() const { return index_; } + // Returns the current node inside the ring buffer for this instance. + // The returned value is undefined if this instance is empty. + CordRep* node() const { return ring_->entry_child(index_); } + // Returns the length of the referenced ring buffer. // Requires the current instance to be non empty. size_t length() const { -- cgit v1.2.3 From 7f4d0ba0be4c70fd71eb4c08a6422c8c2f7faa0b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 5 Apr 2021 09:52:04 -0700 Subject: Export of internal Abseil changes -- 51798d2ac9c95ee8955955c5d8d78b4c9592ecf7 by Derek Mauro : Correctly install pkgconfig files under CMAKE_INSTALL_LIBDIR Fixes #931 PiperOrigin-RevId: 366816645 -- fbaad678b54dae70e4cd9f4442b4fef9efb71b30 by Abseil Team : Add a kDefaultStatusToStringMode representing the default StatusToStringMode. PiperOrigin-RevId: 366478305 -- 25b4be6a591e8b25338a00be1273cd6cae6b0165 by Evan Brown : Fix a typo in a comment. PiperOrigin-RevId: 366464483 -- 6add48ed8f633c219f02c6ef8af876f8dbaa9955 by Abseil Team : Update comment to mention absl::container_literal::Layout which is what is used instead of gtl::Layout. PiperOrigin-RevId: 366452345 GitOrigin-RevId: 51798d2ac9c95ee8955955c5d8d78b4c9592ecf7 Change-Id: I8e68bc55d81445b2f6f707943fed9075cd402844 --- CMake/AbseilHelpers.cmake | 2 +- absl/container/internal/btree.h | 4 ++-- absl/status/status.h | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 4f6394ab..1f754392 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -181,7 +181,7 @@ Requires:${PC_DEPS}\n\ Libs: -L\${libdir} $ $<$>:-labsl_${_NAME}>\n\ Cflags: -I\${includedir}${PC_CFLAGS}\n") INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" - DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") endif() endif() diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index 0bb38366..d372a1d6 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -484,8 +484,8 @@ class btree_node { std::is_same, key_compare>::value)>; - // This class is organized by gtl::Layout as if it had the following - // structure: + // This class is organized by absl::container_internal::Layout as if it had + // the following structure: // // A pointer to the node's parent. // btree_node *parent; // diff --git a/absl/status/status.h b/absl/status/status.h index 61486fee..2e05f46e 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -293,6 +293,8 @@ enum class StatusToStringMode : int { kWithPayload = 1 << 0, // ToString will include all the extra data this Status has. kWithEverything = ~kWithNoExtraData, + // Default mode used by ToString. Its exact value might change in the future. + kDefault = kWithPayload, }; // absl::StatusToStringMode is specified as a bitmask type, which means the @@ -509,7 +511,7 @@ class Status final { // result, and the payloads to be printed use the status payload printer // mechanism (which is internal). std::string ToString( - StatusToStringMode mode = StatusToStringMode::kWithPayload) const; + StatusToStringMode mode = StatusToStringMode::kDefault) const; // Status::IgnoreError() // -- cgit v1.2.3 From 3b4a16abad2c2ddc494371cc39a2946e36d35d11 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 6 Apr 2021 13:57:39 -0700 Subject: Export of internal Abseil changes -- 8e3ed8c8f770f9371f037e4956334b6e38525e91 by Abseil Team : Remove legacy, non-standard sanitizer macros. PiperOrigin-RevId: 367079577 -- c06aa9b9c20ba2e87dfc6933bbf8a30e0502d70a by Martijn Vels : Remove kFakeCordzInfo and last caller to set_profiled() These have all been superseded by clear_cordz_info() and set_cordz_info() PiperOrigin-RevId: 367020835 -- e0bd3e4e0b46db495663261a1664815eb94999f8 by Abseil Team : AbseilConfigureCopts.cmake: fix AppleClang detection restore use of MATCHES in comparison with "Clang"; this was lost in: commit 22771d471930ce88e1e75d0ca9dd8c65a7b0f895 ... 24e1f5f72756046f5265abf618e951c341f09b8d by Derek Mauro : Fixes failing CMake string comparisons https://cmake.org/cmake/help/latest/policy/CMP0054.html fixes: CMake Warning at absl/copts/AbseilConfigureCopts.cmake:61 (message): Unknown compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++. Building with no default flags Fixes #930 PiperOrigin-RevId: 366879337 GitOrigin-RevId: 8e3ed8c8f770f9371f037e4956334b6e38525e91 Change-Id: I7b027b7e82491676b59dc9d23cfe063ea6004d3b --- absl/base/config.h | 12 ------------ absl/copts/AbseilConfigureCopts.cmake | 3 +-- absl/strings/cord.cc | 1 - absl/strings/internal/cord_internal.h | 12 ------------ 4 files changed, 1 insertion(+), 27 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 95449969..7abc75a6 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -690,10 +690,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // a compiler instrumentation module and a run-time library. #ifdef ABSL_HAVE_MEMORY_SANITIZER #error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set." -#elif defined(MEMORY_SANITIZER) -// The MEMORY_SANITIZER macro is deprecated but we will continue to honor it -// for now. -#define ABSL_HAVE_MEMORY_SANITIZER 1 #elif defined(__SANITIZE_MEMORY__) #define ABSL_HAVE_MEMORY_SANITIZER 1 #elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer) @@ -705,10 +701,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // ThreadSanitizer (TSan) is a fast data race detector. #ifdef ABSL_HAVE_THREAD_SANITIZER #error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set." -#elif defined(THREAD_SANITIZER) -// The THREAD_SANITIZER macro is deprecated but we will continue to honor it -// for now. -#define ABSL_HAVE_THREAD_SANITIZER 1 #elif defined(__SANITIZE_THREAD__) #define ABSL_HAVE_THREAD_SANITIZER 1 #elif ABSL_HAVE_FEATURE(thread_sanitizer) @@ -720,10 +712,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // AddressSanitizer (ASan) is a fast memory error detector. #ifdef ABSL_HAVE_ADDRESS_SANITIZER #error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set." -#elif defined(ADDRESS_SANITIZER) -// The ADDRESS_SANITIZER macro is deprecated but we will continue to honor it -// for now. -#define ABSL_HAVE_ADDRESS_SANITIZER 1 #elif defined(__SANITIZE_ADDRESS__) #define ABSL_HAVE_ADDRESS_SANITIZER 1 #elif ABSL_HAVE_FEATURE(address_sanitizer) diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake index 9cd6fd1b..942ce90a 100644 --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -35,8 +35,7 @@ endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}") -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - # MATCHES so we get both Clang and AppleClang +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # MATCHES so we get both Clang and AppleClang if(MSVC) # clang-cl is half MSVC, half LLVM set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}") diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 1aea396f..8f0999f8 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -495,7 +495,6 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { data_ = src.data_; if (is_tree()) { - data_.set_profiled(false); CordRep::Ref(tree()); clear_cordz_info(); } diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index a1ba67fe..01ce70ae 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -335,11 +335,6 @@ class InlineData { // byte of cordz_info overlaps with the last byte holding the tag. static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1); - // kFakeCordzInfo holds a 'fake', non-null cordz-info value we use to - // emulate the previous 'kProfiled' tag logic in 'set_profiled' until - // cord code is changed to store cordz_info values in InlineData. - static constexpr cordz_info_t kFakeCordzInfo = BigEndianByte(9); - constexpr InlineData() : as_chars_{0} {} explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {} explicit constexpr InlineData(absl::string_view chars) @@ -454,13 +449,6 @@ class InlineData { tag() = static_cast(size << 1); } - // Sets or unsets the 'is_profiled' state of this instance. - // Requires the current instance to hold a tree value. - void set_profiled(bool profiled) { - assert(is_tree()); - as_tree_.cordz_info = profiled ? kFakeCordzInfo : kNullCordzInfo; - } - private: // See cordz_info_t for forced alignment and size of `cordz_info` details. struct AsTree { -- cgit v1.2.3 From b97a1ecda869ca8754d467a56c50275cebfeb328 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 9 Apr 2021 08:51:11 -0700 Subject: Export of internal Abseil changes -- 9bd9d083a21d1436816dc842a80d4339aa49a24b by Abseil Team : Inline Status::NewRep, pass `message` via string_view in StatusRep ctor PiperOrigin-RevId: 367641069 -- 9cebe53e8f1717f82394501fd9f4bc70d2051b33 by Benjamin Barenblat : Fix typo in CordRepRing error message PiperOrigin-RevId: 367481280 GitOrigin-RevId: 9bd9d083a21d1436816dc842a80d4339aa49a24b Change-Id: Ie2c51bf6f46abed5c2317ceee30bd2bb59502f8e --- absl/status/internal/status_internal.h | 4 ++-- absl/status/status.cc | 22 +++++++--------------- absl/strings/internal/cord_rep_ring.cc | 2 +- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index 99a2d964..ccafd702 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h @@ -47,11 +47,11 @@ using Payloads = absl::InlinedVector; // Reference-counted representation of Status data. struct StatusRep { - StatusRep(absl::StatusCode code, std::string message, + StatusRep(absl::StatusCode code, absl::string_view message, std::unique_ptr payloads) : ref(int32_t{1}), code(code), - message(std::move(message)), + message(message), payloads(std::move(payloads)) {} std::atomic ref; diff --git a/absl/status/status.cc b/absl/status/status.cc index 51a0d268..5a5cd5c2 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -207,19 +207,10 @@ void Status::UnrefNonInlined(uintptr_t rep) { } } -uintptr_t Status::NewRep( - absl::StatusCode code, absl::string_view msg, - std::unique_ptr payloads) { - status_internal::StatusRep* rep = new status_internal::StatusRep( - code, std::string(msg.data(), msg.size()), - std::move(payloads)); - return PointerToRep(rep); -} - Status::Status(absl::StatusCode code, absl::string_view msg) : rep_(CodeToInlinedRep(code)) { if (code != absl::StatusCode::kOk && !msg.empty()) { - rep_ = NewRep(code, msg, nullptr); + rep_ = PointerToRep(new status_internal::StatusRep(code, msg, nullptr)); } } @@ -238,9 +229,9 @@ absl::StatusCode Status::code() const { void Status::PrepareToModify() { ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status."); if (IsInlined(rep_)) { - rep_ = - NewRep(static_cast(raw_code()), absl::string_view(), - nullptr); + rep_ = PointerToRep(new status_internal::StatusRep( + static_cast(raw_code()), absl::string_view(), + nullptr)); return; } @@ -251,8 +242,9 @@ void Status::PrepareToModify() { if (rep->payloads) { payloads = absl::make_unique(*rep->payloads); } - rep_ = NewRep(rep->code, message(), - std::move(payloads)); + status_internal::StatusRep* const new_rep = new status_internal::StatusRep( + rep->code, message(), std::move(payloads)); + rep_ = PointerToRep(new_rep); UnrefNonInlined(rep_i); } } diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index cb95b19a..09951290 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -301,7 +301,7 @@ bool CordRepRing::IsValid(std::ostream& output) const { if (offset >= child->length || entry_length > child->length - offset) { output << "entry[" << head << "] has offset " << offset << " and entry length " << entry_length - << " which are outside of the childs length of " << child->length; + << " which are outside of the child's length of " << child->length; return false; } -- cgit v1.2.3 From 25bc82d7ef481bd06723aae805d286eba81900ca Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 13 Apr 2021 10:34:23 -0700 Subject: Export of internal Abseil changes -- f09549625d7417fca379ff68334379d281480608 by Martijn Vels : Fix GetNumCPUs() for WIN32 platforms PiperOrigin-RevId: 368245360 -- e903c6690d0568f80356e03b07a895b031553977 by Abseil Team : Optimize `Cord::InlineRep::InlineRep(Cord::InlineRep&& src)`. PiperOrigin-RevId: 368198990 GitOrigin-RevId: f09549625d7417fca379ff68334379d281480608 Change-Id: I2d750c8c87f2804335a60ba8db949dedfaa462e2 --- absl/base/internal/sysinfo.cc | 67 +++++++++++++++++++++++++++++++++++++++++++ absl/strings/cord.h | 3 +- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 4a3b2050..08a1e288 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -61,9 +61,76 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { +namespace { + +#if defined(_WIN32) + +// Returns number of bits set in `bitMask` +DWORD Win32CountSetBits(ULONG_PTR bitMask) { + for (DWORD bitSetCount = 0; ; ++bitSetCount) { + if (bitMask == 0) return bitSetCount; + bitMask &= bitMask - 1; + } +} + +// Returns the number of logical CPUs using GetLogicalProcessorInformation(), or +// 0 if the number of processors is not available or can not be computed. +// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation +int Win32NumCPUs() { +#pragma comment(lib, "kernel32.lib") + using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION; + + DWORD info_size = sizeof(Info); + Info* info(static_cast(malloc(info_size))); + if (info == nullptr) return 0; + + bool success = GetLogicalProcessorInformation(info, &info_size); + if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(info); + info = static_cast(malloc(info_size)); + if (info == nullptr) return 0; + success = GetLogicalProcessorInformation(info, &info_size); + } + + DWORD logicalProcessorCount = 0; + if (success) { + Info* ptr = info; + DWORD byteOffset = 0; + while (byteOffset + sizeof(Info) <= info_size) { + switch (ptr->Relationship) { + case RelationProcessorCore: + logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask); + break; + + case RelationNumaNode: + case RelationCache: + case RelationProcessorPackage: + // Ignore other entries + break; + + default: + // Ignore unknown entries + break; + } + byteOffset += sizeof(Info); + ptr++; + } + } + free(info); + return logicalProcessorCount; +} + +#endif + +} // namespace + + static int GetNumCPUs() { #if defined(__myriad2__) return 1; +#elif defined(_WIN32) + const unsigned hardware_concurrency = Win32NumCPUs(); + return hardware_concurrency ? hardware_concurrency : 1; #else // Other possibilities: // - Read /sys/devices/system/cpu/online and use cpumask_parse() diff --git a/absl/strings/cord.h b/absl/strings/cord.h index cb2ffc5c..7ce938ca 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -946,8 +946,7 @@ inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) } } -inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) { - data_ = src.data_; +inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) : data_(src.data_) { src.ResetToEmpty(); } -- cgit v1.2.3 From db1b7310d7021700b5a7bcea1989b2a625529f40 Mon Sep 17 00:00:00 2001 From: Yasushi Saito Date: Tue, 13 Apr 2021 19:59:46 -0700 Subject: Call FailureSignalHandlerOptions.writenfn with nullptr at the end (#938) * Call FailureSignalHandlerOptions.writenfn with nullptr at the end This behavior has already been alluded to in the document, but it hasn't been implemented. This PR calls changes the failure signal handler to call writerfn(nullptr) at the end of the message block. It also clarifies the documentation. https://github.com/abseil/abseil-cpp/issues/933 * Update failure_signal_handler.h Co-authored-by: Derek Mauro <761129+derekmauro@users.noreply.github.com> --- absl/debugging/failure_signal_handler.cc | 1 + absl/debugging/failure_signal_handler.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index a9ed6ef9..e458a795 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -366,6 +366,7 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { // goes after this point. if (fsh_options.writerfn != nullptr) { WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn); + fsh_options.writerfn(nullptr); } if (fsh_options.call_previous_handler) { diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h index 0c0f585d..500115c0 100644 --- a/absl/debugging/failure_signal_handler.h +++ b/absl/debugging/failure_signal_handler.h @@ -90,7 +90,7 @@ struct FailureSignalHandlerOptions { // If non-null, indicates a pointer to a callback function that will be called // upon failure, with a string argument containing failure data. This function // may be used as a hook to write failure data to a secondary location, such - // as a log file. This function may also be called with null data, as a hint + // as a log file. This function will also be called with null data, as a hint // to flush any buffered data before the program may be terminated. Consider // flushing any buffered data in all calls to this function. // -- cgit v1.2.3 From 46dfbfe31ca1dd414e4c33cbcbcd7199bb4efde3 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 14 Apr 2021 11:19:20 -0700 Subject: Export of internal Abseil changes -- 5ff0c4b38386ae80b25e9f8d0e1bac07fd7ce92c by Martijn Vels : Add CordzUpdateTracker class PiperOrigin-RevId: 368469046 -- 4de916b1ba4b5480b4dbe93d28c5eaa110449c66 by Derek Mauro : Adds `ABSL_ATTRIBUTE_LIFETIME_BOUND` to `absl::Span`s initializer_list constructor. Compilers that support this attribute will emit a warning if the parameter does not have sufficient lifetime. For more information, see https://github.com/abseil/abseil-cpp/blob/b97a1ecda869ca8754d467a56c50275cebfeb328/absl/types/span.h#L209-L248 PiperOrigin-RevId: 368429085 -- bb6669016412bf5afffc02d0818a66dfe1c524cf by Martijn Vels : Reorganize internal cord code in CMakeLists.txt into cord_internal library PiperOrigin-RevId: 368423765 -- d1d7fce066172d5fcfa0310c4e27631d895d7e50 by Derek Mauro : Internal change PiperOrigin-RevId: 368346725 GitOrigin-RevId: 5ff0c4b38386ae80b25e9f8d0e1bac07fd7ce92c Change-Id: Ic4627eab4f0274e400a6d12cde3341fb538de075 --- CMake/AbseilDll.cmake | 1 + absl/strings/BUILD.bazel | 21 ++++ absl/strings/CMakeLists.txt | 75 +++++++++--- absl/strings/internal/cordz_update_tracker.h | 96 +++++++++++++++ absl/strings/internal/cordz_update_tracker_test.cc | 130 +++++++++++++++++++++ absl/types/span.h | 4 +- 6 files changed, 310 insertions(+), 17 deletions(-) create mode 100644 absl/strings/internal/cordz_update_tracker.h create mode 100644 absl/strings/internal/cordz_update_tracker_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 253c73ff..4527ca14 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -203,6 +203,7 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cord_rep_ring.cc" "strings/internal/cord_rep_ring.h" "strings/internal/cord_rep_ring_reader.h" + "strings/internal/cordz_update_tracker.h" "strings/internal/charconv_bigint.cc" "strings/internal/charconv_bigint.h" "strings/internal/charconv_parse.cc" diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 123b5efb..11385163 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -296,6 +296,26 @@ cc_library( ], ) +cc_library( + name = "cordz_update_tracker", + hdrs = ["internal/cordz_update_tracker.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = ["//visibility:private"], + deps = ["//absl/base:config"], +) + +cc_test( + name = "cordz_update_tracker_test", + srcs = ["internal/cordz_update_tracker_test.cc"], + deps = [ + ":cordz_update_tracker", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/synchronization", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "cord", srcs = [ @@ -307,6 +327,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":cord_internal", + ":cordz_update_tracker", ":internal", ":str_format", ":strings", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 3b7ae639..0d93d5ff 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -550,6 +550,59 @@ absl_cc_test( gmock_main ) +absl_cc_library( + NAME + cord_internal + HDRS + "internal/cord_internal.h" + "internal/cord_rep_flat.h" + "internal/cord_rep_ring.h" + "internal/cord_rep_ring_reader.h" + SRCS + "internal/cord_internal.cc" + "internal/cord_rep_ring.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base_internal + absl::compressed_tuple + absl::config + absl::core_headers + absl::endian + absl::inlined_vector + absl::layout + absl::raw_logging_internal + absl::strings + absl::throw_delegate + absl::type_traits +) + +absl_cc_library( + NAME + cordz_update_tracker + HDRS + "internal/cordz_update_tracker.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config +) + +absl_cc_test( + NAME + cordz_update_tracker_test + SRCS + "internal/cordz_update_tracker_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cordz_update_tracker + absl::core_headers + absl::synchronization + gmock_main +) + absl_cc_library( NAME cord @@ -557,19 +610,13 @@ absl_cc_library( "cord.h" SRCS "cord.cc" - "internal/cord_internal.cc" - "internal/cord_internal.h" - "internal/cord_rep_ring.h" - "internal/cord_rep_ring.cc" - "internal/cord_rep_ring_reader.h" - "internal/cord_rep_flat.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS absl::base - absl::base_internal - absl::compressed_tuple absl::config + absl::cord_internal + absl::cordz_update_tracker absl::core_headers absl::endian absl::fixed_array @@ -578,8 +625,6 @@ absl_cc_library( absl::optional absl::raw_logging_internal absl::strings - absl::strings_internal - absl::throw_delegate absl::type_traits PUBLIC ) @@ -624,12 +669,12 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS - absl::config - absl::cord - absl::strings absl::base + absl::config + absl::cord_internal absl::core_headers absl::raw_logging_internal + absl::strings gmock_main ) @@ -641,9 +686,9 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS - absl::cord - absl::strings absl::base + absl::cord_internal absl::core_headers + absl::strings gmock_main ) diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h new file mode 100644 index 00000000..3c617e93 --- /dev/null +++ b/absl/strings/internal/cordz_update_tracker.h @@ -0,0 +1,96 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzUpdateTracker tracks counters for Cord update methods. +// +// The purpose of CordzUpdateTracker is to track the number of calls to methods +// updating Cord data for sampled cords. The class internally uses 'lossy' +// atomic operations: Cord is thread-compatible, so there is no need to +// synchronize updates. However, Cordz collection threads may call 'Value()' at +// any point, so the class needs to provide thread safe access. +// +// This class is thread-safe. But as per above comments, all non-const methods +// should be used single-threaded only: updates are thread-safe but lossy. +class CordzUpdateTracker { + public: + // Tracked update methods. + enum MethodIdentifier { + kAssignString, + kAssignCord, + kMoveAssignCord, + kAppendString, + kAppendCord, + kMoveAppendCord, + kPrependString, + kPrependCord, + kMovePrependCord, + kAppendExternalMemory, + kFlatten, + kGetAppendRegion, + kRemovePrefix, + kRemoveSuffic, + kSubCord, + + // kNumMethods defines the number of entries: must be the last entry. + kNumMethods, + }; + + // Constructs a new instance. All counters are zero-initialized. + constexpr CordzUpdateTracker() noexcept : values_{} {} + + // Copy constructs a new instance. + CordzUpdateTracker(const CordzUpdateTracker& rhs) noexcept { *this = rhs; } + + // Assigns the provided value to this instance. + CordzUpdateTracker& operator=(const CordzUpdateTracker& rhs) noexcept { + for (int i = 0; i < kNumMethods; ++i) { + values_[i].store(rhs.values_[i].load(std::memory_order_relaxed), + std::memory_order_relaxed); + } + return *this; + } + + // Returns the value for the specified method. + int64_t Value(MethodIdentifier method) const { + return values_[method].load(std::memory_order_relaxed); + } + + // Increases the value for the specified method by `n` + void LossyAdd(MethodIdentifier method, int64_t n = 1) { + auto& value = values_[method]; + value.store(value.load(std::memory_order_relaxed) + n, + std::memory_order_relaxed); + } + + private: + std::atomic values_[kNumMethods]; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc new file mode 100644 index 00000000..45782046 --- /dev/null +++ b/absl/strings/internal/cordz_update_tracker_test.cc @@ -0,0 +1,130 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_update_tracker.h" + +#include +#include // NOLINT + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/synchronization/notification.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::AnyOf; +using ::testing::Eq; + +using Method = CordzUpdateTracker::MethodIdentifier; +using Methods = std::array; + +// Returns an array of all methods defined in `MethodIdentifier` +Methods AllMethods() { + return Methods{Method::kAssignString, Method::kAssignCord, + Method::kMoveAssignCord, Method::kAppendString, + Method::kAppendCord, Method::kMoveAppendCord, + Method::kPrependString, Method::kPrependCord, + Method::kMovePrependCord, Method::kAppendExternalMemory, + Method::kFlatten, Method::kGetAppendRegion, + Method::kRemovePrefix, Method::kRemoveSuffic, + Method::kSubCord}; +} + +TEST(CordzUpdateTracker, IsConstExprAndInitializesToZero) { + constexpr CordzUpdateTracker tracker; + for (Method method : AllMethods()) { + ASSERT_THAT(tracker.Value(method), Eq(0)); + } +} + +TEST(CordzUpdateTracker, LossyAdd) { + int64_t n = 1; + CordzUpdateTracker tracker; + for (Method method : AllMethods()) { + tracker.LossyAdd(method, n); + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } +} + +TEST(CordzUpdateTracker, CopyConstructor) { + int64_t n = 1; + CordzUpdateTracker src; + for (Method method : AllMethods()) { + src.LossyAdd(method, n); + n += 2; + } + + n = 1; + CordzUpdateTracker tracker(src); + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } +} + +TEST(CordzUpdateTracker, OperatorAssign) { + int64_t n = 1; + CordzUpdateTracker src; + CordzUpdateTracker tracker; + for (Method method : AllMethods()) { + src.LossyAdd(method, n); + n += 2; + } + + n = 1; + tracker = src; + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } +} + +TEST(CordzUpdateTracker, ThreadSanitizedValueCheck) { + absl::Notification done; + CordzUpdateTracker tracker; + + std::thread reader([&done, &tracker] { + while (!done.HasBeenNotified()) { + int n = 1; + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), AnyOf(Eq(n), Eq(0))); + n += 2; + } + } + int n = 1; + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } + }); + + int64_t n = 1; + for (Method method : AllMethods()) { + tracker.LossyAdd(method, n); + n += 2; + } + done.Notify(); + reader.join(); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/types/span.h b/absl/types/span.h index 95fe7926..41db3420 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -243,8 +243,8 @@ class Span { // template > - Span( - std::initializer_list v) noexcept // NOLINT(runtime/explicit) + Span(std::initializer_list v + ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/explicit) : Span(v.begin(), v.size()) {} // Accessors -- cgit v1.2.3 From e20fe888fabc1fc995dc61180e8a31b5f809a95f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 15 Apr 2021 19:42:51 -0700 Subject: Export of internal Abseil changes -- 341670bce317dd6af8d3c066970230591a47e80c by Martijn Vels : Change GetStack() and GetParentStack() to return absl::Span PiperOrigin-RevId: 368765721 -- 6aaab9536d6957303c7aba100c3afaa6fb0ea2c8 by Martijn Vels : Remove locking from parent stack. This change removes the need to lock all access to `parent_stack' by making the 'copy constructor' logic specify the 'copied from' CordzInfo (where available) to the TrackCord function, after which parent_stack is immutable. PiperOrigin-RevId: 368760630 -- b19e2059cada35a8ede994833018edac94de6ddc by Martijn Vels : Add cordz instrumentation to Cord PiperOrigin-RevId: 368746225 -- 67b8bbf980f0f4e1db79aa32968e9a715a09b51a by Martijn Vels : Create ABSL_INTERNAL_CORDZ_ENABLED define controlling when Cordz code is enabled There are specific builds and condtions under which we don't support cordz sampling, which is per this change represented by ABSL_INTERNAL_CORDZ_ENABLED being defined. PiperOrigin-RevId: 368731603 -- 8cbfe0e3169637a620f4b66ad2bc2ce340879cb0 by Martijn Vels : Add a `rep` property to CordzInfo to be managed by Cord logic. This change adds a `rep` property to CordzInfo, which is intended to be used by collection logic. Mini design: Cord invokes TrackCord() providing the active 'root' cordrep of the newly sampled Cord, returning a CordzInfo with a weak (uncounted) reference to this root. Cord invokes `SetCordRep()` each time the root cordrep of the sampled Cord is updated while holding `mutex()`. Cord must also obtain `mutex()` _before_ removing a reference on the old root. i.e.: Cord must guarantee that the (weak) reference held in CordzInfo is at all times valid. CordzInfo collection code can then safely obtain a (reference counted) rep pointer by adding a reference to `rep_` while holding `mutex()`. This requires only a very brief critical section inside CordzInfo logic, minimizing contention with concurrent Cord updates. Cord code should typically obtain and hold `mutex()` for the entirety of each mutating Cord operation on a sampled cord. As Cord is thread compatible, it never competes on the lock with any other thread. The only possible concurrent access is from Cordz collection code, which should be a relatively rare event. PiperOrigin-RevId: 368673758 -- 1255120dce2bdd6b4205a34a0e555e0b74b6152f by Martijn Vels : Remove 'depth' from active recorded metrics. Going forward we do not 'live' record depth (and size), but will observe these at collection time only. PiperOrigin-RevId: 368636572 -- 83e5146e35f221736b49e9f0a8805f8c159a51db by Martijn Vels : Make cordz targets visible in OSS PiperOrigin-RevId: 368615010 -- dcb16a4f1239151f0a8c70a8cfeb29dabbd113b8 by Martijn Vels : Internal cleanup PiperOrigin-RevId: 368514666 GitOrigin-RevId: 341670bce317dd6af8d3c066970230591a47e80c Change-Id: I94cecfbbd441eb386f99fc5186c468a7a5538862 --- CMake/AbseilDll.cmake | 17 +- absl/strings/BUILD.bazel | 136 ++++++++++++ absl/strings/CMakeLists.txt | 150 +++++++++++++- absl/strings/cord.cc | 38 ++++ absl/strings/cord.h | 41 ++++ absl/strings/internal/cordz_functions.cc | 104 ++++++++++ absl/strings/internal/cordz_functions.h | 85 ++++++++ absl/strings/internal/cordz_functions_test.cc | 131 ++++++++++++ absl/strings/internal/cordz_handle.cc | 127 ++++++++++++ absl/strings/internal/cordz_handle.h | 97 +++++++++ absl/strings/internal/cordz_handle_test.cc | 253 +++++++++++++++++++++++ absl/strings/internal/cordz_info.cc | 138 +++++++++++++ absl/strings/internal/cordz_info.h | 168 +++++++++++++++ absl/strings/internal/cordz_info_test.cc | 237 +++++++++++++++++++++ absl/strings/internal/cordz_sample_token.cc | 64 ++++++ absl/strings/internal/cordz_sample_token.h | 97 +++++++++ absl/strings/internal/cordz_sample_token_test.cc | 209 +++++++++++++++++++ absl/strings/internal/cordz_statistics.h | 55 +++++ 18 files changed, 2142 insertions(+), 5 deletions(-) create mode 100644 absl/strings/internal/cordz_functions.cc create mode 100644 absl/strings/internal/cordz_functions.h create mode 100644 absl/strings/internal/cordz_functions_test.cc create mode 100644 absl/strings/internal/cordz_handle.cc create mode 100644 absl/strings/internal/cordz_handle.h create mode 100644 absl/strings/internal/cordz_handle_test.cc create mode 100644 absl/strings/internal/cordz_info.cc create mode 100644 absl/strings/internal/cordz_info.h create mode 100644 absl/strings/internal/cordz_info_test.cc create mode 100644 absl/strings/internal/cordz_sample_token.cc create mode 100644 absl/strings/internal/cordz_sample_token.h create mode 100644 absl/strings/internal/cordz_sample_token_test.cc create mode 100644 absl/strings/internal/cordz_statistics.h diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 4527ca14..2f2fa13d 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -197,17 +197,26 @@ set(ABSL_INTERNAL_DLL_FILES "strings/cord.h" "strings/escaping.cc" "strings/escaping.h" + "strings/internal/charconv_bigint.cc" + "strings/internal/charconv_bigint.h" + "strings/internal/charconv_parse.cc" + "strings/internal/charconv_parse.h" "strings/internal/cord_internal.cc" "strings/internal/cord_internal.h" "strings/internal/cord_rep_flat.h" "strings/internal/cord_rep_ring.cc" "strings/internal/cord_rep_ring.h" "strings/internal/cord_rep_ring_reader.h" + "strings/internal/cordz_functions.cc" + "strings/internal/cordz_functions.h" + "strings/internal/cordz_handle.cc" + "strings/internal/cordz_handle.h" + "strings/internal/cordz_info.cc" + "strings/internal/cordz_info.h" + "strings/internal/cordz_sample_token.cc" + "strings/internal/cordz_sample_token.h" + "strings/internal/cordz_statistics.h" "strings/internal/cordz_update_tracker.h" - "strings/internal/charconv_bigint.cc" - "strings/internal/charconv_bigint.h" - "strings/internal/charconv_parse.cc" - "strings/internal/charconv_parse.h" "strings/internal/stl_type_traits.h" "strings/internal/string_constant.h" "strings/match.cc" diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 11385163..1306ea65 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -327,11 +327,15 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":cord_internal", + ":cordz_functions", + ":cordz_info", + ":cordz_statistics", ":cordz_update_tracker", ":internal", ":str_format", ":strings", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", "//absl/base:raw_logging_internal", @@ -343,6 +347,138 @@ cc_library( ], ) +cc_library( + name = "cordz_handle", + srcs = ["internal/cordz_handle.cc"], + hdrs = ["internal/cordz_handle.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:config", + "//absl/base:raw_logging_internal", + "//absl/synchronization", + ], +) + +cc_library( + name = "cordz_info", + srcs = ["internal/cordz_info.cc"], + hdrs = ["internal/cordz_info.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord_internal", + ":cordz_handle", + ":cordz_statistics", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/debugging:stacktrace", + "//absl/synchronization", + "//absl/types:span", + ], +) + +cc_library( + name = "cordz_sample_token", + srcs = ["internal/cordz_sample_token.cc"], + hdrs = ["internal/cordz_sample_token.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cordz_handle", + ":cordz_info", + "//absl/base:config", + ], +) + +cc_library( + name = "cordz_functions", + srcs = ["internal/cordz_functions.cc"], + hdrs = ["internal/cordz_functions.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:exponential_biased", + "//absl/base:raw_logging_internal", + ], +) + +cc_library( + name = "cordz_statistics", + hdrs = ["internal/cordz_statistics.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:config", + ], +) + +cc_test( + name = "cordz_functions_test", + srcs = [ + "internal/cordz_functions_test.cc", + ], + deps = [ + ":cord_test_helpers", + ":cordz_functions", + "//absl/base:config", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_handle_test", + srcs = [ + "internal/cordz_handle_test.cc", + ], + deps = [ + ":cordz_handle", + "//absl/base:config", + "//absl/memory", + "//absl/random", + "//absl/random:distributions", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_info_test", + srcs = [ + "internal/cordz_info_test.cc", + ], + deps = [ + ":cord_internal", + ":cordz_handle", + ":cordz_info", + ":strings", + "//absl/base:config", + "//absl/debugging:stacktrace", + "//absl/debugging:symbolize", + "//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_sample_token_test", + srcs = [ + "internal/cordz_sample_token_test.cc", + ], + deps = [ + ":cord_internal", + ":cordz_handle", + ":cordz_info", + ":cordz_sample_token", + "//absl/base:config", + "//absl/memory", + "//absl/random", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "cord_test_helpers", testonly = 1, diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 0d93d5ff..044e38b3 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -259,7 +259,7 @@ absl_cc_test( absl_cc_test( NAME str_join_test - SRCS +ss SRCS "str_join_test.cc" COPTS ${ABSL_TEST_COPTS} @@ -603,6 +603,152 @@ absl_cc_test( gmock_main ) +absl_cc_library( + NAME + cordz_functions + HDRS + "internal/cordz_functions.h" + SRCS + "internal/cordz_functions.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + absl::exponential_biased + absl::raw_logging_internal +) + +absl_cc_test( + NAME + cordz_functions_test + SRCS + "internal/cordz_functions_test.cc" + DEPS + absl::config + absl::cord_test_helpers + absl::cordz_functions + gmock_main +) + +absl_cc_library( + NAME + cordz_statistics + HDRS + "internal/cordz_statistics.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + absl::synchronization +) + +absl_cc_library( + NAME + cordz_handle + HDRS + "internal/cordz_handle.h" + SRCS + "internal/cordz_handle.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::synchronization +) + +absl_cc_test( + NAME + cordz_handle_test + SRCS + "internal/cordz_handle_test.cc" + DEPS + absl::config + absl::cord_test_helpers + absl::cordz_handle + absl::memory + absl::random_random + absl::random_distributions + absl::synchronization + absl::time + gmock_main +) + +absl_cc_library( + NAME + cordz_info + HDRS + "internal/cordz_info.h" + SRCS + "internal/cordz_info.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_handle + absl::cordz_statistics + absl::core_headers + absl::span + absl::stacktrace + absl::synchronization +) + +absl_cc_test( + NAME + cordz_info_test + SRCS + "internal/cordz_info_test.cc" + DEPS + absl::config + absl::cord_internal + absl::cord_test_helpers + absl::cordz_handle + absl::cordz_info + absl::span + absl::stacktrace + absl::symbolize + gmock_main +) + +absl_cc_library( + NAME + cordz_sample_token + HDRS + "internal/cordz_sample_token.h" + SRCS + "internal/cordz_sample_token.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::cordz_handle + absl::cordz_info +) + +absl_cc_test( + NAME + cordz_sample_token_test + SRCS + "internal/cordz_sample_token_test.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_handle + absl::cordz_info + absl::cordz_info + absl::cordz_sample_token + absl::memory + absl::random_random + absl::synchronization + absl::thread_pool + absl::time + gmock_main +) + absl_cc_library( NAME cord @@ -616,6 +762,8 @@ absl_cc_library( absl::base absl::config absl::cord_internal + absl::cordz_functions + absl::cordz_info absl::cordz_update_tracker absl::core_headers absl::endian diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 8f0999f8..d33d6a0d 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -38,6 +38,7 @@ #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -426,6 +427,7 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, CordRep* root = force_tree(max_length); if (PrepareAppendRegion(root, region, size, max_length)) { + UpdateCordzStatistics(); return; } @@ -460,6 +462,7 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) { CordRep* root = force_tree(max_length); if (PrepareAppendRegion(root, region, size, max_length)) { + UpdateCordzStatistics(); return; } @@ -490,6 +493,27 @@ static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { return false; } +void Cord::InlineRep::StartProfiling() { + CordRep* tree = as_tree(); + auto* cordz_info = absl::cord_internal::CordzInfo::TrackCord(tree); + set_cordz_info(cordz_info); + cordz_info->RecordMetrics(size()); +} + +void Cord::InlineRep::StartProfiling(const Cord::InlineRep& src) { + CordRep* tree = as_tree(); + absl::cord_internal::CordzInfo* parent = + src.is_profiled() ? src.cordz_info() : nullptr; + auto* cordz_info = absl::cord_internal::CordzInfo::TrackCord(tree, parent); + set_cordz_info(cordz_info); + cordz_info->RecordMetrics(size()); +} + +void Cord::InlineRep::UpdateCordzStatisticsSlow() { + CordRep* tree = as_tree(); + data_.cordz_info()->RecordMetrics(tree->length); +} + void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { UnrefTree(); @@ -497,11 +521,17 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { if (is_tree()) { CordRep::Ref(tree()); clear_cordz_info(); + if (ABSL_PREDICT_FALSE(should_profile())) { + StartProfiling(src); + } } } void Cord::InlineRep::UnrefTree() { if (is_tree()) { + if (ABSL_PREDICT_FALSE(is_profiled())) { + absl::cord_internal::CordzInfo::UntrackCord(cordz_info()); + } CordRep::Unref(tree()); } } @@ -552,6 +582,9 @@ template Cord::Cord(std::string&& src); // The destruction code is separate so that the compiler can determine // that it does not need to call the destructor on a moved-from Cord. void Cord::DestroyCordSlow() { + if (ABSL_PREDICT_FALSE(contents_.is_profiled())) { + absl::cord_internal::CordzInfo::UntrackCord(contents_.cordz_info()); + } if (CordRep* tree = contents_.tree()) { CordRep::Unref(VerifyTree(tree)); } @@ -567,6 +600,10 @@ void Cord::Clear() { } Cord& Cord::operator=(absl::string_view src) { + if (ABSL_PREDICT_FALSE(contents_.is_profiled())) { + absl::cord_internal::CordzInfo::UntrackCord(contents_.cordz_info()); + contents_.clear_cordz_info(); + } const char* data = src.data(); size_t length = src.size(); @@ -615,6 +652,7 @@ void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) { char* region; if (PrepareAppendRegion(root, ®ion, &appended, src_size)) { memcpy(region, src_data, appended); + UpdateCordzStatistics(); } } else { // Try to fit in the inline buffer if possible. diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 7ce938ca..de1470b2 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -70,6 +70,7 @@ #include #include +#include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/macros.h" @@ -80,6 +81,9 @@ #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cord_rep_ring_reader.h" +#include "absl/strings/internal/cordz_functions.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/internal/string_constant.h" #include "absl/strings/string_view.h" @@ -772,6 +776,20 @@ class Cord { // Resets the current cordz_info to null / empty. void clear_cordz_info() { data_.clear_cordz_info(); } + // Starts profiling this cord. + void StartProfiling(); + + // Starts profiling this cord which has been copied from `src`. + void StartProfiling(const Cord::InlineRep& src); + + // Returns true if a Cord should be profiled and false otherwise. + static bool should_profile(); + + // Updates the cordz statistics. info may be nullptr if the CordzInfo object + // is unknown. + void UpdateCordzStatistics(); + void UpdateCordzStatisticsSlow(); + private: friend class Cord; @@ -943,6 +961,9 @@ inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) if (is_tree()) { data_.clear_cordz_info(); absl::cord_internal::CordRep::Ref(as_tree()); + if (ABSL_PREDICT_FALSE(should_profile())) { + StartProfiling(src); + } } } @@ -1004,6 +1025,9 @@ inline size_t Cord::InlineRep::size() const { inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { if (rep == nullptr) { + if (ABSL_PREDICT_FALSE(is_profiled())) { + absl::cord_internal::CordzInfo::UntrackCord(cordz_info()); + } ResetToEmpty(); } else { if (data_.is_tree()) { @@ -1013,7 +1037,11 @@ inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { } else { // `data_` contains inlined data: initialize data_ to tree value `rep`. data_.make_tree(rep); + if (ABSL_PREDICT_FALSE(should_profile())) { + StartProfiling(); + } } + UpdateCordzStatistics(); } } @@ -1024,9 +1052,13 @@ inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) { return; } data_.set_tree(rep); + UpdateCordzStatistics(); } inline absl::cord_internal::CordRep* Cord::InlineRep::clear() { + if (ABSL_PREDICT_FALSE(is_profiled())) { + absl::cord_internal::CordzInfo::UntrackCord(cordz_info()); + } absl::cord_internal::CordRep* result = tree(); ResetToEmpty(); return result; @@ -1039,6 +1071,15 @@ inline void Cord::InlineRep::CopyToArray(char* dst) const { cord_internal::SmallMemmove(dst, data_.as_chars(), n); } +inline ABSL_ATTRIBUTE_ALWAYS_INLINE bool Cord::InlineRep::should_profile() { + return absl::cord_internal::cordz_should_profile(); +} + +inline void Cord::InlineRep::UpdateCordzStatistics() { + if (ABSL_PREDICT_TRUE(!is_profiled())) return; + UpdateCordzStatisticsSlow(); +} + constexpr inline Cord::Cord() noexcept {} template diff --git a/absl/strings/internal/cordz_functions.cc b/absl/strings/internal/cordz_functions.cc new file mode 100644 index 00000000..6ad864f1 --- /dev/null +++ b/absl/strings/internal/cordz_functions.cc @@ -0,0 +1,104 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_functions.h" + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/exponential_biased.h" +#include "absl/base/internal/raw_logging.h" + +// TODO(b/162942788): weak 'cordz_disabled' value. +// A strong version is in the 'cordz_disabled_hack_for_odr' library which can +// be linked in to disable cordz at compile time. +extern "C" { +bool absl_internal_cordz_disabled ABSL_ATTRIBUTE_WEAK = false; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +// The average interval until the next sample. A value of 0 disables profiling +// while a value of 1 will profile all Cords. +std::atomic g_cordz_mean_interval(50000); + +} // namespace + +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +ABSL_CONST_INIT thread_local int64_t cordz_next_sample = 0; + +// kIntervalIfDisabled is the number of profile-eligible events need to occur +// before the code will confirm that cordz is still disabled. +constexpr int64_t kIntervalIfDisabled = 1 << 16; + +ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() { + // TODO(b/162942788): check if profiling is disabled at compile time. + if (absl_internal_cordz_disabled) { + ABSL_RAW_LOG(WARNING, "Cordz info disabled at compile time"); + // We are permanently disabled: set counter to highest possible value. + cordz_next_sample = std::numeric_limits::max(); + return false; + } + + thread_local absl::base_internal::ExponentialBiased + exponential_biased_generator; + int32_t mean_interval = get_cordz_mean_interval(); + + // Check if we disabled profiling. If so, set the next sample to a "large" + // number to minimize the overhead of the should_profile codepath. + if (mean_interval <= 0) { + cordz_next_sample = kIntervalIfDisabled; + return false; + } + + // Check if we're always sampling. + if (mean_interval == 1) { + cordz_next_sample = 1; + return true; + } + + if (cordz_next_sample <= 0) { + cordz_next_sample = exponential_biased_generator.GetStride(mean_interval); + return true; + } + + --cordz_next_sample; + return false; +} + +void cordz_set_next_sample_for_testing(int64_t next_sample) { + cordz_next_sample = next_sample; +} + +#endif // ABSL_INTERNAL_CORDZ_ENABLED + +int32_t get_cordz_mean_interval() { + return g_cordz_mean_interval.load(std::memory_order_acquire); +} + +void set_cordz_mean_interval(int32_t mean_interval) { + g_cordz_mean_interval.store(mean_interval, std::memory_order_release); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_functions.h b/absl/strings/internal/cordz_functions.h new file mode 100644 index 00000000..c9ba1450 --- /dev/null +++ b/absl/strings/internal/cordz_functions.h @@ -0,0 +1,85 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CORDZ_FUNCTIONS_H_ +#define ABSL_STRINGS_CORDZ_FUNCTIONS_H_ + +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Returns the current sample rate. This represents the average interval +// between samples. +int32_t get_cordz_mean_interval(); + +// Sets the sample rate with the average interval between samples. +void set_cordz_mean_interval(int32_t mean_interval); + +// Enable cordz unless any of the following applies: +// - no thread local support +// - MSVC build +// - Android build +// - Apple build +// - DLL build +// Hashtablez is turned off completely in opensource builds. +// MSVC's static atomics are dynamically initialized in debug mode, which breaks +// sampling. +#if defined(ABSL_HAVE_THREAD_LOCAL) && !defined(_MSC_VER) && \ + !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) && \ + !defined(__ANDROID__) && !defined(__APPLE__) +#define ABSL_INTERNAL_CORDZ_ENABLED 1 +#endif + +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +// cordz_next_sample is the number of events until the next sample event. If +// the value is 1 or less, the code will check on the next event if cordz is +// enabled, and if so, will sample the Cord. cordz is only enabled when we can +// use thread locals. +ABSL_CONST_INIT extern thread_local int64_t cordz_next_sample; + +// Determines if the next sample should be profiled. If it is, the value pointed +// at by next_sample will be set with the interval until the next sample. +bool cordz_should_profile_slow(); + +// Returns true if the next cord should be sampled. +inline bool cordz_should_profile() { + if (ABSL_PREDICT_TRUE(cordz_next_sample > 1)) { + cordz_next_sample--; + return false; + } + return cordz_should_profile_slow(); +} + +// Sets the interval until the next sample (for testing only) +void cordz_set_next_sample_for_testing(int64_t next_sample); + +#else // ABSL_INTERNAL_CORDZ_ENABLED + +inline bool cordz_should_profile() { return false; } +inline void cordz_set_next_sample_for_testing(int64_t) {} + +#endif // ABSL_INTERNAL_CORDZ_ENABLED + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_FUNCTIONS_H_ diff --git a/absl/strings/internal/cordz_functions_test.cc b/absl/strings/internal/cordz_functions_test.cc new file mode 100644 index 00000000..f2cefae3 --- /dev/null +++ b/absl/strings/internal/cordz_functions_test.cc @@ -0,0 +1,131 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_functions.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::Eq; +using ::testing::Ge; +using ::testing::Le; + +TEST(CordzFunctionsTest, SampleRate) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + int32_t expected_sample_rate = 123; + set_cordz_mean_interval(expected_sample_rate); + EXPECT_THAT(get_cordz_mean_interval(), Eq(expected_sample_rate)); + set_cordz_mean_interval(orig_sample_rate); +} + +// Cordz is disabled when we don't have thread_local. All calls to +// should_profile will return false when cordz is diabled, so we might want to +// avoid those tests. +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +TEST(CordzFunctionsTest, ShouldProfileDisable) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(0); + cordz_set_next_sample_for_testing(0); + EXPECT_FALSE(cordz_should_profile()); + // 1 << 16 is from kIntervalIfDisabled in cordz_functions.cc. + EXPECT_THAT(cordz_next_sample, Eq(1 << 16)); + + set_cordz_mean_interval(orig_sample_rate); +} + +TEST(CordzFunctionsTest, ShouldProfileAlways) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(1); + cordz_set_next_sample_for_testing(1); + EXPECT_TRUE(cordz_should_profile()); + EXPECT_THAT(cordz_next_sample, Le(1)); + + set_cordz_mean_interval(orig_sample_rate); +} + +TEST(CordzFunctionsTest, ShouldProfileRate) { + static constexpr int kDesiredMeanInterval = 1000; + static constexpr int kSamples = 10000; + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(kDesiredMeanInterval); + + int64_t sum_of_intervals = 0; + for (int i = 0; i < kSamples; i++) { + // Setting next_sample to 0 will force cordz_should_profile to generate a + // new value for next_sample each iteration. + cordz_set_next_sample_for_testing(0); + cordz_should_profile(); + sum_of_intervals += cordz_next_sample; + } + + // The sum of independent exponential variables is an Erlang distribution, + // which is a gamma distribution where the shape parameter is equal to the + // number of summands. The distribution used for cordz_should_profile is + // actually floor(Exponential(1/mean)) which introduces bias. However, we can + // apply the squint-really-hard correction factor. That is, when mean is + // large, then if we squint really hard the shape of the distribution between + // N and N+1 looks like a uniform distribution. On average, each value for + // next_sample will be about 0.5 lower than we would expect from an + // exponential distribution. This squint-really-hard correction approach won't + // work when mean is smaller than about 10 but works fine when mean is 1000. + // + // We can use R to calculate a confidence interval. This + // shows how to generate a confidence interval with a false positive rate of + // one in a billion. + // + // $ R -q + // > mean = 1000 + // > kSamples = 10000 + // > errorRate = 1e-9 + // > correction = -kSamples / 2 + // > low = qgamma(errorRate/2, kSamples, 1/mean) + correction + // > high = qgamma(1 - errorRate/2, kSamples, 1/mean) + correction + // > low + // [1] 9396115 + // > high + // [1] 10618100 + EXPECT_THAT(sum_of_intervals, Ge(9396115)); + EXPECT_THAT(sum_of_intervals, Le(10618100)); + + set_cordz_mean_interval(orig_sample_rate); +} + +#else // ABSL_INTERNAL_CORDZ_ENABLED + +TEST(CordzFunctionsTest, ShouldProfileDisabled) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(1); + cordz_set_next_sample_for_testing(0); + EXPECT_FALSE(cordz_should_profile()); + + set_cordz_mean_interval(orig_sample_rate); +} + +#endif // ABSL_INTERNAL_CORDZ_ENABLED + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_handle.cc b/absl/strings/internal/cordz_handle.cc new file mode 100644 index 00000000..7e82f85b --- /dev/null +++ b/absl/strings/internal/cordz_handle.cc @@ -0,0 +1,127 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "absl/strings/internal/cordz_handle.h" + +#include + +#include "absl/base/internal/raw_logging.h" // For ABSL_RAW_CHECK + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +ABSL_CONST_INIT absl::Mutex CordzHandle::mutex_(absl::kConstInit); +ABSL_CONST_INIT std::atomic CordzHandle::dq_tail_{nullptr}; + +CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) { + if (is_snapshot) { + MutexLock lock(&mutex_); + CordzHandle* dq_tail = dq_tail_.load(std::memory_order_acquire); + if (dq_tail != nullptr) { + dq_prev_ = dq_tail; + dq_tail->dq_next_ = this; + } + dq_tail_.store(this, std::memory_order_release); + } +} + +CordzHandle::~CordzHandle() { + if (is_snapshot_) { + std::vector to_delete; + { + absl::MutexLock lock(&mutex_); + CordzHandle* next = dq_next_; + if (dq_prev_ == nullptr) { + // We were head of the queue, delete every CordzHandle until we reach + // either the end of the list, or a snapshot handle. + while (next && !next->is_snapshot_) { + to_delete.push_back(next); + next = next->dq_next_; + } + } else { + // Another CordzHandle existed before this one, don't delete anything. + dq_prev_->dq_next_ = next; + } + if (next) { + next->dq_prev_ = dq_prev_; + } else { + dq_tail_.store(dq_prev_, std::memory_order_release); + } + } + for (CordzHandle* handle : to_delete) { + delete handle; + } + } +} + +void CordzHandle::Delete(CordzHandle* handle) { + if (handle) { + if (!handle->is_snapshot_ && !UnsafeDeleteQueueEmpty()) { + MutexLock lock(&mutex_); + CordzHandle* dq_tail = dq_tail_.load(std::memory_order_acquire); + if (dq_tail != nullptr) { + handle->dq_prev_ = dq_tail; + dq_tail->dq_next_ = handle; + dq_tail_.store(handle, std::memory_order_release); + return; + } + } + delete handle; + } +} + +std::vector CordzHandle::DiagnosticsGetDeleteQueue() { + std::vector handles; + MutexLock lock(&mutex_); + CordzHandle* dq_tail = dq_tail_.load(std::memory_order_acquire); + for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) { + handles.push_back(p); + } + return handles; +} + +bool CordzHandle::DiagnosticsHandleIsSafeToInspect( + const CordzHandle* handle) const { + if (!is_snapshot_) return false; + if (handle == nullptr) return true; + if (handle->is_snapshot_) return false; + bool snapshot_found = false; + MutexLock lock(&mutex_); + for (const CordzHandle* p = dq_tail_; p; p = p->dq_prev_) { + if (p == handle) return !snapshot_found; + if (p == this) snapshot_found = true; + } + ABSL_ASSERT(snapshot_found); // Assert that 'this' is in delete queue. + return true; +} + +std::vector +CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() { + std::vector handles; + if (!is_snapshot()) { + return handles; + } + + MutexLock lock(&mutex_); + for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) { + if (!p->is_snapshot()) { + handles.push_back(p); + } + } + return handles; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_handle.h b/absl/strings/internal/cordz_handle.h new file mode 100644 index 00000000..0235d4bd --- /dev/null +++ b/absl/strings/internal/cordz_handle.h @@ -0,0 +1,97 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CORDZ_HANDLE_H_ +#define ABSL_STRINGS_CORDZ_HANDLE_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// This base class allows multiple types of object (CordzInfo and +// CordzSampleToken) to exist simultaneously on the delete queue (pointed to by +// global_dq_tail and traversed using dq_prev_ and dq_next_). The +// delete queue guarantees that once a profiler creates a CordzSampleToken and +// has gained visibility into a CordzInfo object, that CordzInfo object will not +// be deleted prematurely. This allows the profiler to inspect all CordzInfo +// objects that are alive without needing to hold a global lock. +class CordzHandle { + public: + CordzHandle() : CordzHandle(false) {} + + bool is_snapshot() const { return is_snapshot_; } + + // Deletes the provided instance, or puts it on the delete queue to be deleted + // once there are no more sample tokens (snapshot) instances potentially + // referencing the instance. `handle` may be null. + static void Delete(CordzHandle* handle); + + // Returns the current entries in the delete queue in LIFO order. + static std::vector DiagnosticsGetDeleteQueue(); + + // Returns true if the provided handle is nullptr or guarded by this handle. + // Since the CordzSnapshot token is itself a CordzHandle, this method will + // allow tests to check if that token is keeping an arbitrary CordzHandle + // alive. + bool DiagnosticsHandleIsSafeToInspect(const CordzHandle* handle) const; + + // Returns the current entries in the delete queue, in LIFO order, that are + // protected by this. CordzHandle objects are only placed on the delete queue + // after CordzHandle::Delete is called with them as an argument. Only + // CordzHandle objects that are not also CordzSnapshot objects will be + // included in the return vector. For each of the handles in the return + // vector, the earliest that their memory can be freed is when this + // CordzSnapshot object is deleted. + std::vector DiagnosticsGetSafeToInspectDeletedHandles(); + + protected: + explicit CordzHandle(bool is_snapshot); + virtual ~CordzHandle(); + + private: + // Returns true if the delete queue is empty. This method does not acquire the + // lock, but does a 'load acquire' observation on the delete queue tail. It + // is used inside Delete() to check for the presence of a delete queue without + // holding the lock. The assumption is that the caller is in the state of + // 'being deleted', and can not be newly discovered by a concurrent 'being + // constructed' snapshot instance. Practically, this means that any such + // discovery (`find`, 'first' or 'next', etc) must have proper 'happens before + // / after' semantics and atomic fences. + static bool UnsafeDeleteQueueEmpty() ABSL_NO_THREAD_SAFETY_ANALYSIS { + return dq_tail_.load(std::memory_order_acquire) == nullptr; + } + + const bool is_snapshot_; + static absl::Mutex mutex_; + static std::atomic dq_tail_ ABSL_GUARDED_BY(mutex_); + CordzHandle* dq_prev_ ABSL_GUARDED_BY(mutex_) = nullptr; + CordzHandle* dq_next_ ABSL_GUARDED_BY(mutex_) = nullptr; +}; + +class CordzSnapshot : public CordzHandle { + public: + CordzSnapshot() : CordzHandle(true) {} +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_HANDLE_H_ diff --git a/absl/strings/internal/cordz_handle_test.cc b/absl/strings/internal/cordz_handle_test.cc new file mode 100644 index 00000000..c04240e8 --- /dev/null +++ b/absl/strings/internal/cordz_handle_test.cc @@ -0,0 +1,253 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "absl/strings/internal/cordz_handle.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::Gt; +using ::testing::IsEmpty; +using ::testing::SizeIs; + +// Local less verbose helper +std::vector DeleteQueue() { + return CordzHandle::DiagnosticsGetDeleteQueue(); +} + +struct CordzHandleDeleteTracker : public CordzHandle { + bool* deleted; + explicit CordzHandleDeleteTracker(bool* deleted) : deleted(deleted) {} + ~CordzHandleDeleteTracker() override { *deleted = true; } +}; + +TEST(CordzHandleTest, DeleteQueueIsEmpty) { + EXPECT_THAT(DeleteQueue(), SizeIs(0)); +} + +TEST(CordzHandleTest, CordzHandleCreateDelete) { + bool deleted = false; + auto* handle = new CordzHandleDeleteTracker(&deleted); + EXPECT_FALSE(handle->is_snapshot()); + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + + CordzHandle::Delete(handle); + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + EXPECT_TRUE(deleted); +} + +TEST(CordzHandleTest, CordzSnapshotCreateDelete) { + auto* snapshot = new CordzSnapshot(); + EXPECT_TRUE(snapshot->is_snapshot()); + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot)); + delete snapshot; + EXPECT_THAT(DeleteQueue(), SizeIs(0)); +} + +TEST(CordzHandleTest, CordzHandleCreateDeleteWithSnapshot) { + bool deleted = false; + auto* snapshot = new CordzSnapshot(); + auto* handle = new CordzHandleDeleteTracker(&deleted); + + CordzHandle::Delete(handle); + EXPECT_THAT(DeleteQueue(), ElementsAre(handle, snapshot)); + EXPECT_FALSE(deleted); + + delete snapshot; + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + EXPECT_TRUE(deleted); +} + +TEST(CordzHandleTest, MultiSnapshot) { + bool deleted[3] = {false, false, false}; + + CordzSnapshot* snapshot[3]; + CordzHandleDeleteTracker* handle[3]; + for (int i = 0; i < 3; ++i) { + snapshot[i] = new CordzSnapshot(); + handle[i] = new CordzHandleDeleteTracker(&deleted[i]); + CordzHandle::Delete(handle[i]); + } + + EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1], + snapshot[1], handle[0], snapshot[0])); + EXPECT_THAT(deleted, ElementsAre(false, false, false)); + + delete snapshot[1]; + EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1], + handle[0], snapshot[0])); + EXPECT_THAT(deleted, ElementsAre(false, false, false)); + + delete snapshot[0]; + EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2])); + EXPECT_THAT(deleted, ElementsAre(true, true, false)); + + delete snapshot[2]; + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + EXPECT_THAT(deleted, ElementsAre(true, true, deleted)); +} + +TEST(CordzHandleTest, DiagnosticsHandleIsSafeToInspect) { + CordzSnapshot snapshot1; + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(nullptr)); + + auto* handle1 = new CordzHandle(); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); + + CordzHandle::Delete(handle1); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); + + CordzSnapshot snapshot2; + auto* handle2 = new CordzHandle(); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle2)); + EXPECT_FALSE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle1)); + EXPECT_TRUE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle2)); + + CordzHandle::Delete(handle2); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); +} + +TEST(CordzHandleTest, DiagnosticsGetSafeToInspectDeletedHandles) { + EXPECT_THAT(DeleteQueue(), IsEmpty()); + + auto* handle = new CordzHandle(); + auto* snapshot1 = new CordzSnapshot(); + + // snapshot1 should be able to see handle. + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot1)); + EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle)); + EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(), + IsEmpty()); + + // This handle will be safe to inspect as long as snapshot1 is alive. However, + // since only snapshot1 can prove that it's alive, it will be hidden from + // snapshot2. + CordzHandle::Delete(handle); + + // This snapshot shouldn't be able to see handle because handle was already + // sent to Delete. + auto* snapshot2 = new CordzSnapshot(); + + // DeleteQueue elements are LIFO order. + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2, handle, snapshot1)); + + EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle)); + EXPECT_FALSE(snapshot2->DiagnosticsHandleIsSafeToInspect(handle)); + + EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(), + ElementsAre(handle)); + EXPECT_THAT(snapshot2->DiagnosticsGetSafeToInspectDeletedHandles(), + IsEmpty()); + + CordzHandle::Delete(snapshot1); + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2)); + + CordzHandle::Delete(snapshot2); + EXPECT_THAT(DeleteQueue(), IsEmpty()); +} + +// Create and delete CordzHandle and CordzSnapshot objects in multiple threads +// so that tsan has some time to chew on it and look for memory problems. +TEST(CordzHandleTest, MultiThreaded) { + Notification stop; + static constexpr int kNumThreads = 4; + // Keep the number of handles relatively small so that the test will naturally + // transition to an empty delete queue during the test. If there are, say, 100 + // handles, that will virtually never happen. With 10 handles and around 50k + // iterations in each of 4 threads, the delete queue appears to become empty + // around 200 times. + static constexpr int kNumHandles = 10; + + // Each thread is going to pick a random index and atomically swap its + // CordzHandle with one in handles. This way, each thread can avoid + // manipulating a CordzHandle that might be operated upon in another thread. + std::vector> handles(kNumHandles); + + absl::synchronization_internal::ThreadPool pool(kNumThreads); + + for (int i = 0; i < kNumThreads; ++i) { + pool.Schedule([&stop, &handles]() { + std::minstd_rand gen; + std::uniform_int_distribution dist_type(0, 2); + std::uniform_int_distribution dist_handle(0, kNumHandles - 1); + size_t max_safe_to_inspect = 0; + while (!stop.HasBeenNotified()) { + CordzHandle* handle; + switch (dist_type(gen)) { + case 0: + handle = new CordzHandle(); + break; + case 1: + handle = new CordzSnapshot(); + break; + default: + handle = nullptr; + break; + } + CordzHandle* old_handle = handles[dist_handle(gen)].exchange(handle); + if (old_handle != nullptr) { + std::vector safe_to_inspect = + old_handle->DiagnosticsGetSafeToInspectDeletedHandles(); + for (const CordzHandle* handle : safe_to_inspect) { + // We're in a tight loop, so don't generate too many error messages. + ASSERT_FALSE(handle->is_snapshot()); + } + if (safe_to_inspect.size() > max_safe_to_inspect) { + max_safe_to_inspect = safe_to_inspect.size(); + } + } + CordzHandle::Delete(old_handle); + } + + // Confirm that the test did *something*. This check will be satisfied as + // long as this thread has delete a CordzSnapshot object and a + // non-snapshot CordzHandle was deleted after the CordzSnapshot was + // created. This max_safe_to_inspect count will often reach around 30 + // (assuming 4 threads and 10 slots for live handles). Don't use a strict + // bound to avoid making this test flaky. + EXPECT_THAT(max_safe_to_inspect, Gt(0)); + + // Have each thread attempt to clean up everything. Some thread will be + // the last to reach this cleanup code, and it will be guaranteed to clean + // up everything because nothing remains to create new handles. + for (size_t i = 0; i < handles.size(); i++) { + CordzHandle* handle = handles[i].exchange(nullptr); + CordzHandle::Delete(handle); + } + }); + } + + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc new file mode 100644 index 00000000..4dec63d5 --- /dev/null +++ b/absl/strings/internal/cordz_info.cc @@ -0,0 +1,138 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_info.h" + +#include "absl/base/config.h" +#include "absl/debugging/stacktrace.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +constexpr int CordzInfo::kMaxStackDepth; + +ABSL_CONST_INIT std::atomic CordzInfo::ci_head_{nullptr}; +ABSL_CONST_INIT absl::Mutex CordzInfo::ci_mutex_(absl::kConstInit); + +CordzInfo* CordzInfo::Head(const CordzSnapshot& snapshot) { + ABSL_ASSERT(snapshot.is_snapshot()); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(ci_head_unsafe())); + return ci_head_unsafe(); +} + +CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const { + ABSL_ASSERT(snapshot.is_snapshot()); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(this)); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(ci_next_unsafe())); + return ci_next_unsafe(); +} + +CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src) { + CordzInfo* ci = new CordzInfo(rep); + if (src) { + ci->parent_stack_depth_ = src->stack_depth_; + memcpy(ci->parent_stack_, src->stack_, sizeof(void*) * src->stack_depth_); + } + ci->Track(); + return ci; +} + +CordzInfo* CordzInfo::TrackCord(CordRep* rep) { + return TrackCord(rep, nullptr); +} + +void CordzInfo::UntrackCord(CordzInfo* cordz_info) { + assert(cordz_info); + if (cordz_info) { + cordz_info->Untrack(); + CordzHandle::Delete(cordz_info); + } +} + +CordzInfo::CordzInfo(CordRep* rep) + : rep_(rep), + stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth, + /*skip_count=*/1)), + parent_stack_depth_(0), + create_time_(absl::Now()) {} + +CordzInfo::~CordzInfo() { + // `rep_` is potentially kept alive if CordzInfo is included + // in a collection snapshot (which should be rare). + if (ABSL_PREDICT_FALSE(rep_)) { + CordRep::Unref(rep_); + } +} + +void CordzInfo::Track() { + absl::MutexLock l(&ci_mutex_); + + CordzInfo* const head = ci_head_.load(std::memory_order_acquire); + if (head != nullptr) { + head->ci_prev_.store(this, std::memory_order_release); + } + ci_next_.store(head, std::memory_order_release); + ci_head_.store(this, std::memory_order_release); +} + +void CordzInfo::Untrack() { + { + // TODO(b/117940323): change this to assuming ownership instead once all + // Cord logic is properly keeping `rep_` in sync with the Cord root rep. + absl::MutexLock lock(&mutex()); + rep_ = nullptr; + } + + absl::MutexLock l(&ci_mutex_); + + CordzInfo* const head = ci_head_.load(std::memory_order_acquire); + CordzInfo* const next = ci_next_.load(std::memory_order_acquire); + CordzInfo* const prev = ci_prev_.load(std::memory_order_acquire); + + if (next) { + ABSL_ASSERT(next->ci_prev_.load(std::memory_order_acquire) == this); + next->ci_prev_.store(prev, std::memory_order_release); + } + if (prev) { + ABSL_ASSERT(head != this); + ABSL_ASSERT(prev->ci_next_.load(std::memory_order_acquire) == this); + prev->ci_next_.store(next, std::memory_order_release); + } else { + ABSL_ASSERT(head == this); + ci_head_.store(next, std::memory_order_release); + } +} + +void CordzInfo::SetCordRep(CordRep* rep) { + mutex().AssertHeld(); + rep_ = rep; +} + +absl::Span CordzInfo::GetStack() const { + return absl::MakeConstSpan(stack_, stack_depth_); +} + +absl::Span CordzInfo::GetParentStack() const { + return absl::MakeConstSpan(parent_stack_, parent_stack_depth_); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h new file mode 100644 index 00000000..c1090a1c --- /dev/null +++ b/absl/strings/internal/cordz_info.h @@ -0,0 +1,168 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CORDZ_INFO_H_ +#define ABSL_STRINGS_CORDZ_INFO_H_ + +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzInfo tracks a profiled Cord. Each of these objects can be in two places. +// If a Cord is alive, the CordzInfo will be in the global_cordz_infos map, and +// can also be retrieved via the linked list starting with +// global_cordz_infos_head and continued via the cordz_info_next() method. When +// a Cord has reached the end of its lifespan, the CordzInfo object will be +// migrated out of the global_cordz_infos list and the global_cordz_infos_map, +// and will either be deleted or appended to the global_delete_queue. If it is +// placed on the global_delete_queue, the CordzInfo object will be cleaned in +// the destructor of a CordzSampleToken object. +class CordzInfo : public CordzHandle { + public: + // All profiled Cords should be accompanied by a call to TrackCord. + // TrackCord creates a CordzInfo instance which tracks important metrics of + // the sampled cord. CordzInfo instances are placed in a global list which is + // used to discover and snapshot all actively tracked cords. + // Callers are responsible for calling UntrackCord() before the tracked Cord + // instance is deleted, or to stop tracking the sampled Cord. + static CordzInfo* TrackCord(CordRep* rep); + + // Stops tracking changes for a sampled cord, and deletes the provided info. + // This function must be called before the sampled cord instance is deleted, + // and before the root cordrep of the sampled cord is unreffed. + // This function may extend the lifetime of the cordrep in cases where the + // CordInfo instance is being held by a concurrent collection thread. + static void UntrackCord(CordzInfo* cordz_info); + + // Identical to TrackCord(), except that this function fills the + // 'parent_stack' property of the returned CordzInfo instance from the + // provided `src` instance if `src` is not null. + // This function should be used for sampling 'copy constructed' cords. + static CordzInfo* TrackCord(CordRep* rep, const CordzInfo* src); + + CordzInfo() = delete; + CordzInfo(const CordzInfo&) = delete; + CordzInfo& operator=(const CordzInfo&) = delete; + + // Retrieves the oldest existing CordzInfo. + static CordzInfo* Head(const CordzSnapshot& snapshot); + + // Retrieves the next oldest existing CordzInfo older than 'this' instance. + CordzInfo* Next(const CordzSnapshot& snapshot) const; + + // Returns a reference to the mutex guarding the `rep` property of this + // instance. CordzInfo instances hold a weak reference to the rep pointer of + // sampled cords, and rely on Cord logic to update the rep pointer when the + // underlying root tree or ring of the cord changes. + absl::Mutex& mutex() const { return mutex_; } + + // Updates the `rep' property of this instance. This methods is invoked by + // Cord logic each time the root node of a sampled Cord changes, and before + // the old root reference count is deleted. This guarantees that collection + // code can always safely take a reference on the tracked cord. + // Requires `mutex()` to be held. + // TODO(b/117940323): annotate with ABSL_EXCLUSIVE_LOCKS_REQUIRED once all + // Cord code is in a state where this can be proven true by the compiler. + void SetCordRep(CordRep* rep); + + // Returns the current value of `rep_` for testing purposes only. + CordRep* GetCordRepForTesting() const ABSL_NO_THREAD_SAFETY_ANALYSIS { + return rep_; + } + + // Returns the stack trace for where the cord was first sampled. Cords are + // potentially sampled when they promote from an inlined cord to a tree or + // ring representation, which is not necessarily the location where the cord + // was first created. Some cords are created as inlined cords, and only as + // data is added do they become a non-inlined cord. However, typically the + // location represents reasonably well where the cord is 'created'. + absl::Span GetStack() const; + + // Returns the stack trace for a sampled cord's 'parent stack trace'. This + // value may be set if the cord is sampled (promoted) after being created + // from, or being assigned the value of an existing (sampled) cord. + absl::Span GetParentStack() const; + + // Retrieve the CordzStatistics associated with this Cord. The statistics are + // only updated when a Cord goes through a mutation, such as an Append or + // RemovePrefix. The refcounts can change due to external events, so the + // reported refcount stats might be incorrect. + CordzStatistics GetCordzStatistics() const { + CordzStatistics stats; + stats.size = size_.load(std::memory_order_relaxed); + return stats; + } + + // Records size metric for this CordzInfo instance. + void RecordMetrics(int64_t size) { + size_.store(size, std::memory_order_relaxed); + } + + private: + static constexpr int kMaxStackDepth = 64; + + explicit CordzInfo(CordRep* tree); + ~CordzInfo() override; + + void Track(); + void Untrack(); + + // 'Unsafe' head/next/prev accessors not requiring the lock being held. + // These are used exclusively for iterations (Head / Next) where we enforce + // a token being held, so reading an 'old' / deleted pointer is fine. + static CordzInfo* ci_head_unsafe() ABSL_NO_THREAD_SAFETY_ANALYSIS { + return ci_head_.load(std::memory_order_acquire); + } + CordzInfo* ci_next_unsafe() const ABSL_NO_THREAD_SAFETY_ANALYSIS { + return ci_next_.load(std::memory_order_acquire); + } + CordzInfo* ci_prev_unsafe() const ABSL_NO_THREAD_SAFETY_ANALYSIS { + return ci_prev_.load(std::memory_order_acquire); + } + + static absl::Mutex ci_mutex_; + static std::atomic ci_head_ ABSL_GUARDED_BY(ci_mutex_); + std::atomic ci_prev_ ABSL_GUARDED_BY(ci_mutex_){nullptr}; + std::atomic ci_next_ ABSL_GUARDED_BY(ci_mutex_){nullptr}; + + mutable absl::Mutex mutex_; + CordRep* rep_ ABSL_GUARDED_BY(mutex()); + + void* stack_[kMaxStackDepth]; + void* parent_stack_[kMaxStackDepth]; + const int stack_depth_; + int parent_stack_depth_; + const absl::Time create_time_; + + // Last recorded size for the cord. + std::atomic size_{0}; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_INFO_H_ diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc new file mode 100644 index 00000000..be20e4a4 --- /dev/null +++ b/absl/strings/internal/cordz_info_test.cc @@ -0,0 +1,237 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_info.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Ne; + +struct TestCordRep { + CordRepFlat* rep; + + TestCordRep() { + rep = CordRepFlat::New(100); + rep->length = 100; + memset(rep->Data(), 1, 100); + } + ~TestCordRep() { CordRepFlat::Delete(rep); } +}; + +// Local less verbose helper +std::vector DeleteQueue() { + return CordzHandle::DiagnosticsGetDeleteQueue(); +} + +std::string FormatStack(absl::Span raw_stack) { + static constexpr size_t buf_size = 1 << 14; + std::unique_ptr buf(new char[buf_size]); + std::string output; + for (void* stackp : raw_stack) { + if (absl::Symbolize(stackp, buf.get(), buf_size)) { + absl::StrAppend(&output, " ", buf.get(), "\n"); + } + } + return output; +} + +TEST(CordzInfoTest, TrackCord) { + TestCordRep rep; + CordzInfo* info = CordzInfo::TrackCord(rep.rep); + ASSERT_THAT(info, Ne(nullptr)); + EXPECT_FALSE(info->is_snapshot()); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info)); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep.rep)); + CordzInfo::UntrackCord(info); +} + +TEST(CordzInfoTest, UntrackCord) { + TestCordRep rep; + CordzInfo* info = CordzInfo::TrackCord(rep.rep); + + CordzSnapshot snapshot; + CordzInfo::UntrackCord(info); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr)); + EXPECT_THAT(DeleteQueue(), ElementsAre(info, &snapshot)); +} + +TEST(CordzInfoTest, SetCordRep) { + TestCordRep rep; + CordzInfo* info = CordzInfo::TrackCord(rep.rep); + + TestCordRep rep2; + { + absl::MutexLock lock(&info->mutex()); + info->SetCordRep(rep2.rep); + } + EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep2.rep)); + + CordzInfo::UntrackCord(info); +} + +#if GTEST_HAS_DEATH_TEST + +TEST(CordzInfoTest, SetCordRepRequiresMutex) { + TestCordRep rep; + CordzInfo* info = CordzInfo::TrackCord(rep.rep); + TestCordRep rep2; + EXPECT_DEATH(info->SetCordRep(rep2.rep), ".*"); + CordzInfo::UntrackCord(info); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(CordzInfoTest, TrackUntrackHeadFirstV2) { + TestCordRep rep; + CordzSnapshot snapshot; + EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); + + CordzInfo* info1 = CordzInfo::TrackCord(rep.rep); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + CordzInfo* info2 = CordzInfo::TrackCord(rep.rep); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); + EXPECT_THAT(info2->Next(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + CordzInfo::UntrackCord(info2); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + CordzInfo::UntrackCord(info1); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); +} + +TEST(CordzInfoTest, TrackUntrackTailFirstV2) { + TestCordRep rep; + CordzSnapshot snapshot; + EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); + + CordzInfo* info1 = CordzInfo::TrackCord(rep.rep); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + CordzInfo* info2 = CordzInfo::TrackCord(rep.rep); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); + EXPECT_THAT(info2->Next(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + CordzInfo::UntrackCord(info1); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); + EXPECT_THAT(info2->Next(snapshot), Eq(nullptr)); + + CordzInfo::UntrackCord(info2); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); +} + +TEST(CordzInfoTest, StackV2) { + TestCordRep rep; + // kMaxStackDepth is intentionally less than 64 (which is the max depth that + // Cordz will record) because if the actual stack depth is over 64 + // (which it is on Apple platforms) then the expected_stack will end up + // catching a few frames at the end that the actual_stack didn't get and + // it will no longer be subset. At the time of this writing 58 is the max + // that will allow this test to pass (with a minimum os version of iOS 9), so + // rounded down to 50 to hopefully not run into this in the future if Apple + // makes small modifications to its testing stack. 50 is sufficient to prove + // that we got a decent stack. + static constexpr int kMaxStackDepth = 50; + CordzInfo* info = CordzInfo::TrackCord(rep.rep); + std::vector local_stack; + local_stack.resize(kMaxStackDepth); + // In some environments we don't get stack traces. For example in Android + // absl::GetStackTrace will return 0 indicating it didn't find any stack. The + // resultant formatted stack will be "", but that still equals the stack + // recorded in CordzInfo, which is also empty. The skip_count is 1 so that the + // line number of the current stack isn't included in the HasSubstr check. + local_stack.resize(absl::GetStackTrace(local_stack.data(), kMaxStackDepth, + /*skip_count=*/1)); + + std::string got_stack = FormatStack(info->GetStack()); + std::string expected_stack = FormatStack(local_stack); + // If TrackCord is inlined, got_stack should match expected_stack. If it isn't + // inlined, got_stack should include an additional frame not present in + // expected_stack. Either way, expected_stack should be a substring of + // got_stack. + EXPECT_THAT(got_stack, HasSubstr(expected_stack)); + + CordzInfo::UntrackCord(info); +} + +// Local helper functions to get different stacks for child and parent. +CordzInfo* TrackChildCord(CordRep* rep, const CordzInfo* parent) { + return CordzInfo::TrackCord(rep, parent); +} +CordzInfo* TrackParentCord(CordRep* rep) { + return CordzInfo::TrackCord(rep); +} + +TEST(CordzInfoTest, ParentStackV2) { + TestCordRep rep; + CordzInfo* info_parent = TrackParentCord(rep.rep); + CordzInfo* info_child = TrackChildCord(rep.rep, info_parent); + + std::string stack = FormatStack(info_parent->GetStack()); + std::string parent_stack = FormatStack(info_child->GetParentStack()); + EXPECT_THAT(stack, Eq(parent_stack)); + + CordzInfo::UntrackCord(info_parent); + CordzInfo::UntrackCord(info_child); +} + +TEST(CordzInfoTest, ParentStackEmpty) { + TestCordRep rep; + CordzInfo* info = TrackChildCord(rep.rep, nullptr); + EXPECT_TRUE(info->GetParentStack().empty()); + CordzInfo::UntrackCord(info); +} + +TEST(CordzInfoTest, CordzStatisticsV2) { + TestCordRep rep; + CordzInfo* info = TrackParentCord(rep.rep); + + CordzStatistics expected; + expected.size = 100; + info->RecordMetrics(expected.size); + + CordzStatistics actual = info->GetCordzStatistics(); + EXPECT_EQ(actual.size, expected.size); + + CordzInfo::UntrackCord(info); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_sample_token.cc b/absl/strings/internal/cordz_sample_token.cc new file mode 100644 index 00000000..ba1270d8 --- /dev/null +++ b/absl/strings/internal/cordz_sample_token.cc @@ -0,0 +1,64 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_sample_token.h" + +#include "absl/base/config.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_info.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +CordzSampleToken::Iterator& CordzSampleToken::Iterator::operator++() { + if (current_) { + current_ = current_->Next(*token_); + } + return *this; +} + +CordzSampleToken::Iterator CordzSampleToken::Iterator::operator++(int) { + Iterator it(*this); + operator++(); + return it; +} + +bool operator==(const CordzSampleToken::Iterator& lhs, + const CordzSampleToken::Iterator& rhs) { + return lhs.current_ == rhs.current_ && + (lhs.current_ == nullptr || lhs.token_ == rhs.token_); +} + +bool operator!=(const CordzSampleToken::Iterator& lhs, + const CordzSampleToken::Iterator& rhs) { + return !(lhs == rhs); +} + +CordzSampleToken::Iterator::reference CordzSampleToken::Iterator::operator*() + const { + return *current_; +} + +CordzSampleToken::Iterator::pointer CordzSampleToken::Iterator::operator->() + const { + return current_; +} + +CordzSampleToken::Iterator::Iterator(const CordzSampleToken* token) + : token_(token), current_(CordzInfo::Head(*token)) {} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_sample_token.h b/absl/strings/internal/cordz_sample_token.h new file mode 100644 index 00000000..28a1d70c --- /dev/null +++ b/absl/strings/internal/cordz_sample_token.h @@ -0,0 +1,97 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/config.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_info.h" + +#ifndef ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ +#define ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// The existence of a CordzSampleToken guarantees that a reader can traverse the +// global_cordz_infos_head linked-list without needing to hold a mutex. When a +// CordzSampleToken exists, all CordzInfo objects that would be destroyed are +// instead appended to a deletion queue. When the CordzSampleToken is destroyed, +// it will also clean up any of these CordzInfo objects. +// +// E.g., ST are CordzSampleToken objects and CH are CordzHandle objects. +// ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- global_delete_queue_tail +// +// This list tracks that CH1 and CH2 were created after ST1, so the thread +// holding ST1 might have a referece to CH1, CH2, ST2, and CH3. However, ST2 was +// created later, so the thread holding the ST2 token cannot have a reference to +// ST1, CH1, or CH2. If ST1 is cleaned up first, that thread will delete ST1, +// CH1, and CH2. If instead ST2 is cleaned up first, that thread will only +// delete ST2. +// +// If ST1 is cleaned up first, the new list will be: +// ST2 <- CH3 <- global_delete_queue_tail +// +// If ST2 is cleaned up first, the new list will be: +// ST1 <- CH1 <- CH2 <- CH3 <- global_delete_queue_tail +// +// All new CordzHandle objects are appended to the list, so if a new thread +// comes along before either ST1 or ST2 are cleaned up, the new list will be: +// ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- ST3 <- global_delete_queue_tail +// +// A thread must hold the global_delete_queue_mu mutex whenever it's altering +// this list. +// +// It is safe for thread that holds a CordzSampleToken to read +// global_cordz_infos at any time since the objects it is able to retrieve will +// not be deleted while the CordzSampleToken exists. +class CordzSampleToken : public CordzSnapshot { + public: + class Iterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = const CordzInfo&; + using difference_type = ptrdiff_t; + using pointer = const CordzInfo*; + using reference = value_type; + + Iterator() = default; + + Iterator& operator++(); + Iterator operator++(int); + friend bool operator==(const Iterator& lhs, const Iterator& rhs); + friend bool operator!=(const Iterator& lhs, const Iterator& rhs); + reference operator*() const; + pointer operator->() const; + + private: + friend class CordzSampleToken; + explicit Iterator(const CordzSampleToken* token); + + const CordzSampleToken* token_ = nullptr; + pointer current_ = nullptr; + }; + + CordzSampleToken() = default; + CordzSampleToken(const CordzSampleToken&) = delete; + CordzSampleToken& operator=(const CordzSampleToken&) = delete; + + Iterator begin() { return Iterator(this); } + Iterator end() { return Iterator(); } +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ diff --git a/absl/strings/internal/cordz_sample_token_test.cc b/absl/strings/internal/cordz_sample_token_test.cc new file mode 100644 index 00000000..8c052642 --- /dev/null +++ b/absl/strings/internal/cordz_sample_token_test.cc @@ -0,0 +1,209 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_sample_token.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/random/random.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Ne; + +struct TestCordRep { + CordRepFlat* rep; + + TestCordRep() { + rep = CordRepFlat::New(100); + rep->length = 100; + memset(rep->Data(), 1, 100); + } + ~TestCordRep() { CordRepFlat::Delete(rep); } +}; + +TEST(CordzSampleTokenTest, IteratorTraits) { + static_assert(std::is_copy_constructible::value, + ""); + static_assert(std::is_copy_assignable::value, ""); + static_assert(std::is_move_constructible::value, + ""); + static_assert(std::is_move_assignable::value, ""); + static_assert( + std::is_same< + std::iterator_traits::iterator_category, + std::input_iterator_tag>::value, + ""); + static_assert( + std::is_same::value_type, + const CordzInfo&>::value, + ""); + static_assert( + std::is_same< + std::iterator_traits::difference_type, + ptrdiff_t>::value, + ""); + static_assert( + std::is_same::pointer, + const CordzInfo*>::value, + ""); + static_assert( + std::is_same::reference, + const CordzInfo&>::value, + ""); +} + +TEST(CordzSampleTokenTest, IteratorEmpty) { + CordzSampleToken token; + EXPECT_THAT(token.begin(), Eq(token.end())); +} + +TEST(CordzSampleTokenTest, Iterator) { + TestCordRep rep1; + TestCordRep rep2; + TestCordRep rep3; + CordzInfo* info1 = CordzInfo::TrackCord(rep1.rep); + CordzInfo* info2 = CordzInfo::TrackCord(rep2.rep); + CordzInfo* info3 = CordzInfo::TrackCord(rep3.rep); + + CordzSampleToken token; + std::vector found; + for (const CordzInfo& cord_info : token) { + found.push_back(&cord_info); + } + + EXPECT_THAT(found, ElementsAre(info3, info2, info1)); + + CordzInfo::UntrackCord(info1); + CordzInfo::UntrackCord(info2); + CordzInfo::UntrackCord(info3); +} + +TEST(CordzSampleTokenTest, IteratorEquality) { + TestCordRep rep1; + TestCordRep rep2; + TestCordRep rep3; + CordzInfo* info1 = CordzInfo::TrackCord(rep1.rep); + + CordzSampleToken token1; + // lhs starts with the CordzInfo corresponding to cord1 at the head. + CordzSampleToken::Iterator lhs = token1.begin(); + + CordzInfo* info2 = CordzInfo::TrackCord(rep2.rep); + + CordzSampleToken token2; + // rhs starts with the CordzInfo corresponding to cord2 at the head. + CordzSampleToken::Iterator rhs = token2.begin(); + + CordzInfo* info3 = CordzInfo::TrackCord(rep3.rep); + + // lhs is on cord1 while rhs is on cord2. + EXPECT_THAT(lhs, Ne(rhs)); + + rhs++; + // lhs and rhs are both on cord1, but they didn't come from the same + // CordzSampleToken. + EXPECT_THAT(lhs, Ne(rhs)); + + lhs++; + rhs++; + // Both lhs and rhs are done, so they are on nullptr. + EXPECT_THAT(lhs, Eq(rhs)); + + CordzInfo::UntrackCord(info1); + CordzInfo::UntrackCord(info2); + CordzInfo::UntrackCord(info3); +} + +TEST(CordzSampleTokenTest, MultiThreaded) { + Notification stop; + static constexpr int kNumThreads = 4; + static constexpr int kNumCords = 3; + static constexpr int kNumTokens = 3; + absl::synchronization_internal::ThreadPool pool(kNumThreads); + + for (int i = 0; i < kNumThreads; ++i) { + pool.Schedule([&stop]() { + absl::BitGen gen; + TestCordRep reps[kNumCords]; + CordzInfo* infos[kNumCords] = {nullptr}; + std::vector> tokens; + tokens.resize(kNumTokens); + + while (!stop.HasBeenNotified()) { + // Randomly perform one of five actions: + // 1) Untrack + // 2) Track + // 3) Iterate over Cords visible to a token. + // 4) Unsample + // 5) Sample + int index = absl::Uniform(gen, 0, kNumCords); + if (absl::Bernoulli(gen, 0.5)) { + // Track/untrack. + if (infos[index]) { + // 1) Untrack + CordzInfo::UntrackCord(infos[index]); + infos[index] = nullptr; + } else { + // 2) Track + infos[index] = CordzInfo::TrackCord(reps[index].rep); + } + } else { + if (tokens[index]) { + if (absl::Bernoulli(gen, 0.5)) { + // 3) Iterate over Cords visible to a token. + for (const CordzInfo& info : *tokens[index]) { + // This is trivial work to allow us to compile the loop. + EXPECT_THAT(info.Next(*tokens[index]), Ne(&info)); + } + } else { + // 4) Unsample + tokens[index].reset(); + } + } else { + // 5) Sample + tokens[index] = absl::make_unique(); + } + } + } + for (CordzInfo* info : infos) { + if (info != nullptr) { + CordzInfo::UntrackCord(info); + } + } + }); + } + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h new file mode 100644 index 00000000..ce7c39aa --- /dev/null +++ b/absl/strings/internal/cordz_statistics.h @@ -0,0 +1,55 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzStatistics captures some meta information about a Cord's shape. +struct CordzStatistics { + // The size of the cord in bytes. This matches the result of Cord::size(). + int64_t size = 0; + + // The estimated memory used by the sampled cord. This value matches the + // value as reported by Cord::EstimatedMemoryUsage(). + // A value of 0 implies the property has not been recorded. + int64_t estimated_memory_usage = 0; + + // The effective memory used by the sampled cord, inversely weighted by the + // effective indegree of each allocated node. This is a representation of the + // fair share of memory usage that should be attributed to the sampled cord. + // This value is more useful for cases where one or more nodes are referenced + // by multiple Cord instances, and for cases where a Cord includes the same + // node multiple times (either directly or indirectly). + // A value of 0 implies the property has not been recorded. + int64_t estimated_fair_share_memory_usage = 0; + + // The total number of nodes referenced by this cord. + // For ring buffer Cords, this includes the 'ring buffer' node. + // A value of 0 implies the property has not been recorded. + int64_t node_count = 0; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ -- cgit v1.2.3 From 732c6540c19610d2653ce73c09eb6cb66da15f42 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 16 Apr 2021 11:25:33 -0700 Subject: Export of internal Abseil changes -- c7ce91501834b225bc9cf2d3fa2a319dd0b7f864 by Martijn Vels : Implement global data for CordzHandle in an ODR hardened way This change puts the global data into a global delete queue structure, and stores a reference to the global data in the handle itself. This hardens the implementation against ODR violations where handles are crossing dynamic library boundaries which are privately loaded. PiperOrigin-RevId: 368885636 GitOrigin-RevId: c7ce91501834b225bc9cf2d3fa2a319dd0b7f864 Change-Id: I9775151a760b30989dec9517e4bcd2183e8c1651 --- absl/strings/CMakeLists.txt | 1 + absl/strings/internal/cordz_handle.cc | 36 ++++++++++++++------------ absl/strings/internal/cordz_handle.h | 48 +++++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 044e38b3..8f41f064 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -655,6 +655,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::config + absl::raw_logging_internal absl::synchronization ) diff --git a/absl/strings/internal/cordz_handle.cc b/absl/strings/internal/cordz_handle.cc index 7e82f85b..22d07978 100644 --- a/absl/strings/internal/cordz_handle.cc +++ b/absl/strings/internal/cordz_handle.cc @@ -21,26 +21,26 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -ABSL_CONST_INIT absl::Mutex CordzHandle::mutex_(absl::kConstInit); -ABSL_CONST_INIT std::atomic CordzHandle::dq_tail_{nullptr}; +ABSL_CONST_INIT CordzHandle::Queue CordzHandle::global_queue_(absl::kConstInit); CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) { if (is_snapshot) { - MutexLock lock(&mutex_); - CordzHandle* dq_tail = dq_tail_.load(std::memory_order_acquire); + MutexLock lock(&queue_->mutex); + CordzHandle* dq_tail = queue_->dq_tail.load(std::memory_order_acquire); if (dq_tail != nullptr) { dq_prev_ = dq_tail; dq_tail->dq_next_ = this; } - dq_tail_.store(this, std::memory_order_release); + queue_->dq_tail.store(this, std::memory_order_release); } } CordzHandle::~CordzHandle() { + ODRCheck(); if (is_snapshot_) { std::vector to_delete; { - absl::MutexLock lock(&mutex_); + absl::MutexLock lock(&queue_->mutex); CordzHandle* next = dq_next_; if (dq_prev_ == nullptr) { // We were head of the queue, delete every CordzHandle until we reach @@ -56,7 +56,7 @@ CordzHandle::~CordzHandle() { if (next) { next->dq_prev_ = dq_prev_; } else { - dq_tail_.store(dq_prev_, std::memory_order_release); + queue_->dq_tail.store(dq_prev_, std::memory_order_release); } } for (CordzHandle* handle : to_delete) { @@ -67,13 +67,15 @@ CordzHandle::~CordzHandle() { void CordzHandle::Delete(CordzHandle* handle) { if (handle) { - if (!handle->is_snapshot_ && !UnsafeDeleteQueueEmpty()) { - MutexLock lock(&mutex_); - CordzHandle* dq_tail = dq_tail_.load(std::memory_order_acquire); + handle->ODRCheck(); + Queue* const queue = handle->queue_; + if (!handle->is_snapshot_ && !queue->IsEmpty()) { + MutexLock lock(&queue->mutex); + CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire); if (dq_tail != nullptr) { handle->dq_prev_ = dq_tail; dq_tail->dq_next_ = handle; - dq_tail_.store(handle, std::memory_order_release); + queue->dq_tail.store(handle, std::memory_order_release); return; } } @@ -83,8 +85,8 @@ void CordzHandle::Delete(CordzHandle* handle) { std::vector CordzHandle::DiagnosticsGetDeleteQueue() { std::vector handles; - MutexLock lock(&mutex_); - CordzHandle* dq_tail = dq_tail_.load(std::memory_order_acquire); + MutexLock lock(&global_queue_.mutex); + CordzHandle* dq_tail = global_queue_.dq_tail.load(std::memory_order_acquire); for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) { handles.push_back(p); } @@ -93,12 +95,13 @@ std::vector CordzHandle::DiagnosticsGetDeleteQueue() { bool CordzHandle::DiagnosticsHandleIsSafeToInspect( const CordzHandle* handle) const { + ODRCheck(); if (!is_snapshot_) return false; if (handle == nullptr) return true; if (handle->is_snapshot_) return false; bool snapshot_found = false; - MutexLock lock(&mutex_); - for (const CordzHandle* p = dq_tail_; p; p = p->dq_prev_) { + MutexLock lock(&queue_->mutex); + for (const CordzHandle* p = queue_->dq_tail; p; p = p->dq_prev_) { if (p == handle) return !snapshot_found; if (p == this) snapshot_found = true; } @@ -108,12 +111,13 @@ bool CordzHandle::DiagnosticsHandleIsSafeToInspect( std::vector CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() { + ODRCheck(); std::vector handles; if (!is_snapshot()) { return handles; } - MutexLock lock(&mutex_); + MutexLock lock(&queue_->mutex); for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) { if (!p->is_snapshot()) { handles.push_back(p); diff --git a/absl/strings/internal/cordz_handle.h b/absl/strings/internal/cordz_handle.h index 0235d4bd..71563c8b 100644 --- a/absl/strings/internal/cordz_handle.h +++ b/absl/strings/internal/cordz_handle.h @@ -19,6 +19,7 @@ #include #include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" #include "absl/synchronization/mutex.h" namespace absl { @@ -66,23 +67,42 @@ class CordzHandle { virtual ~CordzHandle(); private: - // Returns true if the delete queue is empty. This method does not acquire the - // lock, but does a 'load acquire' observation on the delete queue tail. It - // is used inside Delete() to check for the presence of a delete queue without - // holding the lock. The assumption is that the caller is in the state of - // 'being deleted', and can not be newly discovered by a concurrent 'being - // constructed' snapshot instance. Practically, this means that any such - // discovery (`find`, 'first' or 'next', etc) must have proper 'happens before - // / after' semantics and atomic fences. - static bool UnsafeDeleteQueueEmpty() ABSL_NO_THREAD_SAFETY_ANALYSIS { - return dq_tail_.load(std::memory_order_acquire) == nullptr; + // Global queue data. CordzHandle stores a pointer to the global queue + // instance to harden against ODR violations. + struct Queue { + constexpr explicit Queue(absl::ConstInitType) : mutex(absl::kConstInit) {} + + absl::Mutex mutex; + std::atomic dq_tail ABSL_GUARDED_BY(mutex){nullptr}; + + // Returns true if this delete queue is empty. This method does not acquire + // the lock, but does a 'load acquire' observation on the delete queue tail. + // It is used inside Delete() to check for the presence of a delete queue + // without holding the lock. The assumption is that the caller is in the + // state of 'being deleted', and can not be newly discovered by a concurrent + // 'being constructed' snapshot instance. Practically, this means that any + // such discovery (`find`, 'first' or 'next', etc) must have proper 'happens + // before / after' semantics and atomic fences. + bool IsEmpty() const ABSL_NO_THREAD_SAFETY_ANALYSIS { + return dq_tail.load(std::memory_order_acquire) == nullptr; + } + }; + + void ODRCheck() const { +#ifndef NDEBUG + ABSL_RAW_CHECK(queue_ == &global_queue_, "ODR violation in Cord"); +#endif } + ABSL_CONST_INIT static Queue global_queue_; + Queue* const queue_ = &global_queue_; const bool is_snapshot_; - static absl::Mutex mutex_; - static std::atomic dq_tail_ ABSL_GUARDED_BY(mutex_); - CordzHandle* dq_prev_ ABSL_GUARDED_BY(mutex_) = nullptr; - CordzHandle* dq_next_ ABSL_GUARDED_BY(mutex_) = nullptr; + + // dq_prev_ and dq_next_ require the global queue mutex to be held. + // Unfortunately we can't use thread annotations such that the thread safety + // analysis understands that queue_ and global_queue_ are one and the same. + CordzHandle* dq_prev_ = nullptr; + CordzHandle* dq_next_ = nullptr; }; class CordzSnapshot : public CordzHandle { -- cgit v1.2.3 From 1ae9b71c474628d60eb251a3f62967fe64151bb2 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 20 Apr 2021 07:17:48 -0700 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- ac1df60490c9583e475e22de7adfc40023196fbf by Martijn Vels : Change Cord constructor(string_view) to explicit make_tree and Cordz tracking This CL changes the ctor to use an easier to maintain model where Cord code explicitly invokes Cordz update or new / tree logic, which avoids the ambiguity of the 'branched' InlineRep::set_tree code. This removes the need to equip InlineRep with 'MethodIdentifier' or other necessary call info, and also is a cleaner model: InlineRep is carrying too much code now that should plainly sit in Cord, especially with all internal abstractions having moved to InlineData. See child CL(s) for desired state PiperOrigin-RevId: 369433619 -- b665af7f586e6c679a8b27d4f78d5a1d2b596058 by Abseil Team : Rename the 'Compare' template type to 'LessThan', as the passed-in function is expected to act like operator<. It is worth avoiding confusion with std::compare, which returns an int (-1/0/1), as due to implicit casting this can lead to hard-to-spot bugs. PiperOrigin-RevId: 369391118 -- c3c775269cad0f4982ec63f3616dd78bb9e52dca by Martijn Vels : Integrate CordzUpdateTracker into CordzInfo PiperOrigin-RevId: 369348824 -- 771d81ed357496c117179e1daec76eba5155932d by Martijn Vels : Replace mutex() with Lock() / Unlock() function Mini design future tracking of CordzInfo sampled cords: CordzInfo holds a CordRep* reference without a reference count. Cord is responsible for synchronizing updates for sampled cords such that the CordRep* contained in CordzInfo is at all times valid. This is done by scoping Lock() and Unlock() calls around the code modifying the code of a sampled cord. For example (using the future CL CordzUpdateScope()): CordzInfo* cordz_info = get_cordz_info(); CordzUpdateScope scope(cordz_info, CordzUpdateTracker::kRemovePrefix); CordRep* rep = RemovePrefixImpl(root); set_tree(rep); if (cordz_info) { cordz_info->SetCordRep(rep); } On CordzInfo::Unlock(), if the internal rep is null, the cord is no longer sampled, and CordzInfo will be deleted. Thus any update resulting in the Cord being inlined will automatically no longer be sampled. PiperOrigin-RevId: 369338802 -- 5563c12df04a1e965a03b50bdd032739c55c0706 by Martijn Vels : Add UpdateTracker to CordzStatistics PiperOrigin-RevId: 369318178 -- 6b4d8463722a3e55a3e8f6cb3741a41055e7f83e by Martijn Vels : Add kClear, kConstructor* and kUnknown values and fix typo PiperOrigin-RevId: 369297163 -- 041adcbc929789d6d53371a8236840fc350e1eeb by Derek Mauro : Switch from malloc to operator new in pool_urbg.cc so it can only fail by throwing/aborting PiperOrigin-RevId: 369274087 -- 5d97a5f43e3f2d02d0a5bbe586d93b5751812981 by Benjamin Barenblat : Correct Thumb function bound computation in the symbolizer On 32-bit ARM, all functions are aligned to multiples of two bytes, and the lowest-order bit in a function’s address is ignored by the CPU when computing branch targets. That bit is still present in instructions and ELF symbol tables, though; it’s repurposed to indicate whether the function contains ARM or Thumb code. If the symbolizer doesn’t ignore that bit, it will believe Thumb functions have boundaries that are off by one byte, so instruct the symbolizer to null out the lowest-order bit after retrieving it from the symbol table. PiperOrigin-RevId: 369254082 -- 462bb307c6cc332c1e2c3adb5f0cad51804bf937 by Derek Mauro : Add a check for malloc failure in pool_urbg.cc GitHub #940 PiperOrigin-RevId: 369238100 GitOrigin-RevId: ac1df60490c9583e475e22de7adfc40023196fbf Change-Id: Ic6ec91c62cd3a0031f6a75a43a83da959ece2d25 --- absl/algorithm/container.h | 180 ++++++++++----------- absl/debugging/symbolize_elf.inc | 10 ++ absl/debugging/symbolize_test.cc | 43 +++++ absl/random/internal/pool_urbg.cc | 7 +- absl/strings/BUILD.bazel | 4 + absl/strings/CMakeLists.txt | 4 + absl/strings/cord.cc | 6 +- absl/strings/cord.h | 3 + absl/strings/internal/cordz_info.cc | 78 +++++++-- absl/strings/internal/cordz_info.h | 67 +++++--- absl/strings/internal/cordz_info_test.cc | 76 +++++++-- absl/strings/internal/cordz_statistics.h | 12 ++ absl/strings/internal/cordz_update_tracker.h | 22 +-- absl/strings/internal/cordz_update_tracker_test.cc | 25 ++- 14 files changed, 382 insertions(+), 155 deletions(-) diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 6398438f..1652e7b0 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -905,11 +905,11 @@ void c_sort(C& c) { // Overload of c_sort() for performing a `comp` comparison other than the // default `operator<`. -template -void c_sort(C& c, Compare&& comp) { +template +void c_sort(C& c, LessThan&& comp) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_stable_sort() @@ -925,11 +925,11 @@ void c_stable_sort(C& c) { // Overload of c_stable_sort() for performing a `comp` comparison other than the // default `operator<`. -template -void c_stable_sort(C& c, Compare&& comp) { +template +void c_stable_sort(C& c, LessThan&& comp) { std::stable_sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_is_sorted() @@ -944,11 +944,11 @@ bool c_is_sorted(const C& c) { // c_is_sorted() overload for performing a `comp` comparison other than the // default `operator<`. -template -bool c_is_sorted(const C& c, Compare&& comp) { +template +bool c_is_sorted(const C& c, LessThan&& comp) { return std::is_sorted(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_partial_sort() @@ -966,14 +966,14 @@ void c_partial_sort( // Overload of c_partial_sort() for performing a `comp` comparison other than // the default `operator<`. -template +template void c_partial_sort( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter middle, - Compare&& comp) { + LessThan&& comp) { std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_partial_sort_copy() @@ -994,15 +994,15 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { // Overload of c_partial_sort_copy() for performing a `comp` comparison other // than the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, - Compare&& comp) { + LessThan&& comp) { return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_begin(result), container_algorithm_internal::c_end(result), - std::forward(comp)); + std::forward(comp)); } // c_is_sorted_until() @@ -1018,12 +1018,12 @@ container_algorithm_internal::ContainerIter c_is_sorted_until(C& c) { // Overload of c_is_sorted_until() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_is_sorted_until( - C& c, Compare&& comp) { + C& c, LessThan&& comp) { return std::is_sorted_until(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_nth_element() @@ -1043,14 +1043,14 @@ void c_nth_element( // Overload of c_nth_element() for performing a `comp` comparison other than // the default `operator<`. -template +template void c_nth_element( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter nth, - Compare&& comp) { + LessThan&& comp) { std::nth_element(container_algorithm_internal::c_begin(sequence), nth, container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1072,12 +1072,12 @@ container_algorithm_internal::ContainerIter c_lower_bound( // Overload of c_lower_bound() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_lower_bound( - Sequence& sequence, T&& value, Compare&& comp) { + Sequence& sequence, T&& value, LessThan&& comp) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + std::forward(value), std::forward(comp)); } // c_upper_bound() @@ -1095,12 +1095,12 @@ container_algorithm_internal::ContainerIter c_upper_bound( // Overload of c_upper_bound() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIter c_upper_bound( - Sequence& sequence, T&& value, Compare&& comp) { + Sequence& sequence, T&& value, LessThan&& comp) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + std::forward(value), std::forward(comp)); } // c_equal_range() @@ -1118,12 +1118,12 @@ c_equal_range(Sequence& sequence, T&& value) { // Overload of c_equal_range() for performing a `comp` comparison other than // the default `operator<`. -template +template container_algorithm_internal::ContainerIterPairType -c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { +c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) { return std::equal_range(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(value), std::forward(comp)); + std::forward(value), std::forward(comp)); } // c_binary_search() @@ -1140,12 +1140,12 @@ bool c_binary_search(Sequence&& sequence, T&& value) { // Overload of c_binary_search() for performing a `comp` comparison other than // the default `operator<`. -template -bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) { +template +bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward(value), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1166,14 +1166,14 @@ OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { // Overload of c_merge() for performing a `comp` comparison other than // the default `operator<`. -template +template OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, - Compare&& comp) { + LessThan&& comp) { return std::merge(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), result, - std::forward(comp)); + std::forward(comp)); } // c_inplace_merge() @@ -1189,13 +1189,13 @@ void c_inplace_merge(C& c, // Overload of c_inplace_merge() for performing a merge using a `comp` other // than `operator<`. -template +template void c_inplace_merge(C& c, container_algorithm_internal::ContainerIter middle, - Compare&& comp) { + LessThan&& comp) { std::inplace_merge(container_algorithm_internal::c_begin(c), middle, container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_includes() @@ -1213,13 +1213,13 @@ bool c_includes(const C1& c1, const C2& c2) { // Overload of c_includes() for performing a merge using a `comp` other than // `operator<`. -template -bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { +template +bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) { return std::includes(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), - std::forward(comp)); + std::forward(comp)); } // c_set_union() @@ -1243,7 +1243,7 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { // Overload of c_set_union() for performing a merge using a `comp` other than // `operator<`. -template ::value, void>::type, @@ -1251,12 +1251,12 @@ template ::value, void>::type> OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, - Compare&& comp) { + LessThan&& comp) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_intersection() @@ -1280,7 +1280,7 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Overload of c_set_intersection() for performing a merge using a `comp` other // than `operator<`. -template ::value, void>::type, @@ -1288,12 +1288,12 @@ template ::value, void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { + OutputIterator output, LessThan&& comp) { return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_difference() @@ -1318,7 +1318,7 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Overload of c_set_difference() for performing a merge using a `comp` other // than `operator<`. -template ::value, void>::type, @@ -1326,12 +1326,12 @@ template ::value, void>::type> OutputIterator c_set_difference(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { + OutputIterator output, LessThan&& comp) { return std::set_difference(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } // c_set_symmetric_difference() @@ -1357,7 +1357,7 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, // Overload of c_set_symmetric_difference() for performing a merge using a // `comp` other than `operator<`. -template ::value, void>::type, @@ -1366,13 +1366,13 @@ template ::type> OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output, - Compare&& comp) { + LessThan&& comp) { return std::set_symmetric_difference( container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1391,11 +1391,11 @@ void c_push_heap(RandomAccessContainer& sequence) { // Overload of c_push_heap() for performing a push operation on a heap using a // `comp` other than `operator<`. -template -void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::push_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_pop_heap() @@ -1410,11 +1410,11 @@ void c_pop_heap(RandomAccessContainer& sequence) { // Overload of c_pop_heap() for performing a pop operation on a heap using a // `comp` other than `operator<`. -template -void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::pop_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_make_heap() @@ -1429,11 +1429,11 @@ void c_make_heap(RandomAccessContainer& sequence) { // Overload of c_make_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::make_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_sort_heap() @@ -1448,11 +1448,11 @@ void c_sort_heap(RandomAccessContainer& sequence) { // Overload of c_sort_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) { +template +void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::sort_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_is_heap() @@ -1467,11 +1467,11 @@ bool c_is_heap(const RandomAccessContainer& sequence) { // Overload of c_is_heap() for performing heap comparisons using a // `comp` other than `operator<` -template -bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) { +template +bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_is_heap_until() @@ -1487,12 +1487,12 @@ c_is_heap_until(RandomAccessContainer& sequence) { // Overload of c_is_heap_until() for performing heap comparisons using a // `comp` other than `operator<` -template +template container_algorithm_internal::ContainerIter -c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) { +c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap_until(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1513,12 +1513,12 @@ container_algorithm_internal::ContainerIter c_min_element( // Overload of c_min_element() for performing a `comp` comparison other than // `operator<`. -template +template container_algorithm_internal::ContainerIter c_min_element( - Sequence& sequence, Compare&& comp) { + Sequence& sequence, LessThan&& comp) { return std::min_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_max_element() @@ -1535,12 +1535,12 @@ container_algorithm_internal::ContainerIter c_max_element( // Overload of c_max_element() for performing a `comp` comparison other than // `operator<`. -template +template container_algorithm_internal::ContainerIter c_max_element( - Sequence& sequence, Compare&& comp) { + Sequence& sequence, LessThan&& comp) { return std::max_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward(comp)); + std::forward(comp)); } // c_minmax_element() @@ -1558,12 +1558,12 @@ c_minmax_element(C& c) { // Overload of c_minmax_element() for performing `comp` comparisons other than // `operator<`. -template +template container_algorithm_internal::ContainerIterPairType -c_minmax_element(C& c, Compare&& comp) { +c_minmax_element(C& c, LessThan&& comp) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ @@ -1588,15 +1588,15 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { // Overload of c_lexicographical_compare() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template +template bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, - Compare&& comp) { + LessThan&& comp) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), container_algorithm_internal::c_begin(sequence2), container_algorithm_internal::c_end(sequence2), - std::forward(comp)); + std::forward(comp)); } // c_next_permutation() @@ -1612,11 +1612,11 @@ bool c_next_permutation(C& c) { // Overload of c_next_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_next_permutation(C& c, Compare&& comp) { +template +bool c_next_permutation(C& c, LessThan&& comp) { return std::next_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } // c_prev_permutation() @@ -1632,11 +1632,11 @@ bool c_prev_permutation(C& c) { // Overload of c_prev_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template -bool c_prev_permutation(C& c, Compare&& comp) { +template +bool c_prev_permutation(C& c, LessThan&& comp) { return std::prev_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward(comp)); + std::forward(comp)); } //------------------------------------------------------------------------------ diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index f4d5727b..87dbd078 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -701,6 +701,16 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( const char *start_address = ComputeOffset(original_start_address, relocation); +#ifdef __arm__ + // ARM functions are always aligned to multiples of two bytes; the + // lowest-order bit in start_address is ignored by the CPU and indicates + // whether the function contains ARM (0) or Thumb (1) code. We don't care + // about what encoding is being used; we just want the real start address + // of the function. + start_address = reinterpret_cast( + reinterpret_cast(start_address) & ~1); +#endif + if (deref_function_descriptor_pointer && InSection(original_start_address, opd)) { // The opd section is mapped into memory. Just dereference diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index a2dd4956..35de02e2 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -477,6 +477,46 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { #endif } +#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) +// Test that we correctly identify bounds of Thumb functions on ARM. +// +// Thumb functions have the lowest-order bit set in their addresses in the ELF +// symbol table. This requires some extra logic to properly compute function +// bounds. To test this logic, nudge a Thumb function right up against an ARM +// function and try to symbolize the ARM function. +// +// A naive implementation will simply use the Thumb function's entry point as +// written in the symbol table and will therefore treat the Thumb function as +// extending one byte further in the instruction stream than it actually does. +// When asked to symbolize the start of the ARM function, it will identify an +// overlap between the Thumb and ARM functions, and it will return the name of +// the Thumb function. +// +// A correct implementation, on the other hand, will null out the lowest-order +// bit in the Thumb function's entry point. It will correctly compute the end of +// the Thumb function, it will find no overlap between the Thumb and ARM +// functions, and it will return the name of the ARM function. + +__attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) { + return x * x * x; +} + +__attribute__((target("arm"))) int ArmThumbOverlapArm(int x) { + return x * x * x; +} + +void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() { +#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) + const char *symbol = TrySymbolize((void *)&ArmThumbOverlapArm); + ABSL_RAW_CHECK(symbol != nullptr, "TestArmThumbOverlap failed"); + ABSL_RAW_CHECK(strcmp("ArmThumbOverlapArm()", symbol) == 0, + "TestArmThumbOverlap failed"); + std::cout << "TestArmThumbOverlap passed" << std::endl; +#endif +} + +#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) + #elif defined(_WIN32) #if !defined(ABSL_CONSUME_DLL) @@ -551,6 +591,9 @@ int main(int argc, char **argv) { TestWithPCInsideInlineFunction(); TestWithPCInsideNonInlineFunction(); TestWithReturnAddress(); +#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) + TestArmThumbOverlap(); +#endif #endif return RUN_ALL_TESTS(); diff --git a/absl/random/internal/pool_urbg.cc b/absl/random/internal/pool_urbg.cc index 5bee5307..725100a4 100644 --- a/absl/random/internal/pool_urbg.cc +++ b/absl/random/internal/pool_urbg.cc @@ -194,11 +194,10 @@ RandenPoolEntry* PoolAlignedAlloc() { // Not all the platforms that we build for have std::aligned_alloc, however // since we never free these objects, we can over allocate and munge the // pointers to the correct alignment. - void* memory = std::malloc(sizeof(RandenPoolEntry) + kAlignment); - auto x = reinterpret_cast(memory); + intptr_t x = reinterpret_cast( + new char[sizeof(RandenPoolEntry) + kAlignment]); auto y = x % kAlignment; - void* aligned = - (y == 0) ? memory : reinterpret_cast(x + kAlignment - y); + void* aligned = reinterpret_cast(y == 0 ? x : (x + kAlignment - y)); return new (aligned) RandenPoolEntry(); } diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 1306ea65..95ed0868 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -368,6 +368,7 @@ cc_library( ":cord_internal", ":cordz_handle", ":cordz_statistics", + ":cordz_update_tracker", "//absl/base:config", "//absl/base:core_headers", "//absl/debugging:stacktrace", @@ -406,6 +407,7 @@ cc_library( hdrs = ["internal/cordz_statistics.h"], copts = ABSL_DEFAULT_COPTS, deps = [ + ":cordz_update_tracker", "//absl/base:config", ], ) @@ -450,6 +452,8 @@ cc_test( ":cord_internal", ":cordz_handle", ":cordz_info", + ":cordz_statistics", + ":cordz_update_tracker", ":strings", "//absl/base:config", "//absl/debugging:stacktrace", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 8f41f064..5a13831f 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -641,6 +641,7 @@ absl_cc_library( DEPS absl::config absl::core_headers + absl::cordz_update_tracker absl::synchronization ) @@ -690,6 +691,7 @@ absl_cc_library( absl::cord_internal absl::cordz_handle absl::cordz_statistics + absl::cordz_update_tracker absl::core_headers absl::span absl::stacktrace @@ -707,6 +709,8 @@ absl_cc_test( absl::cord_test_helpers absl::cordz_handle absl::cordz_info + absl::cordz_statistics + absl::cordz_update_tracker absl::span absl::stacktrace absl::symbolize diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index d33d6a0d..770c5a70 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -39,6 +39,7 @@ #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -544,7 +545,10 @@ Cord::Cord(absl::string_view src) { if (n <= InlineRep::kMaxInline) { contents_.set_data(src.data(), n, false); } else { - contents_.set_tree(NewTree(src.data(), n, 0)); + contents_.data_.make_tree(NewTree(src.data(), n, 0)); + if (ABSL_PREDICT_FALSE(absl::cord_internal::cordz_should_profile())) { + contents_.StartProfiling(); + } } } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index de1470b2..1e6a73a5 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -84,6 +84,7 @@ #include "absl/strings/internal/cordz_functions.h" #include "absl/strings/internal/cordz_info.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/internal/string_constant.h" #include "absl/strings/string_view.h" @@ -668,6 +669,8 @@ class Cord { explicit constexpr Cord(strings_internal::StringConstant); private: + using CordzUpdateTracker = cord_internal::CordzUpdateTracker; + friend class CordTestPeer; friend bool operator==(const Cord& lhs, const Cord& rhs); friend bool operator==(const Cord& lhs, absl::string_view rhs); diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 4dec63d5..6540d134 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -19,6 +19,7 @@ #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/synchronization/mutex.h" #include "absl/types/span.h" @@ -44,18 +45,15 @@ CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const { return ci_next_unsafe(); } -CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src) { - CordzInfo* ci = new CordzInfo(rep); - if (src) { - ci->parent_stack_depth_ = src->stack_depth_; - memcpy(ci->parent_stack_, src->stack_, sizeof(void*) * src->stack_depth_); - } +CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src, + MethodIdentifier method) { + CordzInfo* ci = new CordzInfo(rep, src, method); ci->Track(); return ci; } -CordzInfo* CordzInfo::TrackCord(CordRep* rep) { - return TrackCord(rep, nullptr); +CordzInfo* CordzInfo::TrackCord(CordRep* rep, MethodIdentifier method) { + return TrackCord(rep, nullptr, method); } void CordzInfo::UntrackCord(CordzInfo* cordz_info) { @@ -66,12 +64,35 @@ void CordzInfo::UntrackCord(CordzInfo* cordz_info) { } } -CordzInfo::CordzInfo(CordRep* rep) +CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) { + if (src == nullptr) return MethodIdentifier::kUnknown; + return src->parent_method_ != MethodIdentifier::kUnknown ? src->parent_method_ + : src->method_; +} + +int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) { + assert(stack); + if (src == nullptr) return 0; + if (src->parent_stack_depth_) { + memcpy(stack, src->parent_stack_, src->parent_stack_depth_ * sizeof(void*)); + return src->parent_stack_depth_; + } + memcpy(stack, src->stack_, src->stack_depth_ * sizeof(void*)); + return src->stack_depth_; +} + +CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, + MethodIdentifier method) : rep_(rep), stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth, /*skip_count=*/1)), - parent_stack_depth_(0), - create_time_(absl::Now()) {} + parent_stack_depth_(FillParentStack(src, parent_stack_)), + method_(method), + parent_method_(GetParentMethod(src)), + create_time_(absl::Now()), + size_(rep->length) { + update_tracker_.LossyAdd(method); +} CordzInfo::~CordzInfo() { // `rep_` is potentially kept alive if CordzInfo is included @@ -96,7 +117,7 @@ void CordzInfo::Untrack() { { // TODO(b/117940323): change this to assuming ownership instead once all // Cord logic is properly keeping `rep_` in sync with the Cord root rep. - absl::MutexLock lock(&mutex()); + absl::MutexLock lock(&mutex_); rep_ = nullptr; } @@ -120,9 +141,31 @@ void CordzInfo::Untrack() { } } +void CordzInfo::Lock(MethodIdentifier method) + ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_) { + mutex_.Lock(); + update_tracker_.LossyAdd(method); + assert(rep_); +} + +void CordzInfo::Unlock() ABSL_UNLOCK_FUNCTION(mutex_) { + bool tracked = rep_ != nullptr; + if (rep_) { + size_.store(rep_->length); + } + mutex_.Unlock(); + if (!tracked) { + Untrack(); + CordzHandle::Delete(this); + } +} + void CordzInfo::SetCordRep(CordRep* rep) { - mutex().AssertHeld(); + mutex_.AssertHeld(); rep_ = rep; + if (rep) { + size_.store(rep->length); + } } absl::Span CordzInfo::GetStack() const { @@ -133,6 +176,15 @@ absl::Span CordzInfo::GetParentStack() const { return absl::MakeConstSpan(parent_stack_, parent_stack_depth_); } +CordzStatistics CordzInfo::GetCordzStatistics() const { + CordzStatistics stats; + stats.method = method_; + stats.parent_method = parent_method_; + stats.update_tracker = update_tracker_; + stats.size = size_.load(std::memory_order_relaxed); + return stats; +} + } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index c1090a1c..5345be75 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -24,6 +24,7 @@ #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/synchronization/mutex.h" #include "absl/types/span.h" @@ -42,13 +43,20 @@ namespace cord_internal { // the destructor of a CordzSampleToken object. class CordzInfo : public CordzHandle { public: + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + // All profiled Cords should be accompanied by a call to TrackCord. // TrackCord creates a CordzInfo instance which tracks important metrics of // the sampled cord. CordzInfo instances are placed in a global list which is // used to discover and snapshot all actively tracked cords. // Callers are responsible for calling UntrackCord() before the tracked Cord // instance is deleted, or to stop tracking the sampled Cord. - static CordzInfo* TrackCord(CordRep* rep); + // Callers are also responsible for guarding changes to `rep` through the + // Lock() and Unlock() calls, and calling SetCordRep() if the root of the + // sampled cord changes before the old root has been unreffed and/or deleted. + // `method` identifies the Cord method which initiated the cord to be sampled. + static CordzInfo* TrackCord( + CordRep* rep, MethodIdentifier method = MethodIdentifier::kUnknown); // Stops tracking changes for a sampled cord, and deletes the provided info. // This function must be called before the sampled cord instance is deleted, @@ -58,10 +66,12 @@ class CordzInfo : public CordzHandle { static void UntrackCord(CordzInfo* cordz_info); // Identical to TrackCord(), except that this function fills the - // 'parent_stack' property of the returned CordzInfo instance from the - // provided `src` instance if `src` is not null. + // `parent_stack` and `parent_method` properties of the returned CordzInfo + // instance from the provided `src` instance if `src` is not null. // This function should be used for sampling 'copy constructed' cords. - static CordzInfo* TrackCord(CordRep* rep, const CordzInfo* src); + static CordzInfo* TrackCord( + CordRep* rep, const CordzInfo* src, + MethodIdentifier method = MethodIdentifier::kUnknown); CordzInfo() = delete; CordzInfo(const CordzInfo&) = delete; @@ -73,17 +83,20 @@ class CordzInfo : public CordzHandle { // Retrieves the next oldest existing CordzInfo older than 'this' instance. CordzInfo* Next(const CordzSnapshot& snapshot) const; - // Returns a reference to the mutex guarding the `rep` property of this - // instance. CordzInfo instances hold a weak reference to the rep pointer of - // sampled cords, and rely on Cord logic to update the rep pointer when the - // underlying root tree or ring of the cord changes. - absl::Mutex& mutex() const { return mutex_; } + // Locks this instance for the update identified by `method`. + // Increases the count for `method` in `update_tracker`. + void Lock(MethodIdentifier method) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_); + + // Unlocks this instance. If the contained `rep` has been set to null + // indicating the Cord has been cleared or is otherwise no longer sampled, + // then this method will delete this CordzInfo instance. + void Unlock() ABSL_UNLOCK_FUNCTION(mutex_); // Updates the `rep' property of this instance. This methods is invoked by // Cord logic each time the root node of a sampled Cord changes, and before // the old root reference count is deleted. This guarantees that collection // code can always safely take a reference on the tracked cord. - // Requires `mutex()` to be held. + // Requires a lock to be held through the `Lock()` method. // TODO(b/117940323): annotate with ABSL_EXCLUSIVE_LOCKS_REQUIRED once all // Cord code is in a state where this can be proven true by the compiler. void SetCordRep(CordRep* rep); @@ -106,15 +119,10 @@ class CordzInfo : public CordzHandle { // from, or being assigned the value of an existing (sampled) cord. absl::Span GetParentStack() const; - // Retrieve the CordzStatistics associated with this Cord. The statistics are - // only updated when a Cord goes through a mutation, such as an Append or - // RemovePrefix. The refcounts can change due to external events, so the - // reported refcount stats might be incorrect. - CordzStatistics GetCordzStatistics() const { - CordzStatistics stats; - stats.size = size_.load(std::memory_order_relaxed); - return stats; - } + // Retrieves the CordzStatistics associated with this Cord. The statistics + // are only updated when a Cord goes through a mutation, such as an Append + // or RemovePrefix. + CordzStatistics GetCordzStatistics() const; // Records size metric for this CordzInfo instance. void RecordMetrics(int64_t size) { @@ -124,9 +132,21 @@ class CordzInfo : public CordzHandle { private: static constexpr int kMaxStackDepth = 64; - explicit CordzInfo(CordRep* tree); + explicit CordzInfo(CordRep* rep, const CordzInfo* src, + MethodIdentifier method); ~CordzInfo() override; + // Returns the parent method from `src`, which is either `parent_method_` or + // `method_` depending on `parent_method_` being kUnknown. + // Returns kUnknown if `src` is null. + static MethodIdentifier GetParentMethod(const CordzInfo* src); + + // Fills the provided stack from `src`, copying either `parent_stack_` or + // `stack_` depending on `parent_stack_` being empty, returning the size of + // the parent stack. + // Returns 0 if `src` is null. + static int FillParentStack(const CordzInfo* src, void** stack); + void Track(); void Untrack(); @@ -149,12 +169,15 @@ class CordzInfo : public CordzHandle { std::atomic ci_next_ ABSL_GUARDED_BY(ci_mutex_){nullptr}; mutable absl::Mutex mutex_; - CordRep* rep_ ABSL_GUARDED_BY(mutex()); + CordRep* rep_ ABSL_GUARDED_BY(mutex_); void* stack_[kMaxStackDepth]; void* parent_stack_[kMaxStackDepth]; const int stack_depth_; - int parent_stack_depth_; + const int parent_stack_depth_; + const MethodIdentifier method_; + const MethodIdentifier parent_method_; + CordzUpdateTracker update_tracker_; const absl::Time create_time_; // Last recorded size for the cord. diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index be20e4a4..66acf9b6 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -23,6 +23,7 @@ #include "absl/debugging/symbolize.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/str_cat.h" #include "absl/types/span.h" @@ -47,6 +48,12 @@ struct TestCordRep { ~TestCordRep() { CordRepFlat::Delete(rep); } }; +// Used test values +auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown; +auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; +auto constexpr kChildMethod = CordzUpdateTracker::kConstructorCord; +auto constexpr kUpdateMethod = CordzUpdateTracker::kAppendString; + // Local less verbose helper std::vector DeleteQueue() { return CordzHandle::DiagnosticsGetDeleteQueue(); @@ -90,15 +97,27 @@ TEST(CordzInfoTest, SetCordRep) { CordzInfo* info = CordzInfo::TrackCord(rep.rep); TestCordRep rep2; - { - absl::MutexLock lock(&info->mutex()); - info->SetCordRep(rep2.rep); - } + info->Lock(CordzUpdateTracker::kAppendCord); + info->SetCordRep(rep2.rep); + info->Unlock(); EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep2.rep)); CordzInfo::UntrackCord(info); } +TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) { + TestCordRep rep; + CordzInfo* info = CordzInfo::TrackCord(rep.rep); + + info->Lock(CordzUpdateTracker::kAppendString); + info->SetCordRep(nullptr); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr)); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info)); + + info->Unlock(); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); +} + #if GTEST_HAS_DEATH_TEST TEST(CordzInfoTest, SetCordRepRequiresMutex) { @@ -191,13 +210,41 @@ TEST(CordzInfoTest, StackV2) { // Local helper functions to get different stacks for child and parent. CordzInfo* TrackChildCord(CordRep* rep, const CordzInfo* parent) { - return CordzInfo::TrackCord(rep, parent); + return CordzInfo::TrackCord(rep, parent, kChildMethod); } CordzInfo* TrackParentCord(CordRep* rep) { - return CordzInfo::TrackCord(rep); + return CordzInfo::TrackCord(rep, kTrackCordMethod); } -TEST(CordzInfoTest, ParentStackV2) { +TEST(CordzInfoTest, GetStatistics) { + TestCordRep rep; + CordzInfo* info = TrackParentCord(rep.rep); + + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kTrackCordMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kTrackCordMethod), Eq(1)); + + CordzInfo::UntrackCord(info); +} + +TEST(CordzInfoTest, LockCountsMethod) { + TestCordRep rep; + CordzInfo* info = TrackParentCord(rep.rep); + + info->Lock(kUpdateMethod); + info->Unlock(); + info->Lock(kUpdateMethod); + info->Unlock(); + + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.update_tracker.Value(kUpdateMethod), Eq(2)); + + CordzInfo::UntrackCord(info); +} + +TEST(CordzInfoTest, FromParent) { TestCordRep rep; CordzInfo* info_parent = TrackParentCord(rep.rep); CordzInfo* info_child = TrackChildCord(rep.rep, info_parent); @@ -206,18 +253,29 @@ TEST(CordzInfoTest, ParentStackV2) { std::string parent_stack = FormatStack(info_child->GetParentStack()); EXPECT_THAT(stack, Eq(parent_stack)); + CordzStatistics statistics = info_child->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kChildMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kTrackCordMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); + CordzInfo::UntrackCord(info_parent); CordzInfo::UntrackCord(info_child); } -TEST(CordzInfoTest, ParentStackEmpty) { +TEST(CordzInfoTest, FromParentNullptr) { TestCordRep rep; CordzInfo* info = TrackChildCord(rep.rep, nullptr); EXPECT_TRUE(info->GetParentStack().empty()); + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kChildMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); CordzInfo::UntrackCord(info); } -TEST(CordzInfoTest, CordzStatisticsV2) { +TEST(CordzInfoTest, RecordMetrics) { TestCordRep rep; CordzInfo* info = TrackParentCord(rep.rep); diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h index ce7c39aa..6e335c0b 100644 --- a/absl/strings/internal/cordz_statistics.h +++ b/absl/strings/internal/cordz_statistics.h @@ -18,6 +18,7 @@ #include #include "absl/base/config.h" +#include "absl/strings/internal/cordz_update_tracker.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -25,6 +26,8 @@ namespace cord_internal { // CordzStatistics captures some meta information about a Cord's shape. struct CordzStatistics { + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + // The size of the cord in bytes. This matches the result of Cord::size(). int64_t size = 0; @@ -46,6 +49,15 @@ struct CordzStatistics { // For ring buffer Cords, this includes the 'ring buffer' node. // A value of 0 implies the property has not been recorded. int64_t node_count = 0; + + // The cord method responsible for sampling the cord. + MethodIdentifier method = MethodIdentifier::kUnknown; + + // The cord method responsible for sampling the parent cord if applicable. + MethodIdentifier parent_method = MethodIdentifier::kUnknown; + + // Update tracker tracking invocation count per cord method. + CordzUpdateTracker update_tracker; }; } // namespace cord_internal diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index 3c617e93..a090676d 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -38,20 +38,24 @@ class CordzUpdateTracker { public: // Tracked update methods. enum MethodIdentifier { - kAssignString, - kAssignCord, - kMoveAssignCord, - kAppendString, + kUnknown, kAppendCord, - kMoveAppendCord, - kPrependString, - kPrependCord, - kMovePrependCord, kAppendExternalMemory, + kAppendString, + kAssignCord, + kAssignString, + kClear, + kConstructorCord, + kConstructorString, kFlatten, kGetAppendRegion, + kMoveAppendCord, + kMoveAssignCord, + kMovePrependCord, + kPrependCord, + kPrependString, kRemovePrefix, - kRemoveSuffic, + kRemoveSuffix, kSubCord, // kNumMethods defines the number of entries: must be the last entry. diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc index 45782046..eda662f2 100644 --- a/absl/strings/internal/cordz_update_tracker_test.cc +++ b/absl/strings/internal/cordz_update_tracker_test.cc @@ -36,13 +36,24 @@ using Methods = std::array; // Returns an array of all methods defined in `MethodIdentifier` Methods AllMethods() { - return Methods{Method::kAssignString, Method::kAssignCord, - Method::kMoveAssignCord, Method::kAppendString, - Method::kAppendCord, Method::kMoveAppendCord, - Method::kPrependString, Method::kPrependCord, - Method::kMovePrependCord, Method::kAppendExternalMemory, - Method::kFlatten, Method::kGetAppendRegion, - Method::kRemovePrefix, Method::kRemoveSuffic, + return Methods{Method::kUnknown, + Method::kAppendCord, + Method::kAppendExternalMemory, + Method::kAppendString, + Method::kAssignCord, + Method::kAssignString, + Method::kClear, + Method::kConstructorCord, + Method::kConstructorString, + Method::kFlatten, + Method::kGetAppendRegion, + Method::kMoveAppendCord, + Method::kMoveAssignCord, + Method::kMovePrependCord, + Method::kPrependCord, + Method::kPrependString, + Method::kRemovePrefix, + Method::kRemoveSuffix, Method::kSubCord}; } -- cgit v1.2.3 From e38e1aae3866a4ae3a41ccb38d3f05618ea30ca4 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 22 Apr 2021 04:30:35 -0700 Subject: Export of internal Abseil changes -- 9e0df3dd23da17cd0ff75c93c1493a858032639c by Martijn Vels : Prep work for changing Cordz instrumentation Create CordzInfo::TrackCord() and CordzInfo::MaybeTrackCord() methods on InlineData This follows a suggestion from kfm@ to move where possible Cordz logic out of Cord and Cord::InlineRep, which we can now that InlineData provides us the abstraction of Cords internal data. PiperOrigin-RevId: 369844310 -- 45f39709033bd3bc09fa1a7880a5b3c9eaa617c7 by Abseil Team : Fix dependence on C++20 constexpr default construction of std::atomic. PiperOrigin-RevId: 369745251 -- 195ae230963c95068406ab0e73b4e711b5f3cd62 by Martijn Vels : Reduce the cost of 'SetCordRep()` This change inlines SetCordRep(), which is currently two stores and a branch, going forward a naked store. AssertHeld is only enforced for debug builds. The intention here is not to reduce the 'actual' (or 'self') cost of SetCordRep() as it should only be called for sampled cords, but to reduce the cost of a branch and out of line call at the call site. The compiler can now 'perfectly inline' the single branch / store. PiperOrigin-RevId: 369696265 -- d722199ed69d413994740624159ac7bd001a9219 by Martijn Vels : Add kDefaultInit initialization This avoids double store on init PiperOrigin-RevId: 369655217 -- 3499aed79e6cc12ce36277063ec37991bba0cccd by Martijn Vels : Introduce CordzUpdateScope PiperOrigin-RevId: 369491326 -- 325c1bc99c7d1aeca7bd1273e51a0900b6faf731 by Abseil Team : make unary and logical operators constexpr PiperOrigin-RevId: 369476773 -- ad3bed3dea5e5d7d281ff36ee786f802630c3728 by Martijn Vels : Add LOCKABLE attribute to CordzInfo PiperOrigin-RevId: 369476772 GitOrigin-RevId: 9e0df3dd23da17cd0ff75c93c1493a858032639c Change-Id: I00e2859328fe8da46d2e04d3f07dfe70ec6cb1f5 --- CMake/AbseilDll.cmake | 1 + absl/numeric/int128.h | 10 +-- absl/strings/BUILD.bazel | 29 ++++++ absl/strings/CMakeLists.txt | 34 +++++++ absl/strings/cord.cc | 49 ++++------ absl/strings/cord.h | 28 ++---- absl/strings/internal/cord_internal.h | 4 + absl/strings/internal/cordz_info.cc | 29 +++--- absl/strings/internal/cordz_info.h | 85 +++++++++++++----- absl/strings/internal/cordz_info_test.cc | 109 ++++++++++++++--------- absl/strings/internal/cordz_sample_token_test.cc | 74 +++++++++------ absl/strings/internal/cordz_update_scope.h | 65 ++++++++++++++ absl/strings/internal/cordz_update_scope_test.cc | 66 ++++++++++++++ absl/strings/internal/cordz_update_tracker.h | 9 +- 14 files changed, 427 insertions(+), 165 deletions(-) create mode 100644 absl/strings/internal/cordz_update_scope.h create mode 100644 absl/strings/internal/cordz_update_scope_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 2f2fa13d..8ee4120f 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -216,6 +216,7 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cordz_sample_token.cc" "strings/internal/cordz_sample_token.h" "strings/internal/cordz_statistics.h" + "strings/internal/cordz_update_scope.h" "strings/internal/cordz_update_tracker.h" "strings/internal/stl_type_traits.h" "strings/internal/string_constant.h" diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index 0dd814a8..235361a8 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -817,27 +817,27 @@ inline uint128 operator-(uint128 val) { return MakeUint128(hi, lo); } -inline bool operator!(uint128 val) { +constexpr inline bool operator!(uint128 val) { return !Uint128High64(val) && !Uint128Low64(val); } // Logical operators. -inline uint128 operator~(uint128 val) { +constexpr inline uint128 operator~(uint128 val) { return MakeUint128(~Uint128High64(val), ~Uint128Low64(val)); } -inline uint128 operator|(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) { return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs), Uint128Low64(lhs) | Uint128Low64(rhs)); } -inline uint128 operator&(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) { return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs), Uint128Low64(lhs) & Uint128Low64(rhs)); } -inline uint128 operator^(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) { return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs), Uint128Low64(lhs) ^ Uint128Low64(rhs)); } diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 95ed0868..874dd390 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -330,6 +330,7 @@ cc_library( ":cordz_functions", ":cordz_info", ":cordz_statistics", + ":cordz_update_scope", ":cordz_update_tracker", ":internal", ":str_format", @@ -366,6 +367,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":cord_internal", + ":cordz_functions", ":cordz_handle", ":cordz_statistics", ":cordz_update_tracker", @@ -377,6 +379,33 @@ cc_library( ], ) +cc_library( + name = "cordz_update_scope", + hdrs = ["internal/cordz_update_scope.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord_internal", + ":cordz_info", + ":cordz_update_tracker", + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "cordz_update_scope_test", + srcs = ["internal/cordz_update_scope_test.cc"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord_internal", + ":cordz_info", + ":cordz_update_scope", + ":cordz_update_tracker", + "//absl/base:config", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "cordz_sample_token", srcs = ["internal/cordz_sample_token.cc"], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 5a13831f..f48584e0 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -689,6 +689,7 @@ absl_cc_library( DEPS absl::config absl::cord_internal + absl::cordz_functions absl::cordz_handle absl::cordz_statistics absl::cordz_update_tracker @@ -754,6 +755,38 @@ absl_cc_test( gmock_main ) +absl_cc_library( + NAME + cordz_update_scope + HDRS + "internal/cordz_update_scope.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_info + absl::cordz_update_tracker + absl::core_headers +) + +absl_cc_test( + NAME + cordz_update_scope_test + SRCS + "internal/cordz_update_scope_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_info + absl::cordz_update_scope + absl::cordz_update_tracker + absl::core_headers + gmock_main +) + absl_cc_library( NAME cord @@ -769,6 +802,7 @@ absl_cc_library( absl::cord_internal absl::cordz_functions absl::cordz_info + absl::cordz_update_scope absl::cordz_update_tracker absl::core_headers absl::endian diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 770c5a70..44e4d464 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -39,6 +39,7 @@ #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_scope.h" #include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/str_cat.h" @@ -55,8 +56,9 @@ using ::absl::cord_internal::CordRepExternal; using ::absl::cord_internal::CordRepFlat; using ::absl::cord_internal::CordRepRing; using ::absl::cord_internal::CordRepSubstring; -using ::absl::cord_internal::kMinFlatLength; +using ::absl::cord_internal::InlineData; using ::absl::cord_internal::kMaxFlatLength; +using ::absl::cord_internal::kMinFlatLength; using ::absl::cord_internal::CONCAT; using ::absl::cord_internal::EXTERNAL; @@ -301,16 +303,20 @@ inline char* Cord::InlineRep::set_data(size_t n) { return data_.as_chars(); } +static inline CordRepFlat* MakeFlat(const InlineData& data, size_t extra = 0) { + static_assert(kMinFlatLength >= sizeof(data), ""); + size_t len = data.inline_size(); + CordRepFlat* result = CordRepFlat::New(len + extra); + result->length = len; + memcpy(result->Data(), data.as_chars(), sizeof(data)); + return result; +} + inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) { if (data_.is_tree()) { return data_.as_tree(); } - - size_t len = inline_size(); - CordRepFlat* result = CordRepFlat::New(len + extra_hint); - result->length = len; - static_assert(kMinFlatLength >= sizeof(data_), ""); - memcpy(result->Data(), data_.as_chars(), sizeof(data_)); + CordRepFlat* result = MakeFlat(data_, extra_hint); set_tree(result); return result; } @@ -494,22 +500,6 @@ static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { return false; } -void Cord::InlineRep::StartProfiling() { - CordRep* tree = as_tree(); - auto* cordz_info = absl::cord_internal::CordzInfo::TrackCord(tree); - set_cordz_info(cordz_info); - cordz_info->RecordMetrics(size()); -} - -void Cord::InlineRep::StartProfiling(const Cord::InlineRep& src) { - CordRep* tree = as_tree(); - absl::cord_internal::CordzInfo* parent = - src.is_profiled() ? src.cordz_info() : nullptr; - auto* cordz_info = absl::cord_internal::CordzInfo::TrackCord(tree, parent); - set_cordz_info(cordz_info); - cordz_info->RecordMetrics(size()); -} - void Cord::InlineRep::UpdateCordzStatisticsSlow() { CordRep* tree = as_tree(); data_.cordz_info()->RecordMetrics(tree->length); @@ -522,9 +512,7 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { if (is_tree()) { CordRep::Ref(tree()); clear_cordz_info(); - if (ABSL_PREDICT_FALSE(should_profile())) { - StartProfiling(src); - } + CordzInfo::MaybeTrackCord(data_, CordzUpdateTracker::kAssignCord); } } @@ -540,15 +528,14 @@ void Cord::InlineRep::UnrefTree() { // -------------------------------------------------------------------- // Constructors and destructors -Cord::Cord(absl::string_view src) { +Cord::Cord(absl::string_view src) : contents_(InlineData::kDefaultInit) { const size_t n = src.size(); if (n <= InlineRep::kMaxInline) { - contents_.set_data(src.data(), n, false); + contents_.set_data(src.data(), n, true); } else { contents_.data_.make_tree(NewTree(src.data(), n, 0)); - if (ABSL_PREDICT_FALSE(absl::cord_internal::cordz_should_profile())) { - contents_.StartProfiling(); - } + CordzInfo::MaybeTrackCord(contents_.data_, + CordzUpdateTracker::kConstructorString); } } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 1e6a73a5..bca20981 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -84,6 +84,7 @@ #include "absl/strings/internal/cordz_functions.h" #include "absl/strings/internal/cordz_info.h" #include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_scope.h" #include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/internal/string_constant.h" @@ -669,7 +670,11 @@ class Cord { explicit constexpr Cord(strings_internal::StringConstant); private: + using CordzInfo = cord_internal::CordzInfo; + using CordzUpdateScope = cord_internal::CordzUpdateScope; using CordzUpdateTracker = cord_internal::CordzUpdateTracker; + using InlineData = cord_internal::InlineData; + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; friend class CordTestPeer; friend bool operator==(const Cord& lhs, const Cord& rhs); @@ -694,6 +699,7 @@ class Cord { static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), ""); constexpr InlineRep() : data_() {} + explicit InlineRep(InlineData::DefaultInitType init) : data_(init) {} InlineRep(const InlineRep& src); InlineRep(InlineRep&& src); InlineRep& operator=(const InlineRep& src); @@ -779,15 +785,6 @@ class Cord { // Resets the current cordz_info to null / empty. void clear_cordz_info() { data_.clear_cordz_info(); } - // Starts profiling this cord. - void StartProfiling(); - - // Starts profiling this cord which has been copied from `src`. - void StartProfiling(const Cord::InlineRep& src); - - // Returns true if a Cord should be profiled and false otherwise. - static bool should_profile(); - // Updates the cordz statistics. info may be nullptr if the CordzInfo object // is unknown. void UpdateCordzStatistics(); @@ -964,9 +961,8 @@ inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) if (is_tree()) { data_.clear_cordz_info(); absl::cord_internal::CordRep::Ref(as_tree()); - if (ABSL_PREDICT_FALSE(should_profile())) { - StartProfiling(src); - } + CordzInfo::MaybeTrackCord(data_, src.data_, + CordzUpdateTracker::kConstructorCord); } } @@ -1040,9 +1036,7 @@ inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { } else { // `data_` contains inlined data: initialize data_ to tree value `rep`. data_.make_tree(rep); - if (ABSL_PREDICT_FALSE(should_profile())) { - StartProfiling(); - } + CordzInfo::MaybeTrackCord(data_, CordzUpdateTracker::kUnknown); } UpdateCordzStatistics(); } @@ -1074,10 +1068,6 @@ inline void Cord::InlineRep::CopyToArray(char* dst) const { cord_internal::SmallMemmove(dst, data_.as_chars(), n); } -inline ABSL_ATTRIBUTE_ALWAYS_INLINE bool Cord::InlineRep::should_profile() { - return absl::cord_internal::cordz_should_profile(); -} - inline void Cord::InlineRep::UpdateCordzStatistics() { if (ABSL_PREDICT_TRUE(!is_profiled())) return; UpdateCordzStatisticsSlow(); diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 01ce70ae..c7ae0104 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -329,6 +329,9 @@ static constexpr cordz_info_t BigEndianByte(unsigned char value) { class InlineData { public: + // DefaultInitType forces the use of the default initialization constructor. + enum DefaultInitType { kDefaultInit }; + // kNullCordzInfo holds the big endian representation of intptr_t(1) // This is the 'null' / initial value of 'cordz_info'. The null value // is specifically big endian 1 as with 64-bit pointers, the last @@ -336,6 +339,7 @@ class InlineData { static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1); constexpr InlineData() : as_chars_{0} {} + explicit InlineData(DefaultInitType) {} explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {} explicit constexpr InlineData(absl::string_view chars) : as_chars_{ diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 6540d134..d2200680 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -45,15 +45,22 @@ CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const { return ci_next_unsafe(); } -CordzInfo* CordzInfo::TrackCord(CordRep* rep, const CordzInfo* src, - MethodIdentifier method) { - CordzInfo* ci = new CordzInfo(rep, src, method); - ci->Track(); - return ci; +void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) { + assert(cord.is_tree()); + assert(!cord.is_profiled()); + CordzInfo* cordz_info = new CordzInfo(cord.as_tree(), nullptr, method); + cord.set_cordz_info(cordz_info); + cordz_info->Track(); } -CordzInfo* CordzInfo::TrackCord(CordRep* rep, MethodIdentifier method) { - return TrackCord(rep, nullptr, method); +void CordzInfo::TrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method) { + assert(cord.is_tree()); + assert(!cord.is_profiled()); + auto* info = src.is_tree() && src.is_profiled() ? src.cordz_info() : nullptr; + CordzInfo* cordz_info = new CordzInfo(cord.as_tree(), info, method); + cord.set_cordz_info(cordz_info); + cordz_info->Track(); } void CordzInfo::UntrackCord(CordzInfo* cordz_info) { @@ -160,14 +167,6 @@ void CordzInfo::Unlock() ABSL_UNLOCK_FUNCTION(mutex_) { } } -void CordzInfo::SetCordRep(CordRep* rep) { - mutex_.AssertHeld(); - rep_ = rep; - if (rep) { - size_.store(rep->length); - } -} - absl::Span CordzInfo::GetStack() const { return absl::MakeConstSpan(stack_, stack_depth_); } diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index 5345be75..4cf57664 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -22,6 +22,7 @@ #include "absl/base/config.h" #include "absl/base/thread_annotations.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_functions.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_update_tracker.h" @@ -41,22 +42,37 @@ namespace cord_internal { // and will either be deleted or appended to the global_delete_queue. If it is // placed on the global_delete_queue, the CordzInfo object will be cleaned in // the destructor of a CordzSampleToken object. -class CordzInfo : public CordzHandle { +class ABSL_LOCKABLE CordzInfo : public CordzHandle { public: using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; - // All profiled Cords should be accompanied by a call to TrackCord. // TrackCord creates a CordzInfo instance which tracks important metrics of - // the sampled cord. CordzInfo instances are placed in a global list which is - // used to discover and snapshot all actively tracked cords. - // Callers are responsible for calling UntrackCord() before the tracked Cord - // instance is deleted, or to stop tracking the sampled Cord. - // Callers are also responsible for guarding changes to `rep` through the - // Lock() and Unlock() calls, and calling SetCordRep() if the root of the - // sampled cord changes before the old root has been unreffed and/or deleted. - // `method` identifies the Cord method which initiated the cord to be sampled. - static CordzInfo* TrackCord( - CordRep* rep, MethodIdentifier method = MethodIdentifier::kUnknown); + // a sampled cord, and stores the created CordzInfo instance into `cord'. All + // CordzInfo instances are placed in a global list which is used to discover + // and snapshot all actively tracked cords. Callers are responsible for + // calling UntrackCord() before the tracked Cord instance is deleted, or to + // stop tracking the sampled Cord. Callers are also responsible for guarding + // changes to the 'tree' value of a Cord (InlineData.tree) through the Lock() + // and Unlock() calls. Any change resulting in a new tree value for the cord + // requires a call to SetCordRep() before the old tree has been unreffed + // and/or deleted. `method` identifies the Cord public API method initiating + // the cord to be sampled. + // Requires `cord` to hold a tree, and `cord.cordz_info()` to be null. + static void TrackCord(InlineData& cord, MethodIdentifier method); + + // Identical to TrackCord(), except that this function fills the + // `parent_stack` and `parent_method` properties of the returned CordzInfo + // instance from the provided `src` instance if `src` is sampled. + // This function should be used for sampling 'copy constructed' cords. + static void TrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method); + + // Maybe sample the cord identified by 'cord' for method 'method'. + // Uses `cordz_should_profile` to randomly pick cords to be sampled, and if + // so, invokes `TrackCord` to start sampling `cord`. + static void MaybeTrackCord(InlineData& cord, MethodIdentifier method); + static void MaybeTrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method); // Stops tracking changes for a sampled cord, and deletes the provided info. // This function must be called before the sampled cord instance is deleted, @@ -65,14 +81,6 @@ class CordzInfo : public CordzHandle { // CordInfo instance is being held by a concurrent collection thread. static void UntrackCord(CordzInfo* cordz_info); - // Identical to TrackCord(), except that this function fills the - // `parent_stack` and `parent_method` properties of the returned CordzInfo - // instance from the provided `src` instance if `src` is not null. - // This function should be used for sampling 'copy constructed' cords. - static CordzInfo* TrackCord( - CordRep* rep, const CordzInfo* src, - MethodIdentifier method = MethodIdentifier::kUnknown); - CordzInfo() = delete; CordzInfo(const CordzInfo&) = delete; CordzInfo& operator=(const CordzInfo&) = delete; @@ -92,6 +100,9 @@ class CordzInfo : public CordzHandle { // then this method will delete this CordzInfo instance. void Unlock() ABSL_UNLOCK_FUNCTION(mutex_); + // Asserts that this CordzInfo instance is locked. + void AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_); + // Updates the `rep' property of this instance. This methods is invoked by // Cord logic each time the root node of a sampled Cord changes, and before // the old root reference count is deleted. This guarantees that collection @@ -136,6 +147,9 @@ class CordzInfo : public CordzHandle { MethodIdentifier method); ~CordzInfo() override; + void Track(); + void Untrack(); + // Returns the parent method from `src`, which is either `parent_method_` or // `method_` depending on `parent_method_` being kUnknown. // Returns kUnknown if `src` is null. @@ -147,9 +161,6 @@ class CordzInfo : public CordzHandle { // Returns 0 if `src` is null. static int FillParentStack(const CordzInfo* src, void** stack); - void Track(); - void Untrack(); - // 'Unsafe' head/next/prev accessors not requiring the lock being held. // These are used exclusively for iterations (Head / Next) where we enforce // a token being held, so reading an 'old' / deleted pointer is fine. @@ -184,6 +195,34 @@ class CordzInfo : public CordzHandle { std::atomic size_{0}; }; +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( + InlineData& cord, MethodIdentifier method) { + if (ABSL_PREDICT_FALSE(cordz_should_profile())) { + TrackCord(cord, method); + } +} + +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( + InlineData& cord, const InlineData& src, MethodIdentifier method) { + if (ABSL_PREDICT_FALSE(cordz_should_profile())) { + TrackCord(cord, src, method); + } +} + +inline void CordzInfo::AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_) { +#ifndef NDEBUG + mutex_.AssertHeld(); +#endif +} + +inline void CordzInfo::SetCordRep(CordRep* rep) { + AssertHeld(); + rep_ = rep; + if (rep) { + size_.store(rep->length); + } +} + } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index 66acf9b6..89e5fae5 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -1,4 +1,4 @@ -// Copyright 2019 The Abseil Authors. +// Copyright 2019 The Abseil Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -48,6 +48,13 @@ struct TestCordRep { ~TestCordRep() { CordRepFlat::Delete(rep); } }; +struct TestCordData { + TestCordRep rep; + InlineData data; + + TestCordData() { data.make_tree(rep.rep); } +}; + // Used test values auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown; auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; @@ -72,18 +79,20 @@ std::string FormatStack(absl::Span raw_stack) { } TEST(CordzInfoTest, TrackCord) { - TestCordRep rep; - CordzInfo* info = CordzInfo::TrackCord(rep.rep); + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); ASSERT_THAT(info, Ne(nullptr)); EXPECT_FALSE(info->is_snapshot()); EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info)); - EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep.rep)); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep)); CordzInfo::UntrackCord(info); } TEST(CordzInfoTest, UntrackCord) { - TestCordRep rep; - CordzInfo* info = CordzInfo::TrackCord(rep.rep); + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); CordzSnapshot snapshot; CordzInfo::UntrackCord(info); @@ -93,21 +102,23 @@ TEST(CordzInfoTest, UntrackCord) { } TEST(CordzInfoTest, SetCordRep) { - TestCordRep rep; - CordzInfo* info = CordzInfo::TrackCord(rep.rep); + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); - TestCordRep rep2; + TestCordRep rep; info->Lock(CordzUpdateTracker::kAppendCord); - info->SetCordRep(rep2.rep); + info->SetCordRep(rep.rep); info->Unlock(); - EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep2.rep)); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep.rep)); CordzInfo::UntrackCord(info); } TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) { - TestCordRep rep; - CordzInfo* info = CordzInfo::TrackCord(rep.rep); + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); info->Lock(CordzUpdateTracker::kAppendString); info->SetCordRep(nullptr); @@ -121,25 +132,29 @@ TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) { #if GTEST_HAS_DEATH_TEST TEST(CordzInfoTest, SetCordRepRequiresMutex) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); TestCordRep rep; - CordzInfo* info = CordzInfo::TrackCord(rep.rep); - TestCordRep rep2; - EXPECT_DEATH(info->SetCordRep(rep2.rep), ".*"); + EXPECT_DEBUG_DEATH(info->SetCordRep(rep.rep), ".*"); CordzInfo::UntrackCord(info); } #endif // GTEST_HAS_DEATH_TEST TEST(CordzInfoTest, TrackUntrackHeadFirstV2) { - TestCordRep rep; CordzSnapshot snapshot; EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); - CordzInfo* info1 = CordzInfo::TrackCord(rep.rep); + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info1 = data.data.cordz_info(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); - CordzInfo* info2 = CordzInfo::TrackCord(rep.rep); + TestCordData data2; + CordzInfo::TrackCord(data2.data, kTrackCordMethod); + CordzInfo* info2 = data2.data.cordz_info(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); EXPECT_THAT(info2->Next(snapshot), Eq(info1)); EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); @@ -153,15 +168,18 @@ TEST(CordzInfoTest, TrackUntrackHeadFirstV2) { } TEST(CordzInfoTest, TrackUntrackTailFirstV2) { - TestCordRep rep; CordzSnapshot snapshot; EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); - CordzInfo* info1 = CordzInfo::TrackCord(rep.rep); + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info1 = data.data.cordz_info(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); - CordzInfo* info2 = CordzInfo::TrackCord(rep.rep); + TestCordData data2; + CordzInfo::TrackCord(data2.data, kTrackCordMethod); + CordzInfo* info2 = data2.data.cordz_info(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); EXPECT_THAT(info2->Next(snapshot), Eq(info1)); EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); @@ -175,7 +193,7 @@ TEST(CordzInfoTest, TrackUntrackTailFirstV2) { } TEST(CordzInfoTest, StackV2) { - TestCordRep rep; + TestCordData data; // kMaxStackDepth is intentionally less than 64 (which is the max depth that // Cordz will record) because if the actual stack depth is over 64 // (which it is on Apple platforms) then the expected_stack will end up @@ -186,7 +204,8 @@ TEST(CordzInfoTest, StackV2) { // makes small modifications to its testing stack. 50 is sufficient to prove // that we got a decent stack. static constexpr int kMaxStackDepth = 50; - CordzInfo* info = CordzInfo::TrackCord(rep.rep); + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); std::vector local_stack; local_stack.resize(kMaxStackDepth); // In some environments we don't get stack traces. For example in Android @@ -209,19 +228,21 @@ TEST(CordzInfoTest, StackV2) { } // Local helper functions to get different stacks for child and parent. -CordzInfo* TrackChildCord(CordRep* rep, const CordzInfo* parent) { - return CordzInfo::TrackCord(rep, parent, kChildMethod); +CordzInfo* TrackChildCord(InlineData& data, const InlineData& parent) { + CordzInfo::TrackCord(data, parent, kChildMethod); + return data.cordz_info(); } -CordzInfo* TrackParentCord(CordRep* rep) { - return CordzInfo::TrackCord(rep, kTrackCordMethod); +CordzInfo* TrackParentCord(InlineData& data) { + CordzInfo::TrackCord(data, kTrackCordMethod); + return data.cordz_info(); } TEST(CordzInfoTest, GetStatistics) { - TestCordRep rep; - CordzInfo* info = TrackParentCord(rep.rep); + TestCordData data; + CordzInfo* info = TrackParentCord(data.data); CordzStatistics statistics = info->GetCordzStatistics(); - EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.size, Eq(data.rep.rep->length)); EXPECT_THAT(statistics.method, Eq(kTrackCordMethod)); EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); EXPECT_THAT(statistics.update_tracker.Value(kTrackCordMethod), Eq(1)); @@ -230,8 +251,8 @@ TEST(CordzInfoTest, GetStatistics) { } TEST(CordzInfoTest, LockCountsMethod) { - TestCordRep rep; - CordzInfo* info = TrackParentCord(rep.rep); + TestCordData data; + CordzInfo* info = TrackParentCord(data.data); info->Lock(kUpdateMethod); info->Unlock(); @@ -245,16 +266,17 @@ TEST(CordzInfoTest, LockCountsMethod) { } TEST(CordzInfoTest, FromParent) { - TestCordRep rep; - CordzInfo* info_parent = TrackParentCord(rep.rep); - CordzInfo* info_child = TrackChildCord(rep.rep, info_parent); + TestCordData parent; + TestCordData child; + CordzInfo* info_parent = TrackParentCord(parent.data); + CordzInfo* info_child = TrackChildCord(child.data, parent.data); std::string stack = FormatStack(info_parent->GetStack()); std::string parent_stack = FormatStack(info_child->GetParentStack()); EXPECT_THAT(stack, Eq(parent_stack)); CordzStatistics statistics = info_child->GetCordzStatistics(); - EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.size, Eq(child.rep.rep->length)); EXPECT_THAT(statistics.method, Eq(kChildMethod)); EXPECT_THAT(statistics.parent_method, Eq(kTrackCordMethod)); EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); @@ -263,12 +285,13 @@ TEST(CordzInfoTest, FromParent) { CordzInfo::UntrackCord(info_child); } -TEST(CordzInfoTest, FromParentNullptr) { - TestCordRep rep; - CordzInfo* info = TrackChildCord(rep.rep, nullptr); +TEST(CordzInfoTest, FromParentInlined) { + InlineData parent; + TestCordData child; + CordzInfo* info = TrackChildCord(child.data, parent); EXPECT_TRUE(info->GetParentStack().empty()); CordzStatistics statistics = info->GetCordzStatistics(); - EXPECT_THAT(statistics.size, Eq(rep.rep->length)); + EXPECT_THAT(statistics.size, Eq(child.rep.rep->length)); EXPECT_THAT(statistics.method, Eq(kChildMethod)); EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); @@ -276,8 +299,8 @@ TEST(CordzInfoTest, FromParentNullptr) { } TEST(CordzInfoTest, RecordMetrics) { - TestCordRep rep; - CordzInfo* info = TrackParentCord(rep.rep); + TestCordData data; + CordzInfo* info = TrackParentCord(data.data); CordzStatistics expected; expected.size = 100; diff --git a/absl/strings/internal/cordz_sample_token_test.cc b/absl/strings/internal/cordz_sample_token_test.cc index 8c052642..5fbfd01c 100644 --- a/absl/strings/internal/cordz_sample_token_test.cc +++ b/absl/strings/internal/cordz_sample_token_test.cc @@ -14,6 +14,10 @@ #include "absl/strings/internal/cordz_sample_token.h" +#include +#include +#include + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/memory/memory.h" @@ -46,6 +50,16 @@ struct TestCordRep { ~TestCordRep() { CordRepFlat::Delete(rep); } }; +struct TestCord { + TestCordRep rep; + InlineData data; + + TestCord() { data.make_tree(rep.rep); } +}; + +// Used test values +auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; + TEST(CordzSampleTokenTest, IteratorTraits) { static_assert(std::is_copy_constructible::value, ""); @@ -83,12 +97,13 @@ TEST(CordzSampleTokenTest, IteratorEmpty) { } TEST(CordzSampleTokenTest, Iterator) { - TestCordRep rep1; - TestCordRep rep2; - TestCordRep rep3; - CordzInfo* info1 = CordzInfo::TrackCord(rep1.rep); - CordzInfo* info2 = CordzInfo::TrackCord(rep2.rep); - CordzInfo* info3 = CordzInfo::TrackCord(rep3.rep); + TestCord cord1, cord2, cord3; + CordzInfo::TrackCord(cord1.data, kTrackCordMethod); + CordzInfo* info1 = cord1.data.cordz_info(); + CordzInfo::TrackCord(cord2.data, kTrackCordMethod); + CordzInfo* info2 = cord2.data.cordz_info(); + CordzInfo::TrackCord(cord3.data, kTrackCordMethod); + CordzInfo* info3 = cord3.data.cordz_info(); CordzSampleToken token; std::vector found; @@ -104,22 +119,25 @@ TEST(CordzSampleTokenTest, Iterator) { } TEST(CordzSampleTokenTest, IteratorEquality) { - TestCordRep rep1; - TestCordRep rep2; - TestCordRep rep3; - CordzInfo* info1 = CordzInfo::TrackCord(rep1.rep); + TestCord cord1; + TestCord cord2; + TestCord cord3; + CordzInfo::TrackCord(cord1.data, kTrackCordMethod); + CordzInfo* info1 = cord1.data.cordz_info(); CordzSampleToken token1; // lhs starts with the CordzInfo corresponding to cord1 at the head. CordzSampleToken::Iterator lhs = token1.begin(); - CordzInfo* info2 = CordzInfo::TrackCord(rep2.rep); + CordzInfo::TrackCord(cord2.data, kTrackCordMethod); + CordzInfo* info2 = cord2.data.cordz_info(); CordzSampleToken token2; // rhs starts with the CordzInfo corresponding to cord2 at the head. CordzSampleToken::Iterator rhs = token2.begin(); - CordzInfo* info3 = CordzInfo::TrackCord(rep3.rep); + CordzInfo::TrackCord(cord3.data, kTrackCordMethod); + CordzInfo* info3 = cord3.data.cordz_info(); // lhs is on cord1 while rhs is on cord2. EXPECT_THAT(lhs, Ne(rhs)); @@ -149,10 +167,8 @@ TEST(CordzSampleTokenTest, MultiThreaded) { for (int i = 0; i < kNumThreads; ++i) { pool.Schedule([&stop]() { absl::BitGen gen; - TestCordRep reps[kNumCords]; - CordzInfo* infos[kNumCords] = {nullptr}; - std::vector> tokens; - tokens.resize(kNumTokens); + TestCord cords[kNumCords]; + std::unique_ptr tokens[kNumTokens]; while (!stop.HasBeenNotified()) { // Randomly perform one of five actions: @@ -163,36 +179,38 @@ TEST(CordzSampleTokenTest, MultiThreaded) { // 5) Sample int index = absl::Uniform(gen, 0, kNumCords); if (absl::Bernoulli(gen, 0.5)) { + TestCord& cord = cords[index]; // Track/untrack. - if (infos[index]) { + if (cord.data.is_profiled()) { // 1) Untrack - CordzInfo::UntrackCord(infos[index]); - infos[index] = nullptr; + CordzInfo::UntrackCord(cord.data.cordz_info()); + cord.data.clear_cordz_info();; } else { // 2) Track - infos[index] = CordzInfo::TrackCord(reps[index].rep); + CordzInfo::TrackCord(cord.data, kTrackCordMethod); } } else { - if (tokens[index]) { + std::unique_ptr& token = tokens[index]; + if (token) { if (absl::Bernoulli(gen, 0.5)) { // 3) Iterate over Cords visible to a token. - for (const CordzInfo& info : *tokens[index]) { + for (const CordzInfo& info : *token) { // This is trivial work to allow us to compile the loop. - EXPECT_THAT(info.Next(*tokens[index]), Ne(&info)); + EXPECT_THAT(info.Next(*token), Ne(&info)); } } else { // 4) Unsample - tokens[index].reset(); + token = nullptr; } } else { // 5) Sample - tokens[index] = absl::make_unique(); + token = absl::make_unique(); } } } - for (CordzInfo* info : infos) { - if (info != nullptr) { - CordzInfo::UntrackCord(info); + for (TestCord& cord : cords) { + if (cord.data.is_profiled()) { + CordzInfo::UntrackCord(cord.data.cordz_info()); } } }); diff --git a/absl/strings/internal/cordz_update_scope.h b/absl/strings/internal/cordz_update_scope.h new file mode 100644 index 00000000..018359a8 --- /dev/null +++ b/absl/strings/internal/cordz_update_scope.h @@ -0,0 +1,65 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ + +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzUpdateScope scopes an update to the provided CordzInfo. +// The class invokes `info->Lock(method)` and `info->Unlock()` to guard +// cordrep updates. This class does nothing if `info` is null. +// See also the 'Lock`, `Unlock` and `SetCordRep` methods in `CordzInfo`. +class ABSL_SCOPED_LOCKABLE CordzUpdateScope { + public: + CordzUpdateScope(CordzInfo* info, CordzUpdateTracker::MethodIdentifier method) + ABSL_EXCLUSIVE_LOCK_FUNCTION(info) + : info_(info) { + if (ABSL_PREDICT_FALSE(info_)) { + info->Lock(method); + } + } + + ~CordzUpdateScope() ABSL_UNLOCK_FUNCTION() { + if (ABSL_PREDICT_FALSE(info_)) { + info_->Unlock(); + } + } + + void SetCordRep(CordRep* rep) const { + if (ABSL_PREDICT_FALSE(info_)) { + info_->SetCordRep(rep); + } + } + + CordzInfo* info() const { return info_; } + + private: + CordzInfo* const info_; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ diff --git a/absl/strings/internal/cordz_update_scope_test.cc b/absl/strings/internal/cordz_update_scope_test.cc new file mode 100644 index 00000000..4bdc78d3 --- /dev/null +++ b/absl/strings/internal/cordz_update_scope_test.cc @@ -0,0 +1,66 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cordz_update_scope.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +namespace { + +struct TestCordRep { + CordRepFlat* rep; + + TestCordRep() { + rep = CordRepFlat::New(100); + rep->length = 100; + memset(rep->Data(), 1, 100); + } + ~TestCordRep() { CordRepFlat::Delete(rep); } +}; + +struct TestCord { + TestCordRep rep; + InlineData data; + + TestCord() { data.make_tree(rep.rep); } +}; + +// Used test values +auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; + +TEST(CordzUpdateScopeTest, ScopeNullptr) { + CordzUpdateScope scope(nullptr, kTrackCordMethod); +} + +TEST(CordzUpdateScopeTest, ScopeSampledCord) { + TestCord cord; + CordzInfo::TrackCord(cord.data, kTrackCordMethod); + CordzUpdateScope scope(cord.data.cordz_info(), kTrackCordMethod); + cord.data.cordz_info()->SetCordRep(nullptr); +} + +} // namespace +ABSL_NAMESPACE_END +} // namespace cord_internal + +} // namespace absl diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index a090676d..741950b2 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -90,7 +90,14 @@ class CordzUpdateTracker { } private: - std::atomic values_[kNumMethods]; + // Until C++20 std::atomic is not constexpr default-constructible, so we need + // a wrapper for this class to be constexpr constructible. + class Counter : public std::atomic { + public: + constexpr Counter() noexcept : std::atomic(0) {} + }; + + Counter values_[kNumMethods]; }; } // namespace cord_internal -- cgit v1.2.3 From d96e287417766deddbff2d01b96321288c59491e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 23 Apr 2021 05:02:23 -0700 Subject: Export of internal Abseil changes -- f825cf3feb6db06522b2b4ee785de7dfa325780d by Martijn Vels : Move Cordz test helpers to cordz_test_helpers library PiperOrigin-RevId: 370059941 -- 5080249da6a4f5cc2b546aed48503fd028670379 by Martijn Vels : Add new Cordz instrumentation on AppendTree. PiperOrigin-RevId: 369968167 -- 21092b889fad34ec605894e311b436d5f417456f by Benjamin Barenblat : Round floats using round(x), not static_cast(x + 0.5) Adding 0.5 to an IEEE float may cause one bit of precision loss, which is enough to change the result in certain cases. For example, static_cast(std::round(0.49999999999999994)) == 0 static_cast(0.49999999999999994 + 0.5) == 1 PiperOrigin-RevId: 369926519 GitOrigin-RevId: f825cf3feb6db06522b2b4ee785de7dfa325780d Change-Id: Ib78ce1faec79f06578933db5dc6fc05de043ead1 --- absl/strings/BUILD.bazel | 58 +++++++++- absl/strings/CMakeLists.txt | 54 +++++++++- absl/strings/cord.cc | 55 +++++++--- absl/strings/cord.h | 35 +++++- absl/strings/cord_test.cc | 2 +- absl/strings/cord_test_helpers.h | 1 + absl/strings/cordz_test.cc | 92 ++++++++++++++++ absl/strings/cordz_test_helpers.h | 129 +++++++++++++++++++++++ absl/strings/internal/cordz_info_test.cc | 20 +--- absl/strings/internal/cordz_sample_token_test.cc | 33 ++---- absl/strings/internal/cordz_update_scope_test.cc | 21 +--- absl/time/duration_test.cc | 4 +- absl/time/time.h | 2 +- 13 files changed, 422 insertions(+), 84 deletions(-) create mode 100644 absl/strings/cordz_test.cc create mode 100644 absl/strings/cordz_test_helpers.h diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 874dd390..5d4ad7e3 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -399,6 +399,7 @@ cc_test( deps = [ ":cord_internal", ":cordz_info", + ":cordz_test_helpers", ":cordz_update_scope", ":cordz_update_tracker", "//absl/base:config", @@ -447,8 +448,8 @@ cc_test( "internal/cordz_functions_test.cc", ], deps = [ - ":cord_test_helpers", ":cordz_functions", + ":cordz_test_helpers", "//absl/base:config", "@com_google_googletest//:gtest_main", ], @@ -482,6 +483,7 @@ cc_test( ":cordz_handle", ":cordz_info", ":cordz_statistics", + ":cordz_test_helpers", ":cordz_update_tracker", ":strings", "//absl/base:config", @@ -502,6 +504,7 @@ cc_test( ":cordz_handle", ":cordz_info", ":cordz_sample_token", + ":cordz_test_helpers", "//absl/base:config", "//absl/memory", "//absl/random", @@ -521,6 +524,25 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":cord", + ":cord_internal", + ], +) + +cc_library( + name = "cordz_test_helpers", + testonly = 1, + hdrs = ["cordz_test_helpers.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord", + ":cord_internal", + ":cordz_info", + ":cordz_sample_token", + ":cordz_statistics", + ":cordz_update_tracker", + "//absl/base:config", + "//absl/base:core_headers", + "@com_google_googletest//:gtest", ], ) @@ -533,6 +555,8 @@ cc_test( deps = [ ":cord", ":cord_test_helpers", + ":cordz_functions", + ":cordz_test_helpers", ":str_format", ":strings", "//absl/base", @@ -545,6 +569,38 @@ cc_test( ], ) +cc_test( + name = "cordz_test", + size = "medium", + srcs = ["cordz_test.cc"], + copts = ABSL_TEST_COPTS, + tags = [ + "benchmark", + "no_test_android_arm", + "no_test_android_arm64", + "no_test_android_x86", + "no_test_darwin_x86_64", + "no_test_ios_x86_64", + "no_test_loonix", + "no_test_msvc_x64", + ], + visibility = ["//visibility:private"], + deps = [ + ":cord", + ":cordz_functions", + ":cordz_info", + ":cordz_sample_token", + ":cordz_statistics", + ":cordz_test_helpers", + ":cordz_update_tracker", + ":strings", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "cord_ring_test", size = "medium", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index f48584e0..2a8e3df2 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -626,8 +626,8 @@ absl_cc_test( "internal/cordz_functions_test.cc" DEPS absl::config - absl::cord_test_helpers absl::cordz_functions + absl::cordz_test_helpers gmock_main ) @@ -667,8 +667,8 @@ absl_cc_test( "internal/cordz_handle_test.cc" DEPS absl::config - absl::cord_test_helpers absl::cordz_handle + absl::cordz_test_helpers absl::memory absl::random_random absl::random_distributions @@ -707,10 +707,11 @@ absl_cc_test( DEPS absl::config absl::cord_internal - absl::cord_test_helpers + absl::cordz_test_helpers absl::cordz_handle absl::cordz_info absl::cordz_statistics + absl::cordz_test_helpers absl::cordz_update_tracker absl::span absl::stacktrace @@ -747,6 +748,7 @@ absl_cc_test( absl::cordz_info absl::cordz_info absl::cordz_sample_token + absl::cordz_test_helpers absl::memory absl::random_random absl::synchronization @@ -781,6 +783,7 @@ absl_cc_test( absl::config absl::cord_internal absl::cordz_info + absl::cordz_test_helpers absl::cordz_update_scope absl::cordz_update_tracker absl::core_headers @@ -825,6 +828,26 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::cord + absl::cord_internal + TESTONLY +) + +absl_cc_library( + NAME + cordz_test_helpers + HDRS + "cordz_test_helpers.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord + absl::cord_internal + absl::cordz_info + absl::cordz_sample_token + absl::cordz_statistics + absl::cordz_update_tracker + absl::core_headers TESTONLY ) @@ -841,6 +864,8 @@ absl_cc_test( absl::strings absl::base absl::config + absl::cord_test_helpers + absl::cordz_test_helpers absl::core_headers absl::endian absl::raw_logging_internal @@ -879,3 +904,26 @@ absl_cc_test( absl::strings gmock_main ) + +absl_cc_test( + NAME + cordz_test + SRCS + "cordz_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::cord + absl::cordz_test_helpers + absl::cordz_functions + absl::cordz_info + absl::cordz_sample_token + absl::cordz_statistics + absl::cordz_update_tracker + absl::base + absl::config + absl::core_headers + absl::raw_logging_internal + absl::strings + gmock_main +) diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 44e4d464..768106d4 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -342,14 +342,37 @@ static CordRepRing* ForceRing(CordRep* rep, size_t extra) { return (rep->tag == RING) ? rep->ring() : CordRepRing::Create(rep, extra); } -void Cord::InlineRep::AppendTree(CordRep* tree) { +void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, + MethodIdentifier method) { + assert(!is_tree()); + if (!data_.is_empty()) { + CordRepFlat* flat = MakeFlat(data_); + if (cord_ring_enabled()) { + tree = CordRepRing::Append(CordRepRing::Create(flat, 1), tree); + } else { + tree = Concat(flat, tree); + } + } + EmplaceTree(tree, method); +} + +void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) { + assert(is_tree()); + const CordzUpdateScope scope(data_.cordz_info(), method); + if (cord_ring_enabled()) { + tree = CordRepRing::Append(ForceRing(data_.as_tree(), 1), tree); + } else { + tree = Concat(data_.as_tree(), tree); + } + SetTree(tree, scope); +} + +void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) { if (tree == nullptr) return; - if (data_.is_empty()) { - set_tree(tree); - } else if (cord_ring_enabled()) { - set_tree(CordRepRing::Append(ForceRing(force_tree(0), 1), tree)); + if (data_.is_tree()) { + AppendTreeToTree(tree, method); } else { - set_tree(Concat(force_tree(0), tree)); + AppendTreeToInlined(tree, method); } } @@ -533,9 +556,8 @@ Cord::Cord(absl::string_view src) : contents_(InlineData::kDefaultInit) { if (n <= InlineRep::kMaxInline) { contents_.set_data(src.data(), n, true); } else { - contents_.data_.make_tree(NewTree(src.data(), n, 0)); - CordzInfo::MaybeTrackCord(contents_.data_, - CordzUpdateTracker::kConstructorString); + CordRep* rep = NewTree(src.data(), n, 0); + contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString); } } @@ -713,9 +735,15 @@ inline CordRep* Cord::TakeRep() && { template inline void Cord::AppendImpl(C&& src) { if (empty()) { - // In case of an empty destination avoid allocating a new node, do not copy - // data. - *this = std::forward(src); + // Since destination is empty, we can avoid allocating a node, + if (src.contents_.is_tree()) { + // by taking the tree directly + CordRep* rep = std::forward(src).TakeRep(); + contents_.EmplaceTree(rep, CordzUpdateTracker::kAppendCord); + } else { + // or copying over inline data + contents_.data_ = src.contents_.data_; + } return; } @@ -746,7 +774,8 @@ inline void Cord::AppendImpl(C&& src) { } // Guaranteed to be a tree (kMaxBytesToCopy > kInlinedSize) - contents_.AppendTree(std::forward(src).TakeRep()); + CordRep* rep = std::forward(src).TakeRep(); + contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord); } void Cord::Append(const Cord& src) { AppendImpl(src); } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index bca20981..dd6c3bca 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -670,6 +670,7 @@ class Cord { explicit constexpr Cord(strings_internal::StringConstant); private: + using CordRep = absl::cord_internal::CordRep; using CordzInfo = cord_internal::CordzInfo; using CordzUpdateScope = cord_internal::CordzUpdateScope; using CordzUpdateTracker = cord_internal::CordzUpdateTracker; @@ -680,6 +681,8 @@ class Cord { friend bool operator==(const Cord& lhs, const Cord& rhs); friend bool operator==(const Cord& lhs, absl::string_view rhs); + friend const CordzInfo* GetCordzInfoForTesting(const Cord& cord); + // Calls the provided function once for each cord chunk, in order. Unlike // Chunks(), this API will not allocate memory. void ForEachChunk(absl::FunctionRef) const; @@ -730,7 +733,23 @@ class Cord { void remove_prefix(size_t n); // REQUIRES: holding data void AppendArray(const char* src_data, size_t src_size); absl::string_view FindFlatStartPiece() const; - void AppendTree(absl::cord_internal::CordRep* tree); + + // Sets the tree value for this instance. `rep` must not be null. + // Requires the current instance to hold a tree, and a lock to be held on + // any CordzInfo referenced by this instance. The latter is enforced through + // the CordzUpdateScope argument. If the current instance is sampled, then + // the CordzInfo instance is updated to reference the new `rep` value. + void SetTree(CordRep* rep, const CordzUpdateScope& scope); + + // Sets the tree value for this instance, and randomly samples this cord. + // This function disregards existing contents in `data_`, and should be + // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined' + // value to a non-inlined (tree / ring) value. + void EmplaceTree(CordRep* rep, MethodIdentifier method); + + void AppendTreeToInlined(CordRep* tree, MethodIdentifier method); + void AppendTreeToTree(CordRep* tree, MethodIdentifier method); + void AppendTree(CordRep* tree, MethodIdentifier method); void PrependTree(absl::cord_internal::CordRep* tree); void GetAppendRegion(char** region, size_t* size, size_t max_length); void GetAppendRegion(char** region, size_t* size); @@ -1022,6 +1041,20 @@ inline size_t Cord::InlineRep::size() const { return is_tree() ? as_tree()->length : inline_size(); } +inline void Cord::InlineRep::EmplaceTree(CordRep* rep, + MethodIdentifier method) { + data_.make_tree(rep); + CordzInfo::MaybeTrackCord(data_, method); +} + +inline void Cord::InlineRep::SetTree(CordRep* rep, + const CordzUpdateScope& scope) { + assert(rep); + assert(data_.is_tree()); + data_.set_tree(rep); + scope.SetCordRep(rep); +} + inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { if (rep == nullptr) { if (ABSL_PREDICT_FALSE(is_profiled())) { diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 710a1b40..74a90863 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -35,6 +35,7 @@ #include "absl/base/macros.h" #include "absl/container/fixed_array.h" #include "absl/strings/cord_test_helpers.h" +#include "absl/strings/cordz_test_helpers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" @@ -239,7 +240,6 @@ TEST(GigabyteCord, FromExternal) { // caused crashes in production. We grow exponentially so that the code will // execute in a reasonable amount of time. absl::Cord c; - ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size()); c.Append(from); while (c.size() < max_size) { c.Append(c); diff --git a/absl/strings/cord_test_helpers.h b/absl/strings/cord_test_helpers.h index f1036e3b..3815df81 100644 --- a/absl/strings/cord_test_helpers.h +++ b/absl/strings/cord_test_helpers.h @@ -18,6 +18,7 @@ #define ABSL_STRINGS_CORD_TEST_HELPERS_H_ #include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" namespace absl { ABSL_NAMESPACE_BEGIN diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc new file mode 100644 index 00000000..6e8deefd --- /dev/null +++ b/absl/strings/cordz_test.cc @@ -0,0 +1,92 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/strings/cord.h" +#include "absl/strings/cordz_test_helpers.h" +#include "absl/strings/internal/cordz_functions.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_sample_token.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +namespace absl { +ABSL_NAMESPACE_BEGIN + +using cord_internal::CordzInfo; +using cord_internal::CordzSampleToken; +using cord_internal::CordzStatistics; +using cord_internal::CordzUpdateTracker; +using Method = CordzUpdateTracker::MethodIdentifier; + +namespace { + +std::string MakeString(int length) { + std::string s(length, '.'); + for (int i = 4; i < length; i += 2) { + s[i] = '\b'; + } + return s; +} + +TEST(CordzTest, ConstructSmallStringView) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord(absl::string_view(MakeString(50))); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); +} + +TEST(CordzTest, ConstructLargeStringView) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord(absl::string_view(MakeString(5000))); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); +} + +TEST(CordzTest, CopyConstruct) { + CordzSamplingIntervalHelper sample_every(1); + Cord src = UnsampledCord(MakeString(5000)); + Cord cord(src); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); +} + +TEST(CordzTest, AppendLargeCordToEmpty) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord; + Cord src = UnsampledCord(MakeString(5000)); + cord.Append(src); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendCord)); +} + +TEST(CordzTest, MoveAppendLargeCordToEmpty) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord; + cord.Append(UnsampledCord(MakeString(5000))); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendCord)); +} + +} // namespace + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_CORDZ_ENABLED diff --git a/absl/strings/cordz_test_helpers.h b/absl/strings/cordz_test_helpers.h new file mode 100644 index 00000000..453c2f9d --- /dev/null +++ b/absl/strings/cordz_test_helpers.h @@ -0,0 +1,129 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ +#define ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_sample_token.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Returns the CordzInfo for the cord, or nullptr if the cord is not sampled. +inline const cord_internal::CordzInfo* GetCordzInfoForTesting( + const Cord& cord) { + return cord.contents_.cordz_info(); +} + +// Returns true if the provided cordz_info is in the list of sampled cords. +inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info, + cord_internal::CordzSampleToken token = {}) { + for (const cord_internal::CordzInfo& info : token) { + if (cordz_info == &info) return true; + } + return false; +} + +// Matcher on Cord that verifies all of: +// - the cord is sampled +// - the CordzInfo of the cord is listed / discoverable. +// - the reported CordzStatistics match the cord's actual properties +// - the cord has an (initial) UpdateTracker count of 1 for `method` +MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") { + const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); + if (cord_info == nullptr) { + *result_listener << "cord is not sampled"; + return false; + } + if (!CordzInfoIsListed(cord_info)) { + *result_listener << "cord is sampled, but not listed"; + return false; + } + cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics(); + if (stat.size != arg.size()) { + *result_listener << "cordz size " << stat.size + << " does not match cord size " << arg.size(); + return false; + } + if (stat.update_tracker.Value(method) != 1) { + *result_listener << "Expected method count 1 for " << method << ", found " + << stat.update_tracker.Value(method); + return false; + } + return true; +} + +// Cordz will only update with a new rate once the previously scheduled event +// has fired. When we disable Cordz, a long delay takes place where we won't +// consider profiling new Cords. CordzSampleIntervalHelper will burn through +// that interval and allow for testing that assumes that the average sampling +// interval is a particular value. +class CordzSamplingIntervalHelper { + public: + explicit CordzSamplingIntervalHelper(int32_t interval) + : orig_mean_interval_(absl::cord_internal::get_cordz_mean_interval()) { + absl::cord_internal::set_cordz_mean_interval(interval); + absl::cord_internal::cordz_set_next_sample_for_testing(interval); + } + + ~CordzSamplingIntervalHelper() { + absl::cord_internal::set_cordz_mean_interval(orig_mean_interval_); + absl::cord_internal::cordz_set_next_sample_for_testing(orig_mean_interval_); + } + + private: + int32_t orig_mean_interval_; +}; + +// Wrapper struct managing a small CordRep `rep` +struct TestCordRep { + cord_internal::CordRepFlat* rep; + + TestCordRep() { + rep = cord_internal::CordRepFlat::New(100); + rep->length = 100; + memset(rep->Data(), 1, 100); + } + ~TestCordRep() { cord_internal::CordRep::Unref(rep); } +}; + +// Wrapper struct managing a small CordRep `rep`, and +// an InlineData `data` initialized with that CordRep. +struct TestCordData { + TestCordRep rep; + cord_internal::InlineData data{rep.rep}; +}; + +// Creates a Cord that is not sampled +template +Cord UnsampledCord(Args... args) { + CordzSamplingIntervalHelper never(9999); + Cord cord(std::forward(args)...); + ABSL_ASSERT(GetCordzInfoForTesting(cord) == nullptr); + return cord; +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index 89e5fae5..76aa20b0 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -21,8 +21,10 @@ #include "absl/base/config.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" +#include "absl/strings/cordz_test_helpers.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/str_cat.h" #include "absl/types/span.h" @@ -37,24 +39,6 @@ using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Ne; -struct TestCordRep { - CordRepFlat* rep; - - TestCordRep() { - rep = CordRepFlat::New(100); - rep->length = 100; - memset(rep->Data(), 1, 100); - } - ~TestCordRep() { CordRepFlat::Delete(rep); } -}; - -struct TestCordData { - TestCordRep rep; - InlineData data; - - TestCordData() { data.make_tree(rep.rep); } -}; - // Used test values auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown; auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; diff --git a/absl/strings/internal/cordz_sample_token_test.cc b/absl/strings/internal/cordz_sample_token_test.cc index 5fbfd01c..80f41ca1 100644 --- a/absl/strings/internal/cordz_sample_token_test.cc +++ b/absl/strings/internal/cordz_sample_token_test.cc @@ -22,6 +22,7 @@ #include "gtest/gtest.h" #include "absl/memory/memory.h" #include "absl/random/random.h" +#include "absl/strings/cordz_test_helpers.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_info.h" @@ -39,24 +40,6 @@ using ::testing::ElementsAre; using ::testing::Eq; using ::testing::Ne; -struct TestCordRep { - CordRepFlat* rep; - - TestCordRep() { - rep = CordRepFlat::New(100); - rep->length = 100; - memset(rep->Data(), 1, 100); - } - ~TestCordRep() { CordRepFlat::Delete(rep); } -}; - -struct TestCord { - TestCordRep rep; - InlineData data; - - TestCord() { data.make_tree(rep.rep); } -}; - // Used test values auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; @@ -97,7 +80,7 @@ TEST(CordzSampleTokenTest, IteratorEmpty) { } TEST(CordzSampleTokenTest, Iterator) { - TestCord cord1, cord2, cord3; + TestCordData cord1, cord2, cord3; CordzInfo::TrackCord(cord1.data, kTrackCordMethod); CordzInfo* info1 = cord1.data.cordz_info(); CordzInfo::TrackCord(cord2.data, kTrackCordMethod); @@ -119,9 +102,9 @@ TEST(CordzSampleTokenTest, Iterator) { } TEST(CordzSampleTokenTest, IteratorEquality) { - TestCord cord1; - TestCord cord2; - TestCord cord3; + TestCordData cord1; + TestCordData cord2; + TestCordData cord3; CordzInfo::TrackCord(cord1.data, kTrackCordMethod); CordzInfo* info1 = cord1.data.cordz_info(); @@ -167,7 +150,7 @@ TEST(CordzSampleTokenTest, MultiThreaded) { for (int i = 0; i < kNumThreads; ++i) { pool.Schedule([&stop]() { absl::BitGen gen; - TestCord cords[kNumCords]; + TestCordData cords[kNumCords]; std::unique_ptr tokens[kNumTokens]; while (!stop.HasBeenNotified()) { @@ -179,7 +162,7 @@ TEST(CordzSampleTokenTest, MultiThreaded) { // 5) Sample int index = absl::Uniform(gen, 0, kNumCords); if (absl::Bernoulli(gen, 0.5)) { - TestCord& cord = cords[index]; + TestCordData& cord = cords[index]; // Track/untrack. if (cord.data.is_profiled()) { // 1) Untrack @@ -208,7 +191,7 @@ TEST(CordzSampleTokenTest, MultiThreaded) { } } } - for (TestCord& cord : cords) { + for (TestCordData& cord : cords) { if (cord.data.is_profiled()) { CordzInfo::UntrackCord(cord.data.cordz_info()); } diff --git a/absl/strings/internal/cordz_update_scope_test.cc b/absl/strings/internal/cordz_update_scope_test.cc index 4bdc78d3..3d08c622 100644 --- a/absl/strings/internal/cordz_update_scope_test.cc +++ b/absl/strings/internal/cordz_update_scope_test.cc @@ -17,6 +17,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/config.h" +#include "absl/strings/cordz_test_helpers.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cordz_info.h" #include "absl/strings/internal/cordz_update_tracker.h" @@ -27,24 +28,6 @@ namespace cord_internal { namespace { -struct TestCordRep { - CordRepFlat* rep; - - TestCordRep() { - rep = CordRepFlat::New(100); - rep->length = 100; - memset(rep->Data(), 1, 100); - } - ~TestCordRep() { CordRepFlat::Delete(rep); } -}; - -struct TestCord { - TestCordRep rep; - InlineData data; - - TestCord() { data.make_tree(rep.rep); } -}; - // Used test values auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; @@ -53,7 +36,7 @@ TEST(CordzUpdateScopeTest, ScopeNullptr) { } TEST(CordzUpdateScopeTest, ScopeSampledCord) { - TestCord cord; + TestCordData cord; CordzInfo::TrackCord(cord.data, kTrackCordMethod); CordzUpdateScope scope(cord.data.cordz_info(), kTrackCordMethod); cord.data.cordz_info()->SetCordRep(nullptr); diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index fb28fa98..a3617e74 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -1320,7 +1320,7 @@ TEST(Duration, SmallConversions) { EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0)); // TODO(bww): Is the next one OK? - EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0.124999999e-9)); + EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(0.125e-9, 0))); EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.125e-9)); EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.250e-9)); EXPECT_EQ(absl::Nanoseconds(1) / 2, absl::Seconds(0.375e-9)); @@ -1330,7 +1330,7 @@ TEST(Duration, SmallConversions) { EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(0.875e-9)); EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(1.000e-9)); - EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(-0.124999999e-9)); + EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(-0.125e-9, 0))); EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.125e-9)); EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.250e-9)); EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.375e-9)); diff --git a/absl/time/time.h b/absl/time/time.h index 2df68581..48982df4 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -1352,7 +1352,7 @@ constexpr Duration MakeDuration(int64_t hi, int64_t lo) { inline Duration MakePosDoubleDuration(double n) { const int64_t int_secs = static_cast(n); const uint32_t ticks = static_cast( - (n - static_cast(int_secs)) * kTicksPerSecond + 0.5); + std::round((n - static_cast(int_secs)) * kTicksPerSecond)); return ticks < kTicksPerSecond ? MakeDuration(int_secs, ticks) : MakeDuration(int_secs + 1, ticks - kTicksPerSecond); -- cgit v1.2.3 From a5167f986b82e9a7535ee710e87e53cf4c8ef2a8 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 27 Apr 2021 09:11:54 -0700 Subject: Export of internal Abseil changes -- f6fbb03bff276e72123e8590519079e87732ae62 by Abseil Team : Replace static absl::Mutex with SpinLock in absl::Cords to avoid static initializers by absl::Mutex destructors PiperOrigin-RevId: 370694199 -- 654b7d9edfdc24f226990b2b46cbf91451a1d92a by Martijn Vels : Implement global data for CordzInfo in an ODR hardened way This change puts the global data into a global list structure, and stores a reference to the global list in the handle itself. This hardens the implementation against ODR violations where info pointers are crossing dynamic library boundaries which are privately loaded. PiperOrigin-RevId: 370673045 -- 712dba768e66ee2ba85d6010829c617cd2af6ba7 by Martijn Vels : Intrument Cord::operator= for Cordz PiperOrigin-RevId: 370659149 -- c0b347a2289e151b72680269332e264b8fa989c0 by Matt Kulukundis : Fix test guards for ABSL_ATTRIBUTE_RETURNS_NONNULL PiperOrigin-RevId: 370594807 -- c2bedaa3472ef223f907de2604f9b9b58852ec5f by Martijn Vels : Add new Cordz instrumentation on GetAppendRegion. PiperOrigin-RevId: 370587761 -- 84fbfcc852697d509f6094482b86e84743a6b331 by Martijn Vels : Add instrumentation on Cord::Apppend(string_view) PiperOrigin-RevId: 370576590 -- 9e077390b8ca2239e1cb7bfbe1d5a04f2fc11d30 by Abseil Team : Google-internal changes only. PiperOrigin-RevId: 370558424 -- fb53c149eb2364ea34e3a67235f873866618b8ac by Matt Kulukundis : Update config.h macros with a few useful helpers to simplify version checking PiperOrigin-RevId: 370557684 -- abf8142e99b9ff7e15f6528a357f1005461950b0 by Martijn Vels : clang-format cord PiperOrigin-RevId: 370549371 -- e555985eabe63fcf0e980e9c433dd84caffec191 by Martijn Vels : Add MaybeUntrackCord() function This function is near identical to the old UntrackCord() but allows info to be null, moving the cord.is_profiled() branch into CordzInfo. PiperOrigin-RevId: 370528447 -- 3883538efe4601f7864bda70a50d868bb383c63b by Derek Mauro : Internal change PiperOrigin-RevId: 370503186 -- a9514b65542fde1bc73584e6f3c1c4b3a05f215f by Derek Mauro : Add -Winvalid-constexpr to warning options for LLVM PiperOrigin-RevId: 370455171 -- d8a3966de2cf15a2dc28e17e49a3d27d205eca92 by Martijn Vels : Add naive UniqueGenerator to avoid flakes from dup random values. PiperOrigin-RevId: 370179772 -- 46d0caa1a12b68a5998d4f919e20f0f83b9286f8 by Martijn Vels : Add new Cordz instrumentation on PrependTree. PiperOrigin-RevId: 370138969 GitOrigin-RevId: f6fbb03bff276e72123e8590519079e87732ae62 Change-Id: Ifa4c00a5c7b01198ee367a3253bea6b66612135e --- absl/base/attributes.h | 7 +- absl/base/config.h | 46 ++-- absl/container/internal/hash_generator_testing.h | 21 ++ .../internal/unordered_map_constructor_test.h | 38 +-- absl/copts/GENERATED_AbseilCopts.cmake | 1 + absl/copts/GENERATED_copts.bzl | 1 + absl/copts/copts.py | 1 + absl/copts/generate_copts.py | 2 +- absl/strings/BUILD.bazel | 5 + absl/strings/CMakeLists.txt | 5 + absl/strings/cord.cc | 295 +++++++++------------ absl/strings/cord.h | 54 +++- absl/strings/cord_test_helpers.h | 38 +++ absl/strings/cordz_test.cc | 130 +++++++-- absl/strings/cordz_test_helpers.h | 7 +- absl/strings/internal/cordz_handle.cc | 15 +- absl/strings/internal/cordz_handle.h | 7 +- absl/strings/internal/cordz_info.cc | 78 +++--- absl/strings/internal/cordz_info.h | 59 +++-- absl/strings/internal/cordz_info_test.cc | 30 +-- absl/strings/internal/cordz_sample_token_test.cc | 18 +- absl/strings/internal/cordz_update_scope.h | 8 +- absl/synchronization/internal/waiter.cc | 2 +- 23 files changed, 538 insertions(+), 330 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 294e5d0c..d710f287 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -281,10 +281,7 @@ // ABSL_ATTRIBUTE_RETURNS_NONNULL // // Tells the compiler that a particular function never returns a null pointer. -#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \ - (defined(__GNUC__) && \ - (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ - !defined(__clang__)) +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) #define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) #else #define ABSL_ATTRIBUTE_RETURNS_NONNULL @@ -622,7 +619,7 @@ #if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") #define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] #endif -#elif defined(__GNUC__) && __GNUC__ >= 7 +#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 0) #define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] #endif diff --git a/absl/base/config.h b/absl/base/config.h index 7abc75a6..0524196d 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -166,6 +166,22 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_FEATURE(f) 0 #endif +// Portable check for GCC minimum version: +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \ + (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0 +#endif + +#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \ + (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0 +#endif + // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. // We assume __thread is supported on Linux when compiled with Clang or compiled // against libstdc++ with _GLIBCXX_HAVE_TLS defined. @@ -183,10 +199,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // gcc >= 4.8.1 using libstdc++, and Visual Studio. #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE #error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set -#elif defined(_LIBCPP_VERSION) || \ - (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ - defined(_MSC_VER) +#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \ + (!defined(__clang__) && defined(__GLIBCXX__) && \ + ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8)) #define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif @@ -205,10 +220,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set -#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ - (!defined(__clang__) && defined(__GNUC__) && \ - (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ +#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ + (!defined(__clang__) && ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ (defined(_MSC_VER) && !defined(__NVCC__)) #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 @@ -222,7 +236,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) #define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 -#elif defined(__GNUC__) && __GNUC__ >= 5 +#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) #define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 #endif #endif @@ -319,25 +333,21 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // For further details, consult the compiler's documentation. #ifdef ABSL_HAVE_EXCEPTIONS #error ABSL_HAVE_EXCEPTIONS cannot be directly set. - -#elif defined(__clang__) - -#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) +#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6) // Clang >= 3.6 #if ABSL_HAVE_FEATURE(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 #endif // ABSL_HAVE_FEATURE(cxx_exceptions) -#else +#elif defined(__clang__) // Clang < 3.6 // http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro #if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 #endif // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) -#endif // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) - // Handle remaining special cases and default to exceptions being supported. -#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ - !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \ +#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ + !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) && \ + !defined(__cpp_exceptions)) && \ !(defined(_MSC_VER) && !defined(_CPPUNWIND)) #define ABSL_HAVE_EXCEPTIONS 1 #endif diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h index 6869fe45..f1f555a5 100644 --- a/absl/container/internal/hash_generator_testing.h +++ b/absl/container/internal/hash_generator_testing.h @@ -21,11 +21,13 @@ #include #include +#include #include #include #include #include #include +#include #include "absl/container/internal/hash_policy_testing.h" #include "absl/memory/memory.h" @@ -153,6 +155,25 @@ using GeneratedType = decltype( typename Container::value_type, typename Container::key_type>::type>&>()()); +// Naive wrapper that performs a linear search of previous values. +// Beware this is O(SQR), which is reasonable for smaller kMaxValues. +template +struct UniqueGenerator { + Generator gen; + std::vector values; + + T operator()() { + assert(values.size() < kMaxValues); + for (;;) { + T value = gen(); + if (std::find(values.begin(), values.end(), value) == values.end()) { + values.push_back(value); + return value; + } + } + } +}; + } // namespace hash_internal } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h index 3f90ad7c..c1d20f3c 100644 --- a/absl/container/internal/unordered_map_constructor_test.h +++ b/absl/container/internal/unordered_map_constructor_test.h @@ -179,7 +179,7 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { A alloc(0); std::vector values; std::generate_n(std::back_inserter(values), 10, - hash_internal::Generator()); + hash_internal::UniqueGenerator()); TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc); EXPECT_EQ(m.hash_function(), hasher); EXPECT_EQ(m.key_eq(), equal); @@ -198,7 +198,7 @@ void InputIteratorBucketAllocTest(std::true_type) { A alloc(0); std::vector values; std::generate_n(std::back_inserter(values), 10, - hash_internal::Generator()); + hash_internal::UniqueGenerator()); TypeParam m(values.begin(), values.end(), 123, alloc); EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); @@ -221,7 +221,7 @@ void InputIteratorBucketHashAllocTest(std::true_type) { A alloc(0); std::vector values; std::generate_n(std::back_inserter(values), 10, - hash_internal::Generator()); + hash_internal::UniqueGenerator()); TypeParam m(values.begin(), values.end(), 123, hasher, alloc); EXPECT_EQ(m.hash_function(), hasher); EXPECT_EQ(m.get_allocator(), alloc); @@ -241,8 +241,9 @@ TYPED_TEST_P(ConstructorTest, CopyConstructor) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam n(m); EXPECT_EQ(m.hash_function(), n.hash_function()); EXPECT_EQ(m.key_eq(), n.key_eq()); @@ -262,8 +263,9 @@ void CopyConstructorAllocTest(std::true_type) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam n(m, A(11)); EXPECT_EQ(m.hash_function(), n.hash_function()); EXPECT_EQ(m.key_eq(), n.key_eq()); @@ -285,8 +287,9 @@ TYPED_TEST_P(ConstructorTest, MoveConstructor) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam t(m); TypeParam n(std::move(t)); EXPECT_EQ(m.hash_function(), n.hash_function()); @@ -307,8 +310,9 @@ void MoveConstructorAllocTest(std::true_type) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam t(m); TypeParam n(std::move(t), A(1)); EXPECT_EQ(m.hash_function(), n.hash_function()); @@ -325,7 +329,7 @@ TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { using T = hash_internal::GeneratedType; - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -348,7 +352,7 @@ template void InitializerListBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using A = typename TypeParam::allocator_type; - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; A alloc(0); TypeParam m(values, 123, alloc); @@ -371,7 +375,7 @@ void InitializerListBucketHashAllocTest(std::true_type) { using A = typename TypeParam::allocator_type; H hasher; A alloc(0); - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m(values, 123, hasher, alloc); EXPECT_EQ(m.hash_function(), hasher); @@ -392,7 +396,7 @@ TYPED_TEST_P(ConstructorTest, Assignment) { H hasher; E equal; A alloc(0); - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); TypeParam n; n = m; @@ -412,7 +416,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignment) { H hasher; E equal; A alloc(0); - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); TypeParam t(m); TypeParam n; @@ -424,7 +428,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignment) { TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) { using T = hash_internal::GeneratedType; - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m; m = values; @@ -433,7 +437,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) { TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) { using T = hash_internal::GeneratedType; - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; TypeParam m({gen(), gen(), gen()}); TypeParam n({gen()}); n = m; @@ -442,7 +446,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) { TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) { using T = hash_internal::GeneratedType; - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; TypeParam m({gen(), gen(), gen()}); TypeParam t(m); TypeParam n({gen()}); @@ -452,7 +456,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) { TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) { using T = hash_internal::GeneratedType; - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m; m = values; @@ -461,7 +465,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) { TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { using T = hash_internal::GeneratedType; - hash_internal::Generator gen; + hash_internal::UniqueGenerator gen; std::initializer_list values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m(values); m = *&m; // Avoid -Wself-assign diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake index 51742c9b..18a1e5c3 100644 --- a/absl/copts/GENERATED_AbseilCopts.cmake +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -71,6 +71,7 @@ list(APPEND ABSL_LLVM_FLAGS "-Wformat-security" "-Wgnu-redeclared-enum" "-Winfinite-recursion" + "-Winvalid-constexpr" "-Wliteral-conversion" "-Wmissing-declarations" "-Woverlength-strings" diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl index 6707488f..d2bd5608 100644 --- a/absl/copts/GENERATED_copts.bzl +++ b/absl/copts/GENERATED_copts.bzl @@ -72,6 +72,7 @@ ABSL_LLVM_FLAGS = [ "-Wformat-security", "-Wgnu-redeclared-enum", "-Winfinite-recursion", + "-Winvalid-constexpr", "-Wliteral-conversion", "-Wmissing-declarations", "-Woverlength-strings", diff --git a/absl/copts/copts.py b/absl/copts/copts.py index cf52981c..ce30df89 100644 --- a/absl/copts/copts.py +++ b/absl/copts/copts.py @@ -87,6 +87,7 @@ COPT_VARS = { "-Wformat-security", "-Wgnu-redeclared-enum", "-Winfinite-recursion", + "-Winvalid-constexpr", "-Wliteral-conversion", "-Wmissing-declarations", "-Woverlength-strings", diff --git a/absl/copts/generate_copts.py b/absl/copts/generate_copts.py index 0e5dc9fa..34be2fc2 100755 --- a/absl/copts/generate_copts.py +++ b/absl/copts/generate_copts.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 """Generate Abseil compile compile option configs. Usage: /copts/generate_copts.py diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 5d4ad7e3..68c71f0a 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -354,6 +354,7 @@ cc_library( hdrs = ["internal/cordz_handle.h"], copts = ABSL_DEFAULT_COPTS, deps = [ + "//absl/base", "//absl/base:config", "//absl/base:raw_logging_internal", "//absl/synchronization", @@ -371,8 +372,10 @@ cc_library( ":cordz_handle", ":cordz_statistics", ":cordz_update_tracker", + "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/debugging:stacktrace", "//absl/synchronization", "//absl/types:span", @@ -525,6 +528,7 @@ cc_library( deps = [ ":cord", ":cord_internal", + ":strings", ], ) @@ -587,6 +591,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":cord", + ":cord_test_helpers", ":cordz_functions", ":cordz_info", ":cordz_sample_token", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 2a8e3df2..80ae2a60 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -655,6 +655,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::config absl::raw_logging_internal absl::synchronization @@ -687,6 +688,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::config absl::cord_internal absl::cordz_functions @@ -695,6 +697,7 @@ absl_cc_library( absl::cordz_update_tracker absl::core_headers absl::span + absl::raw_logging_internal absl::stacktrace absl::synchronization ) @@ -829,6 +832,7 @@ absl_cc_library( DEPS absl::cord absl::cord_internal + absl::strings TESTONLY ) @@ -914,6 +918,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::cord + absl::cord_test_helpers absl::cordz_test_helpers absl::cordz_functions absl::cordz_info diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 768106d4..15d17337 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -210,7 +210,7 @@ static CordRep* MakeBalancedTree(CordRep** reps, size_t n) { } static CordRepFlat* CreateFlat(const char* data, size_t length, - size_t alloc_hint) { + size_t alloc_hint) { CordRepFlat* flat = CordRepFlat::New(length + alloc_hint); flat->length = length; memcpy(flat->Data(), data, length); @@ -234,9 +234,7 @@ static CordRep* RingNewTree(const char* data, size_t length, // Create a new tree out of the specified array. // The returned node has a refcount of 1. -static CordRep* NewTree(const char* data, - size_t length, - size_t alloc_hint) { +static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) { if (length == 0) return nullptr; if (cord_ring_enabled()) { return RingNewTree(data, length, alloc_hint); @@ -303,24 +301,6 @@ inline char* Cord::InlineRep::set_data(size_t n) { return data_.as_chars(); } -static inline CordRepFlat* MakeFlat(const InlineData& data, size_t extra = 0) { - static_assert(kMinFlatLength >= sizeof(data), ""); - size_t len = data.inline_size(); - CordRepFlat* result = CordRepFlat::New(len + extra); - result->length = len; - memcpy(result->Data(), data.as_chars(), sizeof(data)); - return result; -} - -inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) { - if (data_.is_tree()) { - return data_.as_tree(); - } - CordRepFlat* result = MakeFlat(data_, extra_hint); - set_tree(result); - return result; -} - inline void Cord::InlineRep::reduce_size(size_t n) { size_t tag = inline_size(); assert(tag <= kMaxInline); @@ -346,7 +326,7 @@ void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, MethodIdentifier method) { assert(!is_tree()); if (!data_.is_empty()) { - CordRepFlat* flat = MakeFlat(data_); + CordRepFlat* flat = MakeFlatWithExtraCapacity(0); if (cord_ring_enabled()) { tree = CordRepRing::Append(CordRepRing::Create(flat, 1), tree); } else { @@ -376,14 +356,38 @@ void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) { } } -void Cord::InlineRep::PrependTree(CordRep* tree) { +void Cord::InlineRep::PrependTreeToInlined(CordRep* tree, + MethodIdentifier method) { + assert(!is_tree()); + if (!data_.is_empty()) { + CordRepFlat* flat = MakeFlatWithExtraCapacity(0); + if (cord_ring_enabled()) { + tree = CordRepRing::Prepend(CordRepRing::Create(flat, 1), tree); + } else { + tree = Concat(tree, flat); + } + } + EmplaceTree(tree, method); +} + +void Cord::InlineRep::PrependTreeToTree(CordRep* tree, + MethodIdentifier method) { + assert(is_tree()); + const CordzUpdateScope scope(data_.cordz_info(), method); + if (cord_ring_enabled()) { + tree = CordRepRing::Prepend(ForceRing(data_.as_tree(), 1), tree); + } else { + tree = Concat(tree, data_.as_tree()); + } + SetTree(tree, scope); +} + +void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { assert(tree != nullptr); - if (data_.is_empty()) { - set_tree(tree); - } else if (cord_ring_enabled()) { - set_tree(CordRepRing::Prepend(ForceRing(force_tree(0), 1), tree)); + if (data_.is_tree()) { + PrependTreeToTree(tree, method); } else { - set_tree(Concat(tree, force_tree(0))); + PrependTreeToInlined(tree, method); } } @@ -435,78 +439,43 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, return true; } +template void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, - size_t max_length) { - if (max_length == 0) { - *region = nullptr; - *size = 0; - return; - } - - // Try to fit in the inline buffer if possible. - if (!is_tree()) { - size_t inline_length = inline_size(); - if (max_length <= kMaxInline - inline_length) { - *region = data_.as_chars() + inline_length; - *size = max_length; - set_inline_size(inline_length + max_length); + size_t length) { + auto constexpr method = CordzUpdateTracker::kGetAppendRegion; + + CordRep* root = tree(); + size_t sz = root ? root->length : inline_size(); + if (root == nullptr) { + size_t available = kMaxInline - sz; + if (available >= (has_length ? length : 1)) { + *region = data_.as_chars() + sz; + *size = has_length ? length : available; + set_inline_size(has_length ? sz + length : kMaxInline); return; } } - CordRep* root = force_tree(max_length); - - if (PrepareAppendRegion(root, region, size, max_length)) { - UpdateCordzStatistics(); + size_t extra = has_length ? length : (std::max)(sz, kMinFlatLength); + CordRep* rep = root ? root : MakeFlatWithExtraCapacity(extra); + CordzUpdateScope scope(root ? data_.cordz_info() : nullptr, method); + if (PrepareAppendRegion(rep, region, size, length)) { + CommitTree(root, rep, scope, method); return; } // Allocate new node. - CordRepFlat* new_node = - CordRepFlat::New(std::max(static_cast(root->length), max_length)); - new_node->length = std::min(new_node->Capacity(), max_length); + CordRepFlat* new_node = CordRepFlat::New(extra); + new_node->length = std::min(new_node->Capacity(), length); *region = new_node->Data(); *size = new_node->length; if (cord_ring_enabled()) { - replace_tree(CordRepRing::Append(ForceRing(root, 1), new_node)); - return; - } - replace_tree(Concat(root, new_node)); -} - -void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) { - const size_t max_length = std::numeric_limits::max(); - - // Try to fit in the inline buffer if possible. - if (!data_.is_tree()) { - size_t inline_length = inline_size(); - if (inline_length < kMaxInline) { - *region = data_.as_chars() + inline_length; - *size = kMaxInline - inline_length; - set_inline_size(kMaxInline); - return; - } - } - - CordRep* root = force_tree(max_length); - - if (PrepareAppendRegion(root, region, size, max_length)) { - UpdateCordzStatistics(); - return; - } - - // Allocate new node. - CordRepFlat* new_node = CordRepFlat::New(root->length); - new_node->length = new_node->Capacity(); - *region = new_node->Data(); - *size = new_node->length; - - if (cord_ring_enabled()) { - replace_tree(CordRepRing::Append(ForceRing(root, 1), new_node)); - return; + rep = CordRepRing::Append(ForceRing(rep, 1), new_node); + } else { + rep = Concat(rep, new_node); } - replace_tree(Concat(root, new_node)); + CommitTree(root, rep, scope, method); } // If the rep is a leaf, this will increment the value at total_mem_usage and @@ -529,21 +498,26 @@ void Cord::InlineRep::UpdateCordzStatisticsSlow() { } void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { - UnrefTree(); - - data_ = src.data_; - if (is_tree()) { - CordRep::Ref(tree()); - clear_cordz_info(); - CordzInfo::MaybeTrackCord(data_, CordzUpdateTracker::kAssignCord); + assert(&src != this); + assert(is_tree() || src.is_tree()); + auto constexpr method = CordzUpdateTracker::kAssignCord; + if (CordRep* tree = this->tree()) { + CordzUpdateScope scope(data_.cordz_info(), method); + CordRep::Unref(tree); + if (CordRep* src_tree = src.tree()) { + SetTree(CordRep::Ref(src_tree), scope); + } else { + scope.SetCordRep(nullptr); + data_ = src.data_; + } + } else { + EmplaceTree(CordRep::Ref(src.as_tree()), method); } } void Cord::InlineRep::UnrefTree() { if (is_tree()) { - if (ABSL_PREDICT_FALSE(is_profiled())) { - absl::cord_internal::CordzInfo::UntrackCord(cordz_info()); - } + CordzInfo::MaybeUntrackCord(data_.cordz_info()); CordRep::Unref(tree()); } } @@ -595,12 +569,9 @@ template Cord::Cord(std::string&& src); // The destruction code is separate so that the compiler can determine // that it does not need to call the destructor on a moved-from Cord. void Cord::DestroyCordSlow() { - if (ABSL_PREDICT_FALSE(contents_.is_profiled())) { - absl::cord_internal::CordzInfo::UntrackCord(contents_.cordz_info()); - } - if (CordRep* tree = contents_.tree()) { - CordRep::Unref(VerifyTree(tree)); - } + assert(contents_.is_tree()); + CordzInfo::MaybeUntrackCord(contents_.cordz_info()); + CordRep::Unref(VerifyTree(contents_.as_tree())); } // -------------------------------------------------------------------- @@ -613,23 +584,18 @@ void Cord::Clear() { } Cord& Cord::operator=(absl::string_view src) { - if (ABSL_PREDICT_FALSE(contents_.is_profiled())) { - absl::cord_internal::CordzInfo::UntrackCord(contents_.cordz_info()); - contents_.clear_cordz_info(); - } - const char* data = src.data(); size_t length = src.size(); CordRep* tree = contents_.tree(); if (length <= InlineRep::kMaxInline) { // Embed into this->contents_ + if (tree) CordzInfo::MaybeUntrackCord(contents_.cordz_info()); contents_.set_data(data, length, true); if (tree) CordRep::Unref(tree); return *this; } if (tree != nullptr && tree->tag >= FLAT && - tree->flat()->Capacity() >= length && - tree->refcount.IsOne()) { + tree->flat()->Capacity() >= length && tree->refcount.IsOne()) { // Copy in place if the existing FLAT node is reusable. memmove(tree->flat()->Data(), data, length); tree->length = length; @@ -655,71 +621,70 @@ template Cord& Cord::operator=(std::string&& src); // TODO(sanjay): Move to Cord::InlineRep section of file. For now, // we keep it here to make diffs easier. -void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) { - if (src_size == 0) return; // memcpy(_, nullptr, 0) is undefined. +void Cord::InlineRep::AppendArray(absl::string_view src, + MethodIdentifier method) { + if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined. size_t appended = 0; - CordRep* root = nullptr; - if (is_tree()) { - root = data_.as_tree(); + CordRep* rep = tree(); + const CordRep* const root = rep; + CordzUpdateScope scope(root ? cordz_info() : nullptr, method); + if (root != nullptr) { char* region; - if (PrepareAppendRegion(root, ®ion, &appended, src_size)) { - memcpy(region, src_data, appended); - UpdateCordzStatistics(); + if (PrepareAppendRegion(rep, ®ion, &appended, src.size())) { + memcpy(region, src.data(), appended); } } else { // Try to fit in the inline buffer if possible. size_t inline_length = inline_size(); - if (src_size <= kMaxInline - inline_length) { + if (src.size() <= kMaxInline - inline_length) { // Append new data to embedded array - memcpy(data_.as_chars() + inline_length, src_data, src_size); - set_inline_size(inline_length + src_size); + memcpy(data_.as_chars() + inline_length, src.data(), src.size()); + set_inline_size(inline_length + src.size()); return; } - // It is possible that src_data == data_, but when we transition from an + // It is possible that src.data() == data_, but when we transition from an // InlineRep to a tree we need to assign data_ = root via set_tree. To // avoid corrupting the source data before we copy it, delay calling // set_tree until after we've copied data. // We are going from an inline size to beyond inline size. Make the new size // either double the inlined size, or the added size + 10%. - const size_t size1 = inline_length * 2 + src_size; - const size_t size2 = inline_length + src_size / 10; - root = CordRepFlat::New(std::max(size1, size2)); - appended = std::min( - src_size, root->flat()->Capacity() - inline_length); - memcpy(root->flat()->Data(), data_.as_chars(), inline_length); - memcpy(root->flat()->Data() + inline_length, src_data, appended); - root->length = inline_length + appended; - set_tree(root); - } - - src_data += appended; - src_size -= appended; - if (src_size == 0) { + const size_t size1 = inline_length * 2 + src.size(); + const size_t size2 = inline_length + src.size() / 10; + rep = CordRepFlat::New(std::max(size1, size2)); + appended = std::min(src.size(), rep->flat()->Capacity() - inline_length); + memcpy(rep->flat()->Data(), data_.as_chars(), inline_length); + memcpy(rep->flat()->Data() + inline_length, src.data(), appended); + rep->length = inline_length + appended; + } + + src.remove_prefix(appended); + if (src.empty()) { + CommitTree(root, rep, scope, method); return; } if (cord_ring_enabled()) { - absl::string_view data(src_data, src_size); - root = ForceRing(root, (data.size() - 1) / kMaxFlatLength + 1); - replace_tree(CordRepRing::Append(root->ring(), data)); - return; - } - - // Use new block(s) for any remaining bytes that were not handled above. - // Alloc extra memory only if the right child of the root of the new tree is - // going to be a FLAT node, which will permit further inplace appends. - size_t length = src_size; - if (src_size < kMaxFlatLength) { - // The new length is either - // - old size + 10% - // - old_size + src_size - // This will cause a reasonable conservative step-up in size that is still - // large enough to avoid excessive amounts of small fragments being added. - length = std::max(root->length / 10, src_size); + rep = ForceRing(rep, (src.size() - 1) / kMaxFlatLength + 1); + rep = CordRepRing::Append(rep->ring(), src); + } else { + // Use new block(s) for any remaining bytes that were not handled above. + // Alloc extra memory only if the right child of the root of the new tree + // is going to be a FLAT node, which will permit further inplace appends. + size_t length = src.size(); + if (src.size() < kMaxFlatLength) { + // The new length is either + // - old size + 10% + // - old_size + src.size() + // This will cause a reasonable conservative step-up in size that is + // still large enough to avoid excessive amounts of small fragments + // being added. + length = std::max(rep->length / 10, src.size()); + } + rep = Concat(rep, NewTree(src.data(), src.size(), length - src.size())); } - set_tree(Concat(root, NewTree(src_data, src_size, length - src_size))); + CommitTree(root, rep, scope, method); } inline CordRep* Cord::TakeRep() const& { @@ -734,12 +699,13 @@ inline CordRep* Cord::TakeRep() && { template inline void Cord::AppendImpl(C&& src) { + auto constexpr method = CordzUpdateTracker::kAppendCord; if (empty()) { // Since destination is empty, we can avoid allocating a node, if (src.contents_.is_tree()) { // by taking the tree directly CordRep* rep = std::forward(src).TakeRep(); - contents_.EmplaceTree(rep, CordzUpdateTracker::kAppendCord); + contents_.EmplaceTree(rep, method); } else { // or copying over inline data contents_.data_ = src.contents_.data_; @@ -753,12 +719,12 @@ inline void Cord::AppendImpl(C&& src) { CordRep* src_tree = src.contents_.tree(); if (src_tree == nullptr) { // src has embedded data. - contents_.AppendArray(src.contents_.data(), src_size); + contents_.AppendArray({src.contents_.data(), src_size}, method); return; } if (src_tree->tag >= FLAT) { // src tree just has one flat node. - contents_.AppendArray(src_tree->flat()->Data(), src_size); + contents_.AppendArray({src_tree->flat()->Data(), src_size}, method); return; } if (&src == this) { @@ -797,7 +763,7 @@ void Cord::Prepend(const Cord& src) { CordRep* src_tree = src.contents_.tree(); if (src_tree != nullptr) { CordRep::Ref(src_tree); - contents_.PrependTree(src_tree); + contents_.PrependTree(src_tree, CordzUpdateTracker::kPrependCord); return; } @@ -820,7 +786,8 @@ void Cord::Prepend(absl::string_view src) { return; } } - contents_.PrependTree(NewTree(src.data(), src.size(), 0)); + CordRep* rep = NewTree(src.data(), src.size(), 0); + contents_.PrependTree(rep, CordzUpdateTracker::kPrependString); } template > @@ -1847,8 +1814,7 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, *os << absl::CEscape(std::string(rep->external()->base, rep->length)); *os << "]\n"; } else if (rep->tag >= FLAT) { - *os << "FLAT cap=" << rep->flat()->Capacity() - << " ["; + *os << "FLAT cap=" << rep->flat()->Capacity() << " ["; if (include_data) *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length)); *os << "]\n"; @@ -1860,7 +1826,7 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, do { DumpNode(ring->entry_child(head), include_data, os, indent + kIndentStep); - head = ring->advance(head);; + head = ring->advance(head); } while (head != ring->tail()); } if (stack.empty()) break; @@ -1906,9 +1872,8 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, worklist.push_back(node->concat()->left); } } else if (node->tag >= FLAT) { - ABSL_INTERNAL_CHECK( - node->length <= node->flat()->Capacity(), - ReportError(root, node)); + ABSL_INTERNAL_CHECK(node->length <= node->flat()->Capacity(), + ReportError(root, node)); } else if (node->tag == EXTERNAL) { ABSL_INTERNAL_CHECK(node->external()->base != nullptr, ReportError(root, node)); diff --git a/absl/strings/cord.h b/absl/strings/cord.h index dd6c3bca..d5a13b34 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -671,6 +671,7 @@ class Cord { private: using CordRep = absl::cord_internal::CordRep; + using CordRepFlat = absl::cord_internal::CordRepFlat; using CordzInfo = cord_internal::CordzInfo; using CordzUpdateScope = cord_internal::CordzUpdateScope; using CordzUpdateTracker = cord_internal::CordzUpdateTracker; @@ -728,12 +729,15 @@ class Cord { // Returns non-null iff was holding a pointer absl::cord_internal::CordRep* clear(); // Converts to pointer if necessary. - absl::cord_internal::CordRep* force_tree(size_t extra_hint); void reduce_size(size_t n); // REQUIRES: holding data void remove_prefix(size_t n); // REQUIRES: holding data - void AppendArray(const char* src_data, size_t src_size); + void AppendArray(absl::string_view src, MethodIdentifier method); absl::string_view FindFlatStartPiece() const; + // Creates a CordRepFlat instance from the current inlined data with `extra' + // bytes of desired additional capacity. + CordRepFlat* MakeFlatWithExtraCapacity(size_t extra); + // Sets the tree value for this instance. `rep` must not be null. // Requires the current instance to hold a tree, and a lock to be held on // any CordzInfo referenced by this instance. The latter is enforced through @@ -747,12 +751,22 @@ class Cord { // value to a non-inlined (tree / ring) value. void EmplaceTree(CordRep* rep, MethodIdentifier method); + // Commits the change of a newly created, or updated `rep` root value into + // this cord. `old_rep` indicates the old (inlined or tree) value of the + // cord, and determines if the commit invokes SetTree() or EmplaceTree(). + void CommitTree(const CordRep* old_rep, CordRep* rep, + const CordzUpdateScope& scope, MethodIdentifier method); + void AppendTreeToInlined(CordRep* tree, MethodIdentifier method); void AppendTreeToTree(CordRep* tree, MethodIdentifier method); void AppendTree(CordRep* tree, MethodIdentifier method); - void PrependTree(absl::cord_internal::CordRep* tree); - void GetAppendRegion(char** region, size_t* size, size_t max_length); - void GetAppendRegion(char** region, size_t* size); + void PrependTreeToInlined(CordRep* tree, MethodIdentifier method); + void PrependTreeToTree(CordRep* tree, MethodIdentifier method); + void PrependTree(CordRep* tree, MethodIdentifier method); + + template + void GetAppendRegion(char** region, size_t* size, size_t length); + bool IsSame(const InlineRep& other) const { return memcmp(&data_, &other.data_, sizeof(data_)) == 0; } @@ -1041,6 +1055,16 @@ inline size_t Cord::InlineRep::size() const { return is_tree() ? as_tree()->length : inline_size(); } +inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity( + size_t extra) { + static_assert(cord_internal::kMinFlatLength >= sizeof(data_), ""); + size_t len = data_.inline_size(); + auto* result = CordRepFlat::New(len + extra); + result->length = len; + memcpy(result->Data(), data_.as_chars(), sizeof(data_)); + return result; +} + inline void Cord::InlineRep::EmplaceTree(CordRep* rep, MethodIdentifier method) { data_.make_tree(rep); @@ -1055,10 +1079,20 @@ inline void Cord::InlineRep::SetTree(CordRep* rep, scope.SetCordRep(rep); } +inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep, + const CordzUpdateScope& scope, + MethodIdentifier method) { + if (old_rep) { + SetTree(rep, scope); + } else { + EmplaceTree(rep, method); + } +} + inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { if (rep == nullptr) { - if (ABSL_PREDICT_FALSE(is_profiled())) { - absl::cord_internal::CordzInfo::UntrackCord(cordz_info()); + if (data_.is_tree()) { + CordzInfo::MaybeUntrackCord(data_.cordz_info()); } ResetToEmpty(); } else { @@ -1086,8 +1120,8 @@ inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) { } inline absl::cord_internal::CordRep* Cord::InlineRep::clear() { - if (ABSL_PREDICT_FALSE(is_profiled())) { - absl::cord_internal::CordzInfo::UntrackCord(cordz_info()); + if (is_tree()) { + CordzInfo::MaybeUntrackCord(cordz_info()); } absl::cord_internal::CordRep* result = tree(); ResetToEmpty(); @@ -1180,7 +1214,7 @@ inline absl::string_view Cord::Flatten() { } inline void Cord::Append(absl::string_view src) { - contents_.AppendArray(src.data(), src.size()); + contents_.AppendArray(src, CordzUpdateTracker::kAppendString); } extern template void Cord::Append(std::string&& src); diff --git a/absl/strings/cord_test_helpers.h b/absl/strings/cord_test_helpers.h index 3815df81..6ccccc51 100644 --- a/absl/strings/cord_test_helpers.h +++ b/absl/strings/cord_test_helpers.h @@ -17,12 +17,50 @@ #ifndef ABSL_STRINGS_CORD_TEST_HELPERS_H_ #define ABSL_STRINGS_CORD_TEST_HELPERS_H_ +#include +#include + #include "absl/strings/cord.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN +// Cord sizes relevant for testing +enum class TestCordSize { + kEmpty = 0, + kInlined = cord_internal::kMaxInline / 2 + 1, + kSmall = cord_internal::kMaxBytesToCopy / 2 + 1, + kMedium = cord_internal::kMaxFlatLength / 2 + 1, + kLarge = cord_internal::kMaxFlatLength * 4 +}; + +// To string helper +inline absl::string_view ToString(TestCordSize size) { + switch (size) { + case TestCordSize::kEmpty: + return "Empty"; + case TestCordSize::kInlined: + return "Inlined"; + case TestCordSize::kSmall: + return "Small"; + case TestCordSize::kMedium: + return "Medium"; + case TestCordSize::kLarge: + return "Large"; + } + return "???"; +} + +// Returns the length matching the specified size +inline size_t Length(TestCordSize size) { return static_cast(size); } + +// Stream output helper +inline std::ostream& operator<<(std::ostream& stream, TestCordSize size) { + return stream << ToString(size); +} + // Creates a multi-segment Cord from an iterable container of strings. The // resulting Cord is guaranteed to have one segment for every string in the // container. This allows code to be unit tested with multi-segment Cord diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc index 6e8deefd..b16e9686 100644 --- a/absl/strings/cordz_test.cc +++ b/absl/strings/cordz_test.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include "gmock/gmock.h" @@ -20,6 +21,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/strings/cord.h" +#include "absl/strings/cord_test_helpers.h" #include "absl/strings/cordz_test_helpers.h" #include "absl/strings/internal/cordz_functions.h" #include "absl/strings/internal/cordz_info.h" @@ -31,6 +33,8 @@ #ifdef ABSL_INTERNAL_CORDZ_ENABLED +using testing::Eq; + namespace absl { ABSL_NAMESPACE_BEGIN @@ -40,48 +44,126 @@ using cord_internal::CordzStatistics; using cord_internal::CordzUpdateTracker; using Method = CordzUpdateTracker::MethodIdentifier; +// Do not print cord contents, we only care about 'size' perhaps. +// Note that this method must be inside the named namespace. +inline void PrintTo(const Cord& cord, std::ostream* s) { + if (s) *s << "Cord[" << cord.size() << "]"; +} + namespace { -std::string MakeString(int length) { - std::string s(length, '.'); - for (int i = 4; i < length; i += 2) { - s[i] = '\b'; - } - return s; +// Returns a string_view value of the specified length +// We do this to avoid 'consuming' large strings in Cord by default. +absl::string_view MakeString(size_t size) { + thread_local std::string str; + str = std::string(size, '.'); + return str; +} + +absl::string_view MakeString(TestCordSize size) { + return MakeString(Length(size)); +} + +std::string TestParamToString(::testing::TestParamInfo size) { + return absl::StrCat("On", ToString(size.param), "Cord"); } -TEST(CordzTest, ConstructSmallStringView) { - CordzSamplingIntervalHelper sample_every(1); - Cord cord(absl::string_view(MakeString(50))); +class CordzUpdateTest : public testing::TestWithParam { + public: + Cord& cord() { return cord_; } + + Method InitialOr(Method method) const { + return (GetParam() > TestCordSize::kInlined) ? Method::kConstructorString + : method; + } + + private: + CordzSamplingIntervalHelper sample_every_{1}; + Cord cord_{MakeString(GetParam())}; +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordzUpdateTest, + testing::Values(TestCordSize::kEmpty, + TestCordSize::kInlined, + TestCordSize::kLarge), + TestParamToString); + +TEST(CordzTest, ConstructSmallString) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(MakeString(TestCordSize::kSmall)); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } -TEST(CordzTest, ConstructLargeStringView) { - CordzSamplingIntervalHelper sample_every(1); - Cord cord(absl::string_view(MakeString(5000))); +TEST(CordzTest, ConstructLargeString) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(MakeString(TestCordSize::kLarge)); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } TEST(CordzTest, CopyConstruct) { - CordzSamplingIntervalHelper sample_every(1); - Cord src = UnsampledCord(MakeString(5000)); + CordzSamplingIntervalHelper sample_every{1}; + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); Cord cord(src); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); } -TEST(CordzTest, AppendLargeCordToEmpty) { - CordzSamplingIntervalHelper sample_every(1); - Cord cord; - Cord src = UnsampledCord(MakeString(5000)); - cord.Append(src); - EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendCord)); +TEST(CordzTest, MoveConstruct) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src(MakeString(TestCordSize::kLarge)); + Cord cord(std::move(src)); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } -TEST(CordzTest, MoveAppendLargeCordToEmpty) { - CordzSamplingIntervalHelper sample_every(1); +TEST_P(CordzUpdateTest, AssignCord) { + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord() = src; + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAssignCord))); +} + +TEST(CordzTest, AssignInlinedCord) { + CordzSampleToken token; + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(MakeString(TestCordSize::kLarge)); + const CordzInfo* info = GetCordzInfoForTesting(cord); + Cord src = UnsampledCord(MakeString(TestCordSize::kInlined)); + cord = src; + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); +} + +TEST(CordzTest, MoveAssignCord) { + CordzSamplingIntervalHelper sample_every{1}; Cord cord; - cord.Append(UnsampledCord(MakeString(5000))); - EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendCord)); + Cord src(MakeString(TestCordSize::kLarge)); + cord = std::move(src); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); +} + +TEST_P(CordzUpdateTest, AppendCord) { + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord().Append(src); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord))); +} + +TEST_P(CordzUpdateTest, MoveAppendCord) { + cord().Append(UnsampledCord(MakeString(TestCordSize::kLarge))); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord))); +} + +TEST_P(CordzUpdateTest, PrependCord) { + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord().Prepend(src); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependCord))); +} + +TEST_P(CordzUpdateTest, AppendSmallArray) { + cord().Append(MakeString(TestCordSize::kSmall)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString))); +} + +TEST_P(CordzUpdateTest, AppendLargeArray) { + cord().Append(MakeString(TestCordSize::kLarge)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString))); } } // namespace diff --git a/absl/strings/cordz_test_helpers.h b/absl/strings/cordz_test_helpers.h index 453c2f9d..d9573c97 100644 --- a/absl/strings/cordz_test_helpers.h +++ b/absl/strings/cordz_test_helpers.h @@ -15,6 +15,8 @@ #ifndef ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ #define ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ +#include + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/config.h" @@ -32,6 +34,7 @@ ABSL_NAMESPACE_BEGIN // Returns the CordzInfo for the cord, or nullptr if the cord is not sampled. inline const cord_internal::CordzInfo* GetCordzInfoForTesting( const Cord& cord) { + if (cord.size() <= cord_internal::kMaxInline) return nullptr; return cord.contents_.cordz_info(); } @@ -44,11 +47,13 @@ inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info, return false; } -// Matcher on Cord that verifies all of: +// Matcher on Cord* that verifies all of: // - the cord is sampled // - the CordzInfo of the cord is listed / discoverable. // - the reported CordzStatistics match the cord's actual properties // - the cord has an (initial) UpdateTracker count of 1 for `method` +// This matcher accepts a const Cord* to avoid having the matcher dump +// copious amounts of cord data on failures. MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") { const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); if (cord_info == nullptr) { diff --git a/absl/strings/internal/cordz_handle.cc b/absl/strings/internal/cordz_handle.cc index 22d07978..5297ec8e 100644 --- a/absl/strings/internal/cordz_handle.cc +++ b/absl/strings/internal/cordz_handle.cc @@ -16,16 +16,19 @@ #include #include "absl/base/internal/raw_logging.h" // For ABSL_RAW_CHECK +#include "absl/base/internal/spinlock.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { +using ::absl::base_internal::SpinLockHolder; + ABSL_CONST_INIT CordzHandle::Queue CordzHandle::global_queue_(absl::kConstInit); CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) { if (is_snapshot) { - MutexLock lock(&queue_->mutex); + SpinLockHolder lock(&queue_->mutex); CordzHandle* dq_tail = queue_->dq_tail.load(std::memory_order_acquire); if (dq_tail != nullptr) { dq_prev_ = dq_tail; @@ -40,7 +43,7 @@ CordzHandle::~CordzHandle() { if (is_snapshot_) { std::vector to_delete; { - absl::MutexLock lock(&queue_->mutex); + SpinLockHolder lock(&queue_->mutex); CordzHandle* next = dq_next_; if (dq_prev_ == nullptr) { // We were head of the queue, delete every CordzHandle until we reach @@ -70,7 +73,7 @@ void CordzHandle::Delete(CordzHandle* handle) { handle->ODRCheck(); Queue* const queue = handle->queue_; if (!handle->is_snapshot_ && !queue->IsEmpty()) { - MutexLock lock(&queue->mutex); + SpinLockHolder lock(&queue->mutex); CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire); if (dq_tail != nullptr) { handle->dq_prev_ = dq_tail; @@ -85,7 +88,7 @@ void CordzHandle::Delete(CordzHandle* handle) { std::vector CordzHandle::DiagnosticsGetDeleteQueue() { std::vector handles; - MutexLock lock(&global_queue_.mutex); + SpinLockHolder lock(&global_queue_.mutex); CordzHandle* dq_tail = global_queue_.dq_tail.load(std::memory_order_acquire); for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) { handles.push_back(p); @@ -100,7 +103,7 @@ bool CordzHandle::DiagnosticsHandleIsSafeToInspect( if (handle == nullptr) return true; if (handle->is_snapshot_) return false; bool snapshot_found = false; - MutexLock lock(&queue_->mutex); + SpinLockHolder lock(&queue_->mutex); for (const CordzHandle* p = queue_->dq_tail; p; p = p->dq_prev_) { if (p == handle) return !snapshot_found; if (p == this) snapshot_found = true; @@ -117,7 +120,7 @@ CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() { return handles; } - MutexLock lock(&queue_->mutex); + SpinLockHolder lock(&queue_->mutex); for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) { if (!p->is_snapshot()) { handles.push_back(p); diff --git a/absl/strings/internal/cordz_handle.h b/absl/strings/internal/cordz_handle.h index 71563c8b..93076cfd 100644 --- a/absl/strings/internal/cordz_handle.h +++ b/absl/strings/internal/cordz_handle.h @@ -20,6 +20,7 @@ #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" #include "absl/synchronization/mutex.h" namespace absl { @@ -70,9 +71,11 @@ class CordzHandle { // Global queue data. CordzHandle stores a pointer to the global queue // instance to harden against ODR violations. struct Queue { - constexpr explicit Queue(absl::ConstInitType) : mutex(absl::kConstInit) {} + constexpr explicit Queue(absl::ConstInitType) + : mutex(absl::kConstInit, + absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {} - absl::Mutex mutex; + absl::base_internal::SpinLock mutex; std::atomic dq_tail ABSL_GUARDED_BY(mutex){nullptr}; // Returns true if this delete queue is empty. This method does not acquire diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index d2200680..0461ec46 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -15,6 +15,7 @@ #include "absl/strings/internal/cordz_info.h" #include "absl/base/config.h" +#include "absl/base/internal/spinlock.h" #include "absl/debugging/stacktrace.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cordz_handle.h" @@ -27,22 +28,34 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { +using ::absl::base_internal::SpinLockHolder; + constexpr int CordzInfo::kMaxStackDepth; -ABSL_CONST_INIT std::atomic CordzInfo::ci_head_{nullptr}; -ABSL_CONST_INIT absl::Mutex CordzInfo::ci_mutex_(absl::kConstInit); +ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{absl::kConstInit}; CordzInfo* CordzInfo::Head(const CordzSnapshot& snapshot) { ABSL_ASSERT(snapshot.is_snapshot()); - ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(ci_head_unsafe())); - return ci_head_unsafe(); + + // We can do an 'unsafe' load of 'head', as we are guaranteed that the + // instance it points to is kept alive by the provided CordzSnapshot, so we + // can simply return the current value using an acquire load. + // We do enforce in DEBUG builds that the 'head' value is present in the + // delete queue: ODR violations may lead to 'snapshot' and 'global_list_' + // being in different libraries / modules. + CordzInfo* head = global_list_.head.load(std::memory_order_acquire); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(head)); + return head; } CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const { ABSL_ASSERT(snapshot.is_snapshot()); + + // Similar to the 'Head()' function, we do not need a mutex here. + CordzInfo* next = ci_next_.load(std::memory_order_acquire); ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(this)); - ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(ci_next_unsafe())); - return ci_next_unsafe(); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(next)); + return next; } void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) { @@ -63,14 +76,6 @@ void CordzInfo::TrackCord(InlineData& cord, const InlineData& src, cordz_info->Track(); } -void CordzInfo::UntrackCord(CordzInfo* cordz_info) { - assert(cordz_info); - if (cordz_info) { - cordz_info->Untrack(); - CordzHandle::Delete(cordz_info); - } -} - CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) { if (src == nullptr) return MethodIdentifier::kUnknown; return src->parent_method_ != MethodIdentifier::kUnknown ? src->parent_method_ @@ -110,14 +115,14 @@ CordzInfo::~CordzInfo() { } void CordzInfo::Track() { - absl::MutexLock l(&ci_mutex_); + SpinLockHolder l(&list_->mutex); - CordzInfo* const head = ci_head_.load(std::memory_order_acquire); + CordzInfo* const head = list_->head.load(std::memory_order_acquire); if (head != nullptr) { head->ci_prev_.store(this, std::memory_order_release); } ci_next_.store(head, std::memory_order_release); - ci_head_.store(this, std::memory_order_release); + list_->head.store(this, std::memory_order_release); } void CordzInfo::Untrack() { @@ -128,24 +133,28 @@ void CordzInfo::Untrack() { rep_ = nullptr; } - absl::MutexLock l(&ci_mutex_); - - CordzInfo* const head = ci_head_.load(std::memory_order_acquire); - CordzInfo* const next = ci_next_.load(std::memory_order_acquire); - CordzInfo* const prev = ci_prev_.load(std::memory_order_acquire); - - if (next) { - ABSL_ASSERT(next->ci_prev_.load(std::memory_order_acquire) == this); - next->ci_prev_.store(prev, std::memory_order_release); - } - if (prev) { - ABSL_ASSERT(head != this); - ABSL_ASSERT(prev->ci_next_.load(std::memory_order_acquire) == this); - prev->ci_next_.store(next, std::memory_order_release); - } else { - ABSL_ASSERT(head == this); - ci_head_.store(next, std::memory_order_release); + ODRCheck(); + { + SpinLockHolder l(&list_->mutex); + + CordzInfo* const head = list_->head.load(std::memory_order_acquire); + CordzInfo* const next = ci_next_.load(std::memory_order_acquire); + CordzInfo* const prev = ci_prev_.load(std::memory_order_acquire); + + if (next) { + ABSL_ASSERT(next->ci_prev_.load(std::memory_order_acquire) == this); + next->ci_prev_.store(prev, std::memory_order_release); + } + if (prev) { + ABSL_ASSERT(head != this); + ABSL_ASSERT(prev->ci_next_.load(std::memory_order_acquire) == this); + prev->ci_next_.store(next, std::memory_order_release); + } else { + ABSL_ASSERT(head == this); + list_->head.store(next, std::memory_order_release); + } } + CordzHandle::Delete(this); } void CordzInfo::Lock(MethodIdentifier method) @@ -163,7 +172,6 @@ void CordzInfo::Unlock() ABSL_UNLOCK_FUNCTION(mutex_) { mutex_.Unlock(); if (!tracked) { Untrack(); - CordzHandle::Delete(this); } } diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index 4cf57664..f7682cb3 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -20,6 +20,8 @@ #include #include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" #include "absl/base/thread_annotations.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cordz_functions.h" @@ -79,17 +81,22 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { // and before the root cordrep of the sampled cord is unreffed. // This function may extend the lifetime of the cordrep in cases where the // CordInfo instance is being held by a concurrent collection thread. - static void UntrackCord(CordzInfo* cordz_info); + void Untrack(); + + // Invokes UntrackCord() on `info` if `info` is not null. + static void MaybeUntrackCord(CordzInfo* info); CordzInfo() = delete; CordzInfo(const CordzInfo&) = delete; CordzInfo& operator=(const CordzInfo&) = delete; // Retrieves the oldest existing CordzInfo. - static CordzInfo* Head(const CordzSnapshot& snapshot); + static CordzInfo* Head(const CordzSnapshot& snapshot) + ABSL_NO_THREAD_SAFETY_ANALYSIS; // Retrieves the next oldest existing CordzInfo older than 'this' instance. - CordzInfo* Next(const CordzSnapshot& snapshot) const; + CordzInfo* Next(const CordzSnapshot& snapshot) const + ABSL_NO_THREAD_SAFETY_ANALYSIS; // Locks this instance for the update identified by `method`. // Increases the count for `method` in `update_tracker`. @@ -141,6 +148,17 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { } private: + // Global cordz info list. CordzInfo stores a pointer to the global list + // instance to harden against ODR violations. + struct List { + constexpr explicit List(absl::ConstInitType) + : mutex(absl::kConstInit, + absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {} + + absl::base_internal::SpinLock mutex; + std::atomic head ABSL_GUARDED_BY(mutex){nullptr}; + }; + static constexpr int kMaxStackDepth = 64; explicit CordzInfo(CordRep* rep, const CordzInfo* src, @@ -148,7 +166,6 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { ~CordzInfo() override; void Track(); - void Untrack(); // Returns the parent method from `src`, which is either `parent_method_` or // `method_` depending on `parent_method_` being kUnknown. @@ -161,23 +178,20 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { // Returns 0 if `src` is null. static int FillParentStack(const CordzInfo* src, void** stack); - // 'Unsafe' head/next/prev accessors not requiring the lock being held. - // These are used exclusively for iterations (Head / Next) where we enforce - // a token being held, so reading an 'old' / deleted pointer is fine. - static CordzInfo* ci_head_unsafe() ABSL_NO_THREAD_SAFETY_ANALYSIS { - return ci_head_.load(std::memory_order_acquire); - } - CordzInfo* ci_next_unsafe() const ABSL_NO_THREAD_SAFETY_ANALYSIS { - return ci_next_.load(std::memory_order_acquire); - } - CordzInfo* ci_prev_unsafe() const ABSL_NO_THREAD_SAFETY_ANALYSIS { - return ci_prev_.load(std::memory_order_acquire); + void ODRCheck() const { +#ifndef NDEBUG + ABSL_RAW_CHECK(list_ == &global_list_, "ODR violation in Cord"); +#endif } - static absl::Mutex ci_mutex_; - static std::atomic ci_head_ ABSL_GUARDED_BY(ci_mutex_); - std::atomic ci_prev_ ABSL_GUARDED_BY(ci_mutex_){nullptr}; - std::atomic ci_next_ ABSL_GUARDED_BY(ci_mutex_){nullptr}; + ABSL_CONST_INIT static List global_list_; + List* const list_ = &global_list_; + + // ci_prev_ and ci_next_ require the global list mutex to be held. + // Unfortunately we can't use thread annotations such that the thread safety + // analysis understands that list_ and global_list_ are one and the same. + std::atomic ci_prev_{nullptr}; + std::atomic ci_next_{nullptr}; mutable absl::Mutex mutex_; CordRep* rep_ ABSL_GUARDED_BY(mutex_); @@ -209,6 +223,13 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( } } +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeUntrackCord( + CordzInfo* info) { + if (ABSL_PREDICT_FALSE(info)) { + info->Untrack(); + } +} + inline void CordzInfo::AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_) { #ifndef NDEBUG mutex_.AssertHeld(); diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index 76aa20b0..5eaf4b9c 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -70,7 +70,7 @@ TEST(CordzInfoTest, TrackCord) { EXPECT_FALSE(info->is_snapshot()); EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info)); EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep)); - CordzInfo::UntrackCord(info); + info->Untrack(); } TEST(CordzInfoTest, UntrackCord) { @@ -79,7 +79,7 @@ TEST(CordzInfoTest, UntrackCord) { CordzInfo* info = data.data.cordz_info(); CordzSnapshot snapshot; - CordzInfo::UntrackCord(info); + info->Untrack(); EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr)); EXPECT_THAT(DeleteQueue(), ElementsAre(info, &snapshot)); @@ -96,7 +96,7 @@ TEST(CordzInfoTest, SetCordRep) { info->Unlock(); EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep.rep)); - CordzInfo::UntrackCord(info); + info->Untrack(); } TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) { @@ -121,7 +121,7 @@ TEST(CordzInfoTest, SetCordRepRequiresMutex) { CordzInfo* info = data.data.cordz_info(); TestCordRep rep; EXPECT_DEBUG_DEATH(info->SetCordRep(rep.rep), ".*"); - CordzInfo::UntrackCord(info); + info->Untrack(); } #endif // GTEST_HAS_DEATH_TEST @@ -143,11 +143,11 @@ TEST(CordzInfoTest, TrackUntrackHeadFirstV2) { EXPECT_THAT(info2->Next(snapshot), Eq(info1)); EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); - CordzInfo::UntrackCord(info2); + info2->Untrack(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); - CordzInfo::UntrackCord(info1); + info1->Untrack(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); } @@ -168,11 +168,11 @@ TEST(CordzInfoTest, TrackUntrackTailFirstV2) { EXPECT_THAT(info2->Next(snapshot), Eq(info1)); EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); - CordzInfo::UntrackCord(info1); + info1->Untrack(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); EXPECT_THAT(info2->Next(snapshot), Eq(nullptr)); - CordzInfo::UntrackCord(info2); + info2->Untrack(); ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); } @@ -208,7 +208,7 @@ TEST(CordzInfoTest, StackV2) { // got_stack. EXPECT_THAT(got_stack, HasSubstr(expected_stack)); - CordzInfo::UntrackCord(info); + info->Untrack(); } // Local helper functions to get different stacks for child and parent. @@ -231,7 +231,7 @@ TEST(CordzInfoTest, GetStatistics) { EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); EXPECT_THAT(statistics.update_tracker.Value(kTrackCordMethod), Eq(1)); - CordzInfo::UntrackCord(info); + info->Untrack(); } TEST(CordzInfoTest, LockCountsMethod) { @@ -246,7 +246,7 @@ TEST(CordzInfoTest, LockCountsMethod) { CordzStatistics statistics = info->GetCordzStatistics(); EXPECT_THAT(statistics.update_tracker.Value(kUpdateMethod), Eq(2)); - CordzInfo::UntrackCord(info); + info->Untrack(); } TEST(CordzInfoTest, FromParent) { @@ -265,8 +265,8 @@ TEST(CordzInfoTest, FromParent) { EXPECT_THAT(statistics.parent_method, Eq(kTrackCordMethod)); EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); - CordzInfo::UntrackCord(info_parent); - CordzInfo::UntrackCord(info_child); + info_parent->Untrack(); + info_child->Untrack(); } TEST(CordzInfoTest, FromParentInlined) { @@ -279,7 +279,7 @@ TEST(CordzInfoTest, FromParentInlined) { EXPECT_THAT(statistics.method, Eq(kChildMethod)); EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); - CordzInfo::UntrackCord(info); + info->Untrack(); } TEST(CordzInfoTest, RecordMetrics) { @@ -293,7 +293,7 @@ TEST(CordzInfoTest, RecordMetrics) { CordzStatistics actual = info->GetCordzStatistics(); EXPECT_EQ(actual.size, expected.size); - CordzInfo::UntrackCord(info); + info->Untrack(); } } // namespace diff --git a/absl/strings/internal/cordz_sample_token_test.cc b/absl/strings/internal/cordz_sample_token_test.cc index 80f41ca1..9f54301d 100644 --- a/absl/strings/internal/cordz_sample_token_test.cc +++ b/absl/strings/internal/cordz_sample_token_test.cc @@ -96,9 +96,9 @@ TEST(CordzSampleTokenTest, Iterator) { EXPECT_THAT(found, ElementsAre(info3, info2, info1)); - CordzInfo::UntrackCord(info1); - CordzInfo::UntrackCord(info2); - CordzInfo::UntrackCord(info3); + info1->Untrack(); + info2->Untrack(); + info3->Untrack(); } TEST(CordzSampleTokenTest, IteratorEquality) { @@ -135,9 +135,9 @@ TEST(CordzSampleTokenTest, IteratorEquality) { // Both lhs and rhs are done, so they are on nullptr. EXPECT_THAT(lhs, Eq(rhs)); - CordzInfo::UntrackCord(info1); - CordzInfo::UntrackCord(info2); - CordzInfo::UntrackCord(info3); + info1->Untrack(); + info2->Untrack(); + info3->Untrack(); } TEST(CordzSampleTokenTest, MultiThreaded) { @@ -166,7 +166,7 @@ TEST(CordzSampleTokenTest, MultiThreaded) { // Track/untrack. if (cord.data.is_profiled()) { // 1) Untrack - CordzInfo::UntrackCord(cord.data.cordz_info()); + cord.data.cordz_info()->Untrack(); cord.data.clear_cordz_info();; } else { // 2) Track @@ -192,9 +192,7 @@ TEST(CordzSampleTokenTest, MultiThreaded) { } } for (TestCordData& cord : cords) { - if (cord.data.is_profiled()) { - CordzInfo::UntrackCord(cord.data.cordz_info()); - } + CordzInfo::MaybeUntrackCord(cord.data.cordz_info()); } }); } diff --git a/absl/strings/internal/cordz_update_scope.h b/absl/strings/internal/cordz_update_scope.h index 018359a8..57ba75de 100644 --- a/absl/strings/internal/cordz_update_scope.h +++ b/absl/strings/internal/cordz_update_scope.h @@ -40,6 +40,12 @@ class ABSL_SCOPED_LOCKABLE CordzUpdateScope { } } + // CordzUpdateScope can not be copied or assigned to. + CordzUpdateScope(CordzUpdateScope&& rhs) = delete; + CordzUpdateScope(const CordzUpdateScope&) = delete; + CordzUpdateScope& operator=(CordzUpdateScope&& rhs) = delete; + CordzUpdateScope& operator=(const CordzUpdateScope&) = delete; + ~CordzUpdateScope() ABSL_UNLOCK_FUNCTION() { if (ABSL_PREDICT_FALSE(info_)) { info_->Unlock(); @@ -55,7 +61,7 @@ class ABSL_SCOPED_LOCKABLE CordzUpdateScope { CordzInfo* info() const { return info_; } private: - CordzInfo* const info_; + CordzInfo* info_; }; } // namespace cord_internal diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 2123be60..28ef311e 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -79,6 +79,7 @@ bool Waiter::Wait(KernelTimeout t) { // Note that, since the thread ticker is just reset, we don't need to check // whether the thread is idle on the very first pass of the loop. bool first_pass = true; + while (true) { int32_t x = futex_.load(std::memory_order_relaxed); while (x != 0) { @@ -90,7 +91,6 @@ bool Waiter::Wait(KernelTimeout t) { return true; // Consumed a wakeup, we are done. } - if (!first_pass) MaybeBecomeIdle(); const int err = Futex::WaitUntil(&futex_, 0, t); if (err != 0) { -- cgit v1.2.3 From bcc11a8918f8cc9b43c9a0dc5da7b52d48452bd3 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Tue, 27 Apr 2021 11:26:09 -0600 Subject: Uses alignas for portability in dynamic_annotations.h (#947) Fixes an MSVC compiler error when address sanitizers are enabled --- absl/base/dynamic_annotations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h index 880cbf6e..03d7096b 100644 --- a/absl/base/dynamic_annotations.h +++ b/absl/base/dynamic_annotations.h @@ -471,7 +471,7 @@ using absl::base_internal::ValgrindSlowdown; __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) #define ABSL_ADDRESS_SANITIZER_REDZONE(name) \ struct { \ - char x[8] __attribute__((aligned(8))); \ + alignas(8) char x[8]; \ } name #else -- cgit v1.2.3 From f14d5f4af12eb521a5142151d6ea3addbbed42bb Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 28 Apr 2021 11:49:29 -0700 Subject: Export of internal Abseil changes -- a78c34c705b15598ea00171d4ff8755e38fad863 by Derek Mauro : Improve compiler support for ABSL_FALLTHROUGH_INTENDED PiperOrigin-RevId: 370952333 -- aed0c26f1a2aac98e217ad1b44ce4a858780a223 by Martijn Vels : Add Cordz instrumentation for Flatten PiperOrigin-RevId: 370815149 -- ff4a58d0109d39dc32ef7a5e5e669ca4e630c6d9 by Martijn Vels : Add Cordz instrumentation to RemovePrefix and RemoveSuffix PiperOrigin-RevId: 370751602 -- 40220a058b30ddd89c6e547591488d15342137dd by Martijn Vels : Add Cordz instrumentation to operator=(string_view) PiperOrigin-RevId: 370737600 -- a2e49604f18b92e50b179b5477dfddb8f57538ca by Martijn Vels : Add cordz instrumentation for Subcord PiperOrigin-RevId: 370724107 -- bcc3902e04fb4f14270aef00e18908e6a88474cd by Derek Mauro : Internal change PiperOrigin-RevId: 370707219 GitOrigin-RevId: a78c34c705b15598ea00171d4ff8755e38fad863 Change-Id: I0270e536cbdeaf1f195199da822b314521de3b96 --- absl/base/attributes.h | 27 ++++----- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 1 + absl/strings/cord.cc | 121 ++++++++++++++++++++++++-------------- absl/strings/cord.h | 30 ++++++++++ absl/strings/cordz_test.cc | 62 +++++++++++++++++++ absl/strings/cordz_test_helpers.h | 25 ++++++-- 7 files changed, 202 insertions(+), 65 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index d710f287..52139556 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -599,31 +599,24 @@ // case 42: // ... // -// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED -// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed -// when performing switch labels fall-through diagnostic -// (`-Wimplicit-fallthrough`). See clang documentation on language extensions -// for details: +// Notes: When supported, GCC and Clang can issue a warning on switch labels +// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See +// clang documentation on language extensions for details: // https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough // -// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro -// has no effect on diagnostics. In any case this macro has no effect on runtime +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has +// no effect on diagnostics. In any case this macro has no effect on runtime // behavior and performance of code. #ifdef ABSL_FALLTHROUGH_INTENDED #error "ABSL_FALLTHROUGH_INTENDED should not be defined." -#endif - -// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported. -#if defined(__clang__) && defined(__has_warning) -#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough) #define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] -#endif -#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 0) +#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough) #define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] -#endif - -#ifndef ABSL_FALLTHROUGH_INTENDED +#else #define ABSL_FALLTHROUGH_INTENDED \ do { \ } while (0) diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 68c71f0a..ae88f7bb 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -544,6 +544,7 @@ cc_library( ":cordz_sample_token", ":cordz_statistics", ":cordz_update_tracker", + ":strings", "//absl/base:config", "//absl/base:core_headers", "@com_google_googletest//:gtest", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 80ae2a60..8ebe91e6 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -852,6 +852,7 @@ absl_cc_library( absl::cordz_statistics absl::cordz_update_tracker absl::core_headers + absl::strings TESTONLY ) diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 15d17337..9262aee6 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -584,26 +584,35 @@ void Cord::Clear() { } Cord& Cord::operator=(absl::string_view src) { + auto constexpr method = CordzUpdateTracker::kAssignString; const char* data = src.data(); size_t length = src.size(); CordRep* tree = contents_.tree(); if (length <= InlineRep::kMaxInline) { - // Embed into this->contents_ - if (tree) CordzInfo::MaybeUntrackCord(contents_.cordz_info()); + // Embed into this->contents_, which is somewhat subtle: + // - MaybeUntrackCord must be called before Unref(tree). + // - MaybeUntrackCord must be called before set_data() clobbers cordz_info. + // - set_data() must be called before Unref(tree) as it may reference tree. + if (tree != nullptr) CordzInfo::MaybeUntrackCord(contents_.cordz_info()); contents_.set_data(data, length, true); - if (tree) CordRep::Unref(tree); + if (tree != nullptr) CordRep::Unref(tree); return *this; } - if (tree != nullptr && tree->tag >= FLAT && - tree->flat()->Capacity() >= length && tree->refcount.IsOne()) { - // Copy in place if the existing FLAT node is reusable. - memmove(tree->flat()->Data(), data, length); - tree->length = length; - VerifyTree(tree); - return *this; + if (tree != nullptr) { + CordzUpdateScope scope(contents_.cordz_info(), method); + if (tree->tag >= FLAT && tree->flat()->Capacity() >= length && + tree->refcount.IsOne()) { + // Copy in place if the existing FLAT node is reusable. + memmove(tree->flat()->Data(), data, length); + tree->length = length; + VerifyTree(tree); + return *this; + } + contents_.SetTree(NewTree(data, length, 0), scope); + CordRep::Unref(tree); + } else { + contents_.EmplaceTree(NewTree(data, length, 0), method); } - contents_.set_tree(NewTree(data, length, 0)); - if (tree) CordRep::Unref(tree); return *this; } @@ -893,12 +902,17 @@ void Cord::RemovePrefix(size_t n) { CordRep* tree = contents_.tree(); if (tree == nullptr) { contents_.remove_prefix(n); - } else if (tree->tag == RING) { - contents_.replace_tree(CordRepRing::RemovePrefix(tree->ring(), n)); } else { - CordRep* newrep = RemovePrefixFrom(tree, n); - CordRep::Unref(tree); - contents_.replace_tree(VerifyTree(newrep)); + auto constexpr method = CordzUpdateTracker::kRemovePrefix; + CordzUpdateScope scope(contents_.cordz_info(), method); + if (tree->tag == RING) { + tree = CordRepRing::RemovePrefix(tree->ring(), n); + } else { + CordRep* newrep = RemovePrefixFrom(tree, n); + CordRep::Unref(tree); + tree = VerifyTree(newrep); + } + contents_.SetTreeOrEmpty(tree, scope); } } @@ -909,12 +923,17 @@ void Cord::RemoveSuffix(size_t n) { CordRep* tree = contents_.tree(); if (tree == nullptr) { contents_.reduce_size(n); - } else if (tree->tag == RING) { - contents_.replace_tree(CordRepRing::RemoveSuffix(tree->ring(), n)); } else { - CordRep* newrep = RemoveSuffixFrom(tree, n); - CordRep::Unref(tree); - contents_.replace_tree(VerifyTree(newrep)); + auto constexpr method = CordzUpdateTracker::kRemoveSuffix; + CordzUpdateScope scope(contents_.cordz_info(), method); + if (tree->tag == RING) { + tree = CordRepRing::RemoveSuffix(tree->ring(), n); + } else { + CordRep* newrep = RemoveSuffixFrom(tree, n); + CordRep::Unref(tree); + tree = VerifyTree(newrep); + } + contents_.SetTreeOrEmpty(tree, scope); } } @@ -969,37 +988,51 @@ static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { return results[0]; } +void Cord::CopyDataAtPosition(size_t pos, size_t new_size, char* dest) const { + assert(new_size <= cord_internal::kMaxInline); + assert(pos <= size()); + assert(new_size <= size() - pos); + Cord::ChunkIterator it = chunk_begin(); + it.AdvanceBytes(pos); + size_t remaining_size = new_size; + while (remaining_size > it->size()) { + cord_internal::SmallMemmove(dest, it->data(), it->size()); + remaining_size -= it->size(); + dest += it->size(); + ++it; + } + cord_internal::SmallMemmove(dest, it->data(), remaining_size); +} + Cord Cord::Subcord(size_t pos, size_t new_size) const { Cord sub_cord; size_t length = size(); if (pos > length) pos = length; if (new_size > length - pos) new_size = length - pos; + if (new_size == 0) return sub_cord; + CordRep* tree = contents_.tree(); if (tree == nullptr) { // sub_cord is newly constructed, no need to re-zero-out the tail of // contents_ memory. sub_cord.contents_.set_data(contents_.data() + pos, new_size, false); - } else if (new_size == 0) { - // We want to return empty subcord, so nothing to do. - } else if (new_size <= InlineRep::kMaxInline) { - Cord::ChunkIterator it = chunk_begin(); - it.AdvanceBytes(pos); - char* dest = sub_cord.contents_.data_.as_chars(); - size_t remaining_size = new_size; - while (remaining_size > it->size()) { - cord_internal::SmallMemmove(dest, it->data(), it->size()); - remaining_size -= it->size(); - dest += it->size(); - ++it; - } - cord_internal::SmallMemmove(dest, it->data(), remaining_size); + return sub_cord; + } + + if (new_size <= InlineRep::kMaxInline) { + CopyDataAtPosition(pos, new_size, sub_cord.contents_.data_.as_chars()); sub_cord.contents_.set_inline_size(new_size); - } else if (tree->tag == RING) { - tree = CordRepRing::SubRing(CordRep::Ref(tree)->ring(), pos, new_size); - sub_cord.contents_.set_tree(tree); + return sub_cord; + } + + if (tree->tag == RING) { + CordRepRing* ring = CordRep::Ref(tree)->ring(); + tree = CordRepRing::SubRing(ring, pos, new_size); } else { - sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size)); + tree = NewSubRange(tree, pos, new_size); } + sub_cord.contents_.EmplaceTree(tree, contents_.data_, + CordzUpdateTracker::kSubCord); return sub_cord; } @@ -1676,6 +1709,7 @@ char Cord::operator[](size_t i) const { } absl::string_view Cord::FlattenSlowPath() { + assert(contents_.is_tree()); size_t total_size = size(); CordRep* new_rep; char* new_buffer; @@ -1696,10 +1730,9 @@ absl::string_view Cord::FlattenSlowPath() { s.size()); }); } - if (CordRep* tree = contents_.tree()) { - CordRep::Unref(tree); - } - contents_.set_tree(new_rep); + CordzUpdateScope scope(contents_.cordz_info(), CordzUpdateTracker::kFlatten); + CordRep::Unref(contents_.as_tree()); + contents_.SetTree(new_rep, scope); return absl::string_view(new_buffer, total_size); } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index d5a13b34..d7b43247 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -692,6 +692,10 @@ class Cord { // called by Flatten() when the cord was not already flat. absl::string_view FlattenSlowPath(); + // Copies `new_size` bytes starting at `pos` into `dest`. Requires at least + // `new_size` bytes to be available, and `new_size` to be <= kMaxInline. + void CopyDataAtPosition(size_t pos, size_t new_size, char* dest) const; + // Actual cord contents are hidden inside the following simple // class so that we can isolate the bulk of cord.cc from changes // to the representation. @@ -745,12 +749,21 @@ class Cord { // the CordzInfo instance is updated to reference the new `rep` value. void SetTree(CordRep* rep, const CordzUpdateScope& scope); + // Identical to SetTree(), except that `rep` is allowed to be null, in + // which case the current instance is reset to an empty value. + void SetTreeOrEmpty(CordRep* rep, const CordzUpdateScope& scope); + // Sets the tree value for this instance, and randomly samples this cord. // This function disregards existing contents in `data_`, and should be // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined' // value to a non-inlined (tree / ring) value. void EmplaceTree(CordRep* rep, MethodIdentifier method); + // Identical to EmplaceTree, except that it copies the parent stack from + // the provided `parent` data if the parent is sampled. + void EmplaceTree(CordRep* rep, const InlineData& parent, + MethodIdentifier method); + // Commits the change of a newly created, or updated `rep` root value into // this cord. `old_rep` indicates the old (inlined or tree) value of the // cord, and determines if the commit invokes SetTree() or EmplaceTree(). @@ -1071,6 +1084,12 @@ inline void Cord::InlineRep::EmplaceTree(CordRep* rep, CordzInfo::MaybeTrackCord(data_, method); } +inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent, + MethodIdentifier method) { + data_.make_tree(rep); + CordzInfo::MaybeTrackCord(data_, parent, method); +} + inline void Cord::InlineRep::SetTree(CordRep* rep, const CordzUpdateScope& scope) { assert(rep); @@ -1079,6 +1098,17 @@ inline void Cord::InlineRep::SetTree(CordRep* rep, scope.SetCordRep(rep); } +inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep, + const CordzUpdateScope& scope) { + assert(data_.is_tree()); + if (rep) { + data_.set_tree(rep); + } else { + data_ = {}; + } + scope.SetCordRep(rep); +} + inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep, const CordzUpdateScope& scope, MethodIdentifier method) { diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc index b16e9686..84947cfd 100644 --- a/absl/strings/cordz_test.cc +++ b/absl/strings/cordz_test.cc @@ -52,6 +52,8 @@ inline void PrintTo(const Cord& cord, std::ostream* s) { namespace { +auto constexpr kMaxInline = cord_internal::kMaxInline; + // Returns a string_view value of the specified length // We do this to avoid 'consuming' large strings in Cord by default. absl::string_view MakeString(size_t size) { @@ -139,6 +141,16 @@ TEST(CordzTest, MoveAssignCord) { EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } +TEST_P(CordzUpdateTest, AssignSmallArray) { + cord() = MakeString(TestCordSize::kSmall); + EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString)); +} + +TEST_P(CordzUpdateTest, AssignInlinedArray) { + cord() = MakeString(TestCordSize::kInlined); + EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr)); +} + TEST_P(CordzUpdateTest, AppendCord) { Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); cord().Append(src); @@ -166,6 +178,56 @@ TEST_P(CordzUpdateTest, AppendLargeArray) { EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString))); } +TEST(CordzTest, RemovePrefix) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord(MakeString(TestCordSize::kLarge)); + + // Half the cord + cord.RemovePrefix(cord.size() / 2); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 1)); + + // TODO(mvels): RemovePrefix does not reset to inlined, except if empty? + cord.RemovePrefix(cord.size() - kMaxInline); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 2)); + + cord.RemovePrefix(cord.size()); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST(CordzTest, RemoveSuffix) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord(MakeString(TestCordSize::kLarge)); + + // Half the cord + cord.RemoveSuffix(cord.size() / 2); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 1)); + + // TODO(mvels): RemoveSuffix does not reset to inlined, except if empty? + cord.RemoveSuffix(cord.size() - kMaxInline); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 2)); + + cord.RemoveSuffix(cord.size()); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST(CordzTest, SubCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src(MakeString(TestCordSize::kLarge)); + + Cord cord1 = src.Subcord(10, src.size() / 2); + EXPECT_THAT(cord1, HasValidCordzInfoOf(Method::kSubCord)); + + Cord cord2 = src.Subcord(10, kMaxInline + 1); + EXPECT_THAT(cord2, HasValidCordzInfoOf(Method::kSubCord)); + + Cord cord3 = src.Subcord(10, kMaxInline); + EXPECT_THAT(GetCordzInfoForTesting(cord3), Eq(nullptr)); +} + } // namespace ABSL_NAMESPACE_END diff --git a/absl/strings/cordz_test_helpers.h b/absl/strings/cordz_test_helpers.h index d9573c97..e410eecf 100644 --- a/absl/strings/cordz_test_helpers.h +++ b/absl/strings/cordz_test_helpers.h @@ -27,6 +27,7 @@ #include "absl/strings/internal/cordz_sample_token.h" #include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/strings/str_cat.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -34,7 +35,7 @@ ABSL_NAMESPACE_BEGIN // Returns the CordzInfo for the cord, or nullptr if the cord is not sampled. inline const cord_internal::CordzInfo* GetCordzInfoForTesting( const Cord& cord) { - if (cord.size() <= cord_internal::kMaxInline) return nullptr; + if (!cord.contents_.is_tree()) return nullptr; return cord.contents_.cordz_info(); } @@ -47,13 +48,11 @@ inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info, return false; } -// Matcher on Cord* that verifies all of: +// Matcher on Cord that verifies all of: // - the cord is sampled // - the CordzInfo of the cord is listed / discoverable. // - the reported CordzStatistics match the cord's actual properties // - the cord has an (initial) UpdateTracker count of 1 for `method` -// This matcher accepts a const Cord* to avoid having the matcher dump -// copious amounts of cord data on failures. MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") { const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); if (cord_info == nullptr) { @@ -78,6 +77,24 @@ MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") { return true; } +// Matcher on Cord that verifies that the cord is sampled and that the CordzInfo +// update tracker has 'method' with a call count of 'n' +MATCHER_P2(CordzMethodCountEq, method, n, + absl::StrCat("CordzInfo method count equals ", n)) { + const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); + if (cord_info == nullptr) { + *result_listener << "cord is not sampled"; + return false; + } + cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics(); + if (stat.update_tracker.Value(method) != n) { + *result_listener << "Expected method count " << n << " for " << method + << ", found " << stat.update_tracker.Value(method); + return false; + } + return true; +} + // Cordz will only update with a new rate once the previously scheduled event // has fired. When we disable Cordz, a long delay takes place where we won't // consider profiling new Cords. CordzSampleIntervalHelper will burn through -- cgit v1.2.3 From 5dd240724366295970c613ed23d0092bcf392f18 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 30 Apr 2021 10:41:56 -0700 Subject: Export of internal Abseil changes -- 60b8e77be4bab1bbd3b4c3b70054879229634511 by Derek Mauro : Use _MSVC_LANG for some C++ dialect checks since MSVC doesn't set __cplusplus accurately by default. https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ See GitHub #722. PiperOrigin-RevId: 371362181 -- 5d736accdff04db0e722f377c0d79f2d3ed53263 by Martijn Vels : Fix the estimated memory size for CordRepExternal PiperOrigin-RevId: 371350380 -- eaaa1d8a167aeca67a2aa3a098a2b61a9d72172f by Martijn Vels : Remove flakes by not enforcing re-allocated pointers do never match original Tests that do multiple updates could end up with the original allocated pointer on a 2nd resize, so the 'EqIfPrivate' should not assume that if we do 'not' have the capacity that all following relocations will never match the original. We only care about 'pointer unchanged if private and there is capacity', trying to establish 'pointer changed at some point due to re-allocation; is pointless. PiperOrigin-RevId: 371338965 -- d1837bee6bade1902b095c1cbf64231668bb84c5 by Martijn Vels : Undo inline of small data copy in cord This leads to a performance regression as the code is not inlined (absent hard FDO inputs), and there are no suitable tail call options. PiperOrigin-RevId: 371332332 -- 06dc64b833069efc7d18b11df607c8c22be690da by Martijn Vels : Add final instrumentation for Cordz and remove 'old' cordz logic. This change instruments the last cord function for cordz. It removes the 'old' functions: set_tree, replace_tree, UpdateCordzStatistics and RecordMetrics. PiperOrigin-RevId: 371219909 -- a5e0be538579c603052feec03e6d9910c43ea787 by Martijn Vels : Extend the life of CordRep* if inside a snapshot If a snapshot (potentially) includes the current CordzInfo, we need to extent the lifetime of the CordRep*, as the snapshot 'point in time' observation of the cord should ideally be preserved. PiperOrigin-RevId: 371146151 -- 74d77a89774cd6c8ecdeebee0193b294a39383d6 by Martijn Vels : Instrument std::string consuming methods: ctor, operator=, Append and Prepend This change moves the 'steal into CordRep' logic into a separate function so we can use it directly in the ctor, operator assign and append and prepend, allowing Cordz instrumentation with the proper method attributes. The assign operator is implemented in AssignLargeString leaving the dispatch inlined in cord.h (which as a side effects also allows clean tail calls in the AssignLargeString method) PiperOrigin-RevId: 371094756 -- b39effc45266b7ce2e7f96caa3b16cb6e3acc2dd by Martijn Vels : Add Cordz instrumentation to CordReader PiperOrigin-RevId: 370990181 GitOrigin-RevId: 60b8e77be4bab1bbd3b4c3b70054879229634511 Change-Id: I96af62e6f1a643e8b1228ae01e6c84e33706bb05 --- absl/memory/memory.h | 2 +- absl/meta/type_traits.h | 2 +- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 1 + absl/strings/cord.cc | 154 +++++++++++---------- absl/strings/cord.h | 77 ++++------- absl/strings/cord_ring_test.cc | 47 ++++++- absl/strings/cord_test.cc | 9 +- absl/strings/cord_test_helpers.h | 23 +++ absl/strings/cordz_test.cc | 133 ++++++++++++++++-- absl/strings/internal/cordz_handle.cc | 7 +- absl/strings/internal/cordz_handle.h | 13 +- absl/strings/internal/cordz_handle_test.cc | 13 +- absl/strings/internal/cordz_info.cc | 21 ++- absl/strings/internal/cordz_info.h | 22 ++- absl/strings/internal/cordz_info_test.cc | 24 +++- absl/strings/internal/cordz_update_tracker.h | 2 + absl/strings/internal/cordz_update_tracker_test.cc | 2 + 18 files changed, 394 insertions(+), 159 deletions(-) diff --git a/absl/memory/memory.h b/absl/memory/memory.h index 2b5ff623..d6332606 100644 --- a/absl/memory/memory.h +++ b/absl/memory/memory.h @@ -420,7 +420,7 @@ struct pointer_traits { // // A C++11 compatible implementation of C++17's std::allocator_traits. // -#if __cplusplus >= 201703L +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) using std::allocator_traits; #else // __cplusplus >= 201703L template diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index b5427a43..e7c12393 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -634,7 +634,7 @@ using underlying_type_t = typename std::underlying_type::type; namespace type_traits_internal { -#if __cplusplus >= 201703L +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) // std::result_of is deprecated (C++17) or removed (C++20) template struct result_of; template diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index ae88f7bb..15277de3 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -529,6 +529,7 @@ cc_library( ":cord", ":cord_internal", ":strings", + "//absl/base:config", ], ) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 8ebe91e6..1750f7af 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -830,6 +830,7 @@ absl_cc_library( COPTS ${ABSL_TEST_COPTS} DEPS + absl::config absl::cord absl::cord_internal absl::strings diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 9262aee6..1c2ff9f2 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -56,6 +56,7 @@ using ::absl::cord_internal::CordRepExternal; using ::absl::cord_internal::CordRepFlat; using ::absl::cord_internal::CordRepRing; using ::absl::cord_internal::CordRepSubstring; +using ::absl::cord_internal::CordzUpdateTracker; using ::absl::cord_internal::InlineData; using ::absl::cord_internal::kMaxFlatLength; using ::absl::cord_internal::kMinFlatLength; @@ -281,6 +282,35 @@ static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) { } } +// Creates a CordRep from the provided string. If the string is large enough, +// and not wasteful, we move the string into an external cord rep, preserving +// the already allocated string contents. +// Requires the provided string length to be larger than `kMaxInline`. +static CordRep* CordRepFromString(std::string&& src) { + assert(src.length() > cord_internal::kMaxInline); + if ( + // String is short: copy data to avoid external block overhead. + src.size() <= kMaxBytesToCopy || + // String is wasteful: copy data to avoid pinning too much unused memory. + src.size() < src.capacity() / 2 + ) { + return NewTree(src.data(), src.size(), 0); + } + + struct StringReleaser { + void operator()(absl::string_view /* data */) {} + std::string data; + }; + const absl::string_view original_data = src; + auto* rep = + static_cast<::absl::cord_internal::CordRepExternalImpl*>( + absl::cord_internal::NewExternalRep(original_data, + StringReleaser{std::move(src)})); + // Moving src may have invalidated its data pointer, so adjust it. + rep->base = rep->template get<0>().data.data(); + return rep; +} + // -------------------------------------------------------------------- // Cord::InlineRep functions @@ -486,17 +516,17 @@ static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { return true; } if (rep->tag == EXTERNAL) { - *total_mem_usage += sizeof(CordRepConcat) + rep->length; + // We don't know anything about the embedded / bound data, but we can safely + // assume it is 'at least' a word / pointer to data. In the future we may + // choose to use the 'data' byte as a tag to identify the types of some + // well-known externals, such as a std::string instance. + *total_mem_usage += + sizeof(cord_internal::CordRepExternalImpl) + rep->length; return true; } return false; } -void Cord::InlineRep::UpdateCordzStatisticsSlow() { - CordRep* tree = as_tree(); - data_.cordz_info()->RecordMetrics(tree->length); -} - void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { assert(&src != this); assert(is_tree() || src.is_tree()); @@ -525,42 +555,24 @@ void Cord::InlineRep::UnrefTree() { // -------------------------------------------------------------------- // Constructors and destructors -Cord::Cord(absl::string_view src) : contents_(InlineData::kDefaultInit) { +Cord::Cord(absl::string_view src, MethodIdentifier method) + : contents_(InlineData::kDefaultInit) { const size_t n = src.size(); if (n <= InlineRep::kMaxInline) { contents_.set_data(src.data(), n, true); } else { CordRep* rep = NewTree(src.data(), n, 0); - contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString); + contents_.EmplaceTree(rep, method); } } template > -Cord::Cord(T&& src) { - if ( - // String is short: copy data to avoid external block overhead. - src.size() <= kMaxBytesToCopy || - // String is wasteful: copy data to avoid pinning too much unused memory. - src.size() < src.capacity() / 2 - ) { - if (src.size() <= InlineRep::kMaxInline) { - contents_.set_data(src.data(), src.size(), false); - } else { - contents_.set_tree(NewTree(src.data(), src.size(), 0)); - } +Cord::Cord(T&& src) : contents_(InlineData::kDefaultInit) { + if (src.size() <= InlineRep::kMaxInline) { + contents_.set_data(src.data(), src.size(), true); } else { - struct StringReleaser { - void operator()(absl::string_view /* data */) {} - std::string data; - }; - const absl::string_view original_data = src; - auto* rep = static_cast< - ::absl::cord_internal::CordRepExternalImpl*>( - absl::cord_internal::NewExternalRep( - original_data, StringReleaser{std::forward(src)})); - // Moving src may have invalidated its data pointer, so adjust it. - rep->base = rep->template get<0>().data.data(); - contents_.set_tree(rep); + CordRep* rep = CordRepFromString(std::forward(src)); + contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString); } } @@ -583,6 +595,20 @@ void Cord::Clear() { } } +Cord& Cord::AssignLargeString(std::string&& src) { + auto constexpr method = CordzUpdateTracker::kAssignString; + assert(src.size() > kMaxBytesToCopy); + CordRep* rep = CordRepFromString(std::move(src)); + if (CordRep* tree = contents_.tree()) { + CordzUpdateScope scope(contents_.cordz_info(), method); + contents_.SetTree(rep, scope); + CordRep::Unref(tree); + } else { + contents_.EmplaceTree(rep, method); + } + return *this; +} + Cord& Cord::operator=(absl::string_view src) { auto constexpr method = CordzUpdateTracker::kAssignString; const char* data = src.data(); @@ -616,18 +642,6 @@ Cord& Cord::operator=(absl::string_view src) { return *this; } -template > -Cord& Cord::operator=(T&& src) { - if (src.size() <= kMaxBytesToCopy) { - *this = absl::string_view(src); - } else { - *this = Cord(std::forward(src)); - } - return *this; -} - -template Cord& Cord::operator=(std::string&& src); - // TODO(sanjay): Move to Cord::InlineRep section of file. For now, // we keep it here to make diffs easier. void Cord::InlineRep::AppendArray(absl::string_view src, @@ -653,10 +667,8 @@ void Cord::InlineRep::AppendArray(absl::string_view src, return; } - // It is possible that src.data() == data_, but when we transition from an - // InlineRep to a tree we need to assign data_ = root via set_tree. To - // avoid corrupting the source data before we copy it, delay calling - // set_tree until after we've copied data. + // Note: we don't concern ourselves if src aliases data stored in the + // inlined data of 'this', as we update the InlineData only at the end. // We are going from an inline size to beyond inline size. Make the new size // either double the inlined size, or the added size + 10%. const size_t size1 = inline_length * 2 + src.size(); @@ -762,7 +774,8 @@ void Cord::Append(T&& src) { if (src.size() <= kMaxBytesToCopy) { Append(absl::string_view(src)); } else { - Append(Cord(std::forward(src))); + CordRep* rep = CordRepFromString(std::forward(src)); + contents_.AppendTree(rep, CordzUpdateTracker::kAppendString); } } @@ -804,7 +817,8 @@ inline void Cord::Prepend(T&& src) { if (src.size() <= kMaxBytesToCopy) { Prepend(absl::string_view(src)); } else { - Prepend(Cord(std::forward(src))); + CordRep* rep = CordRepFromString(std::forward(src)); + contents_.PrependTree(rep, CordzUpdateTracker::kPrependString); } } @@ -988,22 +1002,6 @@ static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { return results[0]; } -void Cord::CopyDataAtPosition(size_t pos, size_t new_size, char* dest) const { - assert(new_size <= cord_internal::kMaxInline); - assert(pos <= size()); - assert(new_size <= size() - pos); - Cord::ChunkIterator it = chunk_begin(); - it.AdvanceBytes(pos); - size_t remaining_size = new_size; - while (remaining_size > it->size()) { - cord_internal::SmallMemmove(dest, it->data(), it->size()); - remaining_size -= it->size(); - dest += it->size(); - ++it; - } - cord_internal::SmallMemmove(dest, it->data(), remaining_size); -} - Cord Cord::Subcord(size_t pos, size_t new_size) const { Cord sub_cord; size_t length = size(); @@ -1020,7 +1018,17 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { } if (new_size <= InlineRep::kMaxInline) { - CopyDataAtPosition(pos, new_size, sub_cord.contents_.data_.as_chars()); + char* dest = sub_cord.contents_.data_.as_chars(); + Cord::ChunkIterator it = chunk_begin(); + it.AdvanceBytes(pos); + size_t remaining_size = new_size; + while (remaining_size > it->size()) { + cord_internal::SmallMemmove(dest, it->data(), it->size()); + remaining_size -= it->size(); + dest += it->size(); + ++it; + } + cord_internal::SmallMemmove(dest, it->data(), remaining_size); sub_cord.contents_.set_inline_size(new_size); return sub_cord; } @@ -1474,6 +1482,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { ABSL_HARDENING_ASSERT(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); Cord subcord; + auto constexpr method = CordzUpdateTracker::kCordReader; if (n <= InlineRep::kMaxInline) { // Range to read fits in inline data. Flatten it. @@ -1496,11 +1505,12 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { if (ring_reader_) { size_t chunk_size = current_chunk_.size(); if (n <= chunk_size && n <= kMaxBytesToCopy) { - subcord = Cord(current_chunk_.substr(0, n)); + subcord = Cord(current_chunk_.substr(0, n), method); } else { auto* ring = CordRep::Ref(ring_reader_.ring())->ring(); size_t offset = ring_reader_.length() - bytes_remaining_; - subcord.contents_.set_tree(CordRepRing::SubRing(ring, offset, n)); + CordRep* rep = CordRepRing::SubRing(ring, offset, n); + subcord.contents_.EmplaceTree(rep, method); } if (n < chunk_size) { bytes_remaining_ -= n; @@ -1519,7 +1529,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { const char* data = subnode->tag == EXTERNAL ? subnode->external()->base : subnode->flat()->Data(); subnode = NewSubstring(subnode, current_chunk_.data() - data, n); - subcord.contents_.set_tree(VerifyTree(subnode)); + subcord.contents_.EmplaceTree(VerifyTree(subnode), method); RemoveChunkPrefix(n); return subcord; } @@ -1562,7 +1572,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { if (node == nullptr) { // We have reached the end of the Cord. assert(bytes_remaining_ == 0); - subcord.contents_.set_tree(VerifyTree(subnode)); + subcord.contents_.EmplaceTree(VerifyTree(subnode), method); return subcord; } @@ -1602,7 +1612,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { current_chunk_ = absl::string_view(data + offset + n, length - n); current_leaf_ = node; bytes_remaining_ -= n; - subcord.contents_.set_tree(VerifyTree(subnode)); + subcord.contents_.EmplaceTree(VerifyTree(subnode), method); return subcord; } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index d7b43247..8abc4742 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -678,6 +678,10 @@ class Cord { using InlineData = cord_internal::InlineData; using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + // Creates a cord instance with `method` representing the originating + // public API call causing the cord to be created. + explicit Cord(absl::string_view src, MethodIdentifier method); + friend class CordTestPeer; friend bool operator==(const Cord& lhs, const Cord& rhs); friend bool operator==(const Cord& lhs, absl::string_view rhs); @@ -692,10 +696,6 @@ class Cord { // called by Flatten() when the cord was not already flat. absl::string_view FlattenSlowPath(); - // Copies `new_size` bytes starting at `pos` into `dest`. Requires at least - // `new_size` bytes to be available, and `new_size` to be <= kMaxInline. - void CopyDataAtPosition(size_t pos, size_t new_size, char* dest) const; - // Actual cord contents are hidden inside the following simple // class so that we can isolate the bulk of cord.cc from changes // to the representation. @@ -725,11 +725,6 @@ class Cord { // Returns nullptr if holding bytes absl::cord_internal::CordRep* tree() const; absl::cord_internal::CordRep* as_tree() const; - // Discards old pointer, if any - void set_tree(absl::cord_internal::CordRep* rep); - // Replaces a tree with a new root. This is faster than set_tree, but it - // should only be used when it's clear that the old rep was a tree. - void replace_tree(absl::cord_internal::CordRep* rep); // Returns non-null iff was holding a pointer absl::cord_internal::CordRep* clear(); // Converts to pointer if necessary. @@ -831,11 +826,6 @@ class Cord { // Resets the current cordz_info to null / empty. void clear_cordz_info() { data_.clear_cordz_info(); } - // Updates the cordz statistics. info may be nullptr if the CordzInfo object - // is unknown. - void UpdateCordzStatistics(); - void UpdateCordzStatisticsSlow(); - private: friend class Cord; @@ -892,6 +882,10 @@ class Cord { template void AppendImpl(C&& src); + // Assigns the value in 'src' to this instance, 'stealing' its contents. + // Requires src.length() > kMaxBytesToCopy. + Cord& AssignLargeString(std::string&& src); + // Helper for AbslHashValue(). template H HashFragmented(H hash_state) const { @@ -994,8 +988,11 @@ inline CordRep* NewExternalRep(absl::string_view data, template Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) { Cord cord; - cord.contents_.set_tree(::absl::cord_internal::NewExternalRep( - data, std::forward(releaser))); + if (auto* rep = ::absl::cord_internal::NewExternalRep( + data, std::forward(releaser))) { + cord.contents_.EmplaceTree(rep, + Cord::MethodIdentifier::kMakeCordFromExternal); + } return cord; } @@ -1119,36 +1116,6 @@ inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep, } } -inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { - if (rep == nullptr) { - if (data_.is_tree()) { - CordzInfo::MaybeUntrackCord(data_.cordz_info()); - } - ResetToEmpty(); - } else { - if (data_.is_tree()) { - // `data_` already holds a 'tree' value and an optional cordz_info value. - // Replace the tree value only, leaving the cordz_info value unchanged. - data_.set_tree(rep); - } else { - // `data_` contains inlined data: initialize data_ to tree value `rep`. - data_.make_tree(rep); - CordzInfo::MaybeTrackCord(data_, CordzUpdateTracker::kUnknown); - } - UpdateCordzStatistics(); - } -} - -inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) { - ABSL_ASSERT(is_tree()); - if (ABSL_PREDICT_FALSE(rep == nullptr)) { - set_tree(rep); - return; - } - data_.set_tree(rep); - UpdateCordzStatistics(); -} - inline absl::cord_internal::CordRep* Cord::InlineRep::clear() { if (is_tree()) { CordzInfo::MaybeUntrackCord(cordz_info()); @@ -1165,13 +1132,11 @@ inline void Cord::InlineRep::CopyToArray(char* dst) const { cord_internal::SmallMemmove(dst, data_.as_chars(), n); } -inline void Cord::InlineRep::UpdateCordzStatistics() { - if (ABSL_PREDICT_TRUE(!is_profiled())) return; - UpdateCordzStatisticsSlow(); -} - constexpr inline Cord::Cord() noexcept {} +inline Cord::Cord(absl::string_view src) + : Cord(src, CordzUpdateTracker::kConstructorString) {} + template constexpr Cord::Cord(strings_internal::StringConstant) : contents_(strings_internal::StringConstant::value.size() <= @@ -1187,6 +1152,15 @@ inline Cord& Cord::operator=(const Cord& x) { return *this; } +template > +Cord& Cord::operator=(T&& src) { + if (src.size() <= cord_internal::kMaxBytesToCopy) { + return operator=(absl::string_view(src)); + } else { + return AssignLargeString(std::forward(src)); + } +} + inline Cord::Cord(const Cord& src) : contents_(src.contents_) {} inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {} @@ -1201,7 +1175,6 @@ inline Cord& Cord::operator=(Cord&& x) noexcept { } extern template Cord::Cord(std::string&& src); -extern template Cord& Cord::operator=(std::string&& src); inline size_t Cord::size() const { // Length is 1st field in str.rep_ diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc index 2943cf38..cc8fbaf9 100644 --- a/absl/strings/cord_ring_test.cc +++ b/absl/strings/cord_ring_test.cc @@ -98,15 +98,22 @@ using TestParams = std::vector; // Matcher validating when mutable copies are required / performed. MATCHER_P2(EqIfPrivate, param, rep, absl::StrCat("Equal 0x", absl::Hex(rep), " if private")) { - return param.refcount_is_one ? arg == rep : arg != rep; + return param.refcount_is_one ? arg == rep : true; } // Matcher validating when mutable copies are required / performed. MATCHER_P2(EqIfPrivateAndCapacity, param, rep, absl::StrCat("Equal 0x", absl::Hex(rep), " if private and capacity")) { - return (param.refcount_is_one && param.with_capacity) ? arg == rep - : arg != rep; + return (param.refcount_is_one && param.with_capacity) ? arg == rep : true; +} + +// Matcher validating a shared ring was re-allocated. Should only be used for +// tests doing exactly one update as subsequent updates could return the +// original (freed and re-used) pointer. +MATCHER_P2(NeIfShared, param, rep, + absl::StrCat("Not equal 0x", absl::Hex(rep), " if shared")) { + return param.refcount_is_one ? true : arg != rep; } MATCHER_P2(EqIfInputPrivate, param, rep, "Equal if input is private") { @@ -518,6 +525,7 @@ TEST_P(CordRingCreateTest, CreateFromRing) { CordRepRing* result = NeedsUnref(CordRepRing::Create(ring)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); } @@ -655,6 +663,7 @@ TEST_P(CordRingBuildTest, AppendFlat) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, MakeFlat(str2))); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); } @@ -666,6 +675,7 @@ TEST_P(CordRingBuildTest, PrependFlat) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, MakeFlat(str2))); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1)); } @@ -677,6 +687,7 @@ TEST_P(CordRingBuildTest, AppendString) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); } @@ -689,6 +700,7 @@ TEST_P(CordRingBuildTest, AppendStringHavingExtra) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); } TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) { @@ -710,6 +722,7 @@ TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); if (GetParam().refcount_is_one) { EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str1, str1a), str2a)); } else { @@ -725,6 +738,7 @@ TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(result->length, Eq(4 + str2.size())); if (GetParam().refcount_is_one) { EXPECT_THAT(ToFlats(result), ElementsAre(StrCat("1234", str2))); @@ -758,6 +772,7 @@ TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(result->length, Eq(4 + str2.size())); EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2)); @@ -802,6 +817,7 @@ TEST_P(CordRingBuildTest, PrependStringHavingExtra) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(result->length, Eq(4 + str2.size())); if (GetParam().refcount_is_one) { EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str2, "1234"))); @@ -833,6 +849,7 @@ TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) { ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result->length, Eq(str1a.size() + str2.size())); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a)); CordRep::Unref(shared_type == 1 ? flat1 : flat); } @@ -920,6 +937,7 @@ TEST_P(CordRingSubTest, SubRing) { result = NeedsUnref(CordRepRing::SubRing(ring, offset, len)); ASSERT_THAT(result, IsValidRingBuffer()); ASSERT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); ASSERT_THAT(ToString(result), Eq(all.substr(offset, len))); } } @@ -945,6 +963,7 @@ TEST_P(CordRingSubTest, SubRingFromLargeExternal) { result = NeedsUnref(CordRepRing::SubRing(ring, offset, len)); ASSERT_THAT(result, IsValidRingBuffer()); ASSERT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); auto str = ToString(result); ASSERT_THAT(str, SizeIs(len)); ASSERT_THAT(str, Eq(all.substr(offset, len))); @@ -966,6 +985,7 @@ TEST_P(CordRingSubTest, RemovePrefix) { result = NeedsUnref(CordRepRing::RemovePrefix(ring, len)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToString(result), Eq(all.substr(len))); } } @@ -996,8 +1016,9 @@ TEST_P(CordRingSubTest, RemoveSuffix) { ring = RefIfShared(FromFlats(flats, composition)); result = NeedsUnref(CordRepRing::RemoveSuffix(ring, len)); ASSERT_THAT(result, IsValidRingBuffer()); - EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); - EXPECT_THAT(ToString(result), Eq(all.substr(0, all.size() - len))); + ASSERT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); + ASSERT_THAT(ToString(result), Eq(all.substr(0, all.size() - len))); } } @@ -1010,6 +1031,7 @@ TEST_P(CordRingSubTest, AppendRing) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, child)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); } @@ -1023,6 +1045,7 @@ TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ", "over ", "the ", "lazy ", "dog")); } @@ -1037,6 +1060,7 @@ TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog")); } @@ -1051,6 +1075,7 @@ TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ", "fox ", "jumps ", "over ", "the ")); } @@ -1065,6 +1090,7 @@ TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ", "fox ", "jumps ", "ov")); } @@ -1079,6 +1105,7 @@ TEST_P(CordRingBuildTest, AppendRingMiddlePiece) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "ck ", "brown ", "fox ", "jum")); } @@ -1093,6 +1120,7 @@ TEST_P(CordRingBuildTest, AppendRingSinglePiece) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row")); } @@ -1110,6 +1138,7 @@ TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) { CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row")); } @@ -1123,6 +1152,7 @@ TEST_P(CordRingBuildInputTest, PrependRing) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); } @@ -1136,6 +1166,7 @@ TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ", "the ", "lazy ", "dog", "Tail")); } @@ -1149,6 +1180,7 @@ TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) { CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(21, child)); CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail")); } @@ -1163,6 +1195,7 @@ TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ", "jumps ", "over ", "the ", "Tail")); } @@ -1177,6 +1210,7 @@ TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ", "jumps ", "ov", "Tail")); } @@ -1192,6 +1226,7 @@ TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("ck ", "brown ", "fox ", "jum", "Tail")); } @@ -1206,6 +1241,7 @@ TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail")); } @@ -1222,6 +1258,7 @@ TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) { CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); ASSERT_THAT(result, IsValidRingBuffer()); EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail")); } diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 74a90863..14eca155 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -190,14 +190,15 @@ class CordTestPeer { } 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"); + ABSL_RAW_CHECK(src.contents_.is_tree(), "Can not be inlined"); + Cord cord; auto* rep = new cord_internal::CordRepSubstring; rep->tag = cord_internal::SUBSTRING; - rep->child = cord.contents_.tree(); + rep->child = cord_internal::CordRep::Ref(src.contents_.tree()); rep->start = offset; rep->length = length; - cord.contents_.replace_tree(rep); + cord.contents_.EmplaceTree(rep, + cord_internal::CordzUpdateTracker::kSubCord); return cord; } }; diff --git a/absl/strings/cord_test_helpers.h b/absl/strings/cord_test_helpers.h index 6ccccc51..31a1dc89 100644 --- a/absl/strings/cord_test_helpers.h +++ b/absl/strings/cord_test_helpers.h @@ -19,7 +19,9 @@ #include #include +#include +#include "absl/base/config.h" #include "absl/strings/cord.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/string_view.h" @@ -29,10 +31,27 @@ ABSL_NAMESPACE_BEGIN // Cord sizes relevant for testing enum class TestCordSize { + // An empty value kEmpty = 0, + + // An inlined string value kInlined = cord_internal::kMaxInline / 2 + 1, + + // 'Well known' SSO lengths (excluding terminating zero). + // libstdcxx has a maximum SSO of 15, libc++ has a maximum SSO of 22. + kStringSso1 = 15, + kStringSso2 = 22, + + // A string value which is too large to fit in inlined data, but small enough + // such that Cord prefers copying the value if possible, i.e.: not stealing + // std::string inputs, or referencing existing CordReps on Append, etc. kSmall = cord_internal::kMaxBytesToCopy / 2 + 1, + + // A string value large enough that Cord prefers to reference or steal from + // existing inputs rather than copying contents of the input. kMedium = cord_internal::kMaxFlatLength / 2 + 1, + + // A string value large enough to cause it to be stored in mutliple flats. kLarge = cord_internal::kMaxFlatLength * 4 }; @@ -45,6 +64,10 @@ inline absl::string_view ToString(TestCordSize size) { return "Inlined"; case TestCordSize::kSmall: return "Small"; + case TestCordSize::kStringSso1: + return "StringSso1"; + case TestCordSize::kStringSso2: + return "StringSso2"; case TestCordSize::kMedium: return "Medium"; case TestCordSize::kLarge: diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc index 84947cfd..aeb3d13f 100644 --- a/absl/strings/cordz_test.cc +++ b/absl/strings/cordz_test.cc @@ -34,6 +34,7 @@ #ifdef ABSL_INTERNAL_CORDZ_ENABLED using testing::Eq; +using testing::AnyOf; namespace absl { ABSL_NAMESPACE_BEGIN @@ -84,24 +85,50 @@ class CordzUpdateTest : public testing::TestWithParam { Cord cord_{MakeString(GetParam())}; }; +template +std::string ParamToString(::testing::TestParamInfo param) { + return std::string(ToString(param.param)); +} + INSTANTIATE_TEST_SUITE_P(WithParam, CordzUpdateTest, testing::Values(TestCordSize::kEmpty, TestCordSize::kInlined, TestCordSize::kLarge), TestParamToString); -TEST(CordzTest, ConstructSmallString) { +class CordzStringTest : public testing::TestWithParam { + private: + CordzSamplingIntervalHelper sample_every_{1}; +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordzStringTest, + testing::Values(TestCordSize::kInlined, + TestCordSize::kStringSso1, + TestCordSize::kStringSso2, + TestCordSize::kSmall, + TestCordSize::kLarge), + ParamToString); + +TEST(CordzTest, ConstructSmallArray) { CordzSamplingIntervalHelper sample_every{1}; Cord cord(MakeString(TestCordSize::kSmall)); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } -TEST(CordzTest, ConstructLargeString) { +TEST(CordzTest, ConstructLargeArray) { CordzSamplingIntervalHelper sample_every{1}; Cord cord(MakeString(TestCordSize::kLarge)); EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } +TEST_P(CordzStringTest, ConstructString) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(std::string(Length(GetParam()), '.')); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + } +} + TEST(CordzTest, CopyConstruct) { CordzSamplingIntervalHelper sample_every{1}; Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); @@ -151,6 +178,28 @@ TEST_P(CordzUpdateTest, AssignInlinedArray) { EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr)); } +TEST_P(CordzStringTest, AssignStringToInlined) { + Cord cord; + cord = std::string(Length(GetParam()), '.'); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAssignString)); + } +} + +TEST_P(CordzStringTest, AssignStringToCord) { + Cord cord(MakeString(TestCordSize::kLarge)); + cord = std::string(Length(GetParam()), '.'); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kAssignString, 1)); + } +} + +TEST_P(CordzUpdateTest, AssignInlinedString) { + cord() = std::string(Length(TestCordSize::kInlined), '.'); + EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr)); +} + TEST_P(CordzUpdateTest, AppendCord) { Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); cord().Append(src); @@ -162,12 +211,6 @@ TEST_P(CordzUpdateTest, MoveAppendCord) { EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord))); } -TEST_P(CordzUpdateTest, PrependCord) { - Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); - cord().Prepend(src); - EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependCord))); -} - TEST_P(CordzUpdateTest, AppendSmallArray) { cord().Append(MakeString(TestCordSize::kSmall)); EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString))); @@ -178,6 +221,80 @@ TEST_P(CordzUpdateTest, AppendLargeArray) { EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString))); } +TEST_P(CordzStringTest, AppendStringToEmpty) { + Cord cord; + cord.Append(std::string(Length(GetParam()), '.')); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString)); + } +} + +TEST_P(CordzStringTest, AppendStringToInlined) { + Cord cord(MakeString(TestCordSize::kInlined)); + cord.Append(std::string(Length(GetParam()), '.')); + if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString)); + } +} + +TEST_P(CordzStringTest, AppendStringToCord) { + Cord cord(MakeString(TestCordSize::kLarge)); + cord.Append(std::string(Length(GetParam()), '.')); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kAppendString, 1)); +} + +TEST(CordzTest, MakeCordFromExternal) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord = MakeCordFromExternal("Hello world", [](absl::string_view) {}); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kMakeCordFromExternal)); +} + +TEST(CordzTest, MakeCordFromEmptyExternal) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord = MakeCordFromExternal({}, [](absl::string_view) {}); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST_P(CordzUpdateTest, PrependCord) { + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord().Prepend(src); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependCord))); +} + +TEST_P(CordzUpdateTest, PrependSmallArray) { + cord().Prepend(MakeString(TestCordSize::kSmall)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString))); +} + +TEST_P(CordzUpdateTest, PrependLargeArray) { + cord().Prepend(MakeString(TestCordSize::kLarge)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString))); +} + +TEST_P(CordzStringTest, PrependStringToEmpty) { + Cord cord; + cord.Prepend(std::string(Length(GetParam()), '.')); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString)); + } +} + +TEST_P(CordzStringTest, PrependStringToInlined) { + Cord cord(MakeString(TestCordSize::kInlined)); + cord.Prepend(std::string(Length(GetParam()), '.')); + if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString)); + } +} + +TEST_P(CordzStringTest, PrependStringToCord) { + Cord cord(MakeString(TestCordSize::kLarge)); + cord.Prepend(std::string(Length(GetParam()), '.')); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kPrependString, 1)); +} + TEST(CordzTest, RemovePrefix) { CordzSamplingIntervalHelper sample_every(1); Cord cord(MakeString(TestCordSize::kLarge)); diff --git a/absl/strings/internal/cordz_handle.cc b/absl/strings/internal/cordz_handle.cc index 5297ec8e..a73fefed 100644 --- a/absl/strings/internal/cordz_handle.cc +++ b/absl/strings/internal/cordz_handle.cc @@ -68,11 +68,16 @@ CordzHandle::~CordzHandle() { } } +bool CordzHandle::SafeToDelete() const { + return is_snapshot_ || queue_->IsEmpty(); +} + void CordzHandle::Delete(CordzHandle* handle) { + assert(handle); if (handle) { handle->ODRCheck(); Queue* const queue = handle->queue_; - if (!handle->is_snapshot_ && !queue->IsEmpty()) { + if (!handle->SafeToDelete()) { SpinLockHolder lock(&queue->mutex); CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire); if (dq_tail != nullptr) { diff --git a/absl/strings/internal/cordz_handle.h b/absl/strings/internal/cordz_handle.h index 93076cfd..5df53c78 100644 --- a/absl/strings/internal/cordz_handle.h +++ b/absl/strings/internal/cordz_handle.h @@ -40,9 +40,20 @@ class CordzHandle { bool is_snapshot() const { return is_snapshot_; } + // Returns true if this instance is safe to be deleted because it is either a + // snapshot, which is always safe to delete, or not included in the global + // delete queue and thus not included in any snapshot. + // Callers are responsible for making sure this instance can not be newly + // discovered by other threads. For example, CordzInfo instances first de-list + // themselves from the global CordzInfo list before determining if they are + // safe to be deleted directly. + // If SafeToDelete returns false, callers MUST use the Delete() method to + // safely queue CordzHandle instances for deletion. + bool SafeToDelete() const; + // Deletes the provided instance, or puts it on the delete queue to be deleted // once there are no more sample tokens (snapshot) instances potentially - // referencing the instance. `handle` may be null. + // referencing the instance. `handle` should not be null. static void Delete(CordzHandle* handle); // Returns the current entries in the delete queue in LIFO order. diff --git a/absl/strings/internal/cordz_handle_test.cc b/absl/strings/internal/cordz_handle_test.cc index c04240e8..4eefd72c 100644 --- a/absl/strings/internal/cordz_handle_test.cc +++ b/absl/strings/internal/cordz_handle_test.cc @@ -52,6 +52,7 @@ TEST(CordzHandleTest, CordzHandleCreateDelete) { bool deleted = false; auto* handle = new CordzHandleDeleteTracker(&deleted); EXPECT_FALSE(handle->is_snapshot()); + EXPECT_TRUE(handle->SafeToDelete()); EXPECT_THAT(DeleteQueue(), SizeIs(0)); CordzHandle::Delete(handle); @@ -62,6 +63,7 @@ TEST(CordzHandleTest, CordzHandleCreateDelete) { TEST(CordzHandleTest, CordzSnapshotCreateDelete) { auto* snapshot = new CordzSnapshot(); EXPECT_TRUE(snapshot->is_snapshot()); + EXPECT_TRUE(snapshot->SafeToDelete()); EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot)); delete snapshot; EXPECT_THAT(DeleteQueue(), SizeIs(0)); @@ -71,10 +73,12 @@ TEST(CordzHandleTest, CordzHandleCreateDeleteWithSnapshot) { bool deleted = false; auto* snapshot = new CordzSnapshot(); auto* handle = new CordzHandleDeleteTracker(&deleted); + EXPECT_FALSE(handle->SafeToDelete()); CordzHandle::Delete(handle); EXPECT_THAT(DeleteQueue(), ElementsAre(handle, snapshot)); EXPECT_FALSE(deleted); + EXPECT_FALSE(handle->SafeToDelete()); delete snapshot; EXPECT_THAT(DeleteQueue(), SizeIs(0)); @@ -219,8 +223,8 @@ TEST(CordzHandleTest, MultiThreaded) { if (safe_to_inspect.size() > max_safe_to_inspect) { max_safe_to_inspect = safe_to_inspect.size(); } + CordzHandle::Delete(old_handle); } - CordzHandle::Delete(old_handle); } // Confirm that the test did *something*. This check will be satisfied as @@ -234,9 +238,10 @@ TEST(CordzHandleTest, MultiThreaded) { // Have each thread attempt to clean up everything. Some thread will be // the last to reach this cleanup code, and it will be guaranteed to clean // up everything because nothing remains to create new handles. - for (size_t i = 0; i < handles.size(); i++) { - CordzHandle* handle = handles[i].exchange(nullptr); - CordzHandle::Delete(handle); + for (auto& h : handles) { + if (CordzHandle* handle = h.exchange(nullptr)) { + CordzHandle::Delete(handle); + } } }); } diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 0461ec46..0f657aaf 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -126,13 +126,6 @@ void CordzInfo::Track() { } void CordzInfo::Untrack() { - { - // TODO(b/117940323): change this to assuming ownership instead once all - // Cord logic is properly keeping `rep_` in sync with the Cord root rep. - absl::MutexLock lock(&mutex_); - rep_ = nullptr; - } - ODRCheck(); { SpinLockHolder l(&list_->mutex); @@ -154,6 +147,20 @@ void CordzInfo::Untrack() { list_->head.store(next, std::memory_order_release); } } + + // We can no longer be discovered: perform a fast path check if we are not + // listed on any delete queue, so we can directly delete this instance. + if (SafeToDelete()) { + UnsafeSetCordRep(nullptr); + delete this; + return; + } + + // We are likely part of a snapshot, extend the life of the CordRep + { + absl::MutexLock lock(&mutex_); + if (rep_) CordRep::Ref(rep_); + } CordzHandle::Delete(this); } diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index f7682cb3..1fe046ec 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -110,7 +110,7 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { // Asserts that this CordzInfo instance is locked. void AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_); - // Updates the `rep' property of this instance. This methods is invoked by + // Updates the `rep` property of this instance. This methods is invoked by // Cord logic each time the root node of a sampled Cord changes, and before // the old root reference count is deleted. This guarantees that collection // code can always safely take a reference on the tracked cord. @@ -119,6 +119,11 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { // Cord code is in a state where this can be proven true by the compiler. void SetCordRep(CordRep* rep); + // Returns the current `rep` property of this instance with a reference + // added, or null if this instance represents a cord that has since been + // deleted or untracked. + CordRep* RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_); + // Returns the current value of `rep_` for testing purposes only. CordRep* GetCordRepForTesting() const ABSL_NO_THREAD_SAFETY_ANALYSIS { return rep_; @@ -148,6 +153,9 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { } private: + using SpinLock = absl::base_internal::SpinLock; + using SpinLockHolder = ::absl::base_internal::SpinLockHolder; + // Global cordz info list. CordzInfo stores a pointer to the global list // instance to harden against ODR violations. struct List { @@ -155,7 +163,7 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { : mutex(absl::kConstInit, absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {} - absl::base_internal::SpinLock mutex; + SpinLock mutex; std::atomic head ABSL_GUARDED_BY(mutex){nullptr}; }; @@ -165,6 +173,9 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { MethodIdentifier method); ~CordzInfo() override; + // Sets `rep_` without holding a lock. + void UnsafeSetCordRep(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS; + void Track(); // Returns the parent method from `src`, which is either `parent_method_` or @@ -244,6 +255,13 @@ inline void CordzInfo::SetCordRep(CordRep* rep) { } } +inline void CordzInfo::UnsafeSetCordRep(CordRep* rep) { rep_ = rep; } + +inline CordRep* CordzInfo::RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_) { + MutexLock lock(&mutex_); + return rep_ ? CordRep::Ref(rep_) : nullptr; +} + } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index 5eaf4b9c..5bb23e47 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -38,6 +38,7 @@ using ::testing::ElementsAre; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::Ne; +using ::testing::SizeIs; // Used test values auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown; @@ -78,10 +79,19 @@ TEST(CordzInfoTest, UntrackCord) { CordzInfo::TrackCord(data.data, kTrackCordMethod); CordzInfo* info = data.data.cordz_info(); + info->Untrack(); + EXPECT_THAT(DeleteQueue(), SizeIs(0)); +} + +TEST(CordzInfoTest, UntrackCordWithSnapshot) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + CordzSnapshot snapshot; info->Untrack(); EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); - EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr)); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep)); EXPECT_THAT(DeleteQueue(), ElementsAre(info, &snapshot)); } @@ -113,6 +123,18 @@ TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) { EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); } +TEST(CordzInfoTest, RefCordRep) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + + size_t refcount = data.rep.rep->refcount.Get(); + EXPECT_THAT(info->RefCordRep(), Eq(data.rep.rep)); + EXPECT_THAT(data.rep.rep->refcount.Get(), Eq(refcount + 1)); + CordRep::Unref(data.rep.rep); + info->Untrack(); +} + #if GTEST_HAS_DEATH_TEST TEST(CordzInfoTest, SetCordRepRequiresMutex) { diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index 741950b2..d5b14067 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -47,8 +47,10 @@ class CordzUpdateTracker { kClear, kConstructorCord, kConstructorString, + kCordReader, kFlatten, kGetAppendRegion, + kMakeCordFromExternal, kMoveAppendCord, kMoveAssignCord, kMovePrependCord, diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc index eda662f2..fcd17df7 100644 --- a/absl/strings/internal/cordz_update_tracker_test.cc +++ b/absl/strings/internal/cordz_update_tracker_test.cc @@ -45,8 +45,10 @@ Methods AllMethods() { Method::kClear, Method::kConstructorCord, Method::kConstructorString, + Method::kCordReader, Method::kFlatten, Method::kGetAppendRegion, + Method::kMakeCordFromExternal, Method::kMoveAppendCord, Method::kMoveAssignCord, Method::kMovePrependCord, -- cgit v1.2.3 From a9831f1cbf93fb18dd951453635f488037454ce9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 3 May 2021 07:37:39 -0700 Subject: Export of internal Abseil changes -- cf88f9cf40eab54c06bca7f20795352ec23bb583 by Derek Mauro : Fixes build with latest glibc Fixes #952 PiperOrigin-RevId: 371693908 -- 99bcd0f4a747ce7a401e23c745adf34d0ec5131b by Samuel Benzaquen : Add support for std::string_view in StrFormat even when absl::string_view != std::string_view. PiperOrigin-RevId: 371693633 -- e35463572149a6c2d4a0d439b9300ce03fd6b96d by Abseil Team : Cmake builds should only install pkg-config when explicitly requested. PiperOrigin-RevId: 371403419 GitOrigin-RevId: cf88f9cf40eab54c06bca7f20795352ec23bb583 Change-Id: I4360a18c638a4d901ff44ab1e0a9d8f321c302ea --- CMake/AbseilHelpers.cmake | 3 ++- absl/debugging/failure_signal_handler.cc | 3 ++- absl/strings/internal/str_format/arg.h | 8 ++++++++ absl/strings/internal/str_format/convert_test.cc | 3 +++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 1f754392..1a80b5b4 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -141,7 +141,8 @@ function(absl_cc_library) endif() # Generate a pkg-config file for every library: - if(_build_type STREQUAL "static" OR _build_type STREQUAL "shared") + if((_build_type STREQUAL "static" OR _build_type STREQUAL "shared") + AND ABSL_ENABLE_INSTALL) if(NOT ABSL_CC_LIB_TESTONLY) if(absl_VERSION) set(PC_VERSION "${absl_VERSION}") diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index e458a795..689e5979 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -136,7 +136,8 @@ static bool SetupAlternateStackOnce() { #else const size_t page_mask = sysconf(_SC_PAGESIZE) - 1; #endif - size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask; + size_t stack_size = + (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask; #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER) // Account for sanitizer instrumentation requiring additional stack space. diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 7040c866..3c91be70 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -122,6 +122,14 @@ StringConvertResult FormatConvertImpl(const std::string& v, StringConvertResult FormatConvertImpl(string_view v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); +#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW) +inline StringConvertResult FormatConvertImpl(std::string_view v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink); +} +#endif // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW + ArgConvertResult FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index 926283cf..91e03609 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -229,6 +229,9 @@ TEST_F(FormatConvertTest, BasicString) { TestStringConvert(static_cast("hello")); TestStringConvert(std::string("hello")); TestStringConvert(string_view("hello")); +#if defined(ABSL_HAVE_STD_STRING_VIEW) + TestStringConvert(std::string_view("hello")); +#endif // ABSL_HAVE_STD_STRING_VIEW } TEST_F(FormatConvertTest, NullString) { -- cgit v1.2.3 From cba8cf87bcaac32e90a366fa16216ee89f7f61cc Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 4 May 2021 09:52:56 -0700 Subject: Export of internal Abseil changes -- 5d6734366ec54997df5234ac3b7e21015d7d5fde by Martijn Vels : Increase slop for unit test to reduce flakiness of test PiperOrigin-RevId: 371935786 -- 6e97ff23e7f732ebf969bbc69102e5e677aae8cd by Martijn Vels : Add node and memory usage stats analysis to GetCordzStatistics. PiperOrigin-RevId: 371893353 -- 17f7443e6f988f25efa25c2291c1cde191af2bf2 by Martijn Vels : Add check on n == 0 in CordReader::ReadCord, which breaks invariants in the ring buffer code. PiperOrigin-RevId: 371738207 GitOrigin-RevId: 5d6734366ec54997df5234ac3b7e21015d7d5fde Change-Id: I0fc883f4f49f2380ab9afddbdfe6eb5ccc15dfc3 --- absl/strings/BUILD.bazel | 21 + absl/strings/CMakeLists.txt | 19 + absl/strings/cord.h | 1 + absl/strings/internal/cordz_info.cc | 207 ++++++++- absl/strings/internal/cordz_info.h | 11 - .../strings/internal/cordz_info_statistics_test.cc | 483 +++++++++++++++++++++ absl/strings/internal/cordz_info_test.cc | 14 - absl/strings/internal/cordz_statistics.h | 12 + .../internal/per_thread_sem_test.cc | 2 +- 9 files changed, 738 insertions(+), 32 deletions(-) create mode 100644 absl/strings/internal/cordz_info_statistics_test.cc diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 15277de3..84d4bc4f 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -376,6 +376,7 @@ cc_library( "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", + "//absl/container:inlined_vector", "//absl/debugging:stacktrace", "//absl/synchronization", "//absl/types:span", @@ -497,6 +498,26 @@ cc_test( ], ) +cc_test( + name = "cordz_info_statistics_test", + srcs = [ + "internal/cordz_info_statistics_test.cc", + ], + deps = [ + ":cord", + ":cord_internal", + ":cordz_info", + ":cordz_sample_token", + ":cordz_statistics", + ":cordz_update_scope", + ":cordz_update_tracker", + "//absl/base:config", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "cordz_sample_token_test", srcs = [ diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 1750f7af..92728d91 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -696,6 +696,7 @@ absl_cc_library( absl::cordz_statistics absl::cordz_update_tracker absl::core_headers + absl::inlined_vector absl::span absl::raw_logging_internal absl::stacktrace @@ -722,6 +723,24 @@ absl_cc_test( gmock_main ) +absl_cc_test( + NAME + cordz_info_statistics_test + SRCS + "internal/cordz_info_statistics_test.cc" + DEPS + absl::config + absl::cord + absl::cord_internal + absl::cordz_info + absl::cordz_sample_token + absl::cordz_statistics + absl::cordz_update_scope + absl::cordz_update_tracker + absl::thread_pool + gmock_main +) + absl_cc_library( NAME cordz_sample_token diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 8abc4742..5c80a5cb 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -1077,6 +1077,7 @@ inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity( inline void Cord::InlineRep::EmplaceTree(CordRep* rep, MethodIdentifier method) { + assert(rep); data_.make_tree(rep); CordzInfo::MaybeTrackCord(data_, method); } diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 0f657aaf..e1fa6b6b 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -16,8 +16,10 @@ #include "absl/base/config.h" #include "absl/base/internal/spinlock.h" +#include "absl/container/inlined_vector.h" #include "absl/debugging/stacktrace.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_update_tracker.h" @@ -34,6 +36,198 @@ constexpr int CordzInfo::kMaxStackDepth; ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{absl::kConstInit}; +namespace { + +// CordRepAnalyzer performs the analysis of a cord. +// +// It computes absolute node counts and total memory usage, and an 'estimated +// fair share memory usage` statistic. +// Conceptually, it divides the 'memory usage' at each location in the 'cord +// graph' by the cumulative reference count of that location. The cumulative +// reference count is the factored total of all edges leading into that node. +// +// The top level node is treated specially: we assume the current thread +// (typically called from the CordzHandler) to hold a reference purely to +// perform a safe analysis, and not being part of the application. So we +// substract 1 from the reference count of the top node to compute the +// 'application fair share' excluding the reference of the current thread. +// +// An example of fair sharing, and why we multiply reference counts: +// Assume we have 2 CordReps, both being a Substring referencing a Flat: +// CordSubstring A (refcount = 5) --> child Flat C (refcount = 2) +// CordSubstring B (refcount = 9) --> child Flat C (refcount = 2) +// +// Flat C has 2 incoming edges from the 2 substrings (refcount = 2) and is not +// referenced directly anywhere else. Translated into a 'fair share', we then +// attribute 50% of the memory (memory / refcount = 2) to each incoming edge. +// Rep A has a refcount of 5, so we attribute each incoming edge 1 / 5th of the +// memory cost below it, i.e.: the fair share of Rep A of the memory used by C +// is then 'memory C / (refcount C * refcount A) + (memory A / refcount A)'. +// It is also easy to see how all incoming edges add up to 100%. +class CordRepAnalyzer { + public: + // Creates an analyzer instance binding to `statistics`. + explicit CordRepAnalyzer(CordzStatistics& statistics) + : statistics_(statistics) {} + + // Analyzes the memory statistics and node counts for the provided `rep`, and + // adds the results to `statistics`. Note that node counts and memory sizes + // are not initialized, computed values are added to any existing values. + void AnalyzeCordRep(const CordRep* rep) { + // Process all linear nodes. + // As per the class comments, use refcout - 1 on the top level node, as the + // top level node is assumed to be referenced only for analysis purposes. + size_t refcount = rep->refcount.Get(); + RepRef repref{rep, (refcount > 1) ? refcount - 1 : 1}; + + // Process all top level linear nodes (substrings and flats). + repref = CountLinearReps(repref, memory_usage_); + + // We should have have either a concat or ring node node if not null. + if (repref.rep != nullptr) { + assert(repref.rep->tag == RING || repref.rep->tag == CONCAT); + if (repref.rep->tag == RING) { + AnalyzeRing(repref); + } else if (repref.rep->tag == CONCAT) { + AnalyzeConcat(repref); + } + } + + // Adds values to output + statistics_.estimated_memory_usage += memory_usage_.total; + statistics_.estimated_fair_share_memory_usage += memory_usage_.fair_share; + } + + private: + // RepRef identifies a CordRep* inside the Cord tree with its cumulative + // refcount including itself. For example, a tree consisting of a substring + // with a refcount of 3 and a child flat with a refcount of 4 will have RepRef + // refcounts of 3 and 12 respectively. + struct RepRef { + const CordRep* rep; + size_t refcount; + + // Returns a 'child' RepRef which contains the cumulative reference count of + // this instance multiplied by the child's reference count. + RepRef Child(const CordRep* child) const { + return RepRef{child, refcount * child->refcount.Get()}; + } + }; + + // Memory usage values + struct MemoryUsage { + size_t total = 0; + size_t fair_share = 0; + + // Adds 'size` memory usage to this class, with a cumulative (recursive) + // reference count of `refcount` + void Add(size_t size, size_t refcount) { + total += size; + fair_share += size / refcount; + } + }; + + // Returns `rr` if `rr.rep` is not null and a CONCAT type. + // Asserts that `rr.rep` is a concat node or null. + static RepRef AssertConcat(RepRef repref) { + const CordRep* rep = repref.rep; + assert(rep == nullptr || rep->tag == CONCAT); + return (rep != nullptr && rep->tag == CONCAT) ? repref : RepRef{nullptr, 0}; + } + + // Processes 'linear' reps (substring, flat, external) not requiring iteration + // or recursion. Returns RefRep{null} if all reps were processed, else returns + // the top-most non-linear concat or ring cordrep. + // Node counts are updated into `statistics_`, memory usage is update into + // `memory_usage`, which typically references `memory_usage_` except for ring + // buffers where we count children unrounded. + RepRef CountLinearReps(RepRef rep, MemoryUsage& memory_usage) { + // Consume all substrings + while (rep.rep->tag == SUBSTRING) { + statistics_.node_count++; + statistics_.node_counts.substring++; + memory_usage.Add(sizeof(CordRepSubstring), rep.refcount); + rep = rep.Child(rep.rep->substring()->child); + } + + // Consume possible FLAT + if (rep.rep->tag >= FLAT) { + statistics_.node_count++; + statistics_.node_counts.flat++; + memory_usage.Add(rep.rep->flat()->AllocatedSize(), rep.refcount); + return RepRef{nullptr, 0}; + } + + // Consume possible external + if (rep.rep->tag == EXTERNAL) { + statistics_.node_count++; + statistics_.node_counts.external++; + size_t size = rep.rep->length + sizeof(CordRepExternalImpl); + memory_usage.Add(size, rep.refcount); + return RepRef{nullptr, 0}; + } + + return rep; + } + + // Analyzes the provided concat node in a flattened recursive way. + void AnalyzeConcat(RepRef rep) { + absl::InlinedVector pending; + + while (rep.rep != nullptr) { + const CordRepConcat* concat = rep.rep->concat(); + RepRef left = rep.Child(concat->left); + RepRef right = rep.Child(concat->right); + + statistics_.node_count++; + statistics_.node_counts.concat++; + memory_usage_.Add(sizeof(CordRepConcat), rep.refcount); + + right = AssertConcat(CountLinearReps(right, memory_usage_)); + rep = AssertConcat(CountLinearReps(left, memory_usage_)); + if (rep.rep != nullptr) { + if (right.rep != nullptr) { + pending.push_back(right); + } + } else if (right.rep != nullptr) { + rep = right; + } else if (!pending.empty()) { + rep = pending.back(); + pending.pop_back(); + } + } + } + + // Counts the provided ring buffer child into `child_usage`. + void CountRingChild(const CordRep* child, MemoryUsage& child_usage) { + RepRef rep{child, static_cast(child->refcount.Get())}; + rep = CountLinearReps(rep, child_usage); + assert(rep.rep == nullptr); + } + + // Analyzes the provided ring. As ring buffers can have many child nodes, the + // effect of rounding errors can become non trivial, so we compute the totals + // first at the ring level, and then divide the fair share of the total + // including children fair share totals. + void AnalyzeRing(RepRef rep) { + statistics_.node_count++; + statistics_.node_counts.ring++; + MemoryUsage ring_usage; + const CordRepRing* ring = rep.rep->ring(); + ring_usage.Add(CordRepRing::AllocSize(ring->capacity()), 1); + ring->ForEach([&](CordRepRing::index_type pos) { + CountRingChild(ring->entry_child(pos), ring_usage); + }); + memory_usage_.total += ring_usage.total; + memory_usage_.fair_share += ring_usage.fair_share / rep.refcount; + } + + CordzStatistics& statistics_; + MemoryUsage memory_usage_; +}; + +} // namespace + CordzInfo* CordzInfo::Head(const CordzSnapshot& snapshot) { ABSL_ASSERT(snapshot.is_snapshot()); @@ -101,8 +295,7 @@ CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, parent_stack_depth_(FillParentStack(src, parent_stack_)), method_(method), parent_method_(GetParentMethod(src)), - create_time_(absl::Now()), - size_(rep->length) { + create_time_(absl::Now()) { update_tracker_.LossyAdd(method); } @@ -173,9 +366,6 @@ void CordzInfo::Lock(MethodIdentifier method) void CordzInfo::Unlock() ABSL_UNLOCK_FUNCTION(mutex_) { bool tracked = rep_ != nullptr; - if (rep_) { - size_.store(rep_->length); - } mutex_.Unlock(); if (!tracked) { Untrack(); @@ -195,7 +385,12 @@ CordzStatistics CordzInfo::GetCordzStatistics() const { stats.method = method_; stats.parent_method = parent_method_; stats.update_tracker = update_tracker_; - stats.size = size_.load(std::memory_order_relaxed); + if (CordRep* rep = RefCordRep()) { + stats.size = rep->length; + CordRepAnalyzer analyzer(stats); + analyzer.AnalyzeCordRep(rep); + CordRep::Unref(rep); + } return stats; } diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index 1fe046ec..e84006c5 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -147,11 +147,6 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { // or RemovePrefix. CordzStatistics GetCordzStatistics() const; - // Records size metric for this CordzInfo instance. - void RecordMetrics(int64_t size) { - size_.store(size, std::memory_order_relaxed); - } - private: using SpinLock = absl::base_internal::SpinLock; using SpinLockHolder = ::absl::base_internal::SpinLockHolder; @@ -215,9 +210,6 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { const MethodIdentifier parent_method_; CordzUpdateTracker update_tracker_; const absl::Time create_time_; - - // Last recorded size for the cord. - std::atomic size_{0}; }; inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( @@ -250,9 +242,6 @@ inline void CordzInfo::AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_) { inline void CordzInfo::SetCordRep(CordRep* rep) { AssertHeld(); rep_ = rep; - if (rep) { - size_.store(rep->length); - } } inline void CordzInfo::UnsafeSetCordRep(CordRep* rep) { rep_ = rep; } diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc new file mode 100644 index 00000000..d46d6d62 --- /dev/null +++ b/absl/strings/internal/cordz_info_statistics_test.cc @@ -0,0 +1,483 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_sample_token.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_scope.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/notification.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Do not print statistics contents, the matcher prints them as needed. +inline void PrintTo(const CordzStatistics& stats, std::ostream* s) { + if (s) *s << "CordzStatistics{...}"; +} + +namespace { + +// Creates a flat of the specified length +CordRepFlat* Flat(int length = 512) { + auto* flat = CordRepFlat::New(length); + flat->length = length; + return flat; +} + +// Creates an external of the specified length +CordRepExternal* External(int length = 512) { + return static_cast( + NewExternalRep(absl::string_view("", length), [](absl::string_view) {})); +} + +// Creates a substring on the provided rep of length - 1 +CordRepSubstring* Substring(CordRep* rep) { + auto* substring = new CordRepSubstring; + substring->length = rep->length - 1; + substring->tag = SUBSTRING; + substring->child = rep; + return substring; +} + +// Creates a concat on the provided reps +CordRepConcat* Concat(CordRep* left, CordRep* right) { + auto* concat = new CordRepConcat; + concat->length = left->length + right->length; + concat->tag = CONCAT; + concat->left = left; + concat->right = right; + return concat; +} + +// Reference count helper +struct RefHelper { + std::vector refs; + + ~RefHelper() { + for (CordRep* rep : refs) { + CordRep::Unref(rep); + } + } + + // Invokes CordRep::Unref() on `rep` when this instance is destroyed. + template + T* NeedsUnref(T* rep) { + refs.push_back(rep); + return rep; + } + + // Adds `n` reference counts to `rep` which will be unreffed when this + // instance is destroyed. + template + T* Ref(T* rep, size_t n = 1) { + while (n--) { + NeedsUnref(CordRep::Ref(rep)); + } + return rep; + } +}; + +// Sizeof helper. Returns the allocated size of `p`, excluding any child +// elements for substring, concat and ring cord reps. +template +size_t SizeOf(const T* rep) { + return sizeof(T); +} + +template <> +size_t SizeOf(const CordRepFlat* rep) { + return rep->AllocatedSize(); +} + +template <> +size_t SizeOf(const CordRepExternal* rep) { + // See cord.cc + return sizeof(CordRepExternalImpl) + rep->length; +} + +template <> +size_t SizeOf(const CordRepRing* rep) { + return CordRepRing::AllocSize(rep->capacity()); +} + +// Computes fair share memory used in a naive 'we dare to recurse' way. +size_t FairShare(CordRep* rep, size_t ref = 1) { + size_t self = 0, children = 0; + ref *= rep->refcount.Get(); + if (rep->tag >= FLAT) { + self = SizeOf(rep->flat()); + } else if (rep->tag == EXTERNAL) { + self = SizeOf(rep->external()); + } else if (rep->tag == SUBSTRING) { + self = SizeOf(rep->substring()); + children = FairShare(rep->substring()->child, ref); + } else if (rep->tag == RING) { + self = SizeOf(rep->ring()); + rep->ring()->ForEach([&](CordRepRing::index_type i) { + self += FairShare(rep->ring()->entry_child(i)); + }); + } else if (rep->tag == CONCAT) { + self = SizeOf(rep->concat()); + children = FairShare(rep->concat()->left, ref) + + FairShare(rep->concat()->right, ref); + } else { + assert(false); + } + return self / ref + children; +} + +// Samples the cord and returns CordzInfo::GetStatistics() +CordzStatistics SampleCord(CordRep* rep) { + InlineData cord(rep); + CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown); + CordzStatistics stats = cord.cordz_info()->GetCordzStatistics(); + cord.cordz_info()->Untrack(); + return stats; +} + +MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { + bool ok = true; + +#define STATS_MATCHER_EXPECT_EQ(member) \ + if (stats.member != arg.member) { \ + *result_listener << "\n stats." << #member \ + << ": actual = " << arg.member << ", expected " \ + << stats.member; \ + ok = false; \ + } + + STATS_MATCHER_EXPECT_EQ(size); + STATS_MATCHER_EXPECT_EQ(node_count); + STATS_MATCHER_EXPECT_EQ(node_counts.flat); + STATS_MATCHER_EXPECT_EQ(node_counts.external); + STATS_MATCHER_EXPECT_EQ(node_counts.concat); + STATS_MATCHER_EXPECT_EQ(node_counts.substring); + STATS_MATCHER_EXPECT_EQ(node_counts.ring); + STATS_MATCHER_EXPECT_EQ(estimated_memory_usage); + STATS_MATCHER_EXPECT_EQ(estimated_fair_share_memory_usage); + +#undef STATS_MATCHER_EXPECT_EQ + + return ok; +} + +TEST(CordzInfoStatisticsTest, Flat) { + RefHelper ref; + auto* flat = ref.NeedsUnref(Flat()); + + CordzStatistics expected; + expected.size = flat->length; + expected.estimated_memory_usage = SizeOf(flat); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 1; + expected.node_counts.flat = 1; + + EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedFlat) { + RefHelper ref; + auto* flat = ref.Ref(ref.NeedsUnref(Flat())); + + CordzStatistics expected; + expected.size = flat->length; + expected.estimated_memory_usage = SizeOf(flat); + expected.estimated_fair_share_memory_usage = SizeOf(flat) / 2; + expected.node_count = 1; + expected.node_counts.flat = 1; + + EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, External) { + RefHelper ref; + auto* external = ref.NeedsUnref(External()); + + CordzStatistics expected; + expected.size = external->length; + expected.estimated_memory_usage = SizeOf(external); + expected.estimated_fair_share_memory_usage = SizeOf(external); + expected.node_count = 1; + expected.node_counts.external = 1; + + EXPECT_THAT(SampleCord(external), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedExternal) { + RefHelper ref; + auto* external = ref.Ref(ref.NeedsUnref(External())); + + CordzStatistics expected; + expected.size = external->length; + expected.estimated_memory_usage = SizeOf(external); + expected.estimated_fair_share_memory_usage = SizeOf(external) / 2; + expected.node_count = 1; + expected.node_counts.external = 1; + + EXPECT_THAT(SampleCord(external), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, Substring) { + RefHelper ref; + auto* flat = Flat(); + auto* substring = ref.NeedsUnref(Substring(flat)); + + CordzStatistics expected; + expected.size = substring->length; + expected.estimated_memory_usage = SizeOf(substring) + SizeOf(flat); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 2; + expected.node_counts.flat = 1; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedSubstring) { + RefHelper ref; + auto* flat = ref.Ref(Flat(), 2); + auto* substring = ref.Ref(ref.NeedsUnref(Substring(flat))); + + CordzStatistics expected; + expected.size = substring->length; + expected.estimated_memory_usage = SizeOf(flat) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = + SizeOf(substring) / 2 + SizeOf(flat) / 6; + expected.node_count = 2; + expected.node_counts.flat = 1; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, Concat) { + RefHelper ref; + auto* flat1 = Flat(20); + auto* flat2 = Flat(2000); + auto* concat = ref.NeedsUnref(Concat(flat1, flat2)); + + CordzStatistics expected; + expected.size = concat->length; + expected.estimated_memory_usage = + SizeOf(concat) + SizeOf(flat1) + SizeOf(flat2); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 3; + expected.node_counts.flat = 2; + expected.node_counts.concat = 1; + + EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, DeepConcat) { + RefHelper ref; + auto* flat1 = Flat(20); + auto* flat2 = Flat(2000); + auto* flat3 = Flat(30); + auto* external = External(3000); + auto* substring = Substring(external); + auto* concat1 = Concat(flat1, flat2); + auto* concat2 = Concat(flat3, substring); + auto* concat = ref.NeedsUnref(Concat(concat1, concat2)); + + CordzStatistics expected; + expected.size = concat->length; + expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + + expected.node_count = 8; + expected.node_counts.flat = 3; + expected.node_counts.external = 1; + expected.node_counts.concat = 3; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, DeepSharedConcat) { + RefHelper ref; + auto* flat1 = Flat(20); + auto* flat2 = ref.Ref(Flat(2000), 4); + auto* flat3 = Flat(30); + auto* external = ref.Ref(External(3000)); + auto* substring = ref.Ref(Substring(external), 3); + auto* concat1 = Concat(flat1, flat2); + auto* concat2 = Concat(flat3, substring); + auto* concat = ref.Ref(ref.NeedsUnref(Concat(concat1, concat2))); + + CordzStatistics expected; + expected.size = concat->length; + expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = FairShare(concat); + expected.node_count = 8; + expected.node_counts.flat = 3; + expected.node_counts.external = 1; + expected.node_counts.concat = 3; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, Ring) { + RefHelper ref; + auto* flat1 = Flat(20); + auto* flat2 = Flat(2000); + auto* flat3 = Flat(30); + auto* external = External(3000); + CordRepRing* ring = CordRepRing::Create(flat1); + ring = CordRepRing::Append(ring, flat2); + ring = CordRepRing::Append(ring, flat3); + ring = ref.NeedsUnref(CordRepRing::Append(ring, external)); + + CordzStatistics expected; + expected.size = ring->length; + expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 5; + expected.node_counts.flat = 3; + expected.node_counts.external = 1; + expected.node_counts.ring = 1; + + EXPECT_THAT(SampleCord(ring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedSubstringRing) { + RefHelper ref; + auto* flat1 = ref.Ref(Flat(20)); + auto* flat2 = Flat(200); + auto* flat3 = Flat(30); + auto* external = ref.Ref(External(3000), 5); + CordRepRing* ring = CordRepRing::Create(flat1); + ring = CordRepRing::Append(ring, flat2); + ring = CordRepRing::Append(ring, flat3); + ring = ref.Ref(CordRepRing::Append(ring, external), 4); + auto* substring = ref.Ref(ref.NeedsUnref(Substring(ring))); + + + CordzStatistics expected; + expected.size = substring->length; + expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = FairShare(substring); + expected.node_count = 6; + expected.node_counts.flat = 3; + expected.node_counts.external = 1; + expected.node_counts.ring = 1; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, ThreadSafety) { + Notification stop; + static constexpr int kNumThreads = 8; + int64_t sampled_node_count = 0; + + { + absl::synchronization_internal::ThreadPool pool(kNumThreads); + + // Run analyzer thread emulating a CordzHandler collection. + pool.Schedule([&]() { + while (!stop.HasBeenNotified()) { + // Run every 10us (about 100K total collections). + absl::SleepFor(absl::Microseconds(10)); + CordzSampleToken token; + for (const CordzInfo& cord_info : token) { + CordzStatistics stats = cord_info.GetCordzStatistics(); + sampled_node_count += stats.node_count; + } + } + }); + + // Run 'application threads' + for (int i = 0; i < kNumThreads; ++i) { + pool.Schedule([&]() { + // Track 0 - 2 cordz infos at a time, providing permutations of 0, 1 + // and 2 CordzHandle and CordzInfo queues being active, with plenty of + // 'empty to non empty' transitions. + InlineData cords[2]; + std::minstd_rand gen; + std::uniform_int_distribution coin_toss(0, 1); + + while (!stop.HasBeenNotified()) { + for (InlineData& cord : cords) { + // 50/50 flip the state of the cord + if (coin_toss(gen) != 0) { + if (cord.is_tree()) { + // 50/50 simulate delete (untrack) or 'edit to empty' + if (coin_toss(gen) != 0) { + CordzInfo::MaybeUntrackCord(cord.cordz_info()); + } else { + CordzUpdateScope scope(cord.cordz_info(), + CordzUpdateTracker::kUnknown); + scope.SetCordRep(nullptr); + } + CordRep::Unref(cord.as_tree()); + cord.set_inline_size(0); + } else { + // 50/50 Ring or Flat coin toss + CordRep* rep = Flat(); + rep = (coin_toss(gen) != 0) ? CordRepRing::Create(rep) : rep; + cord.make_tree(rep); + + // 50/50 sample + if (coin_toss(gen) != 0) { + CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown); + } + } + } + } + } + for (InlineData& cord : cords) { + if (cord.is_tree()) { + CordzInfo::MaybeUntrackCord(cord.cordz_info()); + CordRep::Unref(cord.as_tree()); + } + } + }); + } + + // Run for 1 second to give memory and thread safety analyzers plenty of + // time to detect any mishaps or undefined behaviors. + absl::SleepFor(absl::Seconds(1)); + stop.Notify(); + } + + std::cout << "Sampled " << sampled_node_count << " nodes\n"; +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index 5bb23e47..b7eb910d 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -304,20 +304,6 @@ TEST(CordzInfoTest, FromParentInlined) { info->Untrack(); } -TEST(CordzInfoTest, RecordMetrics) { - TestCordData data; - CordzInfo* info = TrackParentCord(data.data); - - CordzStatistics expected; - expected.size = 100; - info->RecordMetrics(expected.size); - - CordzStatistics actual = info->GetCordzStatistics(); - EXPECT_EQ(actual.size, expected.size); - - info->Untrack(); -} - } // namespace } // namespace cord_internal ABSL_NAMESPACE_END diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h index 6e335c0b..1c93dfd0 100644 --- a/absl/strings/internal/cordz_statistics.h +++ b/absl/strings/internal/cordz_statistics.h @@ -28,6 +28,15 @@ namespace cord_internal { struct CordzStatistics { using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + // Node counts information + struct NodeCounts { + size_t flat = 0; + size_t external = 0; + size_t substring = 0; + size_t concat = 0; + size_t ring = 0; + }; + // The size of the cord in bytes. This matches the result of Cord::size(). int64_t size = 0; @@ -50,6 +59,9 @@ struct CordzStatistics { // A value of 0 implies the property has not been recorded. int64_t node_count = 0; + // Detailed node counts per type + NodeCounts node_counts; + // The cord method responsible for sampling the cord. MethodIdentifier method = MethodIdentifier::kUnknown; diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc index 8cf59e64..db1184e6 100644 --- a/absl/synchronization/internal/per_thread_sem_test.cc +++ b/absl/synchronization/internal/per_thread_sem_test.cc @@ -159,7 +159,7 @@ TEST_F(PerThreadSemTest, Timeouts) { const absl::Duration elapsed = absl::Now() - start; // Allow for a slight early return, to account for quality of implementation // issues on various platforms. - const absl::Duration slop = absl::Microseconds(200); + const absl::Duration slop = absl::Milliseconds(1); EXPECT_LE(delay - slop, elapsed) << "Wait returned " << delay - elapsed << " early (with " << slop << " slop), start time was " << start; -- cgit v1.2.3 From 037ade20d1132781aae3cda4d547a9e6a5f557bf Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 4 May 2021 19:23:13 -0700 Subject: Export of internal Abseil changes -- d8a1e7f8059a0bc4cd72b4e4ca2947322c36f7ee by Martijn Vels : Add cordz allowlist and cleanup COPTS PiperOrigin-RevId: 372042499 -- 2abd20ac17127b8823787bfcdd3e7e80709d3034 by Evan Brown : Remove unnecessary string conversion now that FormatTime accepts string_view. PiperOrigin-RevId: 371989142 GitOrigin-RevId: d8a1e7f8059a0bc4cd72b4e4ca2947322c36f7ee Change-Id: I637e9fa434de9b2c38fd0f96d66eefc6e8eec8e8 --- absl/strings/BUILD.bazel | 22 +++++++++++++++++++++- absl/strings/CMakeLists.txt | 14 +++++++++----- absl/time/civil_time.cc | 4 +--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 84d4bc4f..f3b08de6 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -300,7 +300,9 @@ cc_library( name = "cordz_update_tracker", hdrs = ["internal/cordz_update_tracker.h"], copts = ABSL_DEFAULT_COPTS, - visibility = ["//visibility:private"], + visibility = [ + "//absl:__subpackages__", + ], deps = ["//absl/base:config"], ) @@ -353,6 +355,9 @@ cc_library( srcs = ["internal/cordz_handle.cc"], hdrs = ["internal/cordz_handle.h"], copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ "//absl/base", "//absl/base:config", @@ -366,6 +371,9 @@ cc_library( srcs = ["internal/cordz_info.cc"], hdrs = ["internal/cordz_info.h"], copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ ":cord_internal", ":cordz_functions", @@ -387,6 +395,9 @@ cc_library( name = "cordz_update_scope", hdrs = ["internal/cordz_update_scope.h"], copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ ":cord_internal", ":cordz_info", @@ -416,6 +427,9 @@ cc_library( srcs = ["internal/cordz_sample_token.cc"], hdrs = ["internal/cordz_sample_token.h"], copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ ":cordz_handle", ":cordz_info", @@ -428,6 +442,9 @@ cc_library( srcs = ["internal/cordz_functions.cc"], hdrs = ["internal/cordz_functions.h"], copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -440,6 +457,9 @@ cc_library( name = "cordz_statistics", hdrs = ["internal/cordz_statistics.h"], copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ ":cordz_update_tracker", "//absl/base:config", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 92728d91..d01f0f18 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -259,7 +259,7 @@ absl_cc_test( absl_cc_test( NAME str_join_test -ss SRCS + SRCS "str_join_test.cc" COPTS ${ABSL_TEST_COPTS} @@ -562,7 +562,7 @@ absl_cc_library( "internal/cord_internal.cc" "internal/cord_rep_ring.cc" COPTS - ${ABSL_TEST_COPTS} + ${ABSL_DEFAULT_COPTS} DEPS absl::base_internal absl::compressed_tuple @@ -583,7 +583,7 @@ absl_cc_library( HDRS "internal/cordz_update_tracker.h" COPTS - ${ABSL_TEST_COPTS} + ${ABSL_DEFAULT_COPTS} DEPS absl::config ) @@ -708,6 +708,8 @@ absl_cc_test( cordz_info_test SRCS "internal/cordz_info_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::config absl::cord_internal @@ -728,6 +730,8 @@ absl_cc_test( cordz_info_statistics_test SRCS "internal/cordz_info_statistics_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::config absl::cord @@ -762,7 +766,7 @@ absl_cc_test( SRCS "internal/cordz_sample_token_test.cc" COPTS - ${ABSL_DEFAULT_COPTS} + ${ABSL_TEST_COPTS} DEPS absl::config absl::cord_internal @@ -785,7 +789,7 @@ absl_cc_library( HDRS "internal/cordz_update_scope.h" COPTS - ${ABSL_TEST_COPTS} + ${ABSL_DEFAULT_COPTS} DEPS absl::config absl::cord_internal diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc index bdfe9ce0..6a231edb 100644 --- a/absl/time/civil_time.cc +++ b/absl/time/civil_time.cc @@ -38,9 +38,7 @@ std::string FormatYearAnd(string_view fmt, CivilSecond cs) { const CivilSecond ncs(NormalizeYear(cs.year()), cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second()); const TimeZone utc = UTCTimeZone(); - // TODO(absl-team): Avoid conversion of fmt string. - return StrCat(cs.year(), - FormatTime(std::string(fmt), FromCivil(ncs, utc), utc)); + return StrCat(cs.year(), FormatTime(fmt, FromCivil(ncs, utc), utc)); } template -- cgit v1.2.3 From 70b29fe5a5c1752158830eabc9aa273718b477af Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 6 May 2021 07:35:26 -0700 Subject: Export of internal Abseil changes -- daf5a2b9ab3507ad5fb9aebe9165933f33098b83 by Abseil Team : Absl flat containers reserve enough space even in the presence of tombstones. PiperOrigin-RevId: 372339945 -- 9a61504867ba0eccc5046d7333090fbe3439cdd9 by Abseil Team : Add benchmark for BlockingCounter PiperOrigin-RevId: 372246068 -- 91ee87e6de09fc62970667ee52654c9dcf7c478d by Evan Brown : In absl::StrSplit, support btree_multimap, and other non-std::multimap-multimaps by supporting any map type that returns iterator from insert(). Also: - Use emplace() instead of insert() when available, not just for std::(multi)map - we can potentially change some string copies to moves this way. - We no longer need the Insert class so remove it. PiperOrigin-RevId: 372209653 GitOrigin-RevId: daf5a2b9ab3507ad5fb9aebe9165933f33098b83 Change-Id: I83098fde4a722cd4b682f024d3bfa56c613f960c --- absl/container/flat_hash_map_test.cc | 26 +++++++ absl/container/internal/raw_hash_set.h | 4 +- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 2 +- absl/strings/internal/str_split_internal.h | 68 +++++++++--------- absl/strings/str_split_test.cc | 22 ++++++ absl/synchronization/BUILD.bazel | 15 ++++ absl/synchronization/blocking_counter_benchmark.cc | 83 ++++++++++++++++++++++ 8 files changed, 184 insertions(+), 37 deletions(-) create mode 100644 absl/synchronization/blocking_counter_benchmark.cc diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc index 89ec60c9..8dda1d35 100644 --- a/absl/container/flat_hash_map_test.cc +++ b/absl/container/flat_hash_map_test.cc @@ -282,6 +282,32 @@ TEST(FlatHashMap, NodeHandleMutableKeyAccess) { } #endif +TEST(FlatHashMap, Reserve) { + // Verify that if we reserve(size() + n) then we can perform n insertions + // without a rehash, i.e., without invalidating any references. + for (size_t trial = 0; trial < 20; ++trial) { + for (size_t initial = 3; initial < 100; ++initial) { + // Fill in `initial` entries, then erase 2 of them, then reserve space for + // two inserts and check for reference stability while doing the inserts. + flat_hash_map map; + for (size_t i = 0; i < initial; ++i) { + map[i] = i; + } + map.erase(0); + map.erase(1); + map.reserve(map.size() + 2); + size_t& a2 = map[2]; + // In the event of a failure, asan will complain in one of these two + // assignments. + map[initial] = a2; + map[initial + 1] = a2; + // Fail even when not under asan: + size_t& a2new = map[2]; + EXPECT_EQ(&a2, &a2new); + } + } +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 8615de8b..b23e0078 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1323,8 +1323,8 @@ class raw_hash_set { } void reserve(size_t n) { - size_t m = GrowthToLowerboundCapacity(n); - if (m > capacity_) { + if (n > size() + growth_left()) { + size_t m = GrowthToLowerboundCapacity(n); resize(NormalizeCapacity(m)); } } diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index f3b08de6..1cb5b3e5 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -728,6 +728,7 @@ cc_test( ":strings", "//absl/base:core_headers", "//absl/base:dynamic_annotations", + "//absl/container:btree", "//absl/container:flat_hash_map", "//absl/container:node_hash_map", "@com_google_googletest//:gtest_main", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index d01f0f18..d3f1523c 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -221,9 +221,9 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings - absl::base absl::core_headers absl::dynamic_annotations + absl::btree absl::flat_hash_map absl::node_hash_map gmock_main diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h index a2f41c15..17c1bfe8 100644 --- a/absl/strings/internal/str_split_internal.h +++ b/absl/strings/internal/str_split_internal.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -182,6 +182,13 @@ template struct HasConstIterator> : std::true_type {}; +// HasEmplace::value is true iff there exists a method T::emplace(). +template +struct HasEmplace : std::false_type {}; +template +struct HasEmplace().emplace())>> + : std::true_type {}; + // IsInitializerList::value is true iff T is an std::initializer_list. More // details below in Splitter<> where this is used. std::false_type IsInitializerListDispatch(...); // default: No @@ -372,50 +379,43 @@ class Splitter { // value. template struct ConvertToContainer, true> { + using iterator = typename Container::iterator; + Container operator()(const Splitter& splitter) const { Container m; - typename Container::iterator it; + iterator it; bool insert = true; - for (const auto& sp : splitter) { + for (const absl::string_view sv : splitter) { if (insert) { - it = Inserter::Insert(&m, First(sp), Second()); + it = InsertOrEmplace(&m, sv); } else { - it->second = Second(sp); + it->second = Second(sv); } insert = !insert; } return m; } - // Inserts the key and value into the given map, returning an iterator to - // the inserted item. Specialized for std::map and std::multimap to use - // emplace() and adapt emplace()'s return value. - template - struct Inserter { - using M = Map; - template - static typename M::iterator Insert(M* m, Args&&... args) { - return m->insert(std::make_pair(std::forward(args)...)).first; - } - }; - - template - struct Inserter> { - using M = std::map; - template - static typename M::iterator Insert(M* m, Args&&... args) { - return m->emplace(std::make_pair(std::forward(args)...)).first; - } - }; - - template - struct Inserter> { - using M = std::multimap; - template - static typename M::iterator Insert(M* m, Args&&... args) { - return m->emplace(std::make_pair(std::forward(args)...)); - } - }; + // Inserts the key and an empty value into the map, returning an iterator to + // the inserted item. We use emplace() if available, otherwise insert(). + template + static absl::enable_if_t::value, iterator> InsertOrEmplace( + M* m, absl::string_view key) { + // Use piecewise_construct to support old versions of gcc in which pair + // constructor can't otherwise construct string from string_view. + return ToIter(m->emplace(std::piecewise_construct, std::make_tuple(key), + std::tuple<>())); + } + template + static absl::enable_if_t::value, iterator> InsertOrEmplace( + M* m, absl::string_view key) { + return ToIter(m->insert(std::make_pair(First(key), Second("")))); + } + + static iterator ToIter(std::pair pair) { + return pair.first; + } + static iterator ToIter(iterator iter) { return iter; } }; StringType text_; diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc index 7f7c097f..f472f9ed 100644 --- a/absl/strings/str_split_test.cc +++ b/absl/strings/str_split_test.cc @@ -29,6 +29,8 @@ #include "gtest/gtest.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/macros.h" +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" #include "absl/container/node_hash_map.h" #include "absl/strings/numbers.h" @@ -405,6 +407,10 @@ TEST(Splitter, ConversionOperator) { TestConversionOperator>(splitter); TestConversionOperator>(splitter); TestConversionOperator>(splitter); + TestConversionOperator>(splitter); + TestConversionOperator>(splitter); + TestConversionOperator>(splitter); + TestConversionOperator>(splitter); TestConversionOperator>(splitter); // Tests conversion to map-like objects. @@ -421,6 +427,22 @@ TEST(Splitter, ConversionOperator) { TestMapConversionOperator>( splitter); TestMapConversionOperator>(splitter); + TestMapConversionOperator< + absl::btree_map>(splitter); + TestMapConversionOperator>( + splitter); + TestMapConversionOperator>( + splitter); + TestMapConversionOperator>( + splitter); + TestMapConversionOperator< + absl::btree_multimap>(splitter); + TestMapConversionOperator< + absl::btree_multimap>(splitter); + TestMapConversionOperator< + absl::btree_multimap>(splitter); + TestMapConversionOperator>( + splitter); TestMapConversionOperator>( splitter); TestMapConversionOperator< diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 5ce16958..92e2448d 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -136,6 +136,21 @@ cc_test( ], ) +cc_binary( + name = "blocking_counter_benchmark", + testonly = 1, + srcs = ["blocking_counter_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":synchronization", + ":thread_pool", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_test( name = "graphcycles_test", size = "medium", diff --git a/absl/synchronization/blocking_counter_benchmark.cc b/absl/synchronization/blocking_counter_benchmark.cc new file mode 100644 index 00000000..b504d1a5 --- /dev/null +++ b/absl/synchronization/blocking_counter_benchmark.cc @@ -0,0 +1,83 @@ +// Copyright 2021 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "absl/synchronization/blocking_counter.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "benchmark/benchmark.h" + +namespace { + +void BM_BlockingCounter_SingleThread(benchmark::State& state) { + for (auto _ : state) { + int iterations = state.range(0); + absl::BlockingCounter counter{iterations}; + for (int i = 0; i < iterations; ++i) { + counter.DecrementCount(); + } + counter.Wait(); + } +} +BENCHMARK(BM_BlockingCounter_SingleThread) + ->ArgName("iterations") + ->Arg(2) + ->Arg(4) + ->Arg(16) + ->Arg(64) + ->Arg(256); + +void BM_BlockingCounter_DecrementCount(benchmark::State& state) { + static absl::BlockingCounter* counter = + new absl::BlockingCounter{std::numeric_limits::max()}; + for (auto _ : state) { + counter->DecrementCount(); + } +} +BENCHMARK(BM_BlockingCounter_DecrementCount) + ->Threads(2) + ->Threads(4) + ->Threads(6) + ->Threads(8) + ->Threads(10) + ->Threads(12) + ->Threads(16) + ->Threads(32) + ->Threads(64) + ->Threads(128); + +void BM_BlockingCounter_Wait(benchmark::State& state) { + int num_threads = state.range(0); + absl::synchronization_internal::ThreadPool pool(num_threads); + for (auto _ : state) { + absl::BlockingCounter counter{num_threads}; + pool.Schedule([num_threads, &counter, &pool]() { + for (int i = 0; i < num_threads; ++i) { + pool.Schedule([&counter]() { counter.DecrementCount(); }); + } + }); + counter.Wait(); + } +} +BENCHMARK(BM_BlockingCounter_Wait) + ->ArgName("threads") + ->Arg(2) + ->Arg(4) + ->Arg(8) + ->Arg(16) + ->Arg(32) + ->Arg(64) + ->Arg(128); + +} // namespace -- cgit v1.2.3 From 079cf662544a14bd1cfaae6d6512645541ba10fb Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 6 May 2021 20:08:01 -0700 Subject: Export of internal Abseil changes -- 51e9291d9bc385082b4061fe760b236ba59c79c3 by Abseil Team : Cast to uint64_t using braces instead of parentheses. PiperOrigin-RevId: 372475909 -- 939fc409855da2639dcaf2d1d4971ca0e0568d03 by Martijn Vels : Expand # flat nodes into size detailed counters. PiperOrigin-RevId: 372474608 -- 54b158c99b32f8a14821ce74fed0f9f836525ce7 by Martijn Vels : Make copies of sampled cords sampled as well This applies to the following methods: - Cord(const Cord&) - operator=(const Cord&) - Subcord(...) PiperOrigin-RevId: 372468160 -- 876c2581ce008871464e8b471efbb967d150f83b by Andy Getzendanner : Document the type of an ABSL_FLAGS.OnUpdate() callback. PiperOrigin-RevId: 372406390 GitOrigin-RevId: 51e9291d9bc385082b4061fe760b236ba59c79c3 Change-Id: Ifb75122cae56b66c28128aee90a63bbb28d93817 --- absl/flags/flag.h | 3 +- absl/strings/charconv.cc | 6 +- absl/strings/cord.cc | 21 ++-- absl/strings/cord.h | 12 +-- absl/strings/cordz_test.cc | 88 ++++++++++++++-- absl/strings/internal/charconv_parse.cc | 2 +- absl/strings/internal/cord_internal.h | 10 ++ absl/strings/internal/cordz_handle_test.cc | 113 +++++++++++---------- absl/strings/internal/cordz_info.cc | 39 +++++-- absl/strings/internal/cordz_info.h | 16 ++- .../strings/internal/cordz_info_statistics_test.cc | 61 +++++++---- absl/strings/internal/cordz_info_test.cc | 27 ++--- absl/strings/internal/cordz_statistics.h | 15 ++- absl/strings/internal/cordz_update_tracker.h | 10 ++ 14 files changed, 295 insertions(+), 128 deletions(-) diff --git a/absl/flags/flag.h b/absl/flags/flag.h index f09580b0..14209e7b 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -265,6 +265,8 @@ ABSL_NAMESPACE_END // // ABSL_FLAG(T, name, default_value, help).OnUpdate(callback); // +// `callback` should be convertible to `void (*)()`. +// // After any setting of the flag value, the callback will be called at least // once. A rapid sequence of changes may be merged together into the same // callback. No concurrent calls to the callback will be made for the same @@ -279,7 +281,6 @@ ABSL_NAMESPACE_END // Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this // comment serves as its API documentation. - // ----------------------------------------------------------------------------- // Implementation details below this section // ----------------------------------------------------------------------------- diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc index b8674c28..fefcfc90 100644 --- a/absl/strings/charconv.cc +++ b/absl/strings/charconv.cc @@ -111,7 +111,7 @@ struct FloatTraits { return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent); #else constexpr uint64_t kMantissaMask = - (uint64_t(1) << (kTargetMantissaBits - 1)) - 1; + (uint64_t{1} << (kTargetMantissaBits - 1)) - 1; uint64_t dbl = static_cast(sign) << 63; if (mantissa > kMantissaMask) { // Normal value. @@ -151,7 +151,7 @@ struct FloatTraits { return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent); #else constexpr uint32_t kMantissaMask = - (uint32_t(1) << (kTargetMantissaBits - 1)) - 1; + (uint32_t{1} << (kTargetMantissaBits - 1)) - 1; uint32_t flt = static_cast(sign) << 31; if (mantissa > kMantissaMask) { // Normal value. @@ -499,7 +499,7 @@ bool MustRoundUp(uint64_t guess_mantissa, int guess_exponent, template CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) { CalculatedFloat result; - if (mantissa == uint64_t(1) << FloatTraits::kTargetMantissaBits) { + if (mantissa == uint64_t{1} << FloatTraits::kTargetMantissaBits) { mantissa >>= 1; exponent += 1; } diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 1c2ff9f2..238532f9 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -531,18 +531,19 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { assert(&src != this); assert(is_tree() || src.is_tree()); auto constexpr method = CordzUpdateTracker::kAssignCord; - if (CordRep* tree = this->tree()) { - CordzUpdateScope scope(data_.cordz_info(), method); - CordRep::Unref(tree); - if (CordRep* src_tree = src.tree()) { - SetTree(CordRep::Ref(src_tree), scope); - } else { - scope.SetCordRep(nullptr); - data_ = src.data_; - } + if (ABSL_PREDICT_TRUE(!is_tree())) { + EmplaceTree(CordRep::Ref(src.as_tree()), src.data_, method); + return; + } + CordRep* tree = as_tree(); + if (CordRep* src_tree = src.tree()) { + data_.set_tree(CordRep::Ref(src_tree)); + CordzInfo::MaybeTrackCord(data_, src.data_, method); } else { - EmplaceTree(CordRep::Ref(src.as_tree()), method); + CordzInfo::MaybeUntrackCord(data_.cordz_info()); + data_ = src.data_; } + CordRep::Unref(tree); } void Cord::InlineRep::UnrefTree() { diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 5c80a5cb..e758f1cd 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -1000,12 +1000,12 @@ constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data) : data_(data) {} inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) - : data_(src.data_) { - if (is_tree()) { - data_.clear_cordz_info(); - absl::cord_internal::CordRep::Ref(as_tree()); - CordzInfo::MaybeTrackCord(data_, src.data_, - CordzUpdateTracker::kConstructorCord); + : data_(InlineData::kDefaultInit) { + if (CordRep* tree = src.tree()) { + EmplaceTree(CordRep::Ref(tree), src.data_, + CordzUpdateTracker::kConstructorCord); + } else { + data_ = src.data_; } } diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc index aeb3d13f..0e11f5c8 100644 --- a/absl/strings/cordz_test.cc +++ b/absl/strings/cordz_test.cc @@ -67,6 +67,13 @@ absl::string_view MakeString(TestCordSize size) { return MakeString(Length(size)); } +// Returns a cord with a sampled method of kAppendString. +absl::Cord MakeAppendStringCord(TestCordSize size) { + absl::Cord cord; + cord.Append(MakeString(size)); + return cord; +} + std::string TestParamToString(::testing::TestParamInfo size) { return absl::StrCat("On", ToString(size.param), "Cord"); } @@ -136,6 +143,16 @@ TEST(CordzTest, CopyConstruct) { EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); } +TEST(CordzTest, CopyConstructFromSampled) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src(MakeString(TestCordSize::kLarge)); + Cord cord(src); + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kConstructorString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(1)); +} + TEST(CordzTest, MoveConstruct) { CordzSamplingIntervalHelper sample_every{1}; Cord src(MakeString(TestCordSize::kLarge)); @@ -146,7 +163,43 @@ TEST(CordzTest, MoveConstruct) { TEST_P(CordzUpdateTest, AssignCord) { Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); cord() = src; - EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAssignCord))); + EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord())->GetCordzStatistics(); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST_P(CordzUpdateTest, AssignSampledCord) { + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + cord() = src; + ASSERT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord())->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzUpdateTest, AssignSampledCordToUnsampledCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzUpdateTest, AssignSampledCordToSampledCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord(MakeString(TestCordSize::kLarge)); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); } TEST(CordzTest, AssignInlinedCord) { @@ -160,7 +213,7 @@ TEST(CordzTest, AssignInlinedCord) { EXPECT_FALSE(CordzInfoIsListed(info)); } -TEST(CordzTest, MoveAssignCord) { +TEST(CordzUpdateTest, MoveAssignCord) { CordzSamplingIntervalHelper sample_every{1}; Cord cord; Cord src(MakeString(TestCordSize::kLarge)); @@ -168,6 +221,11 @@ TEST(CordzTest, MoveAssignCord) { EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } +TEST_P(CordzUpdateTest, AssignLargeArray) { + cord() = MakeString(TestCordSize::kSmall); + EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString)); +} + TEST_P(CordzUpdateTest, AssignSmallArray) { cord() = MakeString(TestCordSize::kSmall); EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString)); @@ -333,16 +391,26 @@ TEST(CordzTest, RemoveSuffix) { TEST(CordzTest, SubCord) { CordzSamplingIntervalHelper sample_every{1}; - Cord src(MakeString(TestCordSize::kLarge)); - - Cord cord1 = src.Subcord(10, src.size() / 2); - EXPECT_THAT(cord1, HasValidCordzInfoOf(Method::kSubCord)); + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + Cord cord = src.Subcord(10, src.size() / 2); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); +} - Cord cord2 = src.Subcord(10, kMaxInline + 1); - EXPECT_THAT(cord2, HasValidCordzInfoOf(Method::kSubCord)); +TEST(CordzTest, SmallSubCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + Cord cord = src.Subcord(10, kMaxInline + 1); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); +} - Cord cord3 = src.Subcord(10, kMaxInline); - EXPECT_THAT(GetCordzInfoForTesting(cord3), Eq(nullptr)); +TEST(CordzTest, SubCordFromSampledCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src(MakeString(TestCordSize::kLarge)); + Cord cord = src.Subcord(10, src.size() / 2); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kConstructorString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(1)); } } // namespace diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc index 8b11868c..d29acaf4 100644 --- a/absl/strings/internal/charconv_parse.cc +++ b/absl/strings/internal/charconv_parse.cc @@ -52,7 +52,7 @@ static_assert(std::numeric_limits::digits == 53, "IEEE double fact"); // The lowest valued 19-digit decimal mantissa we can read still contains // sufficient information to reconstruct a binary mantissa. -static_assert(1000000000000000000u > (uint64_t(1) << (53 + 3)), "(b) above"); +static_assert(1000000000000000000u > (uint64_t{1} << (53 + 3)), "(b) above"); // ParseFloat<16> will read the first 15 significant digits of the mantissa. // diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index c7ae0104..813b3f35 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -366,6 +366,16 @@ class InlineData { return as_tree_.cordz_info != kNullCordzInfo; } + // Returns true if either of the provided instances hold a cordz_info value. + // This method is more efficient than the equivalent `data1.is_profiled() || + // data2.is_profiled()`. Requires both arguments to hold a tree. + static bool is_either_profiled(const InlineData& data1, + const InlineData& data2) { + assert(data1.is_tree() && data2.is_tree()); + return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) != + kNullCordzInfo; + } + // Returns the cordz_info sampling instance for this instance, or nullptr // if the current instance is not sampled and does not have CordzInfo data. // Requires the current instance to hold a tree value. diff --git a/absl/strings/internal/cordz_handle_test.cc b/absl/strings/internal/cordz_handle_test.cc index 4eefd72c..fd68e06b 100644 --- a/absl/strings/internal/cordz_handle_test.cc +++ b/absl/strings/internal/cordz_handle_test.cc @@ -191,65 +191,72 @@ TEST(CordzHandleTest, MultiThreaded) { // manipulating a CordzHandle that might be operated upon in another thread. std::vector> handles(kNumHandles); - absl::synchronization_internal::ThreadPool pool(kNumThreads); - - for (int i = 0; i < kNumThreads; ++i) { - pool.Schedule([&stop, &handles]() { - std::minstd_rand gen; - std::uniform_int_distribution dist_type(0, 2); - std::uniform_int_distribution dist_handle(0, kNumHandles - 1); - size_t max_safe_to_inspect = 0; - while (!stop.HasBeenNotified()) { - CordzHandle* handle; - switch (dist_type(gen)) { - case 0: - handle = new CordzHandle(); - break; - case 1: - handle = new CordzSnapshot(); - break; - default: - handle = nullptr; - break; - } - CordzHandle* old_handle = handles[dist_handle(gen)].exchange(handle); - if (old_handle != nullptr) { - std::vector safe_to_inspect = - old_handle->DiagnosticsGetSafeToInspectDeletedHandles(); - for (const CordzHandle* handle : safe_to_inspect) { - // We're in a tight loop, so don't generate too many error messages. - ASSERT_FALSE(handle->is_snapshot()); + // global bool which is set when any thread did get some 'safe to inspect' + // handles. On some platforms and OSS tests, we might risk that some pool + // threads are starved, stalled, or just got a few unlikely random 'handle' + // coin tosses, so we satisfy this test with simply observing 'some' thread + // did something meaningful, which should minimize the potential for flakes. + std::atomic found_safe_to_inspect(false); + + { + absl::synchronization_internal::ThreadPool pool(kNumThreads); + for (int i = 0; i < kNumThreads; ++i) { + pool.Schedule([&stop, &handles, &found_safe_to_inspect]() { + std::minstd_rand gen; + std::uniform_int_distribution dist_type(0, 2); + std::uniform_int_distribution dist_handle(0, kNumHandles - 1); + + while (!stop.HasBeenNotified()) { + CordzHandle* handle; + switch (dist_type(gen)) { + case 0: + handle = new CordzHandle(); + break; + case 1: + handle = new CordzSnapshot(); + break; + default: + handle = nullptr; + break; } - if (safe_to_inspect.size() > max_safe_to_inspect) { - max_safe_to_inspect = safe_to_inspect.size(); + CordzHandle* old_handle = handles[dist_handle(gen)].exchange(handle); + if (old_handle != nullptr) { + std::vector safe_to_inspect = + old_handle->DiagnosticsGetSafeToInspectDeletedHandles(); + for (const CordzHandle* handle : safe_to_inspect) { + // We're in a tight loop, so don't generate too many error + // messages. + ASSERT_FALSE(handle->is_snapshot()); + } + if (!safe_to_inspect.empty()) { + found_safe_to_inspect.store(true); + } + CordzHandle::Delete(old_handle); } - CordzHandle::Delete(old_handle); } - } - - // Confirm that the test did *something*. This check will be satisfied as - // long as this thread has delete a CordzSnapshot object and a - // non-snapshot CordzHandle was deleted after the CordzSnapshot was - // created. This max_safe_to_inspect count will often reach around 30 - // (assuming 4 threads and 10 slots for live handles). Don't use a strict - // bound to avoid making this test flaky. - EXPECT_THAT(max_safe_to_inspect, Gt(0)); - - // Have each thread attempt to clean up everything. Some thread will be - // the last to reach this cleanup code, and it will be guaranteed to clean - // up everything because nothing remains to create new handles. - for (auto& h : handles) { - if (CordzHandle* handle = h.exchange(nullptr)) { - CordzHandle::Delete(handle); + + // Have each thread attempt to clean up everything. Some thread will be + // the last to reach this cleanup code, and it will be guaranteed to + // clean up everything because nothing remains to create new handles. + for (auto& h : handles) { + if (CordzHandle* handle = h.exchange(nullptr)) { + CordzHandle::Delete(handle); + } } - } - }); + }); + } + + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); } - // The threads will hammer away. Give it a little bit of time for tsan to - // spot errors. - absl::SleepFor(absl::Seconds(3)); - stop.Notify(); + // Confirm that the test did *something*. This check will be satisfied as + // long as any thread has deleted a CordzSnapshot object and a non-snapshot + // CordzHandle was deleted after the CordzSnapshot was created. + // See also comments on `found_safe_to_inspect` + EXPECT_TRUE(found_safe_to_inspect.load()); } } // namespace diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index e1fa6b6b..a6b045ff 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -135,6 +135,23 @@ class CordRepAnalyzer { return (rep != nullptr && rep->tag == CONCAT) ? repref : RepRef{nullptr, 0}; } + // Counts a flat of the provide allocated size + void CountFlat(size_t size) { + statistics_.node_count++; + statistics_.node_counts.flat++; + if (size <= 64) { + statistics_.node_counts.flat_64++; + } else if (size <= 128) { + statistics_.node_counts.flat_128++; + } else if (size <= 256) { + statistics_.node_counts.flat_256++; + } else if (size <= 512) { + statistics_.node_counts.flat_512++; + } else if (size <= 1024) { + statistics_.node_counts.flat_1k++; + } + } + // Processes 'linear' reps (substring, flat, external) not requiring iteration // or recursion. Returns RefRep{null} if all reps were processed, else returns // the top-most non-linear concat or ring cordrep. @@ -152,9 +169,9 @@ class CordRepAnalyzer { // Consume possible FLAT if (rep.rep->tag >= FLAT) { - statistics_.node_count++; - statistics_.node_counts.flat++; - memory_usage.Add(rep.rep->flat()->AllocatedSize(), rep.refcount); + size_t size = rep.rep->flat()->AllocatedSize(); + CountFlat(size); + memory_usage.Add(size, rep.refcount); return RepRef{nullptr, 0}; } @@ -263,9 +280,15 @@ void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) { void CordzInfo::TrackCord(InlineData& cord, const InlineData& src, MethodIdentifier method) { assert(cord.is_tree()); - assert(!cord.is_profiled()); - auto* info = src.is_tree() && src.is_profiled() ? src.cordz_info() : nullptr; - CordzInfo* cordz_info = new CordzInfo(cord.as_tree(), info, method); + assert(src.is_tree()); + + // Unsample current as we the current cord is being replaced with 'src', + // so any method history is no longer relevant. + CordzInfo* cordz_info = cord.cordz_info(); + if (cordz_info != nullptr) cordz_info->Untrack(); + + // Start new cord sample + cordz_info = new CordzInfo(cord.as_tree(), src.cordz_info(), method); cord.set_cordz_info(cordz_info); cordz_info->Track(); } @@ -297,6 +320,10 @@ CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, parent_method_(GetParentMethod(src)), create_time_(absl::Now()) { update_tracker_.LossyAdd(method); + if (src) { + // Copy parent counters. + update_tracker_.LossyAdd(src->update_tracker_); + } } CordzInfo::~CordzInfo() { diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index e84006c5..29237930 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -65,7 +65,9 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { // Identical to TrackCord(), except that this function fills the // `parent_stack` and `parent_method` properties of the returned CordzInfo // instance from the provided `src` instance if `src` is sampled. - // This function should be used for sampling 'copy constructed' cords. + // This function should be used for sampling 'copy constructed' and 'copy + // assigned' cords. This function allows 'cord` to be already sampled, in + // which case the CordzInfo will be newly created from `src`. static void TrackCord(InlineData& cord, const InlineData& src, MethodIdentifier method); @@ -73,6 +75,15 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { // Uses `cordz_should_profile` to randomly pick cords to be sampled, and if // so, invokes `TrackCord` to start sampling `cord`. static void MaybeTrackCord(InlineData& cord, MethodIdentifier method); + + // Maybe sample the cord identified by 'cord' for method 'method'. + // `src` identifies a 'parent' cord which content is copied into the current + // cord, typically the input cord for an assign emthod or copy constructor. + // Invokes the corresponding `TrackCord` method if either cord is sampled, or + // if `cord` is randomly picked for sampling. Possible scenarios: + // * `src` is sampled: `cord` will be set to sampled if not already sampled. + // Parent stack and update stats of `src` are copied into `cord` + // * `src` is not sampled: `cord` may be randomly picked for sampling. static void MaybeTrackCord(InlineData& cord, const InlineData& src, MethodIdentifier method); @@ -221,7 +232,8 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( InlineData& cord, const InlineData& src, MethodIdentifier method) { - if (ABSL_PREDICT_FALSE(cordz_should_profile())) { + if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src)) || + ABSL_PREDICT_FALSE(cordz_should_profile())) { TrackCord(cord, src, method); } } diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc index d46d6d62..9f2842d9 100644 --- a/absl/strings/internal/cordz_info_statistics_test.cc +++ b/absl/strings/internal/cordz_info_statistics_test.cc @@ -42,10 +42,18 @@ inline void PrintTo(const CordzStatistics& stats, std::ostream* s) { namespace { -// Creates a flat of the specified length -CordRepFlat* Flat(int length = 512) { - auto* flat = CordRepFlat::New(length); - flat->length = length; +// Creates a flat of the specified allocated size +CordRepFlat* Flat(size_t size) { + // Round up to a tag size, as we are going to poke an exact tag size back into + // the allocated flat. 'size returning allocators' could grant us more than we + // wanted, but we are ok to poke the 'requested' size in the tag, even in the + // presence of sized deletes, so we need to make sure the size rounds + // perfectly to a tag value. + assert(size >= kMinFlatSize); + size = RoundUpForTag(size); + CordRepFlat* flat = CordRepFlat::New(size - kFlatOverhead); + flat->tag = AllocatedSizeToTag(size); + flat->length = size - kFlatOverhead; return flat; } @@ -174,6 +182,11 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { STATS_MATCHER_EXPECT_EQ(size); STATS_MATCHER_EXPECT_EQ(node_count); STATS_MATCHER_EXPECT_EQ(node_counts.flat); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_64); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_128); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_256); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_512); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_1k); STATS_MATCHER_EXPECT_EQ(node_counts.external); STATS_MATCHER_EXPECT_EQ(node_counts.concat); STATS_MATCHER_EXPECT_EQ(node_counts.substring); @@ -188,7 +201,7 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { TEST(CordzInfoStatisticsTest, Flat) { RefHelper ref; - auto* flat = ref.NeedsUnref(Flat()); + auto* flat = ref.NeedsUnref(Flat(512)); CordzStatistics expected; expected.size = flat->length; @@ -196,13 +209,14 @@ TEST(CordzInfoStatisticsTest, Flat) { expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.node_count = 1; expected.node_counts.flat = 1; + expected.node_counts.flat_512 = 1; EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); } TEST(CordzInfoStatisticsTest, SharedFlat) { RefHelper ref; - auto* flat = ref.Ref(ref.NeedsUnref(Flat())); + auto* flat = ref.Ref(ref.NeedsUnref(Flat(64))); CordzStatistics expected; expected.size = flat->length; @@ -210,6 +224,7 @@ TEST(CordzInfoStatisticsTest, SharedFlat) { expected.estimated_fair_share_memory_usage = SizeOf(flat) / 2; expected.node_count = 1; expected.node_counts.flat = 1; + expected.node_counts.flat_64 = 1; EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); } @@ -244,7 +259,7 @@ TEST(CordzInfoStatisticsTest, SharedExternal) { TEST(CordzInfoStatisticsTest, Substring) { RefHelper ref; - auto* flat = Flat(); + auto* flat = Flat(1024); auto* substring = ref.NeedsUnref(Substring(flat)); CordzStatistics expected; @@ -253,6 +268,7 @@ TEST(CordzInfoStatisticsTest, Substring) { expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.node_count = 2; expected.node_counts.flat = 1; + expected.node_counts.flat_1k = 1; expected.node_counts.substring = 1; EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); @@ -260,7 +276,7 @@ TEST(CordzInfoStatisticsTest, Substring) { TEST(CordzInfoStatisticsTest, SharedSubstring) { RefHelper ref; - auto* flat = ref.Ref(Flat(), 2); + auto* flat = ref.Ref(Flat(511), 2); auto* substring = ref.Ref(ref.NeedsUnref(Substring(flat))); CordzStatistics expected; @@ -270,6 +286,7 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) { SizeOf(substring) / 2 + SizeOf(flat) / 6; expected.node_count = 2; expected.node_counts.flat = 1; + expected.node_counts.flat_512 = 1; expected.node_counts.substring = 1; EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); @@ -277,7 +294,7 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) { TEST(CordzInfoStatisticsTest, Concat) { RefHelper ref; - auto* flat1 = Flat(20); + auto* flat1 = Flat(300); auto* flat2 = Flat(2000); auto* concat = ref.NeedsUnref(Concat(flat1, flat2)); @@ -288,6 +305,7 @@ TEST(CordzInfoStatisticsTest, Concat) { expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.node_count = 3; expected.node_counts.flat = 2; + expected.node_counts.flat_512 = 1; expected.node_counts.concat = 1; EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); @@ -295,9 +313,9 @@ TEST(CordzInfoStatisticsTest, Concat) { TEST(CordzInfoStatisticsTest, DeepConcat) { RefHelper ref; - auto* flat1 = Flat(20); + auto* flat1 = Flat(300); auto* flat2 = Flat(2000); - auto* flat3 = Flat(30); + auto* flat3 = Flat(400); auto* external = External(3000); auto* substring = Substring(external); auto* concat1 = Concat(flat1, flat2); @@ -313,6 +331,7 @@ TEST(CordzInfoStatisticsTest, DeepConcat) { expected.node_count = 8; expected.node_counts.flat = 3; + expected.node_counts.flat_512 = 2; expected.node_counts.external = 1; expected.node_counts.concat = 3; expected.node_counts.substring = 1; @@ -322,9 +341,9 @@ TEST(CordzInfoStatisticsTest, DeepConcat) { TEST(CordzInfoStatisticsTest, DeepSharedConcat) { RefHelper ref; - auto* flat1 = Flat(20); + auto* flat1 = Flat(40); auto* flat2 = ref.Ref(Flat(2000), 4); - auto* flat3 = Flat(30); + auto* flat3 = Flat(70); auto* external = ref.Ref(External(3000)); auto* substring = ref.Ref(Substring(external), 3); auto* concat1 = Concat(flat1, flat2); @@ -339,6 +358,8 @@ TEST(CordzInfoStatisticsTest, DeepSharedConcat) { expected.estimated_fair_share_memory_usage = FairShare(concat); expected.node_count = 8; expected.node_counts.flat = 3; + expected.node_counts.flat_64 = 1; + expected.node_counts.flat_128 = 1; expected.node_counts.external = 1; expected.node_counts.concat = 3; expected.node_counts.substring = 1; @@ -348,9 +369,9 @@ TEST(CordzInfoStatisticsTest, DeepSharedConcat) { TEST(CordzInfoStatisticsTest, Ring) { RefHelper ref; - auto* flat1 = Flat(20); + auto* flat1 = Flat(240); auto* flat2 = Flat(2000); - auto* flat3 = Flat(30); + auto* flat3 = Flat(70); auto* external = External(3000); CordRepRing* ring = CordRepRing::Create(flat1); ring = CordRepRing::Append(ring, flat2); @@ -365,6 +386,8 @@ TEST(CordzInfoStatisticsTest, Ring) { expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; expected.node_count = 5; expected.node_counts.flat = 3; + expected.node_counts.flat_128 = 1; + expected.node_counts.flat_256 = 1; expected.node_counts.external = 1; expected.node_counts.ring = 1; @@ -373,9 +396,9 @@ TEST(CordzInfoStatisticsTest, Ring) { TEST(CordzInfoStatisticsTest, SharedSubstringRing) { RefHelper ref; - auto* flat1 = ref.Ref(Flat(20)); + auto* flat1 = ref.Ref(Flat(240)); auto* flat2 = Flat(200); - auto* flat3 = Flat(30); + auto* flat3 = Flat(70); auto* external = ref.Ref(External(3000), 5); CordRepRing* ring = CordRepRing::Create(flat1); ring = CordRepRing::Append(ring, flat2); @@ -392,6 +415,8 @@ TEST(CordzInfoStatisticsTest, SharedSubstringRing) { expected.estimated_fair_share_memory_usage = FairShare(substring); expected.node_count = 6; expected.node_counts.flat = 3; + expected.node_counts.flat_128 = 1; + expected.node_counts.flat_256 = 2; expected.node_counts.external = 1; expected.node_counts.ring = 1; expected.node_counts.substring = 1; @@ -447,7 +472,7 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) { cord.set_inline_size(0); } else { // 50/50 Ring or Flat coin toss - CordRep* rep = Flat(); + CordRep* rep = Flat(256); rep = (coin_toss(gen) != 0) ? CordRepRing::Create(rep) : rep; cord.make_tree(rep); diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index b7eb910d..59a8c525 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -74,6 +74,20 @@ TEST(CordzInfoTest, TrackCord) { info->Untrack(); } +TEST(CordzInfoTest, MaybeTrackCordOnSampledCord) { + TestCordData data1; + CordzInfo::TrackCord(data1.data, kTrackCordMethod); + CordzInfo* info1 = data1.data.cordz_info(); + TestCordData data2; + CordzInfo::MaybeTrackCord(data2.data, data1.data, kTrackCordMethod); + CordzInfo* info2 = data2.data.cordz_info(); + ASSERT_THAT(info2, Ne(nullptr)); + EXPECT_THAT(info2->GetCordRepForTesting(), Eq(data2.rep.rep)); + info2->Untrack(); + info1->Untrack(); +} + + TEST(CordzInfoTest, UntrackCord) { TestCordData data; CordzInfo::TrackCord(data.data, kTrackCordMethod); @@ -291,19 +305,6 @@ TEST(CordzInfoTest, FromParent) { info_child->Untrack(); } -TEST(CordzInfoTest, FromParentInlined) { - InlineData parent; - TestCordData child; - CordzInfo* info = TrackChildCord(child.data, parent); - EXPECT_TRUE(info->GetParentStack().empty()); - CordzStatistics statistics = info->GetCordzStatistics(); - EXPECT_THAT(statistics.size, Eq(child.rep.rep->length)); - EXPECT_THAT(statistics.method, Eq(kChildMethod)); - EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); - EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); - info->Untrack(); -} - } // namespace } // namespace cord_internal ABSL_NAMESPACE_END diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h index 1c93dfd0..e03c651e 100644 --- a/absl/strings/internal/cordz_statistics.h +++ b/absl/strings/internal/cordz_statistics.h @@ -30,11 +30,16 @@ struct CordzStatistics { // Node counts information struct NodeCounts { - size_t flat = 0; - size_t external = 0; - size_t substring = 0; - size_t concat = 0; - size_t ring = 0; + size_t flat = 0; // #flats + size_t flat_64 = 0; // #flats up to 64 bytes + size_t flat_128 = 0; // #flats up to 128 bytes + size_t flat_256 = 0; // #flats up to 256 bytes + size_t flat_512 = 0; // #flats up to 512 bytes + size_t flat_1k = 0; // #flats up to 1K bytes + size_t external = 0; // #external reps + size_t substring = 0; // #substring reps + size_t concat = 0; // #concat reps + size_t ring = 0; // #ring buffer reps }; // The size of the cord in bytes. This matches the result of Cord::size(). diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index d5b14067..02efcc3a 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -91,6 +91,16 @@ class CordzUpdateTracker { std::memory_order_relaxed); } + // Adds all the values from `src` to this instance + void LossyAdd(const CordzUpdateTracker& src) { + for (int i = 0; i < kNumMethods; ++i) { + MethodIdentifier method = static_cast(i); + if (int64_t value = src.Value(method)) { + LossyAdd(method, value); + } + } + } + private: // Until C++20 std::atomic is not constexpr default-constructible, so we need // a wrapper for this class to be constexpr constructible. -- cgit v1.2.3 From 7ba826e50dff1878e6ecc6b9af44097c040c8968 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 10 May 2021 12:57:06 -0700 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- 5f3c139695d5c497ca030e95a607537a7be7caa7 by Benjamin Barenblat : Don’t examine irrelevant destination buckets in DiscreteDistributionTest Abseil generates discrete distributions using Walker’s aliasing algorithm. This creates uniformly distributed buckets, each with a probability of sending traffic to a different bucket. Abseil represents a bucket as a pair (probability of retaining traffic × alternate bucket if traffic is passed) and a distribution as a vector of such pairs. For example, {(0.3, 1), (1.0, 1)} represents a distribution with two buckets, the zeroth of which passes 70% of its traffic to bucket 1 and the first of which holds on to all its traffic. This representation is not unique: When a bucket retains traffic with probability 1, the alternate bucket is irrelevant. Continuing the example above, {(0.3, 1), (1.0, 0)} _also_ represents a two-bucket distribution where the zeroth bucket passes 70% of its traffic to the first and the first hangs on to all traffic. Exactly what representation Abseil generates for a given input is related to how much precision is used in intermediate floating-point operations, which is an architectural implementation detail. Remove sensitivity to that detail by not examining the alternate bucket when the retention probability is 1.0. PiperOrigin-RevId: 372993410 -- 062ac80699f748831c09a061538abffec2cdea5c by Martijn Vels : Avoid alredy sampled cord remaining sampled if not picked or source is sampled PiperOrigin-RevId: 372985990 -- a9f3537e1110b7bb6450fd72a03f0c5dc6b8c89b by Evan Brown : Add tests for function pointer comparators, comparators that have SFINAE-visible comparison operators that are unimplemented, and for implicit construction from unadapted comparators. PiperOrigin-RevId: 372927616 GitOrigin-RevId: 5f3c139695d5c497ca030e95a607537a7be7caa7 Change-Id: I996a8452e7bd88f9dd2e59633b01bbc09f42620d --- absl/container/btree_test.cc | 41 +++++++++++++++++++++++++++++++ absl/random/discrete_distribution_test.cc | 7 +++--- absl/strings/cord.cc | 17 +++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index 74337df2..464dabac 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -2893,6 +2893,47 @@ TEST(Btree, AllocMoveConstructor_DifferentAlloc) { EXPECT_EQ(bytes_used2, original_bytes_used); } +bool IntCmp(const int a, const int b) { return a < b; } + +TEST(Btree, SupportsFunctionPtrComparator) { + absl::btree_set set(IntCmp); + set.insert({1, 2, 3}); + EXPECT_THAT(set, ElementsAre(1, 2, 3)); + EXPECT_TRUE(set.key_comp()(1, 2)); + EXPECT_TRUE(set.value_comp()(1, 2)); + + absl::btree_map map(&IntCmp); + map[1] = 1; + EXPECT_THAT(map, ElementsAre(Pair(1, 1))); + EXPECT_TRUE(map.key_comp()(1, 2)); + // TODO(ezb): support value_comp() in this case and uncomment. + // EXPECT_TRUE(map.value_comp()(std::make_pair(1, 1), std::make_pair(2, 2))); +} + +template +struct TransparentPassThroughComp { + using is_transparent = void; + + // This will fail compilation if we attempt a comparison that Compare does not + // support, and the failure will happen inside the function implementation so + // it can't be avoided by using SFINAE on this comparator. + template + bool operator()(const T &lhs, const U &rhs) const { + return Compare()(lhs, rhs); + } +}; + +TEST(Btree, + SupportsTransparentComparatorThatDoesNotImplementAllVisibleOperators) { + absl::btree_set> set; + set.insert(MultiKey{1, 2}); + EXPECT_TRUE(set.contains(1)); +} + +TEST(Btree, ConstructImplicitlyWithUnadaptedComparator) { + absl::btree_set set = {{}, MultiKeyComp{}}; +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/random/discrete_distribution_test.cc b/absl/random/discrete_distribution_test.cc index 6d007006..415b14cc 100644 --- a/absl/random/discrete_distribution_test.cc +++ b/absl/random/discrete_distribution_test.cc @@ -99,6 +99,7 @@ TYPED_TEST(DiscreteDistributionTypeTest, Constructor) { } TEST(DiscreteDistributionTest, InitDiscreteDistribution) { + using testing::_; using testing::Pair; { @@ -111,8 +112,8 @@ TEST(DiscreteDistributionTest, InitDiscreteDistribution) { // Each bucket is p=1/3, so bucket 0 will send half it's traffic // to bucket 2, while the rest will retain all of their traffic. EXPECT_THAT(q, testing::ElementsAre(Pair(0.5, 2), // - Pair(1.0, 1), // - Pair(1.0, 2))); + Pair(1.0, _), // + Pair(1.0, _))); } { @@ -135,7 +136,7 @@ TEST(DiscreteDistributionTest, InitDiscreteDistribution) { EXPECT_THAT(q, testing::ElementsAre(Pair(b0, 3), // Pair(b1, 3), // - Pair(1.0, 2), // + Pair(1.0, _), // Pair(b3, 2), // Pair(b1, 3))); } diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 238532f9..5dad781e 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -535,6 +535,23 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { EmplaceTree(CordRep::Ref(src.as_tree()), src.data_, method); return; } + + // See b/187581164: unsample cord if already sampled + // TODO(b/117940323): continuously 'assigned to' cords would reach 100% + // sampling probability. Imagine a cord x in some cache: + // cache.SetCord(const Cord& foo) { + // x = foo; + // } + // CordzInfo::MaybeTrackCord does: + // x.profiled = foo.profiled | x.profiled | random(cordz_mean_interval) + // Which means it will on the long run converge to 'always samples' + // The real fix is in CordzMaybeTrackCord, but the below is a low risk + // forward fix for b/187581164 and similar BT benchmark regressions. + if (ABSL_PREDICT_FALSE(is_profiled())) { + cordz_info()->Untrack(); + clear_cordz_info(); + } + CordRep* tree = as_tree(); if (CordRep* src_tree = src.tree()) { data_.set_tree(CordRep::Ref(src_tree)); -- cgit v1.2.3 From ce42de10fbea616379826e91c7c23c16bffe6e61 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 12 May 2021 17:01:06 -0700 Subject: Export of internal Abseil changes -- 9fc37c11b9e46287acef00ee06ed9adcba54dd13 by Greg Falcon : Rename absl::hash_internal::HashState to absl::hash_internal::MixingHashState. Before this change, we had two classes named HashState: absl::HashState, the public API used for type erasure, and absl::hash_internal::HashState, the internal concrete implementation ordinarily used. The internal class used to be named `CityHashState`, but we renamed it to `HashState` it when we changed underlying hash implementation to wyhash. This inadvertent naming conflict made the code much harder to read, and this change intends to undo that. PiperOrigin-RevId: 373481959 -- 4aec55ffddebd085c239352a2e20721091f719a1 by Greg Falcon : Introduce absl::HashOf(), a convenience wrapper around absl::Hash that calculates hashes from the values of its arguments. PiperOrigin-RevId: 373461406 -- 86b5fd8db50bbc8bd0aa9258523527381fe0445d by Abseil Team : Improve speed of BlockingCounter by making its most common path lock free. With the new implementation, the fast path of BlockingCounter::DecrementCount() is only a fetch_sub operation. This is most times much more efficient than the previous implementation (full mutex lock/unlock). As a matter of fact, in most actual usecases in practice, the waiter thread is already waiting on the Wait() call when DecrementCount() is called, which makes Mutex::Unlock() take the slow path as there's a waiter thread that it might need to wake up. PiperOrigin-RevId: 373394164 -- 65c876be5eac0cd32583ff8535ede4109d39cf3f by Martijn Vels : Move the 'sample copied cord' logic into MaybeTrackCord(), This changes move the logic for selecting if a cord should remain being sampled from Cord to CordzInfo::MaybeTrackCord, and updates the documentation for the latter method. PiperOrigin-RevId: 373363168 -- e84410bd0aada293a81dfb82656c952e209e21fb by Martijn Vels : Add check for the first call to cordz_should_profile() for each thread. This prevents the first cord of a newly created thread to be always sampled, which is a 'bad' kind of determinism for sampling. PiperOrigin-RevId: 373229768 -- bf09c589dc099ac8f4af780bf7e609c53c27574c by Samuel Benzaquen : Refactor the Flags structure into an enum. This gives us more control over the representation and allows for easier merging during parsing. PiperOrigin-RevId: 373163038 -- b947b0c51083b7b6508284b5d31819596c91729e by Derek Mauro : Fixes warnings about shadowed variables Fixes #956 PiperOrigin-RevId: 373158133 GitOrigin-RevId: 9fc37c11b9e46287acef00ee06ed9adcba54dd13 Change-Id: I91f35699f9bf439d1a870c6493946a310afe088c --- absl/copts/GENERATED_AbseilCopts.cmake | 2 +- absl/copts/GENERATED_copts.bzl | 2 +- absl/copts/copts.py | 2 +- absl/hash/hash.h | 22 ++++ absl/hash/hash_test.cc | 22 ++++ absl/hash/internal/hash.cc | 14 ++- absl/hash/internal/hash.h | 46 ++++---- absl/status/internal/status_internal.h | 10 +- absl/strings/cord.cc | 18 +--- absl/strings/cordz_test.cc | 99 ++++++++++++----- absl/strings/internal/cord_rep_ring.cc | 83 ++++++--------- absl/strings/internal/cord_rep_ring.h | 37 +++---- absl/strings/internal/cordz_functions.cc | 10 +- absl/strings/internal/cordz_functions_test.cc | 18 ++++ absl/strings/internal/cordz_info.cc | 10 ++ absl/strings/internal/cordz_info.h | 43 ++++++-- absl/strings/internal/cordz_info_test.cc | 52 +++++++-- absl/strings/internal/str_format/bind.cc | 7 +- absl/strings/internal/str_format/extension.cc | 12 +-- absl/strings/internal/str_format/extension.h | 54 ++++++---- absl/strings/internal/str_format/parser.cc | 135 +++++++++++------------- absl/strings/internal/str_format/parser.h | 40 ++++--- absl/strings/internal/str_format/parser_test.cc | 23 ++-- absl/synchronization/blocking_counter.cc | 40 ++++--- absl/synchronization/blocking_counter.h | 8 +- absl/synchronization/blocking_counter_test.cc | 12 +++ 26 files changed, 501 insertions(+), 320 deletions(-) diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake index 18a1e5c3..22a25eba 100644 --- a/absl/copts/GENERATED_AbseilCopts.cmake +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -77,7 +77,7 @@ list(APPEND ABSL_LLVM_FLAGS "-Woverlength-strings" "-Wpointer-arith" "-Wself-assign" - "-Wshadow" + "-Wshadow-all" "-Wstring-conversion" "-Wtautological-overlap-compare" "-Wundef" diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl index d2bd5608..0c7a91bd 100644 --- a/absl/copts/GENERATED_copts.bzl +++ b/absl/copts/GENERATED_copts.bzl @@ -78,7 +78,7 @@ ABSL_LLVM_FLAGS = [ "-Woverlength-strings", "-Wpointer-arith", "-Wself-assign", - "-Wshadow", + "-Wshadow-all", "-Wstring-conversion", "-Wtautological-overlap-compare", "-Wundef", diff --git a/absl/copts/copts.py b/absl/copts/copts.py index ce30df89..7268c680 100644 --- a/absl/copts/copts.py +++ b/absl/copts/copts.py @@ -93,7 +93,7 @@ COPT_VARS = { "-Woverlength-strings", "-Wpointer-arith", "-Wself-assign", - "-Wshadow", + "-Wshadow-all", "-Wstring-conversion", "-Wtautological-overlap-compare", "-Wundef", diff --git a/absl/hash/hash.h b/absl/hash/hash.h index 5de132ca..69c44fde 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h @@ -73,6 +73,8 @@ #ifndef ABSL_HASH_HASH_H_ #define ABSL_HASH_HASH_H_ +#include + #include "absl/hash/internal/hash.h" namespace absl { @@ -214,6 +216,26 @@ ABSL_NAMESPACE_BEGIN template using Hash = absl::hash_internal::Hash; +// HashOf +// +// absl::HashOf() is a helper that generates a hash from the values of its +// arguments. It dispatches to absl::Hash directly, as follows: +// * HashOf(t) == absl::Hash{}(t) +// * HashOf(a, b, c) == HashOf(std::make_tuple(a, b, c)) +// +// HashOf(a1, a2, ...) == HashOf(b1, b2, ...) is guaranteed when +// * The argument lists have pairwise identical C++ types +// * a1 == b1 && a2 == b2 && ... +// +// The requirement that the arguments match in both type and value is critical. +// It means that `a == b` does not necessarily imply `HashOf(a) == HashOf(b)` if +// `a` and `b` have different types. For example, `HashOf(2) != HashOf(2.0)`. +template +size_t HashOf(const Types&... values) { + auto tuple = std::tie(values...); + return absl::Hash{}(tuple); +} + // HashState // // A type erased version of the hash state concept, for use in user-defined diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 1d2e6cf0..02b7733d 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -973,4 +973,26 @@ TEST(HashTest, DoesNotUseImplicitConversionsToBool) { absl::Hash()(ValueWithBoolConversion{1})); } +TEST(HashOf, MatchesHashForSingleArgument) { + std::string s = "forty two"; + int i = 42; + double d = 42.0; + std::tuple t{4, 2}; + + EXPECT_EQ(absl::HashOf(s), absl::Hash{}(s)); + EXPECT_EQ(absl::HashOf(i), absl::Hash{}(i)); + EXPECT_EQ(absl::HashOf(d), absl::Hash{}(d)); + EXPECT_EQ(absl::HashOf(t), (absl::Hash>{}(t))); +} + +TEST(HashOf, MatchesHashOfTupleForMultipleArguments) { + std::string hello = "hello"; + std::string world = "world"; + + EXPECT_EQ(absl::HashOf(), absl::HashOf(std::make_tuple())); + EXPECT_EQ(absl::HashOf(hello), absl::HashOf(std::make_tuple(hello))); + EXPECT_EQ(absl::HashOf(hello, world), + absl::HashOf(std::make_tuple(hello, world))); +} + } // namespace diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc index 1433eb9d..06f53a59 100644 --- a/absl/hash/internal/hash.cc +++ b/absl/hash/internal/hash.cc @@ -18,9 +18,8 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace hash_internal { -uint64_t HashState::CombineLargeContiguousImpl32(uint64_t state, - const unsigned char* first, - size_t len) { +uint64_t MixingHashState::CombineLargeContiguousImpl32( + uint64_t state, const unsigned char* first, size_t len) { while (len >= PiecewiseChunkSize()) { state = Mix(state, absl::hash_internal::CityHash32(reinterpret_cast(first), @@ -33,9 +32,8 @@ uint64_t HashState::CombineLargeContiguousImpl32(uint64_t state, std::integral_constant{}); } -uint64_t HashState::CombineLargeContiguousImpl64(uint64_t state, - const unsigned char* first, - size_t len) { +uint64_t MixingHashState::CombineLargeContiguousImpl64( + uint64_t state, const unsigned char* first, size_t len) { while (len >= PiecewiseChunkSize()) { state = Mix(state, Hash64(first, PiecewiseChunkSize())); len -= PiecewiseChunkSize(); @@ -46,7 +44,7 @@ uint64_t HashState::CombineLargeContiguousImpl64(uint64_t state, std::integral_constant{}); } -ABSL_CONST_INIT const void* const HashState::kSeed = &kSeed; +ABSL_CONST_INIT const void* const MixingHashState::kSeed = &kSeed; // The salt array used by Wyhash. This array is NOT the mechanism used to make // absl::Hash non-deterministic between program invocations. See `Seed()` for @@ -61,7 +59,7 @@ constexpr uint64_t kWyhashSalt[5] = { uint64_t{0x452821E638D01377}, }; -uint64_t HashState::WyhashImpl(const unsigned char* data, size_t len) { +uint64_t MixingHashState::WyhashImpl(const unsigned char* data, size_t len) { return Wyhash(data, len, Seed(), kWyhashSalt); } diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index 7fb0af0b..69dbbc6b 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -379,7 +379,7 @@ template // This SFINAE gets MSVC confused under some conditions. Let's just disable it // for now. H -#else // _MSC_VER +#else // _MSC_VER typename std::enable_if...>::value, H>::type #endif // _MSC_VER AbslHashValue(H hash_state, const std::tuple& t) { @@ -714,8 +714,8 @@ template struct is_hashable : std::integral_constant::value> {}; -// HashState -class ABSL_DLL HashState : public HashStateBase { +// MixingHashState +class ABSL_DLL MixingHashState : public HashStateBase { // absl::uint128 is not an alias or a thin wrapper around the intrinsic. // We use the intrinsic when available to improve performance. #ifdef ABSL_HAVE_INTRINSIC_INT128 @@ -734,22 +734,23 @@ class ABSL_DLL HashState : public HashStateBase { public: // Move only - HashState(HashState&&) = default; - HashState& operator=(HashState&&) = default; + MixingHashState(MixingHashState&&) = default; + MixingHashState& operator=(MixingHashState&&) = default; - // HashState::combine_contiguous() + // MixingHashState::combine_contiguous() // // Fundamental base case for hash recursion: mixes the given range of bytes // into the hash state. - static HashState combine_contiguous(HashState hash_state, - const unsigned char* first, size_t size) { - return HashState( + static MixingHashState combine_contiguous(MixingHashState hash_state, + const unsigned char* first, + size_t size) { + return MixingHashState( CombineContiguousImpl(hash_state.state_, first, size, std::integral_constant{})); } - using HashState::HashStateBase::combine_contiguous; + using MixingHashState::HashStateBase::combine_contiguous; - // HashState::hash() + // MixingHashState::hash() // // For performance reasons in non-opt mode, we specialize this for // integral types. @@ -761,24 +762,24 @@ class ABSL_DLL HashState : public HashStateBase { return static_cast(Mix(Seed(), static_cast(value))); } - // Overload of HashState::hash() + // Overload of MixingHashState::hash() template ::value, int> = 0> static size_t hash(const T& value) { - return static_cast(combine(HashState{}, value).state_); + return static_cast(combine(MixingHashState{}, value).state_); } private: // Invoked only once for a given argument; that plus the fact that this is // move-only ensures that there is only one non-moved-from object. - HashState() : state_(Seed()) {} + MixingHashState() : state_(Seed()) {} // Workaround for MSVC bug. // We make the type copyable to fix the calling convention, even though we // never actually copy it. Keep it private to not affect the public API of the // type. - HashState(const HashState&) = default; + MixingHashState(const MixingHashState&) = default; - explicit HashState(uint64_t state) : state_(state) {} + explicit MixingHashState(uint64_t state) : state_(state) {} // Implementation of the base case for combine_contiguous where we actually // mix the bytes into the state. @@ -793,7 +794,6 @@ class ABSL_DLL HashState : public HashStateBase { std::integral_constant /* sizeof_size_t */); - // Slow dispatch path for calls to CombineContiguousImpl with a size argument // larger than PiecewiseChunkSize(). Has the same effect as calling // CombineContiguousImpl() repeatedly with the chunk stride size. @@ -911,8 +911,8 @@ class ABSL_DLL HashState : public HashStateBase { uint64_t state_; }; -// HashState::CombineContiguousImpl() -inline uint64_t HashState::CombineContiguousImpl( +// MixingHashState::CombineContiguousImpl() +inline uint64_t MixingHashState::CombineContiguousImpl( uint64_t state, const unsigned char* first, size_t len, std::integral_constant /* sizeof_size_t */) { // For large values we use CityHash, for small ones we just use a @@ -934,8 +934,8 @@ inline uint64_t HashState::CombineContiguousImpl( return Mix(state, v); } -// Overload of HashState::CombineContiguousImpl() -inline uint64_t HashState::CombineContiguousImpl( +// Overload of MixingHashState::CombineContiguousImpl() +inline uint64_t MixingHashState::CombineContiguousImpl( uint64_t state, const unsigned char* first, size_t len, std::integral_constant /* sizeof_size_t */) { // For large values we use Wyhash or CityHash depending on the platform, for @@ -976,7 +976,9 @@ struct PoisonedHash : private AggregateBarrier { template struct HashImpl { - size_t operator()(const T& value) const { return HashState::hash(value); } + size_t operator()(const T& value) const { + return MixingHashState::hash(value); + } }; template diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index ccafd702..ac12940a 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h @@ -47,12 +47,12 @@ using Payloads = absl::InlinedVector; // Reference-counted representation of Status data. struct StatusRep { - StatusRep(absl::StatusCode code, absl::string_view message, - std::unique_ptr payloads) + StatusRep(absl::StatusCode code_arg, absl::string_view message_arg, + std::unique_ptr payloads_arg) : ref(int32_t{1}), - code(code), - message(message), - payloads(std::move(payloads)) {} + code(code_arg), + message(message_arg), + payloads(std::move(payloads_arg)) {} std::atomic ref; absl::StatusCode code; diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 5dad781e..f5aa6e47 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -536,24 +536,10 @@ void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { return; } - // See b/187581164: unsample cord if already sampled - // TODO(b/117940323): continuously 'assigned to' cords would reach 100% - // sampling probability. Imagine a cord x in some cache: - // cache.SetCord(const Cord& foo) { - // x = foo; - // } - // CordzInfo::MaybeTrackCord does: - // x.profiled = foo.profiled | x.profiled | random(cordz_mean_interval) - // Which means it will on the long run converge to 'always samples' - // The real fix is in CordzMaybeTrackCord, but the below is a low risk - // forward fix for b/187581164 and similar BT benchmark regressions. - if (ABSL_PREDICT_FALSE(is_profiled())) { - cordz_info()->Untrack(); - clear_cordz_info(); - } - CordRep* tree = as_tree(); if (CordRep* src_tree = src.tree()) { + // Leave any existing `cordz_info` in place, and let MaybeTrackCord() + // decide if this cord should be (or remains to be) sampled or not. data_.set_tree(CordRep::Ref(src_tree)); CordzInfo::MaybeTrackCord(data_, src.data_, method); } else { diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc index 0e11f5c8..2b7d30b0 100644 --- a/absl/strings/cordz_test.cc +++ b/absl/strings/cordz_test.cc @@ -69,6 +69,7 @@ absl::string_view MakeString(TestCordSize size) { // Returns a cord with a sampled method of kAppendString. absl::Cord MakeAppendStringCord(TestCordSize size) { + CordzSamplingIntervalHelper always(1); absl::Cord cord; cord.Append(MakeString(size)); return cord; @@ -136,21 +137,21 @@ TEST_P(CordzStringTest, ConstructString) { } } -TEST(CordzTest, CopyConstruct) { +TEST(CordzTest, CopyConstructFromUnsampled) { CordzSamplingIntervalHelper sample_every{1}; Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); Cord cord(src); - EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); } TEST(CordzTest, CopyConstructFromSampled) { - CordzSamplingIntervalHelper sample_every{1}; - Cord src(MakeString(TestCordSize::kLarge)); + CordzSamplingIntervalHelper sample_never{99999}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); Cord cord(src); ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); - EXPECT_THAT(stats.parent_method, Eq(Method::kConstructorString)); - EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(1)); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); } TEST(CordzTest, MoveConstruct) { @@ -160,12 +161,12 @@ TEST(CordzTest, MoveConstruct) { EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); } -TEST_P(CordzUpdateTest, AssignCord) { +TEST_P(CordzUpdateTest, AssignUnsampledCord) { Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + const CordzInfo* info = GetCordzInfoForTesting(cord()); cord() = src; - EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignCord)); - CordzStatistics stats = GetCordzInfoForTesting(cord())->GetCordzStatistics(); - EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); + EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); } TEST_P(CordzUpdateTest, AssignSampledCord) { @@ -178,10 +179,22 @@ TEST_P(CordzUpdateTest, AssignSampledCord) { EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); } -TEST(CordzUpdateTest, AssignSampledCordToUnsampledCord) { - CordzSamplingIntervalHelper sample_every{1}; +TEST(CordzUpdateTest, AssignSampledCordToInlined) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord cord; Cord src = MakeAppendStringCord(TestCordSize::kLarge); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzUpdateTest, AssignSampledCordToUnsampledCord) { + CordzSamplingIntervalHelper sample_never{99999}; Cord cord = UnsampledCord(MakeString(TestCordSize::kLarge)); + Cord src = MakeAppendStringCord(TestCordSize::kLarge); cord = src; ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); @@ -190,6 +203,26 @@ TEST(CordzUpdateTest, AssignSampledCordToUnsampledCord) { EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); } +TEST(CordzUpdateTest, AssignUnsampledCordToSampledCordWithoutSampling) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord cord = MakeAppendStringCord(TestCordSize::kLarge); + const CordzInfo* info = GetCordzInfoForTesting(cord); + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord = src; + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); +} + +TEST(CordzUpdateTest, AssignUnsampledCordToSampledCordWithSampling) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord = MakeAppendStringCord(TestCordSize::kLarge); + const CordzInfo* info = GetCordzInfoForTesting(cord); + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord = src; + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); +} + TEST(CordzUpdateTest, AssignSampledCordToSampledCord) { CordzSamplingIntervalHelper sample_every{1}; Cord src = MakeAppendStringCord(TestCordSize::kLarge); @@ -202,7 +235,19 @@ TEST(CordzUpdateTest, AssignSampledCordToSampledCord) { EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); } -TEST(CordzTest, AssignInlinedCord) { +TEST(CordzUpdateTest, AssignUnsampledCordToSampledCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord(MakeString(TestCordSize::kLarge)); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzTest, AssignInlinedCordToSampledCord) { CordzSampleToken token; CordzSamplingIntervalHelper sample_every{1}; Cord cord(MakeString(TestCordSize::kLarge)); @@ -389,28 +434,28 @@ TEST(CordzTest, RemoveSuffix) { EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); } -TEST(CordzTest, SubCord) { +TEST(CordzTest, SubCordFromUnsampledCord) { CordzSamplingIntervalHelper sample_every{1}; Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); Cord cord = src.Subcord(10, src.size() / 2); - EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); -} - -TEST(CordzTest, SmallSubCord) { - CordzSamplingIntervalHelper sample_every{1}; - Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); - Cord cord = src.Subcord(10, kMaxInline + 1); - EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); } TEST(CordzTest, SubCordFromSampledCord) { - CordzSamplingIntervalHelper sample_every{1}; - Cord src(MakeString(TestCordSize::kLarge)); + CordzSamplingIntervalHelper sample_never{99999}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); Cord cord = src.Subcord(10, src.size() / 2); - EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); - EXPECT_THAT(stats.parent_method, Eq(Method::kConstructorString)); - EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(1)); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); +} + +TEST(CordzTest, SmallSubCord) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord = src.Subcord(10, kMaxInline + 1); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); } } // namespace diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index 09951290..f78c94e1 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -32,15 +32,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -// See https://bugs.llvm.org/show_bug.cgi?id=48477 -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshadow" -#if __has_warning("-Wshadow-field") -#pragma clang diagnostic ignored "-Wshadow-field" -#endif -#endif - namespace { using index_type = CordRepRing::index_type; @@ -450,12 +441,12 @@ Span CordRepRing::GetPrependBuffer(size_t size) { } CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset, - size_t length, size_t extra) { + size_t len, size_t extra) { CordRepRing* rep = CordRepRing::New(1, extra); rep->head_ = 0; rep->tail_ = rep->advance(0); - rep->length = length; - rep->entry_end_pos()[0] = length; + rep->length = len; + rep->entry_end_pos()[0] = len; rep->entry_child()[0] = child; rep->entry_data_offset()[0] = static_cast(offset); return Validate(rep); @@ -463,16 +454,16 @@ CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset, CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) { CordRepRing* rep = nullptr; - Consume(child, [&](CordRep* child, size_t offset, size_t length) { - if (IsFlatOrExternal(child)) { - rep = rep ? AppendLeaf(rep, child, offset, length) - : CreateFromLeaf(child, offset, length, extra); + Consume(child, [&](CordRep* child_arg, size_t offset, size_t len) { + if (IsFlatOrExternal(child_arg)) { + rep = rep ? AppendLeaf(rep, child_arg, offset, len) + : CreateFromLeaf(child_arg, offset, len, extra); } else if (rep) { - rep = AddRing(rep, child->ring(), offset, length); - } else if (offset == 0 && child->length == length) { - rep = Mutable(child->ring(), extra); + rep = AddRing(rep, child_arg->ring(), offset, len); + } else if (offset == 0 && child_arg->length == len) { + rep = Mutable(child_arg->ring(), extra); } else { - rep = SubRing(child->ring(), offset, length, extra); + rep = SubRing(child_arg->ring(), offset, len, extra); } }); return Validate(rep, nullptr, __LINE__); @@ -491,18 +482,18 @@ CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) { template CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring, - size_t offset, size_t length) { + size_t offset, size_t len) { assert(offset < ring->length); constexpr bool append = mode == AddMode::kAppend; Position head = ring->Find(offset); - Position tail = ring->FindTail(head.index, offset + length); + Position tail = ring->FindTail(head.index, offset + len); const index_type entries = ring->entries(head.index, tail.index); rep = Mutable(rep, entries); // The delta for making ring[head].end_pos into 'len - offset' const pos_type delta_length = - (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - length) - + (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - len) - ring->entry_begin_pos(head.index) - head.offset; // Start filling at `tail`, or `entries` before `head` @@ -543,36 +534,36 @@ CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring, } // Commit changes - rep->length += length; + rep->length += len; if (append) { rep->tail_ = filler.pos(); } else { rep->head_ = filler.head(); - rep->begin_pos_ -= length; + rep->begin_pos_ -= len; } return Validate(rep); } CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) { - Consume(child, [&rep](CordRep* child, size_t offset, size_t length) { - if (child->tag == RING) { - rep = AddRing(rep, child->ring(), offset, length); + Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) { + if (child_arg->tag == RING) { + rep = AddRing(rep, child_arg->ring(), offset, len); } else { - rep = AppendLeaf(rep, child, offset, length); + rep = AppendLeaf(rep, child_arg, offset, len); } }); return rep; } CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child, - size_t offset, size_t length) { + size_t offset, size_t len) { rep = Mutable(rep, 1); index_type back = rep->tail_; const pos_type begin_pos = rep->begin_pos_ + rep->length; rep->tail_ = rep->advance(rep->tail_); - rep->length += length; - rep->entry_end_pos()[back] = begin_pos + length; + rep->length += len; + rep->entry_end_pos()[back] = begin_pos + len; rep->entry_child()[back] = child; rep->entry_data_offset()[back] = static_cast(offset); return Validate(rep, nullptr, __LINE__); @@ -590,24 +581,24 @@ CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) { } CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) { - RConsume(child, [&](CordRep* child, size_t offset, size_t length) { - if (IsFlatOrExternal(child)) { - rep = PrependLeaf(rep, child, offset, length); + RConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) { + if (IsFlatOrExternal(child_arg)) { + rep = PrependLeaf(rep, child_arg, offset, len); } else { - rep = AddRing(rep, child->ring(), offset, length); + rep = AddRing(rep, child_arg->ring(), offset, len); } }); return Validate(rep); } CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child, - size_t offset, size_t length) { + size_t offset, size_t len) { rep = Mutable(rep, 1); index_type head = rep->retreat(rep->head_); pos_type end_pos = rep->begin_pos_; rep->head_ = head; - rep->length += length; - rep->begin_pos_ -= length; + rep->length += len; + rep->begin_pos_ -= len; rep->entry_end_pos()[head] = end_pos; rep->entry_child()[head] = child; rep->entry_data_offset()[head] = static_cast(offset); @@ -787,18 +778,18 @@ char CordRepRing::GetCharacter(size_t offset) const { } CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset, - size_t length, size_t extra) { + size_t len, size_t extra) { assert(offset <= rep->length); - assert(offset <= rep->length - length); + assert(offset <= rep->length - len); - if (length == 0) { + if (len == 0) { CordRep::Unref(rep); return nullptr; } // Find position of first byte Position head = rep->Find(offset); - Position tail = rep->FindTail(head.index, offset + length); + Position tail = rep->FindTail(head.index, offset + len); const size_t new_entries = rep->entries(head.index, tail.index); if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) { @@ -815,7 +806,7 @@ CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset, } // Adjust begin_pos and length - rep->length = length; + rep->length = len; rep->begin_pos_ += offset; // Adjust head and tail blocks @@ -889,10 +880,6 @@ CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len, return Validate(rep); } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h index 830f2b2a..2082a565 100644 --- a/absl/strings/internal/cord_rep_ring.h +++ b/absl/strings/internal/cord_rep_ring.h @@ -30,15 +30,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -// See https://bugs.llvm.org/show_bug.cgi?id=48477 -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshadow" -#if __has_warning("-Wshadow-field") -#pragma clang diagnostic ignored "-Wshadow-field" -#endif -#endif - // All operations modifying a ring buffer are implemented as static methods // requiring a CordRepRing instance with a reference adopted by the method. // @@ -210,23 +201,23 @@ class CordRepRing : public CordRep { // referencing up to `size` capacity directly before the existing data. Span GetPrependBuffer(size_t size); - // Returns a cord ring buffer containing `length` bytes of data starting at + // Returns a cord ring buffer containing `len` bytes of data starting at // `offset`. If the input is not shared, this function will remove all head // and tail child nodes outside of the requested range, and adjust the new // head and tail nodes as required. If the input is shared, this function // returns a new instance sharing some or all of the nodes from the input. - static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t length, + static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t len, size_t extra = 0); - // Returns a cord ring buffer with the first `length` bytes removed. + // Returns a cord ring buffer with the first `len` bytes removed. // If the input is not shared, this function will remove all head child nodes // fully inside the first `length` bytes, and adjust the new head as required. // If the input is shared, this function returns a new instance sharing some // or all of the nodes from the input. - static CordRepRing* RemoveSuffix(CordRepRing* r, size_t length, + static CordRepRing* RemoveSuffix(CordRepRing* r, size_t len, size_t extra = 0); - // Returns a cord ring buffer with the last `length` bytes removed. + // Returns a cord ring buffer with the last `len` bytes removed. // If the input is not shared, this function will remove all head child nodes // fully inside the first `length` bytes, and adjust the new head as required. // If the input is shared, this function returns a new instance sharing some @@ -242,12 +233,12 @@ class CordRepRing : public CordRep { // 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 + // Returns true if the data starting at `offset` with length `len` 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; + bool IsFlat(size_t offset, size_t len, absl::string_view* fragment) const; // Testing only: set capacity to requested capacity. void SetCapacityForTesting(size_t capacity); @@ -473,10 +464,10 @@ class CordRepRing : public CordRep { size_t length, size_t extra); // Appends or prepends (depending on AddMode) the ring buffer in `ring' to - // `rep` starting at `offset` with length `length`. + // `rep` starting at `offset` with length `len`. template static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring, - size_t offset, size_t length); + size_t offset, size_t len); // Increases the data offset for entry `index` by `n`. void AddDataOffset(index_type index, size_t n); @@ -596,12 +587,12 @@ inline bool CordRepRing::IsFlat(absl::string_view* fragment) const { return false; } -inline bool CordRepRing::IsFlat(size_t offset, size_t length, +inline bool CordRepRing::IsFlat(size_t offset, size_t len, 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); + if (data.length() >= len && data.length() - len >= pos.offset) { + if (fragment) *fragment = data.substr(pos.offset, len); return true; } return false; @@ -609,10 +600,6 @@ inline bool CordRepRing::IsFlat(size_t offset, size_t length, std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cordz_functions.cc b/absl/strings/internal/cordz_functions.cc index 6ad864f1..f30080f8 100644 --- a/absl/strings/internal/cordz_functions.cc +++ b/absl/strings/internal/cordz_functions.cc @@ -44,7 +44,10 @@ std::atomic g_cordz_mean_interval(50000); #ifdef ABSL_INTERNAL_CORDZ_ENABLED -ABSL_CONST_INIT thread_local int64_t cordz_next_sample = 0; +// Special negative 'not initialized' per thread value for cordz_next_sample. +static constexpr int64_t kInitCordzNextSample = -1; + +ABSL_CONST_INIT thread_local int64_t cordz_next_sample = kInitCordzNextSample; // kIntervalIfDisabled is the number of profile-eligible events need to occur // before the code will confirm that cordz is still disabled. @@ -77,8 +80,11 @@ ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() { } if (cordz_next_sample <= 0) { + // If first check on current thread, check cordz_should_profile() + // again using the created (initial) stride in cordz_next_sample. + const bool initialized = cordz_next_sample != kInitCordzNextSample; cordz_next_sample = exponential_biased_generator.GetStride(mean_interval); - return true; + return initialized || cordz_should_profile(); } --cordz_next_sample; diff --git a/absl/strings/internal/cordz_functions_test.cc b/absl/strings/internal/cordz_functions_test.cc index f2cefae3..350623c1 100644 --- a/absl/strings/internal/cordz_functions_test.cc +++ b/absl/strings/internal/cordz_functions_test.cc @@ -14,6 +14,8 @@ #include "absl/strings/internal/cordz_functions.h" +#include // NOLINT we need real clean new threads + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/config.h" @@ -63,6 +65,22 @@ TEST(CordzFunctionsTest, ShouldProfileAlways) { set_cordz_mean_interval(orig_sample_rate); } +TEST(CordzFunctionsTest, DoesNotAlwaysSampleFirstCord) { + // Set large enough interval such that the chance of 'tons' of threads + // randomly sampling the first call is infinitely small. + set_cordz_mean_interval(10000); + int tries = 0; + bool sampled = false; + do { + ++tries; + ASSERT_THAT(tries, Le(1000)); + std::thread thread([&sampled] { + sampled = cordz_should_profile(); + }); + thread.join(); + } while (sampled); +} + TEST(CordzFunctionsTest, ShouldProfileRate) { static constexpr int kDesiredMeanInterval = 1000; static constexpr int kSamples = 10000; diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index a6b045ff..a3a0b9c0 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -293,6 +293,16 @@ void CordzInfo::TrackCord(InlineData& cord, const InlineData& src, cordz_info->Track(); } +void CordzInfo::MaybeTrackCordImpl(InlineData& cord, const InlineData& src, + MethodIdentifier method) { + if (src.is_profiled()) { + TrackCord(cord, src, method); + } else if (cord.is_profiled()) { + cord.cordz_info()->Untrack(); + cord.clear_cordz_info(); + } +} + CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) { if (src == nullptr) return MethodIdentifier::kUnknown; return src->parent_method_ != MethodIdentifier::kUnknown ? src->parent_method_ diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index 29237930..13aaee17 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -77,13 +77,31 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { static void MaybeTrackCord(InlineData& cord, MethodIdentifier method); // Maybe sample the cord identified by 'cord' for method 'method'. - // `src` identifies a 'parent' cord which content is copied into the current - // cord, typically the input cord for an assign emthod or copy constructor. - // Invokes the corresponding `TrackCord` method if either cord is sampled, or - // if `cord` is randomly picked for sampling. Possible scenarios: - // * `src` is sampled: `cord` will be set to sampled if not already sampled. - // Parent stack and update stats of `src` are copied into `cord` - // * `src` is not sampled: `cord` may be randomly picked for sampling. + // `src` identifies a 'parent' cord which is assigned to `cord`, typically the + // input cord for a copy constructor, or an assign method such as `operator=` + // `cord` will be sampled if (and only if) `src` is sampled. + // If `cord` is currently being sampled and `src` is not being sampled, then + // this function will stop sampling the cord and reset the cord's cordz_info. + // + // Previously this function defined that `cord` will be sampled if either + // `src` is sampled, or if `cord` is randomly picked for sampling. However, + // this can cause issues, as there may be paths where some cord is assigned an + // indirect copy of it's own value. As such a 'string of copies' would then + // remain sampled (`src.is_profiled`), then assigning such a cord back to + // 'itself' creates a cycle where the cord will converge to 'always sampled`. + // + // For example: + // + // Cord x; + // for (...) { + // // Copy ctor --> y.is_profiled := x.is_profiled | random(...) + // Cord y = x; + // ... + // // Assign x = y --> x.is_profiled = y.is_profiled | random(...) + // // ==> x.is_profiled |= random(...) + // // ==> x converges to 'always profiled' + // x = y; + // } static void MaybeTrackCord(InlineData& cord, const InlineData& src, MethodIdentifier method); @@ -201,6 +219,12 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { #endif } + // Non-inlined implementation of `MaybeTrackCord`, which is executed if + // either `src` is sampled or `cord` is sampled, and either untracks or + // tracks `cord` as documented per `MaybeTrackCord`. + static void MaybeTrackCordImpl(InlineData& cord, const InlineData& src, + MethodIdentifier method); + ABSL_CONST_INIT static List global_list_; List* const list_ = &global_list_; @@ -232,9 +256,8 @@ inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( InlineData& cord, const InlineData& src, MethodIdentifier method) { - if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src)) || - ABSL_PREDICT_FALSE(cordz_should_profile())) { - TrackCord(cord, src, method); + if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src))) { + MaybeTrackCordImpl(cord, src, method); } } diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc index 59a8c525..b98343ae 100644 --- a/absl/strings/internal/cordz_info_test.cc +++ b/absl/strings/internal/cordz_info_test.cc @@ -74,19 +74,49 @@ TEST(CordzInfoTest, TrackCord) { info->Untrack(); } -TEST(CordzInfoTest, MaybeTrackCordOnSampledCord) { - TestCordData data1; - CordzInfo::TrackCord(data1.data, kTrackCordMethod); - CordzInfo* info1 = data1.data.cordz_info(); - TestCordData data2; - CordzInfo::MaybeTrackCord(data2.data, data1.data, kTrackCordMethod); - CordzInfo* info2 = data2.data.cordz_info(); - ASSERT_THAT(info2, Ne(nullptr)); - EXPECT_THAT(info2->GetCordRepForTesting(), Eq(data2.rep.rep)); - info2->Untrack(); - info1->Untrack(); +TEST(CordzInfoTest, MaybeTrackChildCordWithoutSampling) { + CordzSamplingIntervalHelper sample_none(99999); + TestCordData parent, child; + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithSampling) { + CordzSamplingIntervalHelper sample_all(1); + TestCordData parent, child; + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); } +TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingParentSampled) { + CordzSamplingIntervalHelper sample_none(99999); + TestCordData parent, child; + CordzInfo::TrackCord(parent.data, kTrackCordMethod); + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + CordzInfo* parent_info = parent.data.cordz_info(); + CordzInfo* child_info = child.data.cordz_info(); + ASSERT_THAT(child_info, Ne(nullptr)); + EXPECT_THAT(child_info->GetCordRepForTesting(), Eq(child.rep.rep)); + EXPECT_THAT(child_info->GetParentStack(), parent_info->GetStack()); + parent_info->Untrack(); + child_info->Untrack(); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingChildSampled) { + CordzSamplingIntervalHelper sample_none(99999); + TestCordData parent, child; + CordzInfo::TrackCord(child.data, kTrackCordMethod); + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithSamplingChildSampled) { + CordzSamplingIntervalHelper sample_all(1); + TestCordData parent, child; + CordzInfo::TrackCord(child.data, kTrackCordMethod); + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); +} TEST(CordzInfoTest, UntrackCord) { TestCordData data; diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc index 4e68b90b..c988ba8f 100644 --- a/absl/strings/internal/str_format/bind.cc +++ b/absl/strings/internal/str_format/bind.cc @@ -58,7 +58,7 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound, if (static_cast(arg_position - 1) >= pack_.size()) return false; arg = &pack_[arg_position - 1]; // 1-based - if (!unbound->flags.basic) { + if (unbound->flags != Flags::kBasic) { int width = unbound->width.value(); bool force_left = false; if (unbound->width.is_from_arg()) { @@ -84,9 +84,8 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound, FormatConversionSpecImplFriend::SetPrecision(precision, bound); if (force_left) { - Flags flags = unbound->flags; - flags.left = true; - FormatConversionSpecImplFriend::SetFlags(flags, bound); + FormatConversionSpecImplFriend::SetFlags(unbound->flags | Flags::kLeft, + bound); } else { FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); } diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc index bb0d96cf..484f6ebf 100644 --- a/absl/strings/internal/str_format/extension.cc +++ b/absl/strings/internal/str_format/extension.cc @@ -23,13 +23,13 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -std::string Flags::ToString() const { +std::string FlagsToString(Flags v) { std::string s; - s.append(left ? "-" : ""); - s.append(show_pos ? "+" : ""); - s.append(sign_col ? " " : ""); - s.append(alt ? "#" : ""); - s.append(zero ? "0" : ""); + s.append(FlagsContains(v, Flags::kLeft) ? "-" : ""); + s.append(FlagsContains(v, Flags::kShowPos) ? "+" : ""); + s.append(FlagsContains(v, Flags::kSignCol) ? " " : ""); + s.append(FlagsContains(v, Flags::kAlt) ? "#" : ""); + s.append(FlagsContains(v, Flags::kZero) ? "0" : ""); return s; } diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index a9b9e137..55cbb56d 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h @@ -128,19 +128,33 @@ class FormatSinkImpl { char buf_[1024]; }; -struct Flags { - bool basic : 1; // fastest conversion: no flags, width, or precision - bool left : 1; // "-" - bool show_pos : 1; // "+" - bool sign_col : 1; // " " - bool alt : 1; // "#" - bool zero : 1; // "0" - std::string ToString() const; - friend std::ostream& operator<<(std::ostream& os, const Flags& v) { - return os << v.ToString(); - } +enum class Flags : uint8_t { + kBasic = 0, + kLeft = 1 << 0, + kShowPos = 1 << 1, + kSignCol = 1 << 2, + kAlt = 1 << 3, + kZero = 1 << 4, + // This is not a real flag. It just exists to turn off kBasic when no other + // flags are set. This is for when width/precision are specified. + kNonBasic = 1 << 5, }; +constexpr Flags operator|(Flags a, Flags b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +constexpr bool FlagsContains(Flags haystack, Flags needle) { + return (static_cast(haystack) & static_cast(needle)) == + static_cast(needle); +} + +std::string FlagsToString(Flags v); + +inline std::ostream& operator<<(std::ostream& os, Flags v) { + return os << FlagsToString(v); +} + // clang-format off #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ /* text */ \ @@ -257,12 +271,16 @@ struct FormatConversionSpecImplFriend; class FormatConversionSpecImpl { public: // Width and precison are not specified, no flags are set. - bool is_basic() const { return flags_.basic; } - bool has_left_flag() const { return flags_.left; } - bool has_show_pos_flag() const { return flags_.show_pos; } - bool has_sign_col_flag() const { return flags_.sign_col; } - bool has_alt_flag() const { return flags_.alt; } - bool has_zero_flag() const { return flags_.zero; } + bool is_basic() const { return flags_ == Flags::kBasic; } + bool has_left_flag() const { return FlagsContains(flags_, Flags::kLeft); } + bool has_show_pos_flag() const { + return FlagsContains(flags_, Flags::kShowPos); + } + bool has_sign_col_flag() const { + return FlagsContains(flags_, Flags::kSignCol); + } + bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); } + bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); } FormatConversionChar conversion_char() const { // Keep this field first in the struct . It generates better code when @@ -306,7 +324,7 @@ struct FormatConversionSpecImplFriend final { conv->precision_ = p; } static std::string FlagsToString(const FormatConversionSpecImpl& spec) { - return spec.flags_.ToString(); + return str_format_internal::FlagsToString(spec.flags_); } }; diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc index f308d023..2c9c07da 100644 --- a/absl/strings/internal/str_format/parser.cc +++ b/absl/strings/internal/str_format/parser.cc @@ -34,60 +34,67 @@ namespace str_format_internal { using CC = FormatConversionCharInternal; using LM = LengthMod; +// Abbreviations to fit in the table below. +constexpr auto f_sign = Flags::kSignCol; +constexpr auto f_alt = Flags::kAlt; +constexpr auto f_pos = Flags::kShowPos; +constexpr auto f_left = Flags::kLeft; +constexpr auto f_zero = Flags::kZero; + ABSL_CONST_INIT const ConvTag kTags[256] = { - {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 - {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f - {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 - {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f - {}, {}, {}, {}, {}, {}, {}, {}, // 20-27 - {}, {}, {}, {}, {}, {}, {}, {}, // 28-2f - {}, {}, {}, {}, {}, {}, {}, {}, // 30-37 - {}, {}, {}, {}, {}, {}, {}, {}, // 38-3f - {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG - {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO - {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW - CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ - {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg - LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno - CC::p, LM::q, {}, CC::s, LM::t, CC::u, {}, {}, // pqrstuvw - CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! - {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 - {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f - {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 - {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f - {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 - {}, {}, {}, {}, {}, {}, {}, {}, // a8-af - {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 - {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf - {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 - {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf - {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 - {}, {}, {}, {}, {}, {}, {}, {}, // d8-df - {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 - {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef - {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 - {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff + {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 + {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f + {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 + {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f + f_sign, {}, {}, f_alt, {}, {}, {}, {}, // !"#$%&' + {}, {}, {}, f_pos, {}, f_left, {}, {}, // ()*+,-./ + f_zero, {}, {}, {}, {}, {}, {}, {}, // 01234567 + {}, {}, {}, {}, {}, {}, {}, {}, // 89:;<=>? + {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG + {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO + {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW + CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ + {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg + LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno + CC::p, LM::q, {}, CC::s, LM::t, CC::u, {}, {}, // pqrstuvw + CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! + {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 + {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f + {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 + {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f + {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 + {}, {}, {}, {}, {}, {}, {}, {}, // a8-af + {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 + {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf + {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 + {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf + {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 + {}, {}, {}, {}, {}, {}, {}, {}, // d8-df + {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 + {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef + {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 + {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff }; namespace { bool CheckFastPathSetting(const UnboundConversion& conv) { - bool should_be_basic = !conv.flags.left && // - !conv.flags.show_pos && // - !conv.flags.sign_col && // - !conv.flags.alt && // - !conv.flags.zero && // - (conv.width.value() == -1) && - (conv.precision.value() == -1); - if (should_be_basic != conv.flags.basic) { + bool width_precision_needed = + conv.width.value() >= 0 || conv.precision.value() >= 0; + if (width_precision_needed && conv.flags == Flags::kBasic) { fprintf(stderr, "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d " "width=%d precision=%d\n", - conv.flags.basic, conv.flags.left, conv.flags.show_pos, - conv.flags.sign_col, conv.flags.alt, conv.flags.zero, - conv.width.value(), conv.precision.value()); + conv.flags == Flags::kBasic ? 1 : 0, + FlagsContains(conv.flags, Flags::kLeft) ? 1 : 0, + FlagsContains(conv.flags, Flags::kShowPos) ? 1 : 0, + FlagsContains(conv.flags, Flags::kSignCol) ? 1 : 0, + FlagsContains(conv.flags, Flags::kAlt) ? 1 : 0, + FlagsContains(conv.flags, Flags::kZero) ? 1 : 0, conv.width.value(), + conv.precision.value()); + return false; } - return should_be_basic == conv.flags.basic; + return true; } template @@ -131,40 +138,21 @@ const char *ConsumeConversion(const char *pos, const char *const end, ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); // We should start with the basic flag on. - assert(conv->flags.basic); + assert(conv->flags == Flags::kBasic); // Any non alpha character makes this conversion not basic. // This includes flags (-+ #0), width (1-9, *) or precision (.). // All conversion characters and length modifiers are alpha characters. if (c < 'A') { - conv->flags.basic = false; - - for (; c <= '0';) { - // FIXME: We might be able to speed this up reusing the lookup table from - // above. It might require changing Flags to be a plain integer where we - // can |= a value. - switch (c) { - case '-': - conv->flags.left = true; - break; - case '+': - conv->flags.show_pos = true; - break; - case ' ': - conv->flags.sign_col = true; - break; - case '#': - conv->flags.alt = true; - break; - case '0': - conv->flags.zero = true; - break; - default: - goto flags_done; + while (c <= '0') { + auto tag = GetTagForChar(c); + if (tag.is_flags()) { + conv->flags = conv->flags | tag.as_flags(); + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + break; } - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); } -flags_done: if (c <= '9') { if (c >= '0') { @@ -173,12 +161,12 @@ flags_done: if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr; // Positional conversion. *next_arg = -1; - conv->flags = Flags(); - conv->flags.basic = true; return ConsumeConversion(original_pos, end, conv, next_arg); } + conv->flags = conv->flags | Flags::kNonBasic; conv->width.set_value(maybe_width); } else if (c == '*') { + conv->flags = conv->flags | Flags::kNonBasic; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (is_positional) { if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; @@ -192,6 +180,7 @@ flags_done: } if (c == '.') { + conv->flags = conv->flags | Flags::kNonBasic; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (std::isdigit(c)) { conv->precision.set_value(parse_digits()); diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index 6504dd3d..ad8646ed 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h @@ -41,10 +41,7 @@ std::string LengthModToString(LengthMod v); // The analyzed properties of a single specified conversion. struct UnboundConversion { - UnboundConversion() - : flags() /* This is required to zero all the fields of flags. */ { - flags.basic = true; - } + UnboundConversion() {} class InputValue { public: @@ -79,7 +76,7 @@ struct UnboundConversion { InputValue width; InputValue precision; - Flags flags; + Flags flags = Flags::kBasic; LengthMod length_mod = LengthMod::none; FormatConversionChar conv = FormatConversionCharInternal::kNone; }; @@ -93,32 +90,43 @@ const char* ConsumeUnboundConversion(const char* p, const char* end, UnboundConversion* conv, int* next_arg); // Helper tag class for the table below. -// It allows fast `char -> ConversionChar/LengthMod` checking and +// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and // conversions. class ConvTag { public: constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT - : tag_(static_cast(conversion_char)) {} - // We invert the length modifiers to make them negative so that we can easily - // test for them. + : tag_(static_cast(conversion_char)) {} constexpr ConvTag(LengthMod length_mod) // NOLINT - : tag_(~static_cast(length_mod)) {} - // Everything else is -128, which is negative to make is_conv() simpler. - constexpr ConvTag() : tag_(-128) {} + : tag_(0x80 | static_cast(length_mod)) {} + constexpr ConvTag(Flags flags) // NOLINT + : tag_(0xc0 | static_cast(flags)) {} + constexpr ConvTag() : tag_(0xFF) {} + + bool is_conv() const { return (tag_ & 0x80) == 0; } + bool is_length() const { return (tag_ & 0xC0) == 0x80; } + bool is_flags() const { return (tag_ & 0xE0) == 0xC0; } - bool is_conv() const { return tag_ >= 0; } - bool is_length() const { return tag_ < 0 && tag_ != -128; } FormatConversionChar as_conv() const { assert(is_conv()); + assert(!is_length()); + assert(!is_flags()); return static_cast(tag_); } LengthMod as_length() const { + assert(!is_conv()); assert(is_length()); - return static_cast(~tag_); + assert(!is_flags()); + return static_cast(tag_ & 0x3F); + } + Flags as_flags() const { + assert(!is_conv()); + assert(!is_length()); + assert(is_flags()); + return static_cast(tag_ & 0x1F); } private: - std::int8_t tag_; + uint8_t tag_; }; extern const ConvTag kTags[256]; diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc index a5fa1c79..fe0d2963 100644 --- a/absl/strings/internal/str_format/parser_test.cc +++ b/absl/strings/internal/str_format/parser_test.cc @@ -270,15 +270,22 @@ TEST_F(ConsumeUnboundConversionTest, Flags) { for (int k = 0; k < kNumFlags; ++k) if ((i >> k) & 1) fmt += kAllFlags[k]; // flag order shouldn't matter - if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); } + if (rev == 1) { + std::reverse(fmt.begin(), fmt.end()); + } fmt += 'd'; SCOPED_TRACE(fmt); EXPECT_TRUE(Run(fmt.c_str())); - EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left); - EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos); - EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col); - EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt); - EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero); + EXPECT_EQ(fmt.find('-') == std::string::npos, + !FlagsContains(o.flags, Flags::kLeft)); + EXPECT_EQ(fmt.find('+') == std::string::npos, + !FlagsContains(o.flags, Flags::kShowPos)); + EXPECT_EQ(fmt.find(' ') == std::string::npos, + !FlagsContains(o.flags, Flags::kSignCol)); + EXPECT_EQ(fmt.find('#') == std::string::npos, + !FlagsContains(o.flags, Flags::kAlt)); + EXPECT_EQ(fmt.find('0') == std::string::npos, + !FlagsContains(o.flags, Flags::kZero)); } } } @@ -288,14 +295,14 @@ TEST_F(ConsumeUnboundConversionTest, BasicFlag) { for (const char* fmt : {"d", "llx", "G", "1$X"}) { SCOPED_TRACE(fmt); EXPECT_TRUE(Run(fmt)); - EXPECT_TRUE(o.flags.basic); + EXPECT_EQ(o.flags, Flags::kBasic); } // Flag is off for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) { SCOPED_TRACE(fmt); EXPECT_TRUE(Run(fmt)); - EXPECT_FALSE(o.flags.basic); + EXPECT_NE(o.flags, Flags::kBasic); } } diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc index 3cea7aed..d2f82da3 100644 --- a/absl/synchronization/blocking_counter.cc +++ b/absl/synchronization/blocking_counter.cc @@ -14,41 +14,51 @@ #include "absl/synchronization/blocking_counter.h" +#include + #include "absl/base/internal/raw_logging.h" namespace absl { ABSL_NAMESPACE_BEGIN -// Return whether int *arg is zero. -static bool IsZero(void *arg) { - return 0 == *reinterpret_cast(arg); +namespace { + +// Return whether int *arg is true. +bool IsDone(void *arg) { return *reinterpret_cast(arg); } + +} // namespace + +BlockingCounter::BlockingCounter(int initial_count) + : count_(initial_count), + num_waiting_(0), + done_{initial_count == 0 ? true : false} { + ABSL_RAW_CHECK(initial_count >= 0, "BlockingCounter initial_count negative"); } bool BlockingCounter::DecrementCount() { - MutexLock l(&lock_); - count_--; - if (count_ < 0) { - ABSL_RAW_LOG( - FATAL, - "BlockingCounter::DecrementCount() called too many times. count=%d", - count_); + int count = count_.fetch_sub(1, std::memory_order_acq_rel) - 1; + ABSL_RAW_CHECK(count >= 0, + "BlockingCounter::DecrementCount() called too many times"); + if (count == 0) { + MutexLock l(&lock_); + done_ = true; + return true; } - return count_ == 0; + return false; } void BlockingCounter::Wait() { MutexLock l(&this->lock_); - ABSL_RAW_CHECK(count_ >= 0, "BlockingCounter underflow"); // only one thread may call Wait(). To support more than one thread, // implement a counter num_to_exit, like in the Barrier class. ABSL_RAW_CHECK(num_waiting_ == 0, "multiple threads called Wait()"); num_waiting_++; - this->lock_.Await(Condition(IsZero, &this->count_)); + this->lock_.Await(Condition(IsDone, &this->done_)); - // At this point, We know that all threads executing DecrementCount have - // released the lock, and so will not touch this object again. + // At this point, we know that all threads executing DecrementCount + // will not touch this object again. // Therefore, the thread calling this method is free to delete the object // after we return from this method. } diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h index 1f53f9f2..1908fdb1 100644 --- a/absl/synchronization/blocking_counter.h +++ b/absl/synchronization/blocking_counter.h @@ -20,6 +20,8 @@ #ifndef ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ #define ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ +#include + #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" @@ -60,8 +62,7 @@ ABSL_NAMESPACE_BEGIN // class BlockingCounter { public: - explicit BlockingCounter(int initial_count) - : count_(initial_count), num_waiting_(0) {} + explicit BlockingCounter(int initial_count); BlockingCounter(const BlockingCounter&) = delete; BlockingCounter& operator=(const BlockingCounter&) = delete; @@ -89,8 +90,9 @@ class BlockingCounter { private: Mutex lock_; - int count_ ABSL_GUARDED_BY(lock_); + std::atomic count_; int num_waiting_ ABSL_GUARDED_BY(lock_); + bool done_ ABSL_GUARDED_BY(lock_); }; ABSL_NAMESPACE_END diff --git a/absl/synchronization/blocking_counter_test.cc b/absl/synchronization/blocking_counter_test.cc index 2926224a..06885f57 100644 --- a/absl/synchronization/blocking_counter_test.cc +++ b/absl/synchronization/blocking_counter_test.cc @@ -63,6 +63,18 @@ TEST(BlockingCounterTest, BasicFunctionality) { } } +TEST(BlockingCounterTest, WaitZeroInitialCount) { + BlockingCounter counter(0); + counter.Wait(); +} + +#if GTEST_HAS_DEATH_TEST +TEST(BlockingCounterTest, WaitNegativeInitialCount) { + EXPECT_DEATH(BlockingCounter counter(-1), + "BlockingCounter initial_count negative"); +} +#endif + } // namespace ABSL_NAMESPACE_END } // namespace absl -- cgit v1.2.3 From aad2c8a3966424bbaa2f26027d104d17f9c1c1ae Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 13 May 2021 11:31:11 -0700 Subject: Export of internal Abseil changes -- 912c205cf80c4ed24a08000c04263403857b7f75 by Abseil Team : Internal change PiperOrigin-RevId: 373620391 -- d454f10549d27d7b025d1ce0ef90a0cdec42c361 by Martijn Vels : Add SetCordRepForTesting() helper to CordzInfo PiperOrigin-RevId: 373602832 -- 7a2d7bdd2e60fb51333e81cd71ebd0a4edb60704 by Evan Brown : For StrAppend, make sure to follow exponential growth like std::string::append. PiperOrigin-RevId: 373589332 -- 24397f19bce45133a42feaa7c883009ef9325095 by Greg Falcon : Import of CCTZ from GitHub. PiperOrigin-RevId: 373569246 GitOrigin-RevId: 912c205cf80c4ed24a08000c04263403857b7f75 Change-Id: I5444f7ca2c0980685ca5c51596c259b845d69673 --- absl/random/internal/BUILD.bazel | 1 - absl/strings/internal/cordz_info.h | 5 +++++ absl/strings/internal/resize_uninitialized.h | 23 ++++++++++++++++++++++ absl/strings/internal/resize_uninitialized_test.cc | 23 ++++++++++++++++++++++ absl/strings/str_cat.cc | 8 ++++---- .../internal/cctz/src/time_zone_format_test.cc | 4 ++-- 6 files changed, 57 insertions(+), 7 deletions(-) diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 612b1505..90efe85a 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -717,7 +717,6 @@ cc_test( cc_test( name = "iostream_state_saver_test", - size = "small", srcs = ["iostream_state_saver_test.cc"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h index 13aaee17..026d5b99 100644 --- a/absl/strings/internal/cordz_info.h +++ b/absl/strings/internal/cordz_info.h @@ -158,6 +158,11 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle { return rep_; } + // Sets the current value of `rep_` for testing purposes only. + void SetCordRepForTesting(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS { + rep_ = rep; + } + // Returns the stack trace for where the cord was first sampled. Cords are // potentially sampled when they promote from an inlined cord to a tree or // ring representation, which is not necessarily the location where the cord diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h index e42628e3..749c66e7 100644 --- a/absl/strings/internal/resize_uninitialized.h +++ b/absl/strings/internal/resize_uninitialized.h @@ -17,6 +17,7 @@ #ifndef ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ #define ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ +#include #include #include #include @@ -66,6 +67,28 @@ inline void STLStringResizeUninitialized(string_type* s, size_t new_size) { ResizeUninitializedTraits::Resize(s, new_size); } +// Used to ensure exponential growth so that the amortized complexity of +// increasing the string size by a small amount is O(1), in contrast to +// O(str->size()) in the case of precise growth. +template +void STLStringReserveAmortized(string_type* s, size_t new_size) { + const size_t cap = s->capacity(); + if (new_size > cap) { + // Make sure to always grow by at least a factor of 2x. + s->reserve((std::max)(new_size, 2 * cap)); + } +} + +// Like STLStringResizeUninitialized(str, new_size), except guaranteed to use +// exponential growth so that the amortized complexity of increasing the string +// size by a small amount is O(1), in contrast to O(str->size()) in the case of +// precise growth. +template +void STLStringResizeUninitializedAmortized(string_type* s, size_t new_size) { + STLStringReserveAmortized(s, new_size); + STLStringResizeUninitialized(s, new_size); +} + } // namespace strings_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/resize_uninitialized_test.cc b/absl/strings/internal/resize_uninitialized_test.cc index 0f8b3c2a..01ee476b 100644 --- a/absl/strings/internal/resize_uninitialized_test.cc +++ b/absl/strings/internal/resize_uninitialized_test.cc @@ -24,11 +24,13 @@ int resize_call_count = 0; // resize() method has been called. struct resizable_string { size_t size() const { return 0; } + size_t capacity() const { return 0; } char& operator[](size_t) { static char c = '\0'; return c; } void resize(size_t) { resize_call_count += 1; } + void reserve(size_t) {} }; int resize_default_init_call_count = 0; @@ -37,12 +39,14 @@ int resize_default_init_call_count = 0; // resize() and __resize_default_init() methods have been called. struct resize_default_init_string { size_t size() const { return 0; } + size_t capacity() const { return 0; } char& operator[](size_t) { static char c = '\0'; return c; } void resize(size_t) { resize_call_count += 1; } void __resize_default_init(size_t) { resize_default_init_call_count += 1; } + void reserve(size_t) {} }; TEST(ResizeUninit, WithAndWithout) { @@ -60,6 +64,9 @@ TEST(ResizeUninit, WithAndWithout) { absl::strings_internal::STLStringResizeUninitialized(&rs, 237); EXPECT_EQ(resize_call_count, 1); EXPECT_EQ(resize_default_init_call_count, 0); + absl::strings_internal::STLStringResizeUninitializedAmortized(&rs, 1000); + EXPECT_EQ(resize_call_count, 2); + EXPECT_EQ(resize_default_init_call_count, 0); } resize_call_count = 0; @@ -76,7 +83,23 @@ TEST(ResizeUninit, WithAndWithout) { absl::strings_internal::STLStringResizeUninitialized(&rus, 237); EXPECT_EQ(resize_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 1); + absl::strings_internal::STLStringResizeUninitializedAmortized(&rus, 1000); + EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 2); + } +} + +TEST(ResizeUninit, Amortized) { + std::string str; + size_t prev_cap = str.capacity(); + int cap_increase_count = 0; + for (int i = 0; i < 1000; ++i) { + absl::strings_internal::STLStringResizeUninitializedAmortized(&str, i); + size_t new_cap = str.capacity(); + if (new_cap > prev_cap) ++cap_increase_count; + prev_cap = new_cap; } + EXPECT_LT(cap_increase_count, 50); } } // namespace diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index dd5d25b0..f4a77493 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -174,7 +174,7 @@ void AppendPieces(std::string* dest, ASSERT_NO_OVERLAP(*dest, piece); total_size += piece.size(); } - strings_internal::STLStringResizeUninitialized(dest, total_size); + strings_internal::STLStringResizeUninitializedAmortized(dest, total_size); char* const begin = &(*dest)[0]; char* out = begin + old_size; @@ -199,7 +199,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) { ASSERT_NO_OVERLAP(*dest, a); ASSERT_NO_OVERLAP(*dest, b); std::string::size_type old_size = dest->size(); - strings_internal::STLStringResizeUninitialized( + strings_internal::STLStringResizeUninitializedAmortized( dest, old_size + a.size() + b.size()); char* const begin = &(*dest)[0]; char* out = begin + old_size; @@ -214,7 +214,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, ASSERT_NO_OVERLAP(*dest, b); ASSERT_NO_OVERLAP(*dest, c); std::string::size_type old_size = dest->size(); - strings_internal::STLStringResizeUninitialized( + strings_internal::STLStringResizeUninitializedAmortized( dest, old_size + a.size() + b.size() + c.size()); char* const begin = &(*dest)[0]; char* out = begin + old_size; @@ -231,7 +231,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, ASSERT_NO_OVERLAP(*dest, c); ASSERT_NO_OVERLAP(*dest, d); std::string::size_type old_size = dest->size(); - strings_internal::STLStringResizeUninitialized( + strings_internal::STLStringResizeUninitializedAmortized( dest, old_size + a.size() + b.size() + c.size() + d.size()); char* const begin = &(*dest)[0]; char* out = begin + old_size; diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc index a11f93e2..294f2e22 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -1135,7 +1135,7 @@ TEST(Parse, ExtendedSeconds) { // All %ES cases are treated the same as %E*S on input. auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}; - for (const std::string& prec : precisions) { + for (const std::string prec : precisions) { const std::string fmt = "%E" + prec + "S"; SCOPED_TRACE(fmt); time_point tp = unix_epoch; @@ -1217,7 +1217,7 @@ TEST(Parse, ExtendedSubeconds) { // All %Ef cases are treated the same as %E*f on input. auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}; - for (const std::string& prec : precisions) { + for (const std::string prec : precisions) { const std::string fmt = "%E" + prec + "f"; SCOPED_TRACE(fmt); time_point tp = unix_epoch - chrono::seconds(1); -- cgit v1.2.3 From 5de90e2673bd0c19de67c68e2fe543444dc1114a Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 18 May 2021 07:41:30 -0700 Subject: Export of internal Abseil changes -- 30af50146f07d7afdb196abf235f44ec44ea6409 by Derek Mauro : Update Abseil dependency versions PiperOrigin-RevId: 374415690 -- 6a241e9ea8ee201789bb66aadf9e1ca9cc23c5dc by Derek Mauro : Fix the install test project, which is doing a mixed-mode build. This manifests in failure with GCC 11, which defaults to C++17 Also explicitly reference python3 in the test script. python2 is being removed by some Linux distros. PiperOrigin-RevId: 374278107 -- 42f85122407e799183880e0b2e2a71f6d35ee003 by Evan Brown : Use `capacity + Group::kWidth` control bytes instead of `capacity + Group::kWidth + 1`. We don't use the extra byte - we only need the first `capacity` ones that are valid, the one sentinel byte and then `Group::kWidth - 1` cloned control bytes. For example, in `EmptyGroup()`, where capacity is 0, we use `Group::kWidth` control bytes. We need to update set_ctrl() to only mirror `Group::kWidth - 1` bytes instead of `Group::kWidth` bytes. Note: this doesn't actually save memory unless `alignof(value_type)` is one. Also add/update related comments. PiperOrigin-RevId: 374198589 -- 5dcd09b2221bf23add583541769b476dac54e112 by Evan Brown : Improve b-tree value_comp() support. - Don't expose internal adapted comparator functionality. - Support value_comp() for maps using function pointer comparators. - For maps, make value_compare's constructor and the member comparator be protected - [reference](https://en.cppreference.com/w/cpp/container/map/value_compare). PiperOrigin-RevId: 373832649 -- d75819786ff5a06e1ed0c5c1e80b3b95ac49c83b by Evan Brown : Don't export adapted comparator functionality in key_comp(). PiperOrigin-RevId: 373651039 GitOrigin-RevId: 30af50146f07d7afdb196abf235f44ec44ea6409 Change-Id: I260480b0923cefeba81c543e2b7c34cacaa7d7c7 --- CMake/install_test_project/CMakeLists.txt | 2 -- WORKSPACE | 20 ++++++------- absl/container/btree_test.cc | 38 +++++++++++++++++-------- absl/container/internal/btree.h | 47 ++++++++++++++++++++++--------- absl/container/internal/btree_container.h | 10 +++---- absl/container/internal/raw_hash_set.h | 14 +++++---- ci/cmake_common.sh | 2 +- create_lts.py | 2 +- 8 files changed, 84 insertions(+), 51 deletions(-) diff --git a/CMake/install_test_project/CMakeLists.txt b/CMake/install_test_project/CMakeLists.txt index 06b797e9..eebfe617 100644 --- a/CMake/install_test_project/CMakeLists.txt +++ b/CMake/install_test_project/CMakeLists.txt @@ -18,8 +18,6 @@ cmake_minimum_required(VERSION 3.5) project(absl_cmake_testing CXX) -set(CMAKE_CXX_STANDARD 11) - add_executable(simple simple.cc) find_package(absl REQUIRED) diff --git a/WORKSPACE b/WORKSPACE index 258d23b5..38db2fde 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -21,25 +21,23 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "com_google_googletest", # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. - urls = ["https://github.com/google/googletest/archive/8567b09290fe402cf01923e2131c5635b8ed851b.zip"], # 2020-06-12T22:24:28Z - strip_prefix = "googletest-8567b09290fe402cf01923e2131c5635b8ed851b", - sha256 = "9a8a166eb6a56c7b3d7b19dc2c946fe4778fd6f21c7a12368ad3b836d8f1be48", + urls = ["https://github.com/google/googletest/archive/f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4.zip"], # 2021-04-29T14:40:44Z + strip_prefix = "googletest-f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4", + sha256 = "e61e3889bd5cc3e6bc1084d2108ecda2f110c0387ba88b394ffd16043a1d5709", ) # Google benchmark. http_archive( name = "com_github_google_benchmark", - urls = ["https://github.com/google/benchmark/archive/bf585a2789e30585b4e3ce6baf11ef2750b54677.zip"], # 2020-11-26T11:14:03Z - strip_prefix = "benchmark-bf585a2789e30585b4e3ce6baf11ef2750b54677", - sha256 = "2a778d821997df7d8646c9c59b8edb9a573a6e04c534c01892a40aa524a7b68c", + urls = ["https://github.com/google/benchmark/archive/7d0d9061d83b663ce05d9de5da3d5865a3845b79.zip"], # 2021-05-11T11:56:00Z + strip_prefix = "benchmark-7d0d9061d83b663ce05d9de5da3d5865a3845b79", + sha256 = "a07789754963e3ea3a1e13fed3a4d48fac0c5f7f749c5065f6c30cd2c1529661", ) # C++ rules for Bazel. http_archive( name = "rules_cc", - sha256 = "9a446e9dd9c1bb180c86977a8dc1e9e659550ae732ae58bd2e8fd51e15b2c91d", - strip_prefix = "rules_cc-262ebec3c2296296526740db4aefce68c80de7fa", - urls = [ - "https://github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip", - ], + urls = ["https://github.com/bazelbuild/rules_cc/archive/ab5395627c80e025e824bd005d41f96b20618b9d.zip"], # 2021-05-10T15:35:04Z + strip_prefix = "rules_cc-ab5395627c80e025e824bd005d41f96b20618b9d", + sha256 = "f3908cb40a6577ab0d1ef9e00052739bf69b4313fa7d20b4d61d4521ea67abf3", ) diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index 464dabac..d5d79151 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -1708,10 +1708,25 @@ TEST(Btree, StrSplitCompatible) { EXPECT_EQ(split_set, expected_set); } -// We can't use EXPECT_EQ/etc. to compare absl::weak_ordering because they -// convert literal 0 to int and absl::weak_ordering can only be compared with -// literal 0. Defining this function allows for avoiding ClangTidy warnings. -bool Identity(const bool b) { return b; } +TEST(Btree, KeyComp) { + absl::btree_set s; + EXPECT_TRUE(s.key_comp()(1, 2)); + EXPECT_FALSE(s.key_comp()(2, 2)); + EXPECT_FALSE(s.key_comp()(2, 1)); + + absl::btree_map m1; + EXPECT_TRUE(m1.key_comp()(1, 2)); + EXPECT_FALSE(m1.key_comp()(2, 2)); + EXPECT_FALSE(m1.key_comp()(2, 1)); + + // Even though we internally adapt the comparator of `m2` to be three-way and + // heterogeneous, the comparator we expose through key_comp() is the original + // unadapted comparator. + absl::btree_map m2; + EXPECT_TRUE(m2.key_comp()("a", "b")); + EXPECT_FALSE(m2.key_comp()("b", "b")); + EXPECT_FALSE(m2.key_comp()("b", "a")); +} TEST(Btree, ValueComp) { absl::btree_set s; @@ -1724,13 +1739,13 @@ TEST(Btree, ValueComp) { EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(2, 0))); EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(1, 0))); + // Even though we internally adapt the comparator of `m2` to be three-way and + // heterogeneous, the comparator we expose through value_comp() is based on + // the original unadapted comparator. absl::btree_map m2; - EXPECT_TRUE(Identity( - m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)) < 0)); - EXPECT_TRUE(Identity( - m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)) == 0)); - EXPECT_TRUE(Identity( - m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)) > 0)); + EXPECT_TRUE(m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0))); + EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0))); + EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0))); } TEST(Btree, DefaultConstruction) { @@ -2906,8 +2921,7 @@ TEST(Btree, SupportsFunctionPtrComparator) { map[1] = 1; EXPECT_THAT(map, ElementsAre(Pair(1, 1))); EXPECT_TRUE(map.key_comp()(1, 2)); - // TODO(ezb): support value_comp() in this case and uncomment. - // EXPECT_TRUE(map.value_comp()(std::make_pair(1, 1), std::make_pair(2, 2))); + EXPECT_TRUE(map.value_comp()(std::make_pair(1, 1), std::make_pair(2, 2))); } template diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index d372a1d6..f636c5fc 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -88,7 +88,12 @@ struct StringBtreeDefaultLess { // Compatibility constructor. StringBtreeDefaultLess(std::less) {} // NOLINT - StringBtreeDefaultLess(std::less) {} // NOLINT + StringBtreeDefaultLess(std::less) {} // NOLINT + + // Allow converting to std::less for use in key_comp()/value_comp(). + explicit operator std::less() const { return {}; } + explicit operator std::less() const { return {}; } + explicit operator std::less() const { return {}; } absl::weak_ordering operator()(absl::string_view lhs, absl::string_view rhs) const { @@ -115,7 +120,12 @@ struct StringBtreeDefaultGreater { StringBtreeDefaultGreater() = default; StringBtreeDefaultGreater(std::greater) {} // NOLINT - StringBtreeDefaultGreater(std::greater) {} // NOLINT + StringBtreeDefaultGreater(std::greater) {} // NOLINT + + // Allow converting to std::greater for use in key_comp()/value_comp(). + explicit operator std::greater() const { return {}; } + explicit operator std::greater() const { return {}; } + explicit operator std::greater() const { return {}; } absl::weak_ordering operator()(absl::string_view lhs, absl::string_view rhs) const { @@ -217,6 +227,8 @@ struct prefers_linear_node_search< template struct common_params { + using original_key_compare = Compare; + // If Compare is a common comparator for a string-like type, then we adapt it // to use heterogeneous lookup and to be a key-compare-to comparator. using key_compare = typename key_compare_to_adapter::type; @@ -317,16 +329,21 @@ struct map_params : common_params + friend class btree; - template - auto operator()(const T &left, const U &right) const - -> decltype(std::declval()(left.first, right.first)) { - return key_compare::operator()(left.first, right.first); + protected: + explicit value_compare(original_key_compare c) : comp(std::move(c)) {} + + original_key_compare comp; // NOLINT + + public: + auto operator()(const value_type &lhs, const value_type &rhs) const + -> decltype(comp(lhs.first, rhs.first)) { + return comp(lhs.first, rhs.first); } }; using is_map_container = std::true_type; @@ -392,7 +409,8 @@ struct set_params : common_params> { using value_type = Key; using slot_type = typename set_params::common_params::slot_type; - using value_compare = typename set_params::common_params::key_compare; + using value_compare = + typename set_params::common_params::original_key_compare; using is_map_container = std::false_type; template @@ -1129,6 +1147,7 @@ class btree { using size_type = typename Params::size_type; using difference_type = typename Params::difference_type; using key_compare = typename Params::key_compare; + using original_key_compare = typename Params::original_key_compare; using value_compare = typename Params::value_compare; using allocator_type = typename Params::allocator_type; using reference = typename Params::reference; @@ -1338,7 +1357,9 @@ class btree { return compare_internal::compare_result_as_less_than(key_comp()(a, b)); } - value_compare value_comp() const { return value_compare(key_comp()); } + value_compare value_comp() const { + return value_compare(original_key_compare(key_comp())); + } // Verifies the structure of the btree. void verify() const; diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index 03be708e..4f5d56dd 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -51,7 +51,7 @@ class btree_container { using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; using difference_type = typename Tree::difference_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using value_compare = typename Tree::value_compare; using allocator_type = typename Tree::allocator_type; using reference = typename Tree::reference; @@ -214,7 +214,7 @@ class btree_container { allocator_type get_allocator() const { return tree_.get_allocator(); } // The key comparator used by the btree. - key_compare key_comp() const { return tree_.key_comp(); } + key_compare key_comp() const { return key_compare(tree_.key_comp()); } value_compare value_comp() const { return tree_.value_comp(); } // Support absl::Hash. @@ -247,7 +247,7 @@ class btree_set_container : public btree_container { using key_type = typename Tree::key_type; using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; @@ -398,7 +398,7 @@ class btree_map_container : public btree_set_container { using key_type = typename Tree::key_type; using mapped_type = typename params_type::mapped_type; using value_type = typename Tree::value_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; @@ -543,7 +543,7 @@ class btree_multiset_container : public btree_container { using key_type = typename Tree::key_type; using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index b23e0078..669785e6 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -628,7 +628,9 @@ class raw_hash_set { static Layout MakeLayout(size_t capacity) { assert(IsValidCapacity(capacity)); - return Layout(capacity + Group::kWidth + 1, capacity); + // The extra control bytes are for 1 sentinel byte followed by + // `Group::kWidth - 1` bytes that are cloned from the beginning. + return Layout(capacity + Group::kWidth, capacity); } using AllocTraits = absl::allocator_traits; @@ -1782,8 +1784,8 @@ class raw_hash_set { growth_left() = CapacityToGrowth(capacity()) - size_; } - // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at - // the end too. + // Sets the control byte, and if `i < Group::kWidth - 1`, set the cloned byte + // at the end too. void set_ctrl(size_t i, ctrl_t h) { assert(i < capacity_); @@ -1794,8 +1796,8 @@ class raw_hash_set { } ctrl_[i] = h; - ctrl_[((i - Group::kWidth) & capacity_) + 1 + - ((Group::kWidth - 1) & capacity_)] = h; + constexpr size_t kClonedBytes = Group::kWidth - 1; + ctrl_[((i - kClonedBytes) & capacity_) + (kClonedBytes & capacity_)] = h; } size_t& growth_left() { return settings_.template get<0>(); } @@ -1814,7 +1816,7 @@ class raw_hash_set { // TODO(alkis): Investigate removing some of these fields: // - ctrl/slots can be derived from each other // - size can be moved into the slot array - ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t] + ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + Group::kWidth) * ctrl_t] slot_type* slots_ = nullptr; // [capacity * slot_type] size_t size_ = 0; // number of full slots size_t capacity_ = 0; // total number of slots diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index aec8a117..f64dd1a0 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="8567b09290fe402cf01923e2131c5635b8ed851b" +readonly ABSL_GOOGLETEST_COMMIT="f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4" # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then diff --git a/create_lts.py b/create_lts.py index 302812ad..d5d7b28c 100755 --- a/create_lts.py +++ b/create_lts.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # # Copyright 2021 The Abseil Authors. # -- cgit v1.2.3 From 7971fb358ae376e016d2d4fc9327aad95659b25e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 19 May 2021 12:55:13 -0700 Subject: Export of internal Abseil changes -- 4c6405d1be98fc669b5167970783da00c6a43b86 by Derek Mauro : Turn off -Warray-bounds for GCC in internal/inlined_vector.h GCC 11 appears to erroneously warn here PiperOrigin-RevId: 374709739 -- be9909b010b3da5967ab0ff44a09be034a1fa82f by Derek Mauro : Add non-templated operator new and new[] to ThrowingValue. GCC 11 is confused and issues -Wmismatched-new-delete. PiperOrigin-RevId: 374648084 -- a30a87bf1498aff26bcd751daec120d14a934d99 by Derek Mauro : Simplify ThrowingAllocator::select_on_container_copy_construction to workaround what may be a clang c++20 bug PiperOrigin-RevId: 374630576 GitOrigin-RevId: 4c6405d1be98fc669b5167970783da00c6a43b86 Change-Id: I48f1091c4a0f262961d9059dac4e6b44a82ae91d --- absl/base/internal/exception_safety_testing.h | 26 +++++++++++++++++--------- absl/container/internal/inlined_vector.h | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index 6ba89d05..77a5aec6 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h @@ -536,7 +536,22 @@ class ThrowingValue : private exceptions_internal::TrackedObject { } // Memory management operators - // Args.. allows us to overload regular and placement new in one shot + static void* operator new(size_t s) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new(s); + } + + static void* operator new[](size_t s) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new[](s); + } + template static void* operator new(size_t s, Args&&... args) noexcept( IsSpecified(TypeSpec::kNoThrowNew)) { @@ -557,12 +572,6 @@ class ThrowingValue : private exceptions_internal::TrackedObject { // Abseil doesn't support throwing overloaded operator delete. These are // provided so a throwing operator-new can clean up after itself. - // - // We provide both regular and templated operator delete because if only the - // templated version is provided as we did with operator new, the compiler has - // no way of knowing which overload of operator delete to call. See - // https://en.cppreference.com/w/cpp/memory/new/operator_delete and - // https://en.cppreference.com/w/cpp/language/delete for the gory details. void operator delete(void* p) noexcept { ::operator delete(p); } template @@ -726,9 +735,8 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { ThrowingAllocator select_on_container_copy_construction() noexcept( IsSpecified(AllocSpec::kNoThrowAllocate)) { - auto& out = *this; ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); - return out; + return *this; } template diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index b8aec45b..49822af0 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -36,6 +36,7 @@ namespace inlined_vector_internal { // GCC does not deal very well with the below code #if !defined(__clang__) && defined(__GNUC__) #pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif @@ -955,7 +956,7 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); } -// End ignore "maybe-uninitialized" +// End ignore "array-bounds" and "maybe-uninitialized" #if !defined(__clang__) && defined(__GNUC__) #pragma GCC diagnostic pop #endif -- cgit v1.2.3 From 7e8ec214f2ceb949ad8ad33d83c194de3028ae55 Mon Sep 17 00:00:00 2001 From: Gabriel Ebner Date: Tue, 25 May 2021 22:16:06 +0200 Subject: Use CMAKE_INSTALL_FULL_{LIBDIR,INCLUDEDIR}. (#963) --- CMake/AbseilHelpers.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 1a80b5b4..1fa57a73 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -171,8 +171,8 @@ function(absl_cc_library) FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\ prefix=${CMAKE_INSTALL_PREFIX}\n\ exec_prefix=\${prefix}\n\ -libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n\ -includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\n\ +libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\ +includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}\n\ \n\ Name: absl_${_NAME}\n\ Description: Abseil ${_NAME} library\n\ -- cgit v1.2.3 From 18045c4e32cbe6217224f46261b9e2607d6e723d Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 25 May 2021 13:13:33 -0700 Subject: Export of internal Abseil changes -- d74f30ef0da7139c30a24eb6c1776bc0c257e642 by Derek Mauro : Internal change PiperOrigin-RevId: 375779441 -- 25e33cde30d78dbafcb81ee3ce77c3d70c74db5a by Abseil Team : Internal change PiperOrigin-RevId: 375520720 -- 46857d17d4dbc6fbd13a27dd82e14ec814dd117a by Samuel Benzaquen : Add an explicit template parameter barrier to absl::HashOf. PiperOrigin-RevId: 375103300 -- cf2bf52b9a425b0360ed1f46778f0ef1326a3658 by Abseil Team : Improve string_view API compliance with C++17 std::basic_string_view. This adds missing overloads, default values, and constexpr specifiers. PiperOrigin-RevId: 374885658 -- 41e55166dc5bd3ed7bce567c400781585d55e1ce by Derek Mauro : Update CI to GCC 11.1, Bazel 4.0.0, CMake 3.20.2, and LLVM git:7bcc0a1e399d461af7ec013ff33bc330a2de6641 PiperOrigin-RevId: 374858288 GitOrigin-RevId: d74f30ef0da7139c30a24eb6c1776bc0c257e642 Change-Id: I657c815c83522b030495ff54ca327e7012ed151e --- absl/debugging/internal/stacktrace_x86-inl.inc | 7 +- absl/hash/hash.h | 2 +- absl/hash/hash_test.cc | 13 +++ absl/strings/string_view.cc | 37 +++---- absl/strings/string_view.h | 130 +++++++++++++++++++------ absl/strings/string_view_test.cc | 46 ++++++++- ci/linux_docker_containers.sh | 4 +- 7 files changed, 182 insertions(+), 57 deletions(-) diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc index bc320ff7..70f79dfc 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -132,9 +132,8 @@ static uintptr_t GetFP(const void *vuc) { const uintptr_t bp = 0; const uintptr_t sp = 0; #endif - // Sanity-check that the base pointer is valid. It should be as long as - // SHRINK_WRAP_FRAME_POINTER is not set, but it's possible that some code in - // the process is compiled with --copt=-fomit-frame-pointer or + // Sanity-check that the base pointer is valid. It's possible that some + // code in the process is compiled with --copt=-fomit-frame-pointer or // --copt=-momit-leaf-frame-pointer. // // TODO(bcmills): -momit-leaf-frame-pointer is currently the default @@ -247,7 +246,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) { // using an alternate signal stack. // // TODO(bcmills): The GetFP call should be completely unnecessary when - // SHRINK_WRAP_FRAME_POINTER is set (because we should be back in the thread's + // ENABLE_COMBINED_UNWINDER is set (because we should be back in the thread's // stack by this point), but it is empirically still needed (e.g. when the // stack includes a call to abort). unw_get_reg returns UNW_EBADREG for some // frames. Figure out why GetValidFrameAddr and/or libunwind isn't doing what diff --git a/absl/hash/hash.h b/absl/hash/hash.h index 69c44fde..8282ea53 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h @@ -230,7 +230,7 @@ using Hash = absl::hash_internal::Hash; // The requirement that the arguments match in both type and value is critical. // It means that `a == b` does not necessarily imply `HashOf(a) == HashOf(b)` if // `a` and `b` have different types. For example, `HashOf(2) != HashOf(2.0)`. -template +template size_t HashOf(const Types&... values) { auto tuple = std::tie(values...); return absl::Hash{}(tuple); diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 02b7733d..b3ddebdd 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -995,4 +995,17 @@ TEST(HashOf, MatchesHashOfTupleForMultipleArguments) { absl::HashOf(std::make_tuple(hello, world))); } +template +std::true_type HashOfExplicitParameter(decltype(absl::HashOf(0))) { + return {}; +} +template +std::false_type HashOfExplicitParameter(size_t) { + return {}; +} + +TEST(HashOf, CantPassExplicitTemplateParameters) { + EXPECT_FALSE(HashOfExplicitParameter(0)); +} + } // namespace diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc index c5f5de93..d596e08c 100644 --- a/absl/strings/string_view.cc +++ b/absl/strings/string_view.cc @@ -78,8 +78,8 @@ std::ostream& operator<<(std::ostream& o, string_view piece) { return o; } -string_view::size_type string_view::find(string_view s, size_type pos) const - noexcept { +string_view::size_type string_view::find(string_view s, + size_type pos) const noexcept { if (empty() || pos > length_) { if (empty() && pos == 0 && s.empty()) return 0; return npos; @@ -98,8 +98,8 @@ string_view::size_type string_view::find(char c, size_type pos) const noexcept { return result != nullptr ? result - ptr_ : npos; } -string_view::size_type string_view::rfind(string_view s, size_type pos) const - noexcept { +string_view::size_type string_view::rfind(string_view s, + size_type pos) const noexcept { if (length_ < s.length_) return npos; if (s.empty()) return std::min(length_, pos); const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_; @@ -108,8 +108,8 @@ string_view::size_type string_view::rfind(string_view s, size_type pos) const } // Search range is [0..pos] inclusive. If pos == npos, search everything. -string_view::size_type string_view::rfind(char c, size_type pos) const - noexcept { +string_view::size_type string_view::rfind(char c, + size_type pos) const noexcept { // Note: memrchr() is not available on Windows. if (empty()) return npos; for (size_type i = std::min(pos, length_ - 1);; --i) { @@ -121,9 +121,8 @@ string_view::size_type string_view::rfind(char c, size_type pos) const return npos; } -string_view::size_type string_view::find_first_of(string_view s, - size_type pos) const - noexcept { +string_view::size_type string_view::find_first_of( + string_view s, size_type pos) const noexcept { if (empty() || s.empty()) { return npos; } @@ -138,9 +137,8 @@ string_view::size_type string_view::find_first_of(string_view s, return npos; } -string_view::size_type string_view::find_first_not_of(string_view s, - size_type pos) const - noexcept { +string_view::size_type string_view::find_first_not_of( + string_view s, size_type pos) const noexcept { if (empty()) return npos; // Avoid the cost of LookupTable() for a single-character search. if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos); @@ -153,9 +151,8 @@ string_view::size_type string_view::find_first_not_of(string_view s, return npos; } -string_view::size_type string_view::find_first_not_of(char c, - size_type pos) const - noexcept { +string_view::size_type string_view::find_first_not_of( + char c, size_type pos) const noexcept { if (empty()) return npos; for (; pos < length_; ++pos) { if (ptr_[pos] != c) { @@ -180,9 +177,8 @@ string_view::size_type string_view::find_last_of(string_view s, return npos; } -string_view::size_type string_view::find_last_not_of(string_view s, - size_type pos) const - noexcept { +string_view::size_type string_view::find_last_not_of( + string_view s, size_type pos) const noexcept { if (empty()) return npos; size_type i = std::min(pos, length_ - 1); if (s.empty()) return i; @@ -198,9 +194,8 @@ string_view::size_type string_view::find_last_not_of(string_view s, return npos; } -string_view::size_type string_view::find_last_not_of(char c, - size_type pos) const - noexcept { +string_view::size_type string_view::find_last_not_of( + char c, size_type pos) const noexcept { if (empty()) return npos; size_type i = std::min(pos, length_ - 1); for (;; --i) { diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 1f14a758..968549be 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -62,6 +62,12 @@ ABSL_NAMESPACE_END #define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp #endif // ABSL_HAVE_BUILTIN(__builtin_memcmp) +#if defined(__cplusplus) && __cplusplus >= 201402L +#define ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR constexpr +#else +#define ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR +#endif + namespace absl { ABSL_NAMESPACE_BEGIN @@ -265,9 +271,7 @@ class string_view { // string_view::size() // // Returns the number of characters in the `string_view`. - constexpr size_type size() const noexcept { - return length_; - } + constexpr size_type size() const noexcept { return length_; } // string_view::length() // @@ -334,7 +338,7 @@ class string_view { // // Removes the first `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. - void remove_prefix(size_type n) { + ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_prefix(size_type n) { ABSL_HARDENING_ASSERT(n <= length_); ptr_ += n; length_ -= n; @@ -344,7 +348,7 @@ class string_view { // // Removes the last `n` characters from the `string_view`. Note that the // underlying string is not changed, only the view. - void remove_suffix(size_type n) { + ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_suffix(size_type n) { ABSL_HARDENING_ASSERT(n <= length_); length_ -= n; } @@ -352,7 +356,7 @@ class string_view { // string_view::swap() // // Swaps this `string_view` with another `string_view`. - void swap(string_view& s) noexcept { + ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void swap(string_view& s) noexcept { auto t = *this; *this = s; s = t; @@ -389,7 +393,7 @@ class string_view { // `n`) as another string_view. This function throws `std::out_of_bounds` if // `pos > size`. // Use absl::ClippedSubstr if you need a truncating substr operation. - constexpr string_view substr(size_type pos, size_type n = npos) const { + constexpr string_view substr(size_type pos = 0, size_type n = npos) const { return ABSL_PREDICT_FALSE(pos > length_) ? (base_internal::ThrowStdOutOfRange( "absl::string_view::substr"), @@ -413,31 +417,31 @@ class string_view { // Overload of `string_view::compare()` for comparing a substring of the // 'string_view` and another `absl::string_view`. - int compare(size_type pos1, size_type count1, string_view v) const { + constexpr int compare(size_type pos1, size_type count1, string_view v) const { return substr(pos1, count1).compare(v); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a substring of another `absl::string_view`. - int compare(size_type pos1, size_type count1, string_view v, size_type pos2, - size_type count2) const { + constexpr int compare(size_type pos1, size_type count1, string_view v, + size_type pos2, size_type count2) const { return substr(pos1, count1).compare(v.substr(pos2, count2)); } // Overload of `string_view::compare()` for comparing a `string_view` and a - // a different C-style string `s`. - int compare(const char* s) const { return compare(string_view(s)); } + // a different C-style string `s`. + constexpr int compare(const char* s) const { return compare(string_view(s)); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a different string C-style string `s`. - int compare(size_type pos1, size_type count1, const char* s) const { + constexpr int compare(size_type pos1, size_type count1, const char* s) const { return substr(pos1, count1).compare(string_view(s)); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a substring of a different C-style string `s`. - int compare(size_type pos1, size_type count1, const char* s, - size_type count2) const { + constexpr int compare(size_type pos1, size_type count1, const char* s, + size_type count2) const { return substr(pos1, count1).compare(string_view(s, count2)); } @@ -454,48 +458,92 @@ class string_view { // within the `string_view`. size_type find(char c, size_type pos = 0) const noexcept; + // Overload of `string_view::find()` for finding a substring of a different + // C-style string `s` within the `string_view`. + size_type find(const char* s, size_type pos, size_type count) const { + return find(string_view(s, count), pos); + } + + // Overload of `string_view::find()` for finding a different C-style string + // `s` within the `string_view`. + size_type find(const char* s, size_type pos = 0) const { + return find(string_view(s), pos); + } + // string_view::rfind() // // Finds the last occurrence of a substring `s` within the `string_view`, // returning the position of the first character's match, or `npos` if no // match was found. - size_type rfind(string_view s, size_type pos = npos) const - noexcept; + size_type rfind(string_view s, size_type pos = npos) const noexcept; // Overload of `string_view::rfind()` for finding the last given character `c` // within the `string_view`. size_type rfind(char c, size_type pos = npos) const noexcept; + // Overload of `string_view::rfind()` for finding a substring of a different + // C-style string `s` within the `string_view`. + size_type rfind(const char* s, size_type pos, size_type count) const { + return rfind(string_view(s, count), pos); + } + + // Overload of `string_view::rfind()` for finding a different C-style string + // `s` within the `string_view`. + size_type rfind(const char* s, size_type pos = npos) const { + return rfind(string_view(s), pos); + } + // string_view::find_first_of() // // Finds the first occurrence of any of the characters in `s` within the // `string_view`, returning the start position of the match, or `npos` if no // match was found. - size_type find_first_of(string_view s, size_type pos = 0) const - noexcept; + size_type find_first_of(string_view s, size_type pos = 0) const noexcept; // Overload of `string_view::find_first_of()` for finding a character `c` // within the `string_view`. - size_type find_first_of(char c, size_type pos = 0) const - noexcept { + size_type find_first_of(char c, size_type pos = 0) const noexcept { return find(c, pos); } + // Overload of `string_view::find_first_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_first_of(const char* s, size_type pos, + size_type count) const { + return find_first_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_first_of()` for finding a different C-style + // string `s` within the `string_view`. + size_type find_first_of(const char* s, size_type pos = 0) const { + return find_first_of(string_view(s), pos); + } + // string_view::find_last_of() // // Finds the last occurrence of any of the characters in `s` within the // `string_view`, returning the start position of the match, or `npos` if no // match was found. - size_type find_last_of(string_view s, size_type pos = npos) const - noexcept; + size_type find_last_of(string_view s, size_type pos = npos) const noexcept; // Overload of `string_view::find_last_of()` for finding a character `c` // within the `string_view`. - size_type find_last_of(char c, size_type pos = npos) const - noexcept { + size_type find_last_of(char c, size_type pos = npos) const noexcept { return rfind(c, pos); } + // Overload of `string_view::find_last_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_last_of(const char* s, size_type pos, size_type count) const { + return find_last_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_last_of()` for finding a different C-style + // string `s` within the `string_view`. + size_type find_last_of(const char* s, size_type pos = npos) const { + return find_last_of(string_view(s), pos); + } + // string_view::find_first_not_of() // // Finds the first occurrence of any of the characters not in `s` within the @@ -507,18 +555,43 @@ class string_view { // that is not `c` within the `string_view`. size_type find_first_not_of(char c, size_type pos = 0) const noexcept; + // Overload of `string_view::find_first_not_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_first_not_of(const char* s, size_type pos, + size_type count) const { + return find_first_not_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_first_not_of()` for finding a different + // C-style string `s` within the `string_view`. + size_type find_first_not_of(const char* s, size_type pos = 0) const { + return find_first_not_of(string_view(s), pos); + } + // string_view::find_last_not_of() // // Finds the last occurrence of any of the characters not in `s` within the // `string_view`, returning the start position of the last non-match, or // `npos` if no non-match was found. size_type find_last_not_of(string_view s, - size_type pos = npos) const noexcept; + size_type pos = npos) const noexcept; // Overload of `string_view::find_last_not_of()` for finding a character // that is not `c` within the `string_view`. - size_type find_last_not_of(char c, size_type pos = npos) const - noexcept; + size_type find_last_not_of(char c, size_type pos = npos) const noexcept; + + // Overload of `string_view::find_last_not_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_last_not_of(const char* s, size_type pos, + size_type count) const { + return find_last_not_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_last_not_of()` for finding a different + // C-style string `s` within the `string_view`. + size_type find_last_not_of(const char* s, size_type pos = npos) const { + return find_last_not_of(string_view(s), pos); + } private: static constexpr size_type kMaxSize = @@ -596,6 +669,7 @@ std::ostream& operator<<(std::ostream& o, string_view piece); ABSL_NAMESPACE_END } // namespace absl +#undef ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR #undef ABSL_INTERNAL_STRING_VIEW_MEMCMP #endif // ABSL_USES_STD_STRING_VIEW diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index 643af8f8..2c13dd1c 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -449,6 +449,24 @@ TEST(StringViewTest, STL2) { EXPECT_EQ(d.find('x', 4), absl::string_view::npos); EXPECT_EQ(e.find('x', 7), absl::string_view::npos); + EXPECT_EQ(a.find(b.data(), 1, 0), 1); + EXPECT_EQ(a.find(c.data(), 9, 0), 9); + EXPECT_EQ(a.find(c.data(), absl::string_view::npos, 0), + absl::string_view::npos); + EXPECT_EQ(b.find(c.data(), absl::string_view::npos, 0), + absl::string_view::npos); + // empty string nonsense + EXPECT_EQ(d.find(b.data(), 4, 0), absl::string_view::npos); + EXPECT_EQ(e.find(b.data(), 7, 0), absl::string_view::npos); + + EXPECT_EQ(a.find(b.data(), 1), absl::string_view::npos); + EXPECT_EQ(a.find(c.data(), 9), 23); + EXPECT_EQ(a.find(c.data(), absl::string_view::npos), absl::string_view::npos); + EXPECT_EQ(b.find(c.data(), absl::string_view::npos), absl::string_view::npos); + // empty string nonsense + EXPECT_EQ(d.find(b.data(), 4), absl::string_view::npos); + EXPECT_EQ(e.find(b.data(), 7), absl::string_view::npos); + EXPECT_EQ(a.rfind(b), 0); EXPECT_EQ(a.rfind(b, 1), 0); EXPECT_EQ(a.rfind(c), 23); @@ -490,6 +508,14 @@ TEST(StringViewTest, STL2) { EXPECT_EQ(e.rfind('o'), absl::string_view::npos); EXPECT_EQ(d.rfind('o', 4), absl::string_view::npos); EXPECT_EQ(e.rfind('o', 7), absl::string_view::npos); + + EXPECT_EQ(a.rfind(b.data(), 1, 0), 1); + EXPECT_EQ(a.rfind(c.data(), 22, 0), 22); + EXPECT_EQ(a.rfind(c.data(), 1, 0), 1); + EXPECT_EQ(a.rfind(c.data(), 0, 0), 0); + EXPECT_EQ(b.rfind(c.data(), 0, 0), 0); + EXPECT_EQ(d.rfind(b.data(), 4, 0), 0); + EXPECT_EQ(e.rfind(b.data(), 7, 0), 0); } // Continued from STL2 @@ -678,6 +704,7 @@ TEST(StringViewTest, STL2Substr) { EXPECT_EQ(a.substr(23, 3), c); EXPECT_EQ(a.substr(23, 99), c); EXPECT_EQ(a.substr(0), a); + EXPECT_EQ(a.substr(), a); EXPECT_EQ(a.substr(3, 2), "de"); // empty string nonsense EXPECT_EQ(d.substr(0, 99), e); @@ -1087,7 +1114,24 @@ TEST(StringViewTest, ConstexprCompiles) { EXPECT_EQ(sp_npos, -1); } -TEST(StringViewTest, ConstexprSubstr) { +constexpr char ConstexprMethodsHelper() { +#if defined(__cplusplus) && __cplusplus >= 201402L + absl::string_view str("123", 3); + str.remove_prefix(1); + str.remove_suffix(1); + absl::string_view bar; + str.swap(bar); + return bar.front(); +#else + return '2'; +#endif +} + +TEST(StringViewTest, ConstexprMethods) { + // remove_prefix, remove_suffix, swap + static_assert(ConstexprMethodsHelper() == '2', ""); + + // substr constexpr absl::string_view foobar("foobar", 6); constexpr absl::string_view foo = foobar.substr(0, 3); constexpr absl::string_view bar = foobar.substr(3); diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh index 1c29d9a1..e7eef0fc 100644 --- a/ci/linux_docker_containers.sh +++ b/ci/linux_docker_containers.sh @@ -16,6 +16,6 @@ # Test scripts should source this file to get the identifiers. readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026" -readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20201008" -readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20201008" +readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210519" +readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210519" readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20201015" -- cgit v1.2.3 From 0d5156018dd3d0d075cc14a0aa6078979c7a85d3 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 27 May 2021 10:22:20 -0700 Subject: Export of internal Abseil changes -- cbd86b318ce206d1fe9eb001979b8d2efe57e5fa by Derek Mauro : Stop inlining `CurrentThreadIdentityIfPresent()` on Apple platforms In some build configurations the Apple linker rejects thread_local variables exposed in headers Fixes #954 Fixes #965 PiperOrigin-RevId: 375930997 GitOrigin-RevId: e34600e09f748b686d20a4f729ae914ebfd9e7bf Change-Id: I9fcd2f02bd6abdea3ed412217e1be18f2c7bf762 --- absl/base/internal/thread_identity.cc | 8 ++++---- absl/base/internal/thread_identity.h | 17 +++++++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index 6ea010ed..9950e63a 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -120,10 +120,10 @@ void SetCurrentThreadIdentity( ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 // Please see the comment on `CurrentThreadIdentityIfPresent` in -// thread_identity.h. Because DLLs cannot expose thread_local variables in -// headers, we opt for the correct-but-slower option of placing the definition -// of this function only in a translation unit inside DLL. -#if defined(ABSL_BUILD_DLL) || defined(ABSL_CONSUME_DLL) +// thread_identity.h. When we cannot expose thread_local variables in +// headers, we opt for the correct-but-slower option of not inlining this +// function. +#ifndef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } #endif #endif diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index 9ee651a3..6e25b92f 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -236,13 +236,18 @@ ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; #error Thread-local storage not detected on this platform #endif -// thread_local variables cannot be in headers exposed by DLLs. However, it is -// important for performance reasons in general that -// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a -// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note +// thread_local variables cannot be in headers exposed by DLLs or in certain +// build configurations on Apple platforms. However, it is important for +// performance reasons in general that `CurrentThreadIdentityIfPresent` be +// inlined. In the other cases we opt to have the function not be inlined. Note // that `CurrentThreadIdentityIfPresent` is declared above so we can exclude -// this entire inline definition when compiling as a DLL. -#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) +// this entire inline definition. +#if !defined(__APPLE__) && !defined(ABSL_BUILD_DLL) && \ + !defined(ABSL_CONSUME_DLL) +#define ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT 1 +#endif + +#ifdef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT inline ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } -- cgit v1.2.3 From 702cae1e762dc6f2f9d31777db04e1adbdb36697 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 1 Jun 2021 18:32:49 -0700 Subject: Export of internal Abseil changes -- 2c81c02d2b68303134e777f31921679ab87a2639 by Derek Mauro : Use getentropy() to get seed material when using glibc >= 2.25 getentropy() uses the getrandom syscall on Linux to avoid file descriptors. It is never interputed (EINTR) and uses the same source as /dev/urandom. https://man7.org/linux/man-pages/man3/getentropy.3.html getrandom has been in the Linux since kernel 3.17 When unavailable (ENOSYS), fallback to /dev/urandom. PiperOrigin-RevId: 376962620 -- 81cd41372a04eda400a2e3be53c239c0dac6bdf3 by Abseil Team : Make FunctionRef no longer have -Wdeprecated-copy warnings from Clang by defaulting the copy constructor. This is needed because the assignment operator is deleted. Fixes #948 PiperOrigin-RevId: 376928548 GitOrigin-RevId: 2c81c02d2b68303134e777f31921679ab87a2639 Change-Id: I0abb18052ffff5dd1448f0b68edbb668045335f0 --- absl/functional/function_ref.h | 1 + absl/random/internal/seed_material.cc | 40 ++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h index 6e03ac2e..5790a652 100644 --- a/absl/functional/function_ref.h +++ b/absl/functional/function_ref.h @@ -122,6 +122,7 @@ class FunctionRef { // To help prevent subtle lifetime bugs, FunctionRef is not assignable. // Typically, it should only be used as an argument type. FunctionRef& operator=(const FunctionRef& rhs) = delete; + FunctionRef(const FunctionRef& rhs) = default; // Call the underlying object. R operator()(Args... args) const { diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc index 4d38a574..0fcba509 100644 --- a/absl/random/internal/seed_material.cc +++ b/absl/random/internal/seed_material.cc @@ -50,6 +50,12 @@ #endif +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +// glibc >= 2.25 has getentropy() +#define ABSL_RANDOM_USE_GET_ENTROPY 1 +#endif + #if defined(ABSL_RANDOM_USE_BCRYPT) #include @@ -122,8 +128,29 @@ bool ReadSeedMaterialFromOSEntropyImpl(absl::Span values) { #else +#if defined(ABSL_RANDOM_USE_GET_ENTROPY) +// On *nix, use getentropy() if supported. Note that libc may support +// getentropy(), but the kernel may not, in which case this function will return +// false. +bool ReadSeedMaterialFromGetEntropy(absl::Span values) { + auto buffer = reinterpret_cast(values.data()); + size_t buffer_size = sizeof(uint32_t) * values.size(); + while (buffer_size > 0) { + // getentropy() has a maximum permitted length of 256. + size_t to_read = std::min(buffer_size, 256); + int result = getentropy(buffer, to_read); + if (result < 0) { + return false; + } + buffer += to_read; + buffer_size -= to_read; + } + return true; +} +#endif // defined(ABSL_RANDOM_GETENTROPY) + // On *nix, read entropy from /dev/urandom. -bool ReadSeedMaterialFromOSEntropyImpl(absl::Span values) { +bool ReadSeedMaterialFromDevURandom(absl::Span values) { const char kEntropyFile[] = "/dev/urandom"; auto buffer = reinterpret_cast(values.data()); @@ -150,6 +177,17 @@ bool ReadSeedMaterialFromOSEntropyImpl(absl::Span values) { return success; } +bool ReadSeedMaterialFromOSEntropyImpl(absl::Span values) { +#if defined(ABSL_RANDOM_USE_GET_ENTROPY) + if (ReadSeedMaterialFromGetEntropy(values)) { + return true; + } +#endif + // Libc may support getentropy, but the kernel may not, so we still have + // to fallback to ReadSeedMaterialFromDevURandom(). + return ReadSeedMaterialFromDevURandom(values); +} + #endif } // namespace -- cgit v1.2.3 From ed53ad03abd7baf39dda2ac8037ff3d4f5c533e5 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 2 Jun 2021 13:32:24 -0700 Subject: Export of internal Abseil changes -- b2a781121ff72fb485b7e67539d5e4ff1eb66df2 by Gennadiy Rozental : Consistently use absl::flat_hash_map instead of std::map in Flags implementation. PiperOrigin-RevId: 377132816 -- 9ab83a154d8f22d51fed0092bf94245b5af1f498 by Derek Mauro : Workaround for MSAN being unable to see through getentropy(). https://github.com/google/sanitizers/issues/1173 PiperOrigin-RevId: 377097059 -- 8d28e921442d1b246c26f3200f21027557c47657 by Greg Falcon : Disable stack_consumption_test in tsan builds. A recent tsan change broke the way this test-only utility was counting stack usage. PiperOrigin-RevId: 377053169 GitOrigin-RevId: b2a781121ff72fb485b7e67539d5e4ff1eb66df2 Change-Id: Ib56356f8128f6c083f32b950091f3a56d9e2cd51 --- absl/debugging/BUILD.bazel | 1 + absl/flags/BUILD.bazel | 1 + absl/flags/CMakeLists.txt | 1 + absl/flags/internal/usage.cc | 12 +++++++++--- absl/flags/reflection.cc | 8 ++++++-- absl/random/internal/BUILD.bazel | 1 + absl/random/internal/seed_material.cc | 4 ++++ 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index 5385bcb6..2aac0f66 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -344,6 +344,7 @@ cc_test( srcs = ["internal/stack_consumption_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["notsan"], deps = [ ":stack_consumption", "//absl/base:core_headers", diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 147249ed..c178b86b 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -259,6 +259,7 @@ cc_library( ":reflection", "//absl/base:config", "//absl/base:core_headers", + "//absl/container:flat_hash_map", "//absl/strings", ], ) diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index caac69cf..827784b1 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -239,6 +239,7 @@ absl_cc_library( absl::flags_private_handle_accessor absl::flags_program_name absl::flags_reflection + absl::flat_hash_map absl::strings absl::synchronization ) diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index a588c7f7..949709e8 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -245,7 +245,7 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, << XMLElement("usage", program_usage_message) << '\n'; } - // Map of package name to + // Ordered map of package name to // map of file name to // vector of flags in the file. // This map is used to output matching flags grouped by package and file @@ -273,20 +273,26 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, absl::string_view package_separator; // controls blank lines between packages absl::string_view file_separator; // controls blank lines between files - for (const auto& package : matching_flags) { + for (auto& package : matching_flags) { if (format == HelpFormat::kHumanReadable) { out << package_separator; package_separator = "\n\n"; } file_separator = ""; - for (const auto& flags_in_file : package.second) { + for (auto& flags_in_file : package.second) { if (format == HelpFormat::kHumanReadable) { out << file_separator << " Flags from " << flags_in_file.first << ":\n"; file_separator = "\n"; } + std::sort(std::begin(flags_in_file.second), + std::end(flags_in_file.second), + [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) { + return lhs->Name() < rhs->Name(); + }); + for (const auto* flag : flags_in_file.second) { flags_internal::FlagHelp(out, *flag, format); } diff --git a/absl/flags/reflection.cc b/absl/flags/reflection.cc index 0c761101..dbce4032 100644 --- a/absl/flags/reflection.cc +++ b/absl/flags/reflection.cc @@ -18,11 +18,11 @@ #include #include -#include #include #include "absl/base/config.h" #include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" #include "absl/flags/commandlineflag.h" #include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/registry.h" @@ -68,7 +68,7 @@ class FlagRegistry { friend void FinalizeRegistry(); // The map from name to flag, for FindFlag(). - using FlagMap = std::map; + using FlagMap = absl::flat_hash_map; using FlagIterator = FlagMap::iterator; using FlagConstIterator = FlagMap::const_iterator; FlagMap flags_; @@ -204,6 +204,10 @@ void FinalizeRegistry() { for (const auto& f : registry.flags_) { registry.flat_flags_.push_back(f.second); } + std::sort(std::begin(registry.flat_flags_), std::end(registry.flat_flags_), + [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) { + return lhs->Name() < rhs->Name(); + }); registry.flags_.clear(); registry.finalized_flags_.store(true, std::memory_order_release); } diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 90efe85a..6e06fe9a 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -82,6 +82,7 @@ cc_library( deps = [ ":fast_uniform_bits", "//absl/base:core_headers", + "//absl/base:dynamic_annotations", "//absl/base:raw_logging_internal", "//absl/strings", "//absl/types:optional", diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc index 0fcba509..7c1d9efa 100644 --- a/absl/random/internal/seed_material.cc +++ b/absl/random/internal/seed_material.cc @@ -28,6 +28,7 @@ #include #include +#include "absl/base/dynamic_annotations.h" #include "absl/base/internal/raw_logging.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" @@ -142,6 +143,9 @@ bool ReadSeedMaterialFromGetEntropy(absl::Span values) { if (result < 0) { return false; } + // https://github.com/google/sanitizers/issues/1173 + // MemorySanitizer can't see through getentropy(). + ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buffer, to_read); buffer += to_read; buffer_size -= to_read; } -- cgit v1.2.3 From 17c954d90d5661e27db8fc5f086085690a8372d9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 3 Jun 2021 14:05:41 -0700 Subject: Export of internal Abseil changes -- 066144400e12616f6771e512427bcd98aa203455 by Abseil Team : Internal comment cleanup. PiperOrigin-RevId: 377368470 -- 3ba56d263bd90a9797d12b5ce29edce3fa65917c by Abseil Team : Add tests for hash table capacity being unchanged by insert. PiperOrigin-RevId: 377303140 GitOrigin-RevId: 066144400e12616f6771e512427bcd98aa203455 Change-Id: I2edf14b412e45a2ad490dcf9f06e009c12a60e3e --- absl/container/internal/raw_hash_map.h | 5 +-- absl/container/internal/raw_hash_set_test.cc | 31 +++++++++++++++++ .../internal/unordered_map_modifiers_test.h | 39 ++++++++++++++++++++-- .../internal/unordered_set_modifiers_test.h | 35 +++++++++++++++++-- 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index 0a02757d..c7df2efc 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h @@ -51,8 +51,9 @@ class raw_hash_map : public raw_hash_set { using key_arg = typename KeyArgImpl::template type; static_assert(!std::is_reference::value, ""); - // TODO(alkis): remove this assertion and verify that reference mapped_type is - // supported. + + // TODO(b/187807849): Evaluate whether to support reference mapped_type and + // remove this assertion if/when it is supported. static_assert(!std::is_reference::value, ""); using iterator = typename raw_hash_map::raw_hash_set::iterator; diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 7dac65a0..af882ef4 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -541,6 +541,37 @@ TEST(Table, InsertCollisionAndFindAfterDelete) { EXPECT_TRUE(t.empty()); } +TEST(Table, InsertWithinCapacity) { + IntTable t; + t.reserve(10); + const size_t original_capacity = t.capacity(); + const auto addr = [&](int i) { + return reinterpret_cast(&*t.find(i)); + }; + // Inserting an element does not change capacity. + t.insert(0); + EXPECT_THAT(t.capacity(), original_capacity); + const uintptr_t original_addr_0 = addr(0); + // Inserting another element does not rehash. + t.insert(1); + EXPECT_THAT(t.capacity(), original_capacity); + EXPECT_THAT(addr(0), original_addr_0); + // Inserting lots of duplicate elements does not rehash. + for (int i = 0; i < 100; ++i) { + t.insert(i % 10); + } + EXPECT_THAT(t.capacity(), original_capacity); + EXPECT_THAT(addr(0), original_addr_0); + // Inserting a range of duplicate elements does not rehash. + std::vector dup_range; + for (int i = 0; i < 100; ++i) { + dup_range.push_back(i % 10); + } + t.insert(dup_range.begin(), dup_range.end()); + EXPECT_THAT(t.capacity(), original_capacity); + EXPECT_THAT(addr(0), original_addr_0); +} + TEST(Table, LazyEmplace) { StringTable t; bool called = false; diff --git a/absl/container/internal/unordered_map_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h index 8c9ca779..d3543936 100644 --- a/absl/container/internal/unordered_map_modifiers_test.h +++ b/absl/container/internal/unordered_map_modifiers_test.h @@ -81,6 +81,38 @@ TYPED_TEST_P(ModifiersTest, InsertRange) { ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); } +TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) { + using T = hash_internal::GeneratedType; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator()(); + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(val); + EXPECT_EQ(m.bucket_count(), original_capacity); + T val2 = {val.first, hash_internal::Generator()()}; + m.insert(val2); + EXPECT_EQ(m.bucket_count(), original_capacity); +} + +TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) { +#if !defined(__GLIBCXX__) + using T = hash_internal::GeneratedType; + std::vector base_values; + std::generate_n(std::back_inserter(base_values), 10, + hash_internal::Generator()); + std::vector values; + while (values.size() != 100) { + std::copy_n(base_values.begin(), 10, std::back_inserter(values)); + } + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(values.begin(), values.end()); + EXPECT_EQ(m.bucket_count(), original_capacity); +#endif +} + TYPED_TEST_P(ModifiersTest, InsertOrAssign) { #ifdef UNORDERED_MAP_CXX17 using std::get; @@ -266,9 +298,10 @@ TYPED_TEST_P(ModifiersTest, Swap) { // TODO(alkis): Write tests for merge. REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, - InsertRange, InsertOrAssign, InsertOrAssignHint, - Emplace, EmplaceHint, TryEmplace, TryEmplaceHint, - Erase, EraseRange, EraseKey, Swap); + InsertRange, InsertWithinCapacity, + InsertRangeWithinCapacity, InsertOrAssign, + InsertOrAssignHint, Emplace, EmplaceHint, TryEmplace, + TryEmplaceHint, Erase, EraseRange, EraseKey, Swap); template struct is_unique_ptr : std::false_type {}; diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h index 26be58d9..6e473e45 100644 --- a/absl/container/internal/unordered_set_modifiers_test.h +++ b/absl/container/internal/unordered_set_modifiers_test.h @@ -74,6 +74,36 @@ TYPED_TEST_P(ModifiersTest, InsertRange) { ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); } +TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) { + using T = hash_internal::GeneratedType; + T val = hash_internal::Generator()(); + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(val); + EXPECT_EQ(m.bucket_count(), original_capacity); + m.insert(val); + EXPECT_EQ(m.bucket_count(), original_capacity); +} + +TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) { +#if !defined(__GLIBCXX__) + using T = hash_internal::GeneratedType; + std::vector base_values; + std::generate_n(std::back_inserter(base_values), 10, + hash_internal::Generator()); + std::vector values; + while (values.size() != 100) { + values.insert(values.end(), base_values.begin(), base_values.end()); + } + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(values.begin(), values.end()); + EXPECT_EQ(m.bucket_count(), original_capacity); +#endif +} + TYPED_TEST_P(ModifiersTest, Emplace) { using T = hash_internal::GeneratedType; T val = hash_internal::Generator()(); @@ -180,8 +210,9 @@ TYPED_TEST_P(ModifiersTest, Swap) { // TODO(alkis): Write tests for merge. REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, - InsertRange, Emplace, EmplaceHint, Erase, EraseRange, - EraseKey, Swap); + InsertRange, InsertWithinCapacity, + InsertRangeWithinCapacity, Emplace, EmplaceHint, + Erase, EraseRange, EraseKey, Swap); } // namespace container_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From f72972654b69085b1d58fde04c618b77d3ac6935 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 8 Jun 2021 18:40:58 -0700 Subject: Export of internal Abseil changes -- 72ce5b636488f17753d110ec18f57132d6180db3 by Derek Mauro : Update GoogleTest version used by Abseil PiperOrigin-RevId: 378296419 -- 1eaa36f65315a1cb95c95dfee0bc31307d280d18 by Abseil Team : Define unary + operators for absl::int128 and absl::uint128. These are rarely used but apparently missing. PiperOrigin-RevId: 377975179 -- 1a029d6ff8f9e21ddf0b89949be04c0a56661359 by Abseil Team : Remove gratuitous reinterpret_cast. PiperOrigin-RevId: 377894806 GitOrigin-RevId: 72ce5b636488f17753d110ec18f57132d6180db3 Change-Id: I8a06f69b3489c9aef8260fd271bde2a55f01807f --- WORKSPACE | 6 +++--- absl/container/internal/raw_hash_set.h | 2 +- absl/numeric/int128.h | 8 ++++++++ absl/numeric/int128_test.cc | 18 ++++++++++++++++++ ci/cmake_common.sh | 2 +- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 38db2fde..bc00ea3f 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -21,9 +21,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "com_google_googletest", # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. - urls = ["https://github.com/google/googletest/archive/f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4.zip"], # 2021-04-29T14:40:44Z - strip_prefix = "googletest-f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4", - sha256 = "e61e3889bd5cc3e6bc1084d2108ecda2f110c0387ba88b394ffd16043a1d5709", + urls = ["https://github.com/google/googletest/archive/5bcd8e3bb929714e031a542d303f818e5a5af45d.zip"], # 2021-06-08T22:36:38Z + strip_prefix = "googletest-5bcd8e3bb929714e031a542d303f818e5a5af45d", + sha256 = "3adecb6686ac7367452561dca518fad5a990fb09c5a961bfa1836f15eb774348", ) # Google benchmark. diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 669785e6..aa78265c 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1553,7 +1553,7 @@ class raw_hash_set { auto layout = MakeLayout(capacity_); char* mem = static_cast( Allocate(&alloc_ref(), layout.AllocSize())); - ctrl_ = reinterpret_cast(layout.template Pointer<0>(mem)); + ctrl_ = layout.template Pointer<0>(mem); slots_ = layout.template Pointer<1>(mem); reset_ctrl(); reset_growth_left(); diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index 235361a8..198aa195 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -810,6 +810,14 @@ inline bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } // Unary operators. +constexpr inline uint128 operator+(uint128 val) { + return val; +} + +constexpr inline int128 operator+(int128 val) { + return val; +} + inline uint128 operator-(uint128 val) { uint64_t hi = ~Uint128High64(val); uint64_t lo = ~Uint128Low64(val) + 1; diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc index bc86c714..c445d89a 100644 --- a/absl/numeric/int128_test.cc +++ b/absl/numeric/int128_test.cc @@ -226,6 +226,11 @@ TEST(Uint128, AllTests) { EXPECT_EQ(test >>= 1, one); EXPECT_EQ(test <<= 1, two); + EXPECT_EQ(big, +big); + EXPECT_EQ(two, +two); + EXPECT_EQ(absl::Uint128Max(), +absl::Uint128Max()); + EXPECT_EQ(zero, +zero); + EXPECT_EQ(big, -(-big)); EXPECT_EQ(two, -((-one) - 1)); EXPECT_EQ(absl::Uint128Max(), -one); @@ -769,6 +774,19 @@ TEST(Int128, ComparisonTest) { } } +TEST(Int128, UnaryPlusTest) { + int64_t values64[] = {0, 1, 12345, 0x4000000000000000, + std::numeric_limits::max()}; + for (int64_t value : values64) { + SCOPED_TRACE(::testing::Message() << "value = " << value); + + EXPECT_EQ(absl::int128(value), +absl::int128(value)); + EXPECT_EQ(absl::int128(-value), +absl::int128(-value)); + EXPECT_EQ(absl::MakeInt128(value, 0), +absl::MakeInt128(value, 0)); + EXPECT_EQ(absl::MakeInt128(-value, 0), +absl::MakeInt128(-value, 0)); + } +} + TEST(Int128, UnaryNegationTest) { int64_t values64[] = {0, 1, 12345, 0x4000000000000000, std::numeric_limits::max()}; diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index f64dd1a0..626fed0d 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4" +readonly ABSL_GOOGLETEST_COMMIT="5bcd8e3bb929714e031a542d303f818e5a5af45d" # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then -- cgit v1.2.3 From 8f92175783c9685045c50f227e7c10f1cddb4d58 Mon Sep 17 00:00:00 2001 From: Florin Crișan Date: Thu, 10 Jun 2021 02:26:40 +0300 Subject: CMake: add option to use Google Test already installed on system (#969) As of this change, you can use `-DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON` to have Abseil use the standard CMake find_package(GTest) mechanism. --- CMake/AbseilHelpers.cmake | 4 +- CMake/README.md | 45 ++++++++++++++ CMakeLists.txt | 39 +++++++++--- absl/algorithm/CMakeLists.txt | 4 +- absl/base/CMakeLists.txt | 58 ++++++++--------- absl/cleanup/CMakeLists.txt | 2 +- absl/container/CMakeLists.txt | 60 +++++++++--------- absl/debugging/CMakeLists.txt | 14 ++--- absl/flags/CMakeLists.txt | 22 +++---- absl/functional/CMakeLists.txt | 4 +- absl/hash/CMakeLists.txt | 8 +-- absl/memory/CMakeLists.txt | 4 +- absl/meta/CMakeLists.txt | 2 +- absl/numeric/CMakeLists.txt | 4 +- absl/random/CMakeLists.txt | 120 ++++++++++++++++++------------------ absl/status/CMakeLists.txt | 4 +- absl/strings/CMakeLists.txt | 80 ++++++++++++------------ absl/synchronization/CMakeLists.txt | 14 ++--- absl/time/CMakeLists.txt | 4 +- absl/types/CMakeLists.txt | 26 ++++---- absl/utility/CMakeLists.txt | 2 +- 21 files changed, 295 insertions(+), 225 deletions(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 1fa57a73..6a64a2c7 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -336,8 +336,8 @@ endfunction() # "awesome_test.cc" # DEPS # absl::awesome -# gmock -# gtest_main +# GTest::gmock +# GTest::gtest_main # ) function(absl_cc_test) if(NOT BUILD_TESTING) diff --git a/CMake/README.md b/CMake/README.md index 5eee8171..fa8e8d30 100644 --- a/CMake/README.md +++ b/CMake/README.md @@ -99,3 +99,48 @@ absl::synchronization absl::time absl::utility ``` + +## Traditional CMake Set-Up + +For larger projects, it may make sense to use the traditional CMake set-up where you build and install projects separately. + +First, you'd need to build and install Google Test: +``` +cmake -S /source/googletest -B /build/googletest -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/installation/dir -DBUILD_GMOCK=ON +cmake --build /build/googletest --target install +``` + +Then you need to configure and build Abseil. Make sure you enable `ABSL_USE_EXTERNAL_GOOGLETEST` and `ABSL_FIND_GOOGLETEST`. You also need to enable `ABSL_ENABLE_INSTALL` so that you can install Abseil itself. +``` +cmake -S /source/abseil-cpp -B /build/abseil-cpp -DCMAKE_PREFIX_PATH=/installation/dir -DCMAKE_INSTALL_PREFIX=/installation/dir -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON +cmake --build /temporary/build/abseil-cpp +``` + +(`CMAKE_PREFIX_PATH` is where you already have Google Test installed; `CMAKE_INSTALL_PREFIX` is where you want to have Abseil installed; they can be different.) + +Run the tests: +``` +ctest --test-dir /temporary/build/abseil-cpp +``` + +And finally install: +``` +cmake --build /temporary/build/abseil-cpp --target install +``` + +# CMake Option Synposis + +## Enable Standard CMake Installation + +`-DABSL_ENABLE_INSTALL=ON` + +## Google Test Options + +`-DBUILD_TESTING=ON` must be set to enable testing + +- Have Abseil download and build Google Test for you: `-DABSL_USE_EXTERNAL_GOOGLETEST=OFF` (default) + - Download and build latest Google Test: `-DABSL_USE_GOOGLETEST_HEAD=ON` + - Download specific Google Test version (ZIP archive): `-DABSL_GOOGLETEST_DOWNLOAD_URL=https://.../version.zip` + - Use Google Test from specific local directory: `-DABSL_LOCAL_GOOGLETEST_DIR=/path/to/googletest` +- Use Google Test included elsewhere in your project: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON` +- Use standard CMake `find_package(CTest)` to find installed Google Test: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON` diff --git a/CMakeLists.txt b/CMakeLists.txt index d0c6e608..42bcbe10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,9 +102,18 @@ endif() ## pthread find_package(Threads REQUIRED) +include(CMakeDependentOption) + option(ABSL_USE_EXTERNAL_GOOGLETEST "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subproject." OFF) +cmake_dependent_option(ABSL_FIND_GOOGLETEST + "If ON, Abseil will use find_package(GTest) rather than assuming that GoogleTest is already provided by the including project." + ON + "ABSL_USE_EXTERNAL_GOOGLETEST" + OFF) + + option(ABSL_USE_GOOGLETEST_HEAD "If ON, abseil will download HEAD from GoogleTest at config time." OFF) @@ -116,7 +125,15 @@ set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH if(BUILD_TESTING) ## check targets - if (NOT ABSL_USE_EXTERNAL_GOOGLETEST) + if (ABSL_USE_EXTERNAL_GOOGLETEST) + if (ABSL_FIND_GOOGLETEST) + find_package(GTest REQUIRED) + else() + if (NOT TARGET gtest AND NOT TARGET GTest::gtest) + message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.") + endif() + endif() + else() set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build) if(ABSL_USE_GOOGLETEST_HEAD AND ABSL_GOOGLETEST_DOWNLOAD_URL) message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL") @@ -134,14 +151,22 @@ if(BUILD_TESTING) include(CMake/Googletest/DownloadGTest.cmake) endif() - check_target(gtest) - check_target(gtest_main) - check_target(gmock) + if (NOT ABSL_FIND_GOOGLETEST) + # When Google Test is included directly rather than through find_package, the aliases are missing. + add_library(GTest::gtest_main ALIAS gtest_main) + add_library(GTest::gtest ALIAS gtest) + add_library(GTest::gmock ALIAS gmock) + endif() + + check_target(GTest::gtest) + check_target(GTest::gtest_main) + check_target(GTest::gmock) + check_target(GTest::gmock_main) list(APPEND ABSL_TEST_COMMON_LIBRARIES - gtest_main - gtest - gmock + GTest::gtest_main + GTest::gtest + GTest::gmock ${CMAKE_THREAD_LIBS_INIT} ) endif() diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index 56cd0fb8..609d8589 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt @@ -35,7 +35,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::algorithm - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -65,5 +65,5 @@ absl_cc_test( absl::core_headers absl::memory absl::span - gmock_main + GTest::gmock_main ) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 981b8cc0..7d56aa13 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -230,7 +230,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::config - gtest + GTest::gtest TESTONLY ) @@ -259,7 +259,7 @@ absl_cc_library( absl::meta absl::strings absl::utility - gtest + GTest::gtest TESTONLY ) @@ -273,7 +273,7 @@ absl_cc_test( DEPS absl::exception_safety_testing absl::memory - gtest_main + GTest::gtest_main ) absl_cc_library( @@ -300,8 +300,8 @@ absl_cc_test( absl::atomic_hook_test_helper absl::atomic_hook absl::core_headers - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -314,7 +314,7 @@ absl_cc_test( DEPS absl::base absl::core_headers - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -327,8 +327,8 @@ absl_cc_test( DEPS absl::errno_saver absl::strerror - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -342,7 +342,7 @@ absl_cc_test( absl::base absl::config absl::throw_delegate - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -357,7 +357,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base_internal - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -371,8 +371,8 @@ absl_cc_test( absl::base_internal absl::memory absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_library( @@ -388,7 +388,7 @@ absl_cc_library( absl::base_internal absl::core_headers absl::synchronization - gtest + GTest::gtest TESTONLY ) @@ -406,7 +406,7 @@ absl_cc_test( absl::config absl::core_headers absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_library( @@ -435,7 +435,7 @@ absl_cc_test( absl::base absl::config absl::endian - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -448,7 +448,7 @@ absl_cc_test( DEPS absl::config absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -462,7 +462,7 @@ absl_cc_test( absl::base absl::core_headers absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -475,7 +475,7 @@ absl_cc_test( DEPS absl::raw_logging_internal absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -488,7 +488,7 @@ absl_cc_test( DEPS absl::base absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -516,7 +516,7 @@ absl_cc_test( absl::core_headers absl::synchronization Threads::Threads - gtest_main + GTest::gtest_main ) absl_cc_library( @@ -543,7 +543,7 @@ absl_cc_test( DEPS absl::exponential_biased absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -570,7 +570,7 @@ absl_cc_test( DEPS absl::core_headers absl::periodic_sampler - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -596,7 +596,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::scoped_set_env - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -620,8 +620,8 @@ absl_cc_test( absl::flags_marshalling absl::log_severity absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_library( @@ -651,8 +651,8 @@ absl_cc_test( DEPS absl::strerror absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_library( @@ -677,7 +677,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::fast_type_id - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -690,5 +690,5 @@ absl_cc_test( DEPS absl::core_headers absl::optional - gtest_main + GTest::gtest_main ) diff --git a/absl/cleanup/CMakeLists.txt b/absl/cleanup/CMakeLists.txt index a2dd78a8..26a6d0dc 100644 --- a/absl/cleanup/CMakeLists.txt +++ b/absl/cleanup/CMakeLists.txt @@ -51,5 +51,5 @@ absl_cc_test( absl::cleanup absl::config absl::utility - gmock_main + GTest::gmock_main ) diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 2d7d0e65..91c40154 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -80,7 +80,7 @@ absl_cc_test( absl::strings absl::test_instance_tracker absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -109,7 +109,7 @@ absl_cc_test( absl::optional absl::test_instance_tracker absl::utility - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -144,7 +144,7 @@ absl_cc_test( absl::exception_testing absl::hash_testing absl::memory - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -158,7 +158,7 @@ absl_cc_test( absl::fixed_array absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -222,7 +222,7 @@ absl_cc_test( absl::memory absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -236,7 +236,7 @@ absl_cc_test( absl::inlined_vector absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -262,7 +262,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::test_instance_tracker - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -297,7 +297,7 @@ absl_cc_test( absl::unordered_map_modifiers_test absl::any absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -335,7 +335,7 @@ absl_cc_test( absl::memory absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -370,7 +370,7 @@ absl_cc_test( absl::unordered_map_lookup_test absl::unordered_map_members_test absl::unordered_map_modifiers_test - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -404,7 +404,7 @@ absl_cc_test( absl::unordered_set_lookup_test absl::unordered_set_members_test absl::unordered_set_modifiers_test - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -433,7 +433,7 @@ absl_cc_test( absl::container_memory absl::strings absl::test_instance_tracker - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -465,7 +465,7 @@ absl_cc_test( absl::hash absl::random_random absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -507,7 +507,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::hash_policy_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -531,7 +531,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::hash_policy_traits - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -561,7 +561,7 @@ absl_cc_test( DEPS absl::hashtablez_sampler absl::have_sse - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -618,7 +618,7 @@ absl_cc_test( DEPS absl::hash_policy_traits absl::node_hash_policy - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -693,7 +693,7 @@ absl_cc_test( absl::core_headers absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -707,7 +707,7 @@ absl_cc_test( absl::raw_hash_set absl::tracked absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -740,7 +740,7 @@ absl_cc_test( absl::core_headers absl::raw_logging_internal absl::span - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -765,7 +765,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -779,7 +779,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -792,7 +792,7 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::type_traits - gmock + GTest::gmock TESTONLY ) @@ -806,7 +806,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -820,7 +820,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -834,7 +834,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -847,7 +847,7 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::type_traits - gmock + GTest::gmock TESTONLY ) @@ -861,7 +861,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -877,7 +877,7 @@ absl_cc_test( absl::unordered_set_lookup_test absl::unordered_set_members_test absl::unordered_set_modifiers_test - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -892,5 +892,5 @@ absl_cc_test( absl::unordered_map_lookup_test absl::unordered_map_members_test absl::unordered_map_modifiers_test - gmock_main + GTest::gmock_main ) diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 074b44cf..bb4d4c92 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -87,7 +87,7 @@ absl_cc_test( absl::memory absl::raw_logging_internal absl::strings - gmock + GTest::gmock ) absl_cc_library( @@ -141,7 +141,7 @@ absl_cc_test( absl::strings absl::raw_logging_internal Threads::Threads - gmock + GTest::gmock ) absl_cc_library( @@ -194,7 +194,7 @@ absl_cc_test( absl::core_headers absl::memory absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -261,7 +261,7 @@ absl_cc_test( DEPS absl::leak_check_api_enabled_for_testing absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -275,7 +275,7 @@ absl_cc_test( DEPS absl::leak_check_api_disabled_for_testing absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -292,7 +292,7 @@ absl_cc_test( absl::leak_check_disable absl::base absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -322,7 +322,7 @@ absl_cc_test( absl::stack_consumption absl::core_headers absl::raw_logging_internal - gmock_main + GTest::gmock_main ) # component target diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 827784b1..956f70f8 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -310,7 +310,7 @@ absl_cc_test( absl::flags_reflection absl::memory absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -322,7 +322,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags_config - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -342,7 +342,7 @@ absl_cc_test( absl::flags_reflection absl::strings absl::time - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -354,7 +354,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags_marshalling - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -373,7 +373,7 @@ absl_cc_test( absl::scoped_set_env absl::span absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -385,7 +385,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags_path_util - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -398,7 +398,7 @@ absl_cc_test( DEPS absl::flags_program_name absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -415,7 +415,7 @@ absl_cc_test( absl::flags_usage absl::memory absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -429,7 +429,7 @@ absl_cc_test( absl::base absl::flags_internal absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -444,7 +444,7 @@ absl_cc_test( absl::flags_path_util absl::flags_program_name absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -463,5 +463,5 @@ absl_cc_test( absl::flags_reflection absl::flags_usage absl::strings - gtest + GTest::gtest ) diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt index cda914f2..3919e9a1 100644 --- a/absl/functional/CMakeLists.txt +++ b/absl/functional/CMakeLists.txt @@ -39,7 +39,7 @@ absl_cc_test( DEPS absl::bind_front absl::memory - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -68,5 +68,5 @@ absl_cc_test( absl::function_ref absl::memory absl::test_instance_tracker - gmock_main + GTest::gmock_main ) diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index b43bfa54..c82f66f0 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -52,7 +52,7 @@ absl_cc_library( absl::meta absl::strings absl::variant - gmock + GTest::gmock TESTONLY ) @@ -72,7 +72,7 @@ absl_cc_test( absl::spy_hash_state absl::meta absl::int128 - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -113,7 +113,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::city - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -141,5 +141,5 @@ absl_cc_test( DEPS absl::wyhash absl::strings - gmock_main + GTest::gmock_main ) diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt index 78fb7e1b..9d50e1dc 100644 --- a/absl/memory/CMakeLists.txt +++ b/absl/memory/CMakeLists.txt @@ -37,7 +37,7 @@ absl_cc_test( DEPS absl::memory absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -51,5 +51,5 @@ absl_cc_test( absl::memory absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt index 672ead2f..9de4bd37 100644 --- a/absl/meta/CMakeLists.txt +++ b/absl/meta/CMakeLists.txt @@ -35,7 +35,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::type_traits - gmock_main + GTest::gmock_main ) # component target diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt index 781987dc..26df5cf7 100644 --- a/absl/numeric/CMakeLists.txt +++ b/absl/numeric/CMakeLists.txt @@ -38,7 +38,7 @@ absl_cc_test( absl::bits absl::core_headers absl::random_random - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -73,7 +73,7 @@ absl_cc_test( absl::core_headers absl::hash_testing absl::type_traits - gmock_main + GTest::gmock_main ) # component target diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 3009a034..9d1c67fb 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -62,8 +62,8 @@ absl_cc_test( absl::random_random absl::random_internal_sequence_urbg absl::fast_type_id - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -119,8 +119,8 @@ absl_cc_library( absl::type_traits absl::utility absl::variant - gmock - gtest + GTest::gmock + GTest::gtest TESTONLY ) @@ -136,8 +136,8 @@ absl_cc_test( DEPS absl::random_mocking_bit_gen absl::random_random - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -153,8 +153,8 @@ absl_cc_test( absl::random_bit_gen_ref absl::random_mocking_bit_gen absl::random_random - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_library( @@ -245,8 +245,8 @@ absl_cc_test( absl::random_random absl::random_internal_sequence_urbg absl::random_internal_pcg_engine - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -268,8 +268,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -285,8 +285,8 @@ absl_cc_test( absl::random_distributions absl::random_random absl::random_internal_distribution_test_util - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -301,8 +301,8 @@ absl_cc_test( absl::random_distributions absl::random_random absl::raw_logging_internal - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -322,8 +322,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -343,8 +343,8 @@ absl_cc_test( absl::random_random absl::raw_logging_internal absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -367,8 +367,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -391,8 +391,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -414,8 +414,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -435,8 +435,8 @@ absl_cc_test( absl::random_random absl::raw_logging_internal absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -456,8 +456,8 @@ absl_cc_test( absl::random_internal_sequence_urbg absl::random_random absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -477,8 +477,8 @@ absl_cc_test( absl::random_random absl::raw_logging_internal absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -492,7 +492,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_random - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -508,8 +508,8 @@ absl_cc_test( absl::random_seed_sequences absl::random_internal_nonsecure_base absl::random_random - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -894,7 +894,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_traits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -911,7 +911,7 @@ absl_cc_test( absl::bits absl::flags absl::random_internal_generate_real - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -926,7 +926,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_distribution_test_util - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -941,7 +941,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_fastmath - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -957,8 +957,8 @@ absl_cc_test( DEPS absl::random_internal_explicit_seed_seq absl::random_seed_sequences - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -973,8 +973,8 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_salted_seed_seq - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -990,7 +990,7 @@ absl_cc_test( DEPS absl::core_headers absl::random_internal_distribution_test_util - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1005,7 +1005,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_fast_uniform_bits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1024,7 +1024,7 @@ absl_cc_test( absl::random_distributions absl::random_seed_sequences absl::strings - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1039,8 +1039,8 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_seed_material - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1057,7 +1057,7 @@ absl_cc_test( absl::random_internal_pool_urbg absl::span absl::type_traits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1074,8 +1074,8 @@ absl_cc_test( absl::random_internal_explicit_seed_seq absl::random_internal_pcg_engine absl::time - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1094,8 +1094,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::time - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1111,7 +1111,7 @@ absl_cc_test( DEPS absl::random_internal_randen absl::type_traits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1127,7 +1127,7 @@ absl_cc_test( DEPS absl::endian absl::random_internal_randen_slow - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1146,8 +1146,8 @@ absl_cc_test( absl::random_internal_randen_hwaes_impl absl::raw_logging_internal absl::str_format - gmock - gtest + GTest::gmock + GTest::gtest ) # Internal-only target, do not depend on directly. @@ -1178,7 +1178,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_uniform_helper - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1193,7 +1193,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_iostream_state_saver - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1210,5 +1210,5 @@ absl_cc_test( absl::random_internal_wide_multiply absl::bits absl::int128 - gtest_main + GTest::gtest_main ) diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index f0d798a3..1248dff0 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt @@ -50,7 +50,7 @@ absl_cc_test( DEPS absl::status absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -84,5 +84,5 @@ absl_cc_test( DEPS absl::status absl::statusor - gmock_main + GTest::gmock_main ) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index d3f1523c..0246dc38 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -101,7 +101,7 @@ absl_cc_test( DEPS absl::strings absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -115,7 +115,7 @@ absl_cc_test( absl::strings absl::core_headers absl::fixed_array - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -128,7 +128,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -142,7 +142,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -156,7 +156,7 @@ absl_cc_test( absl::strings_internal absl::base absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -169,7 +169,7 @@ absl_cc_test( DEPS absl::strings absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -184,7 +184,7 @@ absl_cc_test( absl::config absl::core_headers absl::dynamic_annotations - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -197,7 +197,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -209,7 +209,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -226,7 +226,7 @@ absl_cc_test( absl::btree absl::flat_hash_map absl::node_hash_map - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -238,7 +238,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -253,7 +253,7 @@ absl_cc_test( absl::base absl::core_headers absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -268,7 +268,7 @@ absl_cc_test( absl::base absl::core_headers absl::memory - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -281,7 +281,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -301,7 +301,7 @@ absl_cc_test( absl::random_random absl::random_distributions absl::strings_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -314,7 +314,7 @@ absl_cc_test( DEPS absl::strings absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -326,7 +326,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -340,7 +340,7 @@ absl_cc_test( absl::strings absl::str_format absl::pow10_helper - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -355,7 +355,7 @@ absl_cc_test( absl::strings absl::config absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -370,7 +370,7 @@ absl_cc_test( DEPS absl::strings absl::config - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -428,7 +428,7 @@ absl_cc_test( absl::cord absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -442,7 +442,7 @@ absl_cc_test( absl::str_format absl::str_format_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -455,7 +455,7 @@ absl_cc_test( DEPS absl::str_format absl::str_format_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -467,7 +467,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -479,7 +479,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -494,7 +494,7 @@ absl_cc_test( absl::str_format_internal absl::raw_logging_internal absl::int128 - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -507,7 +507,7 @@ absl_cc_test( DEPS absl::str_format_internal absl::cord - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -520,7 +520,7 @@ absl_cc_test( DEPS absl::str_format_internal absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -547,7 +547,7 @@ absl_cc_test( DEPS absl::pow10_helper absl::str_format - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -600,7 +600,7 @@ absl_cc_test( absl::cordz_update_tracker absl::core_headers absl::synchronization - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -628,7 +628,7 @@ absl_cc_test( absl::config absl::cordz_functions absl::cordz_test_helpers - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -675,7 +675,7 @@ absl_cc_test( absl::random_distributions absl::synchronization absl::time - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -722,7 +722,7 @@ absl_cc_test( absl::span absl::stacktrace absl::symbolize - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -742,7 +742,7 @@ absl_cc_test( absl::cordz_update_scope absl::cordz_update_tracker absl::thread_pool - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -780,7 +780,7 @@ absl_cc_test( absl::synchronization absl::thread_pool absl::time - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -813,7 +813,7 @@ absl_cc_test( absl::cordz_update_scope absl::cordz_update_tracker absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -899,7 +899,7 @@ absl_cc_test( absl::endian absl::raw_logging_internal absl::fixed_array - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -916,7 +916,7 @@ absl_cc_test( absl::core_headers absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -931,7 +931,7 @@ absl_cc_test( absl::cord_internal absl::core_headers absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -955,5 +955,5 @@ absl_cc_test( absl::core_headers absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index e633d0bf..605efe2d 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -95,7 +95,7 @@ absl_cc_test( DEPS absl::synchronization absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -108,7 +108,7 @@ absl_cc_test( DEPS absl::synchronization absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -122,7 +122,7 @@ absl_cc_test( absl::graphcycles_internal absl::core_headers absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -154,7 +154,7 @@ absl_cc_test( absl::memory absl::raw_logging_internal absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -167,7 +167,7 @@ absl_cc_test( DEPS absl::synchronization absl::time - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -183,7 +183,7 @@ absl_cc_library( absl::config absl::strings absl::time - gmock + GTest::gmock TESTONLY ) @@ -199,7 +199,7 @@ absl_cc_test( absl::synchronization absl::strings absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index 00bdd499..f6ff8bd1 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt @@ -102,7 +102,7 @@ absl_cc_library( absl::config absl::raw_logging_internal absl::time_zone - gmock + GTest::gmock TESTONLY ) @@ -124,5 +124,5 @@ absl_cc_test( absl::config absl::core_headers absl::time_zone - gmock_main + GTest::gmock_main ) diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index c356b211..d7e8614e 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt @@ -69,7 +69,7 @@ absl_cc_test( absl::exception_testing absl::raw_logging_internal absl::test_instance_tracker - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -85,7 +85,7 @@ absl_cc_test( absl::exception_testing absl::raw_logging_internal absl::test_instance_tracker - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -99,7 +99,7 @@ absl_cc_test( absl::any absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -136,7 +136,7 @@ absl_cc_test( absl::inlined_vector absl::hash_testing absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -156,7 +156,7 @@ absl_cc_test( absl::inlined_vector absl::hash_testing absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -222,7 +222,7 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -236,7 +236,7 @@ absl_cc_test( absl::optional absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -258,7 +258,7 @@ absl_cc_library( absl::type_traits absl::strings absl::utility - gmock_main + GTest::gmock_main TESTONLY ) @@ -275,7 +275,7 @@ absl_cc_test( DEPS absl::conformance_testing absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -288,7 +288,7 @@ absl_cc_test( DEPS absl::conformance_testing absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -324,7 +324,7 @@ absl_cc_test( absl::memory absl::type_traits absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -350,7 +350,7 @@ absl_cc_test( DEPS absl::base absl::compare - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -365,5 +365,5 @@ absl_cc_test( absl::config absl::exception_safety_testing absl::memory - gmock_main + GTest::gmock_main ) diff --git a/absl/utility/CMakeLists.txt b/absl/utility/CMakeLists.txt index e1edd19a..865b758f 100644 --- a/absl/utility/CMakeLists.txt +++ b/absl/utility/CMakeLists.txt @@ -40,5 +40,5 @@ absl_cc_test( absl::core_headers absl::memory absl::strings - gmock_main + GTest::gmock_main ) -- cgit v1.2.3 From 311bbd2e50ea35e921a08186840d3b6ca279e880 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 10 Jun 2021 13:29:59 -0700 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- ca5ce10fa5286f2bfb51890a37b547308b8d6d93 by Benjamin Barenblat : Skip floating-point edge-case tests when using an x87 32-bit Intel CPUs use 80-bit floats for intermediate values, which can change the results of floating point computations from what we normally expect. Identify tests that are sensitive to the x87, and skip them when we’re on 32-bit Intel. PiperOrigin-RevId: 378722613 -- e5798bb017854e7f3b6d8721fed7dd553642b83d by Abseil Team : Build without -Wl,-no-undefined. PiperOrigin-RevId: 378690619 -- 3587685a2c932405e401546ec383abcfbf8495c8 by Derek Mauro : Update CCTZ BUILD file. PiperOrigin-RevId: 378688996 -- 06c7841b2bf8851410b716823b7ff9b42d86085e by Derek Mauro : Change the CMake install test to use installed version of GoogleTest PiperOrigin-RevId: 378537383 -- eaa8122a7062c56bed80e806344cca0c8325bf6f by Derek Mauro : Internal change PiperOrigin-RevId: 378525523 -- 381f505cce894b8eec031a541855650c4aa46e64 by Abseil Team : Mark btree_container::clear() with the ABSL_ATTRIBUTE_REINITIALIZES attribute. This prevents false positives in the clang-tidy check bugprone-use-after-move; it allows clear() to be called on a moved-from btree_container without any warnings, and the btree_container will thereafter be regarded as initialized again. PiperOrigin-RevId: 378472690 GitOrigin-RevId: ca5ce10fa5286f2bfb51890a37b547308b8d6d93 Change-Id: I4267246f418538c5baacb562d1a40213fb13f246 --- CMake/install_test_project/test.sh | 24 ++++++++++++++++++++---- absl/base/BUILD.bazel | 5 +++++ absl/container/BUILD.bazel | 1 + absl/container/internal/btree_container.h | 3 ++- absl/flags/BUILD.bazel | 10 ++++++++++ absl/hash/BUILD.bazel | 1 + absl/numeric/BUILD.bazel | 1 + absl/random/beta_distribution_test.cc | 9 +++++++++ absl/random/distributions_test.cc | 10 ++++++++++ absl/random/exponential_distribution_test.cc | 10 ++++++++++ absl/random/uniform_real_distribution_test.cc | 9 +++++++++ absl/status/BUILD.bazel | 2 ++ absl/strings/BUILD.bazel | 7 +++++++ absl/synchronization/BUILD.bazel | 2 ++ absl/time/BUILD.bazel | 1 + absl/time/duration_test.cc | 9 +++++++++ absl/time/internal/cctz/BUILD.bazel | 2 ++ ci/cmake_install_test.sh | 3 +++ 18 files changed, 104 insertions(+), 5 deletions(-) diff --git a/CMake/install_test_project/test.sh b/CMake/install_test_project/test.sh index a3d39773..5a78c92c 100755 --- a/CMake/install_test_project/test.sh +++ b/CMake/install_test_project/test.sh @@ -19,10 +19,9 @@ # Fail on any error. Treat unset variables an error. Print commands as executed. set -euox pipefail -source ci/cmake_common.sh - absl_dir=/abseil-cpp absl_build_dir=/buildfs +googletest_builddir=/googletest_builddir project_dir="${absl_dir}"/CMake/install_test_project project_build_dir=/buildfs/project-build @@ -31,13 +30,30 @@ if [ "${LINK_TYPE:-}" = "DYNAMIC" ]; then build_shared_libs="ON" fi +# Build and install GoogleTest +mkdir "${googletest_builddir}" +pushd "${googletest_builddir}" +curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${ABSL_GOOGLETEST_COMMIT}".zip +unzip "${ABSL_GOOGLETEST_COMMIT}".zip +pushd "googletest-${ABSL_GOOGLETEST_COMMIT}" +mkdir build +pushd build +cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS="${build_shared_libs}" .. +make -j $(nproc) +make install +ldconfig +popd +popd +popd + # Run the LTS transformations ./create_lts.py 99998877 -# Install Abseil +# Build and install Abseil pushd "${absl_build_dir}" cmake "${absl_dir}" \ - -DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}" \ + -DABSL_USE_EXTERNAL_GOOGLETEST=ON \ + -DABSL_FIND_GOOGLETEST=ON \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING=ON \ -DBUILD_SHARED_LIBS="${build_shared_libs}" diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 65ff0dde..16b0e2fb 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -56,6 +56,7 @@ cc_library( srcs = ["log_severity.cc"], hdrs = ["log_severity.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", @@ -159,6 +160,7 @@ cc_library( "internal/low_level_alloc.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = select({ "//absl:msvc_compiler": [], "//absl:clang-cl_compiler": [], @@ -220,6 +222,7 @@ cc_library( "internal/unscaledcycleclock.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = select({ "//absl:msvc_compiler": [ "-DEFAULTLIB:advapi32.lib", @@ -290,6 +293,7 @@ cc_library( srcs = ["internal/throw_delegate.cc"], hdrs = ["internal/throw_delegate.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", @@ -709,6 +713,7 @@ cc_library( srcs = ["internal/strerror.cc"], hdrs = ["internal/strerror.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index f22fdc60..96510541 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -505,6 +505,7 @@ cc_library( ], hdrs = ["internal/hashtablez_sampler.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":have_sse", diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index 4f5d56dd..a99668c7 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -20,6 +20,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/base/internal/throw_delegate.h" #include "absl/container/internal/btree.h" // IWYU pragma: export #include "absl/container/internal/common.h" @@ -176,7 +177,7 @@ class btree_container { } // Utility routines. - void clear() { tree_.clear(); } + ABSL_ATTRIBUTE_REINITIALIZES void clear() { tree_.clear(); } void swap(btree_container &other) { tree_.swap(other.tree_); } void verify() const { tree_.verify(); } diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index c178b86b..940e3561 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -51,6 +51,7 @@ cc_library( "internal/program_name.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/flags:__pkg__", @@ -74,6 +75,7 @@ cc_library( "usage_config.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":path_util", @@ -94,6 +96,7 @@ cc_library( "marshalling.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", @@ -113,6 +116,7 @@ cc_library( "internal/commandlineflag.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", @@ -129,6 +133,7 @@ cc_library( "commandlineflag.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":commandlineflag_internal", @@ -170,6 +175,7 @@ cc_library( "reflection.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":commandlineflag", @@ -194,6 +200,7 @@ cc_library( "internal/sequence_lock.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//absl/base:__subpackages__"], deps = [ @@ -244,6 +251,7 @@ cc_library( "internal/usage.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/flags:__pkg__", @@ -273,6 +281,7 @@ cc_library( "usage.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":usage_internal", @@ -291,6 +300,7 @@ cc_library( "parse.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":commandlineflag", diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 4b2c220f..f5005a04 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -34,6 +34,7 @@ cc_library( ], hdrs = ["hash.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index ea587bfa..a0ef9055 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel @@ -62,6 +62,7 @@ cc_library( ], hdrs = ["int128.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bits", diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc index 44cdfdd0..d980c969 100644 --- a/absl/random/beta_distribution_test.cc +++ b/absl/random/beta_distribution_test.cc @@ -15,6 +15,7 @@ #include "absl/random/beta_distribution.h" #include +#include #include #include #include @@ -558,6 +559,14 @@ TEST(BetaDistributionTest, StabilityTest) { // dependencies of the distribution change, such as RandU64ToDouble, then this // is also likely to change. TEST(BetaDistributionTest, AlgorithmBounds) { +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations are + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + { absl::random_internal::sequence_urbg urbg( {0x7fbe76c8b4395800ull, 0x8000000000000000ull}); diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc index 5866a072..d3a5dd75 100644 --- a/absl/random/distributions_test.cc +++ b/absl/random/distributions_test.cc @@ -14,6 +14,7 @@ #include "absl/random/distributions.h" +#include #include #include #include @@ -224,6 +225,15 @@ TEST_F(RandomDistributionsTest, UniformNoBounds) { TEST_F(RandomDistributionsTest, UniformNonsenseRanges) { // The ranges used in this test are undefined behavior. // The results are arbitrary and subject to future changes. + +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations can be + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + absl::InsecureBitGen gen; // diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc index af11d61c..81a5d17b 100644 --- a/absl/random/exponential_distribution_test.cc +++ b/absl/random/exponential_distribution_test.cc @@ -15,6 +15,7 @@ #include "absl/random/exponential_distribution.h" #include +#include #include #include #include @@ -384,6 +385,15 @@ TEST(ExponentialDistributionTest, StabilityTest) { TEST(ExponentialDistributionTest, AlgorithmBounds) { // Relies on absl::uniform_real_distribution, so some of these comments // reference that. + +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations can be + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + absl::exponential_distribution dist; { diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc index 18bcd3bc..035bd284 100644 --- a/absl/random/uniform_real_distribution_test.cc +++ b/absl/random/uniform_real_distribution_test.cc @@ -14,6 +14,7 @@ #include "absl/random/uniform_real_distribution.h" +#include #include #include #include @@ -70,6 +71,14 @@ using RealTypes = TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations are + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + using param_type = typename absl::uniform_real_distribution::param_type; diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 189bd73d..4db72c0d 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -40,6 +40,7 @@ cc_library( "status_payload_printer.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], deps = [ "//absl/base:atomic_hook", "//absl/base:config", @@ -76,6 +77,7 @@ cc_library( "statusor.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], deps = [ ":status", "//absl/base:core_headers", diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 1cb5b3e5..a02ee395 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -66,6 +66,7 @@ cc_library( "substitute.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], deps = [ ":internal", "//absl/base", @@ -96,6 +97,7 @@ cc_library( "internal/utf8.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -278,6 +280,7 @@ cc_library( "internal/cord_rep_ring_reader.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], visibility = [ "//visibility:private", ], @@ -327,6 +330,7 @@ cc_library( "cord.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], deps = [ ":cord_internal", ":cordz_functions", @@ -355,6 +359,7 @@ cc_library( srcs = ["internal/cordz_handle.cc"], hdrs = ["internal/cordz_handle.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], visibility = [ "//absl:__subpackages__", ], @@ -371,6 +376,7 @@ cc_library( srcs = ["internal/cordz_info.cc"], hdrs = ["internal/cordz_info.h"], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], visibility = [ "//absl:__subpackages__", ], @@ -995,6 +1001,7 @@ cc_library( "internal/str_format/parser.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], visibility = ["//visibility:private"], deps = [ ":strings", diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 92e2448d..46c4b917 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -36,6 +36,7 @@ cc_library( "internal/graphcycles.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", @@ -87,6 +88,7 @@ cc_library( "notification.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = select({ "//absl:msvc_compiler": [], "//absl:clang-cl_compiler": [], diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index 3e25ca26..4700616d 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -43,6 +43,7 @@ cc_library( "time.h", ], copts = ABSL_DEFAULT_COPTS, + features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base", diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index a3617e74..b7209e1c 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -17,6 +17,7 @@ #endif #include // NOLINT(build/c++11) +#include #include #include #include @@ -1390,6 +1391,14 @@ void VerifyApproxSameAsMul(double time_as_seconds, int* const misses) { // Seconds(point) returns a duration near point * Seconds(1.0). (They may // not be exactly equal due to fused multiply/add contraction.) TEST(Duration, ToDoubleSecondsCheckEdgeCases) { +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations can be + // performed with 80-bit floats. This means the edge cases are different than + // what we expect here, so just skip this test. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + constexpr uint32_t kTicksPerSecond = absl::time_internal::kTicksPerSecond; constexpr auto duration_tick = absl::time_internal::MakeDuration(0, 1u); int misses = 0; diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index 45a95292..35747e57 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -45,6 +45,7 @@ cc_library( hdrs = [ "include/cctz/civil_time.h", ], + features = ["-no_undefined"], textual_hdrs = ["include/cctz/civil_time_detail.h"], visibility = ["//visibility:public"], deps = ["//absl/base:config"], @@ -74,6 +75,7 @@ cc_library( "include/cctz/time_zone.h", "include/cctz/zone_info_source.h", ], + features = ["-no_undefined"], linkopts = select({ ":osx": [ "-framework Foundation", diff --git a/ci/cmake_install_test.sh b/ci/cmake_install_test.sh index ffc6b516..97ed8478 100755 --- a/ci/cmake_install_test.sh +++ b/ci/cmake_install_test.sh @@ -36,8 +36,11 @@ for link_type in ${LINK_TYPE}; do --tmpfs=/abseil-cpp:exec \ --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ + -e "ABSL_GOOGLETEST_COMMIT=${ABSL_GOOGLETEST_COMMIT}" \ + -e "ABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL}" \ -e "LINK_TYPE=${link_type}" \ --rm \ + ${DOCKER_EXTRA_ARGS:-} \ ${DOCKER_CONTAINER} \ /bin/bash -c "cp -r /abseil-cpp-ro/* . && CMake/install_test_project/test.sh" done -- cgit v1.2.3 From 60be12ed9822078970f05f3c560324184302df6b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 17 Jun 2021 15:24:30 -0700 Subject: Export of internal Abseil changes -- b1fc72630aaa81c8395c3b22ba267d938fe29a2e by Derek Mauro : Fix -Wdeprecated-copy warnings from Clang 13. Example: error: definition of implicit copy assignment operator for 'UDT' is deprecated because it has a user-declared copy constructor [-Werror,-Wdeprecated-copy] PiperOrigin-RevId: 380058303 -- 0422744812b1a2010d9eea5b17fbe89f3441b66b by Evan Brown : Change the "full table!" asserts in raw_hash_set to use `<= capacity` instead of `< capacity`. If we add support for non-power-of-two-minus-one capacities, this is the correct thing to assert. For example, consider: Group::kWidth = 8, capacity_ = 8, ctrl_ = {kEmpty, 1, 2, 3, 4, 5, 6, 7, kSentinel, kEmpty, 1, 2, 3, 4, 5, 6}. In this case, if we do an unsuccessful lookup with H2 mapping to slot 1, then the first Group will contain {1, 2, 3, 4, 5, 6, 7, kSentinel} so we need to continue to the second Group (at which point seq.index() == 8 == capacity_) to find a kEmpty. Note: this is a no-op change for now since we never have `capacity % Group::kWidth == 0`. PiperOrigin-RevId: 380033480 -- 40628c34d540356de65fabb16c1439c0ec7a0764 by Abseil Team : Drop out-of-date documentation about `absl::FixedArray`'s allocator support PiperOrigin-RevId: 379811653 -- e7ad047863ae55c9b7aec0753cfc527a4ea614bc by Evan Brown : Fix a bug in ConvertDeletedToEmptyAndFullToDeleted in which we were copying 1 more cloned control byte than actually exists. When alignof(slot_type)>1, this wouldn't cause a problem because the extra byte is padding. Also change loop bounds to not rely on the fact that capacity_+1 is a multiple of Group::kWidth. PiperOrigin-RevId: 379311830 -- 1a3ba500fb2c33205854eb9258cd6e0fb1061bca by Martijn Vels : Change Ring, EXTERNAL and FLAT tag values to be consecutive values The purpose of this change is to have FLAT = EXTERNAL + 1. Especially in the ring and btree alternative code, there is a common check if a node is a 'plain' edge (EXTERNAL or FLAT), or 'something else'. This change can make that check a single branch, i.e., instead of 'tag == EXTERNAL || tag >= FLAT', we can simply check for 'tag >= EXTERNAL'. Likewise we have some cases where we check for RING, EXTERNAL or FLAT, so we align RING + 1 with EXTERNAL. PiperOrigin-RevId: 379291576 -- 0c78e65ca4d85244b106c3f8e24cf268e09e72a3 by Benjamin Barenblat : Round a double multiplication before casting it to integer The code static_cast(x * y) (for double x and y) performs a double multiplication into a temporary that, by standard, may have excess precision. The subsequent cast to int discards the excess precision. However, the cast may examine the excess precision during conversion, producing surprising results like static_cast(1.7 * 10) == 16 on certain systems. Correct this case by explicitly rounding 1.7 * 10 before casting it. PiperOrigin-RevId: 378922064 GitOrigin-RevId: b1fc72630aaa81c8395c3b22ba267d938fe29a2e Change-Id: Ica708a006921118673e78d5fd2d61fe0fb0894d1 --- absl/container/fixed_array.h | 5 ---- absl/container/internal/raw_hash_set.cc | 4 +-- absl/container/internal/raw_hash_set.h | 8 +++--- absl/container/internal/raw_hash_set_test.cc | 40 +++++++++++++++++++++++++++- absl/flags/flag_test.cc | 1 + absl/flags/internal/usage_test.cc | 1 + absl/flags/parse_test.cc | 1 + absl/random/mocking_bit_gen_test.cc | 6 +++-- absl/strings/internal/cord_internal.h | 16 ++++++++--- 9 files changed, 65 insertions(+), 17 deletions(-) diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index fcb3e545..839ba0bc 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -73,11 +73,6 @@ constexpr static auto kFixedArrayUseDefault = static_cast(-1); // uninitialized (e.g. int, int[4], double), and others default-constructed. // This matches the behavior of c-style arrays and `std::array`, but not // `std::vector`. -// -// Note that `FixedArray` does not provide a public allocator; if it requires a -// heap allocation, it will do so with global `::operator new[]()` and -// `::operator delete[]()`, even if T provides class-scope overrides for these -// operators. template > class FixedArray { diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index bfef071f..c9840f79 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -47,11 +47,11 @@ void ConvertDeletedToEmptyAndFullToDeleted( ctrl_t* ctrl, size_t capacity) { assert(ctrl[capacity] == kSentinel); assert(IsValidCapacity(capacity)); - for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) { + for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) { Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); } // Copy the cloned ctrl bytes. - std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth); + std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth - 1); ctrl[capacity] = kSentinel; } diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index aa78265c..844890f0 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -564,7 +564,7 @@ inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash, return {seq.offset(mask.LowestBitSet()), seq.index()}; } seq.next(); - assert(seq.index() < capacity && "full table!"); + assert(seq.index() <= capacity && "full table!"); } } @@ -1380,7 +1380,7 @@ class raw_hash_set { } if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end(); seq.next(); - assert(seq.index() < capacity_ && "full table!"); + assert(seq.index() <= capacity_ && "full table!"); } } template @@ -1698,7 +1698,7 @@ class raw_hash_set { } if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false; seq.next(); - assert(seq.index() < capacity_ && "full table!"); + assert(seq.index() <= capacity_ && "full table!"); } return false; } @@ -1730,7 +1730,7 @@ class raw_hash_set { } if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break; seq.next(); - assert(seq.index() < capacity_ && "full table!"); + assert(seq.index() <= capacity_ && "full table!"); } return {prepare_insert(hash), true}; } diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index af882ef4..87cbdfcc 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -226,7 +226,7 @@ TEST(Batch, DropDeletes) { } ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity); ASSERT_EQ(ctrl[kCapacity], kSentinel); - for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) { + for (size_t i = 0; i < kCapacity + kGroupWidth; ++i) { ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()]; if (i == kCapacity) expected = kSentinel; if (expected == kDeleted) expected = kEmpty; @@ -294,6 +294,7 @@ struct ValuePolicy { }; using IntPolicy = ValuePolicy; +using Uint8Policy = ValuePolicy; class StringPolicy { template , + std::equal_to, std::allocator> { + using Base = typename Uint8Table::raw_hash_set; + using Base::Base; +}; + template struct CustomAlloc : std::allocator { CustomAlloc() {} @@ -2009,6 +2017,36 @@ TEST(Sanitizer, PoisoningOnErase) { } #endif // ABSL_HAVE_ADDRESS_SANITIZER +TEST(Table, AlignOne) { + // We previously had a bug in which we were copying a control byte over the + // first slot when alignof(value_type) is 1. We test repeated + // insertions/erases and verify that the behavior is correct. + Uint8Table t; + std::unordered_set verifier; // NOLINT + + // Do repeated insertions/erases from the table. + for (int64_t i = 0; i < 100000; ++i) { + SCOPED_TRACE(i); + const uint8_t u = (i * -i) & 0xFF; + auto it = t.find(u); + auto verifier_it = verifier.find(u); + if (it == t.end()) { + ASSERT_EQ(verifier_it, verifier.end()); + t.insert(u); + verifier.insert(u); + } else { + ASSERT_NE(verifier_it, verifier.end()); + t.erase(it); + verifier.erase(verifier_it); + } + } + + EXPECT_EQ(t.size(), verifier.size()); + for (uint8_t u : t) { + EXPECT_EQ(verifier.count(u), 1); + } +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 6912b546..ba81317b 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -61,6 +61,7 @@ void TestCallback() {} struct UDT { UDT() = default; UDT(const UDT&) = default; + UDT& operator=(const UDT&) = default; }; bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } std::string AbslUnparseFlag(const UDT&) { return ""; } diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index b5c2487d..044d71c8 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -45,6 +45,7 @@ static const char kTestUsageMessage[] = "Custom usage message"; struct UDT { UDT() = default; UDT(const UDT&) = default; + UDT& operator=(const UDT&) = default; }; bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; } diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc index 41bc0bc6..8dc91db2 100644 --- a/absl/flags/parse_test.cc +++ b/absl/flags/parse_test.cc @@ -46,6 +46,7 @@ using absl::base_internal::ScopedSetEnv; struct UDT { UDT() = default; UDT(const UDT&) = default; + UDT& operator=(const UDT&) = default; UDT(int v) : value(v) {} // NOLINT int value; diff --git a/absl/random/mocking_bit_gen_test.cc b/absl/random/mocking_bit_gen_test.cc index f63b6e42..c713ceaf 100644 --- a/absl/random/mocking_bit_gen_test.cc +++ b/absl/random/mocking_bit_gen_test.cc @@ -15,6 +15,7 @@ // #include "absl/random/mocking_bit_gen.h" +#include #include #include @@ -328,8 +329,9 @@ TEST(BasicMocking, WillByDefaultWithArgs) { absl::MockingBitGen gen; ON_CALL(absl::MockPoisson(), Call(gen, _)) - .WillByDefault( - [](double lambda) { return static_cast(lambda * 10); }); + .WillByDefault([](double lambda) { + return static_cast(std::rint(lambda * 10)); + }); EXPECT_EQ(absl::Poisson(gen, 1.7), 17); EXPECT_EQ(absl::Poisson(gen, 0.03), 0); } diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 813b3f35..1e2436ba 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -154,9 +154,9 @@ class CordRepRing; // Various representations that we allow enum CordRepKind { CONCAT = 0, - EXTERNAL = 1, - SUBSTRING = 2, - RING = 3, + SUBSTRING = 1, + RING = 2, + EXTERNAL = 3, // We have different tags for different sized flat arrays, // starting with FLAT, and limited to MAX_FLAT_TAG. The 224 value is based on @@ -168,6 +168,16 @@ enum CordRepKind { MAX_FLAT_TAG = 224 }; +// There are various locations where we want to check if some rep is a 'plain' +// data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we +// can perform this check in a single branch as 'tag >= EXTERNAL' +// Likewise, we have some locations where we check for 'ring or external/flat', +// so likewise align RING to EXTERNAL. +// Note that we can leave this optimization to the compiler. The compiler will +// DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`. +static_assert(EXTERNAL == RING + 1, "RING and EXTERNAL values not consecutive"); +static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT values not consecutive"); + struct CordRep { CordRep() = default; constexpr CordRep(Refcount::Immortal immortal, size_t l) -- cgit v1.2.3 From 4a23151e7ee089f54f0575f0a6d499e3a3fb6728 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 21 Jun 2021 23:19:49 -0700 Subject: Export of internal Abseil changes -- f6d1ddef9a38e3fb8492181bf1a7a006b7f2145d by Abseil Team : Update the implementation of `operator<<` in Status to use `ToString(StatusToStringMode::kWithEverything)` PiperOrigin-RevId: 380740880 -- 5f13b20c4b85c1c6e94b69c74f80f8f3f3941747 by Derek Mauro : Update Docker images This also disables the Clang/libstdc++/C++20 combo as it seems that the latest libstdc++ is relying on C++20 Concepts to a greater extent than Clang supports. PiperOrigin-RevId: 380714572 -- f8f4dee12cfd02559bf741ad6b06f10ac0c48c73 by Abseil Team : Fix shadow member warnings in randen_hwaes.cc These happen when attempting to use abseil in github.com/google/benchmark. The project sets -Wshadow. The warning is due to the name of the Vector128 ctor parameter. Using v instead, which I see used elsewhere (e.g. line 290) PiperOrigin-RevId: 380704197 -- 2e1a09e9cb1239485715acb4828d9b4799fcfbb5 by Tom Manshreck : Add more precise documentation for AbslParseFlag declarations in the Time API PiperOrigin-RevId: 380649107 -- 153e5f7a960c03e4161c03737a0ff18ba377ff73 by Evan Brown : Make the number of control bytes a constant. We use a constexpr function because we need to support C++11, which doesn't have inline variables. The motivation is to avoid future bugs where the number changes and we forget to update all the places it's used. This CL should be a no-op. PiperOrigin-RevId: 380253975 GitOrigin-RevId: f6d1ddef9a38e3fb8492181bf1a7a006b7f2145d Change-Id: Id584138f898bf3ebef95fabcf48e41098c4db954 --- absl/container/internal/raw_hash_set.cc | 2 +- absl/container/internal/raw_hash_set.h | 20 ++++++++++++-------- absl/random/internal/randen_hwaes.cc | 2 +- absl/status/status.cc | 2 +- absl/time/time.h | 28 ++++++++++++++++++++++++---- ci/linux_clang-latest_libcxx_asan_bazel.sh | 1 + ci/linux_clang-latest_libcxx_bazel.sh | 1 + ci/linux_clang-latest_libcxx_tsan_bazel.sh | 1 + ci/linux_clang-latest_libstdcxx_bazel.sh | 3 ++- ci/linux_docker_containers.sh | 6 +++--- ci/linux_gcc-floor_libstdcxx_bazel.sh | 1 + ci/linux_gcc-latest_libstdcxx_bazel.sh | 1 + 12 files changed, 49 insertions(+), 19 deletions(-) diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index c9840f79..f5a9ce68 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -51,7 +51,7 @@ void ConvertDeletedToEmptyAndFullToDeleted( Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); } // Copy the cloned ctrl bytes. - std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth - 1); + std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes()); ctrl[capacity] = kSentinel; } diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 844890f0..508f2124 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -448,6 +448,10 @@ using Group = GroupSse2Impl; using Group = GroupPortableImpl; #endif +// The number of cloned control bytes that we copy from the beginning to the +// end of the control bytes array. +constexpr size_t NumClonedBytes() { return Group::kWidth - 1; } + template class raw_hash_set; @@ -629,8 +633,8 @@ class raw_hash_set { static Layout MakeLayout(size_t capacity) { assert(IsValidCapacity(capacity)); // The extra control bytes are for 1 sentinel byte followed by - // `Group::kWidth - 1` bytes that are cloned from the beginning. - return Layout(capacity + Group::kWidth, capacity); + // NumClonedBytes() bytes that are cloned from the beginning. + return Layout(capacity + 1 + NumClonedBytes(), capacity); } using AllocTraits = absl::allocator_traits; @@ -1796,8 +1800,8 @@ class raw_hash_set { } ctrl_[i] = h; - constexpr size_t kClonedBytes = Group::kWidth - 1; - ctrl_[((i - kClonedBytes) & capacity_) + (kClonedBytes & capacity_)] = h; + ctrl_[((i - NumClonedBytes()) & capacity_) + + (NumClonedBytes() & capacity_)] = h; } size_t& growth_left() { return settings_.template get<0>(); } @@ -1816,10 +1820,10 @@ class raw_hash_set { // TODO(alkis): Investigate removing some of these fields: // - ctrl/slots can be derived from each other // - size can be moved into the slot array - ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + Group::kWidth) * ctrl_t] - slot_type* slots_ = nullptr; // [capacity * slot_type] - size_t size_ = 0; // number of full slots - size_t capacity_ = 0; // total number of slots + ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1 + NumClonedBytes()) * ctrl_t] + slot_type* slots_ = nullptr; // [capacity * slot_type] + size_t size_ = 0; // number of full slots + size_t capacity_ = 0; // total number of slots absl::container_internal::CompressedTuple diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc index b5a3f90a..3738cc38 100644 --- a/absl/random/internal/randen_hwaes.cc +++ b/absl/random/internal/randen_hwaes.cc @@ -270,7 +270,7 @@ namespace { class Vector128 { public: // Convert from/to intrinsics. - inline explicit Vector128(const __m128i& Vector128) : data_(Vector128) {} + inline explicit Vector128(const __m128i& v) : data_(v) {} inline __m128i data() const { return data_; } diff --git a/absl/status/status.cc b/absl/status/status.cc index 5a5cd5c2..53c198e1 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -308,7 +308,7 @@ std::string Status::ToStringSlow(StatusToStringMode mode) const { } std::ostream& operator<<(std::ostream& os, const Status& x) { - os << x.ToString(); + os << x.ToString(StatusToStringMode::kWithEverything); return os; } diff --git a/absl/time/time.h b/absl/time/time.h index 48982df4..7fa0f5c6 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -547,10 +547,20 @@ inline std::ostream& operator<<(std::ostream& os, Duration d) { // `ZeroDuration()`. Parses "inf" and "-inf" as +/- `InfiniteDuration()`. bool ParseDuration(absl::string_view dur_string, Duration* d); -// Support for flag values of type Duration. Duration flags must be specified -// in a format that is valid input for absl::ParseDuration(). +// AbslParseFlag() +// +// Parses a command-line flag string representation `text` into a a Duration +// value. Duration flags must be specified in a format that is valid input for +// `absl::ParseDuration()`. bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error); + + +// AbslUnparseFlag() +// +// Unparses a Duration value into a command-line string representation using +// the format specified by `absl::ParseDuration()`. std::string AbslUnparseFlag(Duration d); + ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Duration* dst, std::string* error); ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") @@ -813,8 +823,12 @@ Time FromChrono(const std::chrono::system_clock::time_point& tp); // // tp == std::chrono::system_clock::from_time_t(123); std::chrono::system_clock::time_point ToChronoTime(Time); -// Support for flag values of type Time. Time flags must be specified in a -// format that matches absl::RFC3339_full. For example: +// AbslParseFlag() +// +// Parses the command-line flag string representation `text` into a Time value. +// Time flags must be specified in a format that matches absl::RFC3339_full. +// +// For example: // // --start_time=2016-01-02T03:04:05.678+08:00 // @@ -824,7 +838,13 @@ std::chrono::system_clock::time_point ToChronoTime(Time); // seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag // and add that duration to absl::UnixEpoch() to get an absl::Time. bool AbslParseFlag(absl::string_view text, Time* t, std::string* error); + +// AbslUnparseFlag() +// +// Unparses a Time value into a command-line string representation using +// the format specified by `absl::ParseTime()`. std::string AbslUnparseFlag(Time t); + ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Time* t, std::string* error); ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh index ffbb8327..5245933a 100755 --- a/ci/linux_clang-latest_libcxx_asan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh @@ -83,6 +83,7 @@ for std in ${STD}; do --copt="-fsanitize=undefined" \ --copt="-fno-sanitize-blacklist" \ --copt=-Werror \ + --distdir="/bazel-distdir" \ --keep_going \ --linkopt="-fsanitize=address" \ --linkopt="-fsanitize-link-c++-runtime" \ diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh index f6a2221e..e0fe653d 100755 --- a/ci/linux_clang-latest_libcxx_bazel.sh +++ b/ci/linux_clang-latest_libcxx_bazel.sh @@ -85,6 +85,7 @@ for std in ${STD}; do --copt=\"${exceptions_mode}\" \ --copt=-Werror \ --define=\"absl=1\" \ + --distdir=\"/bazel-distdir\" \ --keep_going \ --show_timestamps \ --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \ diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh index e70e8214..555f6b1c 100755 --- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh @@ -81,6 +81,7 @@ for std in ${STD}; do --copt="-fsanitize=thread" \ --copt="-fno-sanitize-blacklist" \ --copt=-Werror \ + --distdir="/bazel-distdir" \ --keep_going \ --linkopt="-fsanitize=thread" \ --show_timestamps \ diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh index 0986ff40..36fdf82c 100755 --- a/ci/linux_clang-latest_libstdcxx_bazel.sh +++ b/ci/linux_clang-latest_libstdcxx_bazel.sh @@ -25,7 +25,7 @@ if [[ -z ${ABSEIL_ROOT:-} ]]; then fi if [[ -z ${STD:-} ]]; then - STD="c++11 c++14 c++17 c++20" + STD="c++11 c++14 c++17" fi if [[ -z ${COMPILATION_MODE:-} ]]; then @@ -78,6 +78,7 @@ for std in ${STD}; do --copt="${exceptions_mode}" \ --copt=-Werror \ --define="absl=1" \ + --distdir="/bazel-distdir" \ --keep_going \ --linkopt="--gcc-toolchain=/usr/local" \ --show_timestamps \ diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh index e7eef0fc..32865b83 100644 --- a/ci/linux_docker_containers.sh +++ b/ci/linux_docker_containers.sh @@ -16,6 +16,6 @@ # Test scripts should source this file to get the identifiers. readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026" -readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210519" -readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210519" -readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20201015" +readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617" +readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617" +readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20210617" diff --git a/ci/linux_gcc-floor_libstdcxx_bazel.sh b/ci/linux_gcc-floor_libstdcxx_bazel.sh index 224aef81..54ab68a3 100755 --- a/ci/linux_gcc-floor_libstdcxx_bazel.sh +++ b/ci/linux_gcc-floor_libstdcxx_bazel.sh @@ -77,6 +77,7 @@ for std in ${STD}; do --copt="${exceptions_mode}" \ --copt=-Werror \ --define="absl=1" \ + --distdir="/bazel-distdir" \ --keep_going \ --show_timestamps \ --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \ diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh index 37d89d9f..0555eced 100755 --- a/ci/linux_gcc-latest_libstdcxx_bazel.sh +++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh @@ -83,6 +83,7 @@ for std in ${STD}; do --copt=\"${exceptions_mode}\" \ --copt=-Werror \ --define=\"absl=1\" \ + --distdir=\"/bazel-distdir\" \ --keep_going \ --show_timestamps \ --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \ -- cgit v1.2.3 From a2419e63b8ae3b924152822f3c9f9da67ff6e215 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 23 Jun 2021 09:44:41 -0700 Subject: Export of internal Abseil changes -- 2684e80d877b688b8d9e0af1b7acddbadc973152 by Evan Brown : Add an insert codegen function for raw_hash_set_benchmark. PiperOrigin-RevId: 381052237 -- 8394ef3071714a41484cb5b271cba0611d954a7a by Abseil Team : Optimize raw_hash_set ctor for random-access iterators PiperOrigin-RevId: 380832215 GitOrigin-RevId: 2684e80d877b688b8d9e0af1b7acddbadc973152 Change-Id: Icf7929fdfab50a1b26f3dc5505575363b4f5838d --- absl/container/internal/raw_hash_set.h | 19 +++- absl/container/internal/raw_hash_set_benchmark.cc | 24 +++++ absl/container/internal/raw_hash_set_test.cc | 104 +++++++++++++++++++--- 3 files changed, 135 insertions(+), 12 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 508f2124..03f199a5 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -502,6 +502,22 @@ inline size_t GrowthToLowerboundCapacity(size_t growth) { return growth + static_cast((static_cast(growth) - 1) / 7); } +template +size_t SelectBucketCountForIterRange(InputIter first, InputIter last, + size_t bucket_count) { + if (bucket_count != 0) { + return bucket_count; + } + using InputIterCategory = + typename std::iterator_traits::iterator_category; + if (std::is_base_of::value) { + return GrowthToLowerboundCapacity( + static_cast(std::distance(first, last))); + } + return 0; +} + inline void AssertIsFull(ctrl_t* ctrl) { ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && "Invalid operation on iterator. The element might have " @@ -820,7 +836,8 @@ class raw_hash_set { raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0, const hasher& hash = hasher(), const key_equal& eq = key_equal(), const allocator_type& alloc = allocator_type()) - : raw_hash_set(bucket_count, hash, eq, alloc) { + : raw_hash_set(SelectBucketCountForIterRange(first, last, bucket_count), + hash, eq, alloc) { insert(first, last); } diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc index f9be2c5a..977ff4c0 100644 --- a/absl/container/internal/raw_hash_set_benchmark.cc +++ b/absl/container/internal/raw_hash_set_benchmark.cc @@ -254,6 +254,23 @@ void BM_CopyAssign(benchmark::State& state) { } BENCHMARK(BM_CopyAssign)->Range(128, 4096); +void BM_RangeCtor(benchmark::State& state) { + std::random_device rd; + std::mt19937 rng(rd()); + std::uniform_int_distribution dist(0, ~uint64_t{}); + std::vector values; + const size_t desired_size = state.range(0); + while (values.size() < desired_size) { + values.emplace_back(dist(rng)); + } + + for (auto unused : state) { + IntTable t{values.begin(), values.end()}; + benchmark::DoNotOptimize(t); + } +} +BENCHMARK(BM_RangeCtor)->Range(128, 65536); + void BM_NoOpReserveIntTable(benchmark::State& state) { IntTable t; t.reserve(100000); @@ -378,6 +395,12 @@ bool CodegenAbslRawHashSetInt64FindNeEnd( return table->find(key) != table->end(); } +auto CodegenAbslRawHashSetInt64Insert(absl::container_internal::IntTable* table, + int64_t key) + -> decltype(table->insert(key)) { + return table->insert(key); +} + bool CodegenAbslRawHashSetInt64Contains( absl::container_internal::IntTable* table, int64_t key) { return table->contains(key); @@ -391,6 +414,7 @@ void CodegenAbslRawHashSetInt64Iterate( int odr = (::benchmark::DoNotOptimize(std::make_tuple( &CodegenAbslRawHashSetInt64Find, &CodegenAbslRawHashSetInt64FindNeEnd, + &CodegenAbslRawHashSetInt64Insert, &CodegenAbslRawHashSetInt64Contains, &CodegenAbslRawHashSetInt64Iterate)), 1); diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 87cbdfcc..a191bff9 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -627,28 +627,53 @@ TEST(Table, Contains2) { } int decompose_constructed; +int decompose_copy_constructed; +int decompose_copy_assigned; +int decompose_move_constructed; +int decompose_move_assigned; struct DecomposeType { - DecomposeType(int i) : i(i) { // NOLINT + DecomposeType(int i = 0) : i(i) { // NOLINT ++decompose_constructed; } explicit DecomposeType(const char* d) : DecomposeType(*d) {} + DecomposeType(const DecomposeType& other) : i(other.i) { + ++decompose_copy_constructed; + } + DecomposeType& operator=(const DecomposeType& other) { + ++decompose_copy_assigned; + i = other.i; + return *this; + } + DecomposeType(DecomposeType&& other) : i(other.i) { + ++decompose_move_constructed; + } + DecomposeType& operator=(DecomposeType&& other) { + ++decompose_move_assigned; + i = other.i; + return *this; + } + int i; }; struct DecomposeHash { using is_transparent = void; - size_t operator()(DecomposeType a) const { return a.i; } + size_t operator()(const DecomposeType& a) const { return a.i; } size_t operator()(int a) const { return a; } size_t operator()(const char* a) const { return *a; } }; struct DecomposeEq { using is_transparent = void; - bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; } - bool operator()(DecomposeType a, int b) const { return a.i == b; } - bool operator()(DecomposeType a, const char* b) const { return a.i == *b; } + bool operator()(const DecomposeType& a, const DecomposeType& b) const { + return a.i == b.i; + } + bool operator()(const DecomposeType& a, int b) const { return a.i == b; } + bool operator()(const DecomposeType& a, const char* b) const { + return a.i == *b; + } }; struct DecomposePolicy { @@ -658,9 +683,9 @@ struct DecomposePolicy { template static void construct(void*, DecomposeType* slot, T&& v) { - *slot = DecomposeType(std::forward(v)); + ::new (slot) DecomposeType(std::forward(v)); } - static void destroy(void*, DecomposeType*) {} + static void destroy(void*, DecomposeType* slot) { slot->~DecomposeType(); } static DecomposeType& element(slot_type* slot) { return *slot; } template @@ -675,8 +700,13 @@ void TestDecompose(bool construct_three) { const int one = 1; const char* three_p = "3"; const auto& three = three_p; + const int elem_vector_count = 256; + std::vector elem_vector(elem_vector_count, DecomposeType{0}); + std::iota(elem_vector.begin(), elem_vector.end(), 0); - raw_hash_set> set1; + using DecomposeSet = + raw_hash_set>; + DecomposeSet set1; decompose_constructed = 0; int expected_constructed = 0; @@ -734,20 +764,72 @@ void TestDecompose(bool construct_three) { expected_constructed += construct_three; EXPECT_EQ(expected_constructed, decompose_constructed); } + + decompose_copy_constructed = 0; + decompose_copy_assigned = 0; + decompose_move_constructed = 0; + decompose_move_assigned = 0; + int expected_copy_constructed = 0; + int expected_move_constructed = 0; + { // raw_hash_set(first, last) with random-access iterators + DecomposeSet set2(elem_vector.begin(), elem_vector.end()); + // Expect exactly one copy-constructor call for each element if no + // rehashing is done. + expected_copy_constructed += elem_vector_count; + EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed); + EXPECT_EQ(expected_move_constructed, decompose_move_constructed); + EXPECT_EQ(0, decompose_move_assigned); + EXPECT_EQ(0, decompose_copy_assigned); + } + + { // raw_hash_set(first, last) with forward iterators + std::list elem_list(elem_vector.begin(), elem_vector.end()); + expected_copy_constructed = decompose_copy_constructed; + DecomposeSet set2(elem_list.begin(), elem_list.end()); + // Expect exactly N elements copied into set, expect at most 2*N elements + // moving internally for all resizing needed (for a growth factor of 2). + expected_copy_constructed += elem_vector_count; + EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed); + expected_move_constructed += elem_vector_count; + EXPECT_LT(expected_move_constructed, decompose_move_constructed); + expected_move_constructed += elem_vector_count; + EXPECT_GE(expected_move_constructed, decompose_move_constructed); + EXPECT_EQ(0, decompose_move_assigned); + EXPECT_EQ(0, decompose_copy_assigned); + expected_copy_constructed = decompose_copy_constructed; + expected_move_constructed = decompose_move_constructed; + } + + { // insert(first, last) + DecomposeSet set2; + set2.insert(elem_vector.begin(), elem_vector.end()); + // Expect exactly N elements copied into set, expect at most 2*N elements + // moving internally for all resizing needed (for a growth factor of 2). + const int expected_new_elements = elem_vector_count; + const int expected_max_element_moves = 2 * elem_vector_count; + expected_copy_constructed += expected_new_elements; + EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed); + expected_move_constructed += expected_max_element_moves; + EXPECT_GE(expected_move_constructed, decompose_move_constructed); + EXPECT_EQ(0, decompose_move_assigned); + EXPECT_EQ(0, decompose_copy_assigned); + expected_copy_constructed = decompose_copy_constructed; + expected_move_constructed = decompose_move_constructed; + } } TEST(Table, Decompose) { TestDecompose(false); struct TransparentHashIntOverload { - size_t operator()(DecomposeType a) const { return a.i; } + size_t operator()(const DecomposeType& a) const { return a.i; } size_t operator()(int a) const { return a; } }; struct TransparentEqIntOverload { - bool operator()(DecomposeType a, DecomposeType b) const { + bool operator()(const DecomposeType& a, const DecomposeType& b) const { return a.i == b.i; } - bool operator()(DecomposeType a, int b) const { return a.i == b; } + bool operator()(const DecomposeType& a, int b) const { return a.i == b; } }; TestDecompose(true); TestDecompose(true); -- cgit v1.2.3 From 9a7e447c511dae7276ab65fde4d04f6ed52b39c9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 24 Jun 2021 09:53:14 -0700 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- 373171b46238585c818cec37af26959f5412f813 by Abseil Team : Build with -Wl,-no-undefined. PiperOrigin-RevId: 381276748 -- da32624792d2948fe83d0ce58794d505799ab5d0 by Benjamin Barenblat : s/round/rint/ in exponential_biased `rint` differs from `round` in that it uses the current FPU rounding mode. It’s thus potentially faster, since it doesn’t have to save and restore FPU state. It also is more reflective of developer intent – most developers expect all FPU operations to use the current rounding mode, and having exponential_biased follow that rule seems ideal. PiperOrigin-RevId: 381268264 -- 8f860253a4283d2cc8230fe98d7cdf7bcb3e05f1 by Abseil Team : Internal change. PiperOrigin-RevId: 381264180 GitOrigin-RevId: 373171b46238585c818cec37af26959f5412f813 Change-Id: Iefe60b15c80318a7707e0c32159ac004bfa26d72 --- absl/base/BUILD.bazel | 5 ----- absl/base/internal/exponential_biased.cc | 2 +- absl/base/internal/exponential_biased.h | 2 +- absl/container/BUILD.bazel | 1 - absl/flags/BUILD.bazel | 10 ---------- absl/hash/BUILD.bazel | 1 - absl/numeric/BUILD.bazel | 1 - absl/status/BUILD.bazel | 2 -- absl/strings/BUILD.bazel | 7 ------- absl/synchronization/BUILD.bazel | 2 -- absl/time/BUILD.bazel | 1 - absl/time/internal/cctz/BUILD.bazel | 2 -- 12 files changed, 2 insertions(+), 34 deletions(-) diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 16b0e2fb..65ff0dde 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -56,7 +56,6 @@ cc_library( srcs = ["log_severity.cc"], hdrs = ["log_severity.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", @@ -160,7 +159,6 @@ cc_library( "internal/low_level_alloc.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = select({ "//absl:msvc_compiler": [], "//absl:clang-cl_compiler": [], @@ -222,7 +220,6 @@ cc_library( "internal/unscaledcycleclock.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = select({ "//absl:msvc_compiler": [ "-DEFAULTLIB:advapi32.lib", @@ -293,7 +290,6 @@ cc_library( srcs = ["internal/throw_delegate.cc"], hdrs = ["internal/throw_delegate.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", @@ -713,7 +709,6 @@ cc_library( srcs = ["internal/strerror.cc"], hdrs = ["internal/strerror.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", diff --git a/absl/base/internal/exponential_biased.cc b/absl/base/internal/exponential_biased.cc index 1b30c061..05aeea56 100644 --- a/absl/base/internal/exponential_biased.cc +++ b/absl/base/internal/exponential_biased.cc @@ -64,7 +64,7 @@ int64_t ExponentialBiased::GetSkipCount(int64_t mean) { // Assume huge values are bias neutral, retain bias for next call. return std::numeric_limits::max() / 2; } - double value = std::round(interval); + double value = std::rint(interval); bias_ = interval - value; return value; } diff --git a/absl/base/internal/exponential_biased.h b/absl/base/internal/exponential_biased.h index 94f79a33..a81f10e2 100644 --- a/absl/base/internal/exponential_biased.h +++ b/absl/base/internal/exponential_biased.h @@ -66,7 +66,7 @@ namespace base_internal { // Adjusting with rounding bias is relatively trivial: // // double value = bias_ + exponential_distribution(mean)(); -// double rounded_value = std::round(value); +// double rounded_value = std::rint(value); // bias_ = value - rounded_value; // return rounded_value; // diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 96510541..f22fdc60 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -505,7 +505,6 @@ cc_library( ], hdrs = ["internal/hashtablez_sampler.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":have_sse", diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 940e3561..c178b86b 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -51,7 +51,6 @@ cc_library( "internal/program_name.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/flags:__pkg__", @@ -75,7 +74,6 @@ cc_library( "usage_config.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":path_util", @@ -96,7 +94,6 @@ cc_library( "marshalling.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", @@ -116,7 +113,6 @@ cc_library( "internal/commandlineflag.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", @@ -133,7 +129,6 @@ cc_library( "commandlineflag.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":commandlineflag_internal", @@ -175,7 +170,6 @@ cc_library( "reflection.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":commandlineflag", @@ -200,7 +194,6 @@ cc_library( "internal/sequence_lock.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//absl/base:__subpackages__"], deps = [ @@ -251,7 +244,6 @@ cc_library( "internal/usage.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/flags:__pkg__", @@ -281,7 +273,6 @@ cc_library( "usage.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":usage_internal", @@ -300,7 +291,6 @@ cc_library( "parse.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":commandlineflag", diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index f5005a04..4b2c220f 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -34,7 +34,6 @@ cc_library( ], hdrs = ["hash.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index a0ef9055..ea587bfa 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel @@ -62,7 +62,6 @@ cc_library( ], hdrs = ["int128.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bits", diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 4db72c0d..189bd73d 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -40,7 +40,6 @@ cc_library( "status_payload_printer.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], deps = [ "//absl/base:atomic_hook", "//absl/base:config", @@ -77,7 +76,6 @@ cc_library( "statusor.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], deps = [ ":status", "//absl/base:core_headers", diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index a02ee395..1cb5b3e5 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -66,7 +66,6 @@ cc_library( "substitute.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], deps = [ ":internal", "//absl/base", @@ -97,7 +96,6 @@ cc_library( "internal/utf8.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -280,7 +278,6 @@ cc_library( "internal/cord_rep_ring_reader.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], visibility = [ "//visibility:private", ], @@ -330,7 +327,6 @@ cc_library( "cord.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], deps = [ ":cord_internal", ":cordz_functions", @@ -359,7 +355,6 @@ cc_library( srcs = ["internal/cordz_handle.cc"], hdrs = ["internal/cordz_handle.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], visibility = [ "//absl:__subpackages__", ], @@ -376,7 +371,6 @@ cc_library( srcs = ["internal/cordz_info.cc"], hdrs = ["internal/cordz_info.h"], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], visibility = [ "//absl:__subpackages__", ], @@ -1001,7 +995,6 @@ cc_library( "internal/str_format/parser.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], visibility = ["//visibility:private"], deps = [ ":strings", diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 46c4b917..92e2448d 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -36,7 +36,6 @@ cc_library( "internal/graphcycles.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", @@ -88,7 +87,6 @@ cc_library( "notification.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = select({ "//absl:msvc_compiler": [], "//absl:clang-cl_compiler": [], diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index 4700616d..3e25ca26 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -43,7 +43,6 @@ cc_library( "time.h", ], copts = ABSL_DEFAULT_COPTS, - features = ["-no_undefined"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base", diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index 35747e57..45a95292 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -45,7 +45,6 @@ cc_library( hdrs = [ "include/cctz/civil_time.h", ], - features = ["-no_undefined"], textual_hdrs = ["include/cctz/civil_time_detail.h"], visibility = ["//visibility:public"], deps = ["//absl/base:config"], @@ -75,7 +74,6 @@ cc_library( "include/cctz/time_zone.h", "include/cctz/zone_info_source.h", ], - features = ["-no_undefined"], linkopts = select({ ":osx": [ "-framework Foundation", -- cgit v1.2.3 From 58e042da9210710dc4ac3b320e48b54e2449521e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 2 Jul 2021 20:42:20 -0700 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- 1620e8ffaa93ef24510ca60c7fff2a07248ac9f6 by Abseil Team : Update comment. PiperOrigin-RevId: 382858259 -- 20db116f28469149d10e0f7f8b976cb903dd4879 by Gennadiy Rozental : Add benchmark running on multiple flags. Update size_tester to include cost of absl::GetFlag call. Add size_tester invocation for bool flag. New benchmark better represent GetFlag usage. PiperOrigin-RevId: 382820341 -- 2e097ad3811c4e329f75b98877a5e74c1d3d84fd by Abseil Team : Avoid 64x64->128 multiplication in absl::Hash's mix on AArch64 On AArch64, calculating a 128-bit product is inefficient, because it requires a sequence of two instructions to calculate the upper and lower halves of the result. So calculate a 64-bit product instead. Making MultType 64-bits means the upper 32 bits of the result do not participate in shift/xor, but the add/multiply gives us sufficient mixing. PiperOrigin-RevId: 382625931 -- f3ae3f32cb53168c8dc91b766f2932dc87cec503 by Abseil Team : Remove homegrown Round implementation absl/time/duration.cc defined a Round implementation to accommodate old versions of MSVC that lacked std::round(long double). Abseil no longer supports those MSVCs, so we don’t need the homegrown implementation anymore. Remove it, and replace calls to it with std::rint. PiperOrigin-RevId: 382605191 -- a13631c91bf5478289e1a512ce215c85501a26f7 by Martijn Vels : Move the Consume() conversion functions out of cord_rep_ring into cord_rep_consume. This makes these functions generic, so we can repurpose these for the new Btree conversion functions. PiperOrigin-RevId: 382594902 -- 7394c737500c2d8371fcf913b21ad1b321ba499d by Benjamin Barenblat : Remove homegrown Round implementation absl/time/duration.cc defined a Round implementation to accommodate old versions of MSVC that lacked std::round(long double). Abseil no longer supports those MSVCs, so we don’t need the homegrown implementation anymore. Remove it, and replace calls to it with std::rint. PiperOrigin-RevId: 382569900 -- d72a761f43dc5c9b9510c3a1363177ed26646b5d by Abseil Team : Prefer `getentropy` for Emscripten. It needs a different header, so I've separated it out from the GLIBC check above. PiperOrigin-RevId: 382332475 -- 74e261dbb467741b2ddd8b490e04c531fdd2f559 by Martijn Vels : Add BTREE tag for CordRepNode implementing a Btree cord. This change only forward declared the CordRepBtree class (not implemented yet) and defines the enum value BTREE. While RING and BTREE should never co-exist, we define a new value for BTREE so as not to make transitioning between RING and BTREE harder than it needs to be. This changes shifts the FLAT value / computation from FLAT = 4 to FLAT =5 PiperOrigin-RevId: 382326710 GitOrigin-RevId: 1620e8ffaa93ef24510ca60c7fff2a07248ac9f6 Change-Id: Ia8f99dde3874808f56062bd37ab3e63764099734 --- CMake/AbseilDll.cmake | 2 + absl/flags/flag_benchmark.cc | 138 ++++++++++++++++---- absl/hash/internal/hash.h | 7 + absl/random/internal/seed_material.cc | 6 + absl/strings/BUILD.bazel | 19 +++ absl/strings/CMakeLists.txt | 20 +++ absl/strings/internal/cord_internal.h | 20 ++- absl/strings/internal/cord_rep_consume.cc | 129 ++++++++++++++++++ absl/strings/internal/cord_rep_consume.h | 50 +++++++ absl/strings/internal/cord_rep_consume_test.cc | 173 +++++++++++++++++++++++++ absl/strings/internal/cord_rep_flat.h | 10 +- absl/strings/internal/cord_rep_ring.cc | 118 +---------------- absl/strings/internal/str_split_internal.h | 2 +- 13 files changed, 537 insertions(+), 157 deletions(-) create mode 100644 absl/strings/internal/cord_rep_consume.cc create mode 100644 absl/strings/internal/cord_rep_consume.h create mode 100644 absl/strings/internal/cord_rep_consume_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 8ee4120f..78ace82b 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -203,6 +203,8 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/charconv_parse.h" "strings/internal/cord_internal.cc" "strings/internal/cord_internal.h" + "strings/internal/cord_rep_consume.h" + "strings/internal/cord_rep_consume.cc" "strings/internal/cord_rep_flat.h" "strings/internal/cord_rep_ring.cc" "strings/internal/cord_rep_ring.h" diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc index 57584f85..fc572d9c 100644 --- a/absl/flags/flag_benchmark.cc +++ b/absl/flags/flag_benchmark.cc @@ -101,7 +101,39 @@ std::string AbslUnparseFlag(const UDT&) { return ""; } A(AbslDuration) \ A(UDT) -#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, ""); +#define REPLICATE_0(A, T, name, index) A(T, name, index) +#define REPLICATE_1(A, T, name, index) \ + REPLICATE_0(A, T, name, index##0) REPLICATE_0(A, T, name, index##1) +#define REPLICATE_2(A, T, name, index) \ + REPLICATE_1(A, T, name, index##0) REPLICATE_1(A, T, name, index##1) +#define REPLICATE_3(A, T, name, index) \ + REPLICATE_2(A, T, name, index##0) REPLICATE_2(A, T, name, index##1) +#define REPLICATE_4(A, T, name, index) \ + REPLICATE_3(A, T, name, index##0) REPLICATE_3(A, T, name, index##1) +#define REPLICATE_5(A, T, name, index) \ + REPLICATE_4(A, T, name, index##0) REPLICATE_4(A, T, name, index##1) +#define REPLICATE_6(A, T, name, index) \ + REPLICATE_5(A, T, name, index##0) REPLICATE_5(A, T, name, index##1) +#define REPLICATE_7(A, T, name, index) \ + REPLICATE_6(A, T, name, index##0) REPLICATE_6(A, T, name, index##1) +#define REPLICATE_8(A, T, name, index) \ + REPLICATE_7(A, T, name, index##0) REPLICATE_7(A, T, name, index##1) +#define REPLICATE_9(A, T, name, index) \ + REPLICATE_8(A, T, name, index##0) REPLICATE_8(A, T, name, index##1) +#if defined(_MSC_VER) +#define REPLICATE(A, T, name) \ + REPLICATE_7(A, T, name, 0) REPLICATE_7(A, T, name, 1) +#define SINGLE_FLAG(T) FLAGS_##T##_flag_00000000 +#else +#define REPLICATE(A, T, name) \ + REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1) +#define SINGLE_FLAG(T) FLAGS_##T##_flag_0000000000 +#endif +#define REPLICATE_ALL(A, T, name) \ + REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1) + +#define COUNT(T, name, index) +1 +constexpr size_t kNumFlags = 0 REPLICATE(COUNT, _, _); #if defined(__clang__) && defined(__linux__) // Force the flags used for benchmarks into a separate ELF section. @@ -110,38 +142,87 @@ std::string AbslUnparseFlag(const UDT&) { return ""; } // benchmark results more reproducible across unrelated code changes. #pragma clang section data = ".benchmark_flags" #endif +#define DEFINE_FLAG(T, name, index) ABSL_FLAG(T, name##_##index, {}, ""); +#define FLAG_DEF(T) REPLICATE(DEFINE_FLAG, T, T##_flag); BENCHMARKED_TYPES(FLAG_DEF) #if defined(__clang__) && defined(__linux__) #pragma clang section data = "" #endif // Register thousands of flags to bloat up the size of the registry. // This mimics real life production binaries. -#define DEFINE_FLAG_0(name) ABSL_FLAG(int, name, 0, ""); -#define DEFINE_FLAG_1(name) DEFINE_FLAG_0(name##0) DEFINE_FLAG_0(name##1) -#define DEFINE_FLAG_2(name) DEFINE_FLAG_1(name##0) DEFINE_FLAG_1(name##1) -#define DEFINE_FLAG_3(name) DEFINE_FLAG_2(name##0) DEFINE_FLAG_2(name##1) -#define DEFINE_FLAG_4(name) DEFINE_FLAG_3(name##0) DEFINE_FLAG_3(name##1) -#define DEFINE_FLAG_5(name) DEFINE_FLAG_4(name##0) DEFINE_FLAG_4(name##1) -#define DEFINE_FLAG_6(name) DEFINE_FLAG_5(name##0) DEFINE_FLAG_5(name##1) -#define DEFINE_FLAG_7(name) DEFINE_FLAG_6(name##0) DEFINE_FLAG_6(name##1) -#define DEFINE_FLAG_8(name) DEFINE_FLAG_7(name##0) DEFINE_FLAG_7(name##1) -#define DEFINE_FLAG_9(name) DEFINE_FLAG_8(name##0) DEFINE_FLAG_8(name##1) -#define DEFINE_FLAG_10(name) DEFINE_FLAG_9(name##0) DEFINE_FLAG_9(name##1) -#define DEFINE_FLAG_11(name) DEFINE_FLAG_10(name##0) DEFINE_FLAG_10(name##1) -#define DEFINE_FLAG_12(name) DEFINE_FLAG_11(name##0) DEFINE_FLAG_11(name##1) -DEFINE_FLAG_12(bloat_flag_); +#define BLOAT_FLAG(_unused1, _unused2, index) \ + ABSL_FLAG(int, bloat_flag_##index, 0, ""); +REPLICATE_ALL(BLOAT_FLAG, _, _) namespace { -#define BM_GetFlag(T) \ - void BM_GetFlag_##T(benchmark::State& state) { \ - for (auto _ : state) { \ - benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \ - } \ - } \ - BENCHMARK(BM_GetFlag_##T)->ThreadRange(1, 16); +#define FLAG_PTR(T, name, index) &FLAGS_##name##_##index, +#define FLAG_PTR_ARR(T) \ + static constexpr absl::Flag* FlagPtrs_##T[] = { \ + REPLICATE(FLAG_PTR, T, T##_flag)}; +BENCHMARKED_TYPES(FLAG_PTR_ARR) + +#define BM_SingleGetFlag(T) \ + void BM_SingleGetFlag_##T(benchmark::State& state) { \ + for (auto _ : state) { \ + benchmark::DoNotOptimize(absl::GetFlag(SINGLE_FLAG(T))); \ + } \ + } \ + BENCHMARK(BM_SingleGetFlag_##T)->ThreadRange(1, 16); + +BENCHMARKED_TYPES(BM_SingleGetFlag) + +template +struct Accumulator { + using type = T; +}; +template <> +struct Accumulator { + using type = size_t; +}; +template <> +struct Accumulator { + using type = size_t; +}; +template <> +struct Accumulator { + using type = bool; +}; +template <> +struct Accumulator { + using type = bool; +}; +template <> +struct Accumulator { + using type = bool; +}; + +template +void Accumulate(typename Accumulator::type& a, const T& f) { + a += f; +} +void Accumulate(bool& a, bool f) { a = a || f; } +void Accumulate(size_t& a, const std::string& f) { a += f.size(); } +void Accumulate(size_t& a, const std::vector& f) { a += f.size(); } +void Accumulate(bool& a, const OptionalInt& f) { a |= f.has_value(); } +void Accumulate(bool& a, const OptionalString& f) { a |= f.has_value(); } +void Accumulate(bool& a, const UDT& f) { + a |= reinterpret_cast(&f) & 0x1; +} + +#define BM_ManyGetFlag(T) \ + void BM_ManyGetFlag_##T(benchmark::State& state) { \ + Accumulator::type res = {}; \ + while (state.KeepRunningBatch(kNumFlags)) { \ + for (auto* flag_ptr : FlagPtrs_##T) { \ + Accumulate(res, absl::GetFlag(*flag_ptr)); \ + } \ + } \ + benchmark::DoNotOptimize(res); \ + } \ + BENCHMARK(BM_ManyGetFlag_##T)->ThreadRange(1, 8); -BENCHMARKED_TYPES(BM_GetFlag) +BENCHMARKED_TYPES(BM_ManyGetFlag) void BM_ThreadedFindCommandLineFlag(benchmark::State& state) { char dummy[] = "dummy"; @@ -150,17 +231,18 @@ void BM_ThreadedFindCommandLineFlag(benchmark::State& state) { // is finalized. absl::ParseCommandLine(1, argv); - for (auto s : state) { - benchmark::DoNotOptimize( - absl::FindCommandLineFlag("bloat_flag_010101010101")); + while (state.KeepRunningBatch(kNumFlags)) { + for (auto* flag_ptr : FlagPtrs_bool) { + benchmark::DoNotOptimize(absl::FindCommandLineFlag(flag_ptr->Name())); + } } } BENCHMARK(BM_ThreadedFindCommandLineFlag)->ThreadRange(1, 16); } // namespace -#define InvokeGetFlag(T) \ - T AbslInvokeGetFlag##T() { return absl::GetFlag(FLAGS_##T##_flag); } \ +#define InvokeGetFlag(T) \ + T AbslInvokeGetFlag##T() { return absl::GetFlag(SINGLE_FLAG(T)); } \ int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1); BENCHMARKED_TYPES(InvokeGetFlag) diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index 69dbbc6b..90627e00 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -856,8 +856,15 @@ class ABSL_DLL MixingHashState : public HashStateBase { } ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) { +#if defined(__aarch64__) + // On AArch64, calculating a 128-bit product is inefficient, because it + // requires a sequence of two instructions to calculate the upper and lower + // halves of the result. + using MultType = uint64_t; +#else using MultType = absl::conditional_t; +#endif // We do the addition in 64-bit space to make sure the 128-bit // multiplication is fast. If we were to do it as MultType the compiler has // to assume that the high word is non-zero and needs to perform 2 diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc index 7c1d9efa..c03cad85 100644 --- a/absl/random/internal/seed_material.cc +++ b/absl/random/internal/seed_material.cc @@ -57,6 +57,12 @@ #define ABSL_RANDOM_USE_GET_ENTROPY 1 #endif +#if defined(__EMSCRIPTEN__) +#include +// Emscripten has getentropy, but it resides in a different header. +#define ABSL_RANDOM_USE_GET_ENTROPY 1 +#endif + #if defined(ABSL_RANDOM_USE_BCRYPT) #include diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 1cb5b3e5..b70aac3e 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -269,10 +269,12 @@ cc_library( name = "cord_internal", srcs = [ "internal/cord_internal.cc", + "internal/cord_rep_consume.cc", "internal/cord_rep_ring.cc", ], hdrs = [ "internal/cord_internal.h", + "internal/cord_rep_consume.h", "internal/cord_rep_flat.h", "internal/cord_rep_ring.h", "internal/cord_rep_ring_reader.h", @@ -292,6 +294,7 @@ cc_library( "//absl/container:compressed_tuple", "//absl/container:inlined_vector", "//absl/container:layout", + "//absl/functional:function_ref", "//absl/meta:type_traits", ], ) @@ -649,6 +652,22 @@ cc_test( ], ) +cc_test( + name = "cord_rep_consume_test", + size = "medium", + srcs = ["internal/cord_rep_consume_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":strings", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/debugging:leak_check", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "cord_ring_test", size = "medium", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 0246dc38..d79ef712 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -555,11 +555,13 @@ absl_cc_library( cord_internal HDRS "internal/cord_internal.h" + "internal/cord_rep_consume.h" "internal/cord_rep_flat.h" "internal/cord_rep_ring.h" "internal/cord_rep_ring_reader.h" SRCS "internal/cord_internal.cc" + "internal/cord_rep_consume.cc" "internal/cord_rep_ring.cc" COPTS ${ABSL_DEFAULT_COPTS} @@ -902,6 +904,24 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + cord_rep_consume_test + SRCS + "internal/cord_rep_consume_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::core_headers + absl::function_ref + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + absl_cc_test( NAME cord_ring_test diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 1e2436ba..bddc9815 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -150,22 +150,24 @@ struct CordRepExternal; struct CordRepFlat; struct CordRepSubstring; class CordRepRing; +class CordRepBtree; // Various representations that we allow enum CordRepKind { CONCAT = 0, SUBSTRING = 1, - RING = 2, - EXTERNAL = 3, + BTREE = 2, + RING = 3, + EXTERNAL = 4, // We have different tags for different sized flat arrays, - // starting with FLAT, and limited to MAX_FLAT_TAG. The 224 value is based on + // starting with FLAT, and limited to MAX_FLAT_TAG. The 225 value is based on // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well // as the Tag <---> Size logic so that FLAT stil represents the minimum flat // allocation size. (32 bytes as of now). - FLAT = 4, - MAX_FLAT_TAG = 224 + FLAT = 5, + MAX_FLAT_TAG = 225 }; // There are various locations where we want to check if some rep is a 'plain' @@ -175,8 +177,9 @@ enum CordRepKind { // so likewise align RING to EXTERNAL. // Note that we can leave this optimization to the compiler. The compiler will // DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`. -static_assert(EXTERNAL == RING + 1, "RING and EXTERNAL values not consecutive"); -static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT values not consecutive"); +static_assert(RING == BTREE + 1, "BTREE and RING not consecutive"); +static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive"); +static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive"); struct CordRep { CordRep() = default; @@ -203,6 +206,9 @@ struct CordRep { inline CordRepFlat* flat(); inline const CordRepFlat* flat() const; + inline CordRepBtree* btree(); + inline const CordRepBtree* btree() const; + // -------------------------------------------------------------------- // Memory management diff --git a/absl/strings/internal/cord_rep_consume.cc b/absl/strings/internal/cord_rep_consume.cc new file mode 100644 index 00000000..81514543 --- /dev/null +++ b/absl/strings/internal/cord_rep_consume.cc @@ -0,0 +1,129 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_consume.h" + +#include +#include + +#include "absl/container/inlined_vector.h" +#include "absl/functional/function_ref.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +namespace { + +// Unrefs the provided `substring`, and returns `substring->child` +// Adds or assumes a reference on `substring->child` +CordRep* ClipSubstring(CordRepSubstring* substring) { + CordRep* child = substring->child; + if (substring->refcount.IsOne()) { + delete substring; + } else { + CordRep::Ref(child); + CordRep::Unref(substring); + } + return child; +} + +// Unrefs the provided `concat`, and returns `{concat->left, concat->right}` +// Adds or assumes a reference on `concat->left` and `concat->right`. +// Returns an array of 2 elements containing the left and right nodes. +std::array ClipConcat(CordRepConcat* concat) { + std::array result{concat->left, concat->right}; + if (concat->refcount.IsOne()) { + delete concat; + } else { + CordRep::Ref(result[0]); + CordRep::Ref(result[1]); + CordRep::Unref(concat); + } + return result; +} + +void Consume(bool forward, CordRep* rep, ConsumeFn consume_fn) { + size_t offset = 0; + size_t length = rep->length; + struct Entry { + CordRep* rep; + size_t offset; + size_t length; + }; + absl::InlinedVector stack; + + for (;;) { + if (rep->tag == CONCAT) { + std::array res = ClipConcat(rep->concat()); + CordRep* left = res[0]; + CordRep* right = res[1]; + + if (left->length <= offset) { + // Don't need left node + offset -= left->length; + CordRep::Unref(left); + rep = right; + continue; + } + + size_t length_left = left->length - offset; + if (length_left >= length) { + // Don't need right node + CordRep::Unref(right); + rep = left; + continue; + } + + // Need both nodes + size_t length_right = length - length_left; + if (forward) { + stack.push_back({right, 0, length_right}); + rep = left; + length = length_left; + } else { + stack.push_back({left, offset, length_left}); + rep = right; + offset = 0; + length = length_right; + } + } else if (rep->tag == SUBSTRING) { + offset += rep->substring()->start; + rep = ClipSubstring(rep->substring()); + } else { + consume_fn(rep, offset, length); + if (stack.empty()) return; + + rep = stack.back().rep; + offset = stack.back().offset; + length = stack.back().length; + stack.pop_back(); + } + } +} + +} // namespace + +void Consume(CordRep* rep, ConsumeFn consume_fn) { + return Consume(true, rep, std::move(consume_fn)); +} + +void ReverseConsume(CordRep* rep, ConsumeFn consume_fn) { + return Consume(false, rep, std::move(consume_fn)); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_consume.h b/absl/strings/internal/cord_rep_consume.h new file mode 100644 index 00000000..d46fca2b --- /dev/null +++ b/absl/strings/internal/cord_rep_consume.h @@ -0,0 +1,50 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ + +#include + +#include "absl/functional/function_ref.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Functor for the Consume() and ReverseConsume() functions: +// void ConsumeFunc(CordRep* rep, size_t offset, size_t length); +// See the Consume() and ReverseConsume() function comments for documentation. +using ConsumeFn = FunctionRef; + +// Consume() and ReverseConsume() consume CONCAT based trees and invoke the +// provided functor with the contained nodes in the proper forward or reverse +// order, which is used to convert CONCAT trees into other tree or cord data. +// All CONCAT and SUBSTRING nodes are processed internally. The 'offset` +// parameter of the functor is non-zero for any nodes below SUBSTRING nodes. +// It's up to the caller to form these back into SUBSTRING nodes or otherwise +// store offset / prefix information. These functions are intended to be used +// only for migration / transitional code where due to factors such as ODR +// violations, we can not 100% guarantee that all code respects 'new format' +// settings and flags, so we need to be able to parse old data on the fly until +// all old code is deprecated / no longer the default format. +void Consume(CordRep* rep, ConsumeFn consume_fn); +void ReverseConsume(CordRep* rep, ConsumeFn consume_fn); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ diff --git a/absl/strings/internal/cord_rep_consume_test.cc b/absl/strings/internal/cord_rep_consume_test.cc new file mode 100644 index 00000000..e507824b --- /dev/null +++ b/absl/strings/internal/cord_rep_consume_test.cc @@ -0,0 +1,173 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_consume.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using testing::InSequence; +using testing::MockFunction; + +// Returns the depth of a node +int Depth(const CordRep* rep) { + return (rep->tag == CONCAT) ? rep->concat()->depth() : 0; +} + +// Creates a concatenation of the specified nodes. +CordRepConcat* CreateConcat(CordRep* left, CordRep* right) { + auto* concat = new CordRepConcat(); + concat->tag = CONCAT; + concat->left = left; + concat->right = right; + concat->length = left->length + right->length; + concat->set_depth(1 + (std::max)(Depth(left), Depth(right))); + return concat; +} + +// Creates a flat with the length set to `length` +CordRepFlat* CreateFlatWithLength(size_t length) { + auto* flat = CordRepFlat::New(length); + flat->length = length; + return flat; +} + +// Creates a substring node on the specified child. +CordRepSubstring* CreateSubstring(CordRep* child, size_t start, size_t length) { + auto* rep = new CordRepSubstring(); + rep->length = length; + rep->tag = SUBSTRING; + rep->start = start; + rep->child = child; + return rep; +} + +// Flats we use in the tests +CordRep* flat[6]; + +// Creates a test tree +CordRep* CreateTestTree() { + flat[0] = CreateFlatWithLength(1); + flat[1] = CreateFlatWithLength(7); + CordRepConcat* left = CreateConcat(flat[0], CreateSubstring(flat[1], 2, 4)); + + flat[2] = CreateFlatWithLength(9); + flat[3] = CreateFlatWithLength(13); + CordRepConcat* right1 = CreateConcat(flat[2], flat[3]); + + flat[4] = CreateFlatWithLength(15); + flat[5] = CreateFlatWithLength(19); + CordRepConcat* right2 = CreateConcat(flat[4], flat[5]); + + CordRepConcat* right = CreateConcat(right1, CreateSubstring(right2, 5, 17)); + return CreateConcat(left, right); +} + +TEST(CordRepConsumeTest, Consume) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction consume; + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + Consume(tree, consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_TRUE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } +} + +TEST(CordRepConsumeTest, ConsumeShared) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction consume; + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + Consume(CordRep::Ref(tree), consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_FALSE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } + CordRep::Unref(tree); +} + +TEST(CordRepConsumeTest, Reverse) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction consume; + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + ReverseConsume(tree, consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_TRUE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } +} + +TEST(CordRepConsumeTest, ReverseShared) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction consume; + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + ReverseConsume(CordRep::Ref(tree), consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_FALSE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } + CordRep::Unref(tree); +} + +TEST(CordRepConsumeTest, UnreachableFlat) { + InSequence in_sequence; + CordRepFlat* flat1 = CreateFlatWithLength(10); + CordRepFlat* flat2 = CreateFlatWithLength(20); + CordRepConcat* concat = CreateConcat(flat1, flat2); + CordRepSubstring* tree = CreateSubstring(concat, 15, 10); + MockFunction consume; + EXPECT_CALL(consume, Call(flat2, 5, 10)); + Consume(tree, consume.AsStdFunction()); + EXPECT_TRUE(flat2->refcount.IsOne()); + CordRep::Unref(flat2); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index a98aa9df..cddb282f 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -44,11 +44,11 @@ static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead; constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) { - return static_cast((size <= 1024) ? size / 8 - : 128 + size / 32 - 1024 / 32); + return static_cast((size <= 1024) ? size / 8 + 1 + : 129 + size / 32 - 1024 / 32); } -static_assert(kMinFlatSize / 8 >= FLAT, ""); +static_assert(kMinFlatSize / 8 + 1 >= FLAT, ""); static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, ""); // Helper functions for rounded div, and rounding to exact sizes. @@ -73,7 +73,7 @@ inline uint8_t AllocatedSizeToTag(size_t size) { // Converts the provided tag to the corresponding allocated size constexpr size_t TagToAllocatedSize(uint8_t tag) { - return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32); + return (tag <= 129) ? ((tag - 1) * 8) : (1024 + (tag - 129) * 32); } // Converts the provided tag to the corresponding available data length @@ -82,7 +82,7 @@ constexpr size_t TagToLength(uint8_t tag) { } // Enforce that kMaxFlatSize maps to a well-known exact tag value. -static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic"); +static_assert(TagToAllocatedSize(225) == kMaxFlatSize, "Bad tag logic"); struct CordRepFlat : public CordRep { // Creates a new flat node. diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index f78c94e1..20a6fc2b 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -26,6 +26,7 @@ #include "absl/base/macros.h" #include "absl/container/inlined_vector.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_consume.h" #include "absl/strings/internal/cord_rep_flat.h" namespace absl { @@ -49,14 +50,6 @@ inline void CheckCapacity(size_t n, size_t extra) { } } -// Removes a reference from `rep` only. -// Asserts that the refcount after decrement is not zero. -inline bool UnrefNeverOne(CordRep* rep) { - bool result = rep->refcount.Decrement(); - assert(result); - return result; -} - // Creates a flat from the provided string data, allocating up to `extra` // capacity in the returned flat depending on kMaxFlatLength limitations. // Requires `len` to be less or equal to `kMaxFlatLength` @@ -68,40 +61,6 @@ CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) { // NOLINT return rep; } -// Unrefs the provided `substring`, and returns `substring->child` -// Adds or assumes a reference on `substring->child` -CordRep* ClipSubstring(CordRepSubstring* substring) { - CordRep* child = substring->child; - if (substring->refcount.IsOne()) { - delete substring; - } else { - CordRep::Ref(child); - if (ABSL_PREDICT_FALSE(!substring->refcount.Decrement())) { - UnrefNeverOne(child); - delete substring; - } - } - return child; -} - -// Unrefs the provided `concat`, and returns `{concat->left, concat->right}` -// Adds or assumes a reference on `concat->left` and `concat->right`. -std::pair ClipConcat(CordRepConcat* concat) { - auto result = std::make_pair(concat->left, concat->right); - if (concat->refcount.IsOne()) { - delete concat; - } else { - CordRep::Ref(result.first); - CordRep::Ref(result.second); - if (ABSL_PREDICT_FALSE(!concat->refcount.Decrement())) { - UnrefNeverOne(result.first); - UnrefNeverOne(result.second); - delete concat; - } - } - return result; -} - // Unrefs the entries in `[head, tail)`. // Requires all entries to be a FLAT or EXTERNAL node. void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) { @@ -117,79 +76,6 @@ void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) { }); } -template -void Consume(Direction direction, CordRep* rep, F&& fn) { - size_t offset = 0; - size_t length = rep->length; - struct Entry { - CordRep* rep; - size_t offset; - size_t length; - }; - absl::InlinedVector stack; - - for (;;) { - if (rep->tag >= FLAT || rep->tag == EXTERNAL || rep->tag == RING) { - fn(rep, offset, length); - if (stack.empty()) return; - - rep = stack.back().rep; - offset = stack.back().offset; - length = stack.back().length; - stack.pop_back(); - } else if (rep->tag == SUBSTRING) { - offset += rep->substring()->start; - rep = ClipSubstring(rep->substring()); - } else if (rep->tag == CONCAT) { - auto res = ClipConcat(rep->concat()); - CordRep* left = res.first; - CordRep* right = res.second; - - if (left->length <= offset) { - // Don't need left node - offset -= left->length; - CordRep::Unref(left); - rep = right; - continue; - } - - size_t length_left = left->length - offset; - if (length_left >= length) { - // Don't need right node - CordRep::Unref(right); - rep = left; - continue; - } - - // Need both nodes - size_t length_right = length - length_left; - if (direction == Direction::kReversed) { - stack.push_back({left, offset, length_left}); - rep = right; - offset = 0; - length = length_right; - } else { - stack.push_back({right, 0, length_right}); - rep = left; - length = length_left; - } - } else { - assert("Valid tag" == nullptr); - return; - } - } -} - -template -void Consume(CordRep* rep, F&& fn) { - return Consume(Direction::kForward, rep, std::forward(fn)); -} - -template -void RConsume(CordRep* rep, F&& fn) { - return Consume(Direction::kReversed, rep, std::forward(fn)); -} - } // namespace std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) { @@ -581,7 +467,7 @@ CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) { } CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) { - RConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) { + ReverseConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) { if (IsFlatOrExternal(child_arg)) { rep = PrependLeaf(rep, child_arg, offset, len); } else { diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h index 17c1bfe8..e7664216 100644 --- a/absl/strings/internal/str_split_internal.h +++ b/absl/strings/internal/str_split_internal.h @@ -64,7 +64,7 @@ class ConvertibleToStringView { ConvertibleToStringView(const std::string& s) // NOLINT(runtime/explicit) : value_(s) {} - // Matches rvalue strings and moves their data to a member. + // Disable conversion from rvalue strings. ConvertibleToStringView(std::string&& s) = delete; ConvertibleToStringView(const std::string&& s) = delete; -- cgit v1.2.3 From b06e719ee985ecd63e0dffbc68499549216f817f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 8 Jul 2021 15:41:34 -0700 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- 007ce045d5d38a727ededdb5bf06e64785fd73bd by Martijn Vels : Add `cord_enable_btree` feature flag (default false). PiperOrigin-RevId: 383729939 -- 98e7dc6a0407b0fd7b8713d883cdb3a766e0583d by Benjamin Barenblat : Eliminate some byte swapping from randen_slow Stop swapping bytes when serializing randen_slow’s Vector128 into and out of memory. Instead, simply index different bytes in the AES round function. This requires byte swapping the te{0..3} lookup tables, but it produces an 8% speedup on my Xeon W-2135. PiperOrigin-RevId: 383689402 -- 180b6bf45049188840d439b16a28e6b968669340 by Evan Brown : Minor simplification in drop_deletes_without_resize() - save probe_offset outside the lambda. Also, add some consts, avoid an auto, and use lambda capture by value instead of reference. I realized that the compiler can already optimize this - https://godbolt.org/z/Wxd9c4TfK, but I think this way makes the code a bit clearer. PiperOrigin-RevId: 383646658 -- 781706a974c4dc1c0abbb6b801fca0550229e883 by Martijn Vels : Change storage to contain 3 bytes. As per the comments in the code, this allows us to utilize all available space in CordRep that may otherwise be 'lost' in padding in derived clases. For the upcoming CordrepBtree class, we want a strong guarantee on having a 64 bytes aligned implementation. PiperOrigin-RevId: 383633963 -- 8fe22ecf92492fa6649938a2215934ebfe01c714 by Derek Mauro : Remove reference to str_format_arg.h, which no longer exists PiperOrigin-RevId: 383517865 -- 79397f3b18f18c1e2d7aea993b687329d626ce64 by Benjamin Barenblat : Use absl::uint128 for AES random number generator Replace randen’s internal 128-bit integer struct, u64x2, with absl::uint128. This eliminates some code and improves support for big-endian platforms. PiperOrigin-RevId: 383475671 GitOrigin-RevId: 007ce045d5d38a727ededdb5bf06e64785fd73bd Change-Id: Ia9d9c40de557221f1744fb0d6d4d6ca7ac569070 --- absl/container/internal/raw_hash_set.h | 14 +- absl/random/internal/BUILD.bazel | 2 + absl/random/internal/randen_hwaes.cc | 29 +- absl/random/internal/randen_slow.cc | 479 +++++++++++++++------------------ absl/strings/cord_ring_test.cc | 2 +- absl/strings/internal/cord_internal.cc | 1 + absl/strings/internal/cord_internal.h | 21 +- absl/strings/internal/cord_rep_flat.h | 4 +- absl/strings/str_format.h | 3 +- 9 files changed, 268 insertions(+), 287 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 03f199a5..a9034b0b 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1654,18 +1654,18 @@ class raw_hash_set { slot_type* slot = reinterpret_cast(&raw); for (size_t i = 0; i != capacity_; ++i) { if (!IsDeleted(ctrl_[i])) continue; - size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, - PolicyTraits::element(slots_ + i)); - auto target = find_first_non_full(ctrl_, hash, capacity_); - size_t new_i = target.offset; + const size_t hash = PolicyTraits::apply( + HashElement{hash_ref()}, PolicyTraits::element(slots_ + i)); + const FindInfo target = find_first_non_full(ctrl_, hash, capacity_); + const size_t new_i = target.offset; total_probe_length += target.probe_length; // Verify if the old and new i fall within the same group wrt the hash. // If they do, we don't need to move the object as it falls already in the // best probe we can. - const auto probe_index = [&](size_t pos) { - return ((pos - probe(ctrl_, hash, capacity_).offset()) & capacity_) / - Group::kWidth; + const size_t probe_offset = probe(ctrl_, hash, capacity_).offset(); + const auto probe_index = [probe_offset, this](size_t pos) { + return ((pos - probe_offset) & capacity_) / Group::kWidth; }; // Element doesn't move. diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 6e06fe9a..cf6b5389 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -297,6 +297,7 @@ cc_library( ":platform", "//absl/base:config", "//absl/base:core_headers", + "//absl/numeric:int128", ], ) @@ -337,6 +338,7 @@ cc_library( ":platform", "//absl/base:config", "//absl/base:core_headers", + "//absl/numeric:int128", ], ) diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc index 3738cc38..ab51e4a3 100644 --- a/absl/random/internal/randen_hwaes.cc +++ b/absl/random/internal/randen_hwaes.cc @@ -23,6 +23,7 @@ #include #include "absl/base/attributes.h" +#include "absl/numeric/int128.h" #include "absl/random/internal/platform.h" #include "absl/random/internal/randen_traits.h" @@ -120,11 +121,6 @@ namespace { using absl::random_internal::RandenTraits; -// Randen operates on 128-bit vectors. -struct alignas(16) u64x2 { - uint64_t data[2]; -}; - } // namespace // TARGET_CRYPTO defines a crypto attribute for each architecture. @@ -186,7 +182,7 @@ inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, } // Enables native loads in the round loop by pre-swapping. -inline ABSL_TARGET_CRYPTO void SwapEndian(u64x2* state) { +inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) { for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) { Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block); } @@ -327,7 +323,7 @@ namespace { // Block shuffles applies a shuffle to the entire state between AES rounds. // Improved odd-even shuffle from "New criterion for diffusion property". -inline ABSL_TARGET_CRYPTO void BlockShuffle(u64x2* state) { +inline ABSL_TARGET_CRYPTO void BlockShuffle(absl::uint128* state) { static_assert(RandenTraits::kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); @@ -374,8 +370,9 @@ inline ABSL_TARGET_CRYPTO void BlockShuffle(u64x2* state) { // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel // XORs are 'free' (included in the second AES instruction). -inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( - u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { +inline ABSL_TARGET_CRYPTO const absl::uint128* FeistelRound( + absl::uint128* state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { static_assert(RandenTraits::kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); @@ -436,7 +433,8 @@ inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( // 2^64 queries if the round function is a PRF. This is similar to the b=8 case // of Simpira v2, but more efficient than its generic construction for b=16. inline ABSL_TARGET_CRYPTO void Permute( - u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + absl::uint128* state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { // (Successfully unrolled; the first iteration jumps into the second half) #ifdef __clang__ #pragma clang loop unroll_count(2) @@ -473,10 +471,11 @@ void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void, static_assert(RandenTraits::kStateBytes / sizeof(Vector128) == 16, "Unexpected Randen kStateBlocks"); - auto* state = - reinterpret_cast(state_void); + auto* state = reinterpret_cast( + state_void); const auto* seed = - reinterpret_cast(seed_void); + reinterpret_cast( + seed_void); Vector128 b1 = Vector128Load(state + 1); b1 ^= Vector128Load(seed + 0); @@ -545,8 +544,8 @@ void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys_void, static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128), "Capacity mismatch"); - auto* state = reinterpret_cast(state_void); - const auto* keys = reinterpret_cast(keys_void); + auto* state = reinterpret_cast(state_void); + const auto* keys = reinterpret_cast(keys_void); const Vector128 prev_inner = Vector128Load(state); diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc index 4e5f3dc1..56aeb030 100644 --- a/absl/random/internal/randen_slow.cc +++ b/absl/random/internal/randen_slow.cc @@ -19,6 +19,7 @@ #include #include "absl/base/attributes.h" +#include "absl/numeric/int128.h" #include "absl/random/internal/platform.h" #include "absl/random/internal/randen_traits.h" @@ -38,192 +39,193 @@ namespace { // AES portions based on rijndael-alg-fst.c, -// https://fastcrypto.org/front/misc/rijndael-alg-fst.c +// https://fastcrypto.org/front/misc/rijndael-alg-fst.c, and modified for +// little-endianness. // // Implementation of // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf constexpr uint32_t te0[256] = { - 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, - 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, - 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, - 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, - 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, - 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, - 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, - 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, - 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, - 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, - 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, - 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, - 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, - 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, - 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, - 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, - 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, - 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, - 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, - 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, - 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, - 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, - 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, - 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, - 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, - 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, - 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, - 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, - 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, - 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, - 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, - 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, - 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, - 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, - 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, - 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, - 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, - 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, - 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, - 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, - 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, - 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, - 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, + 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, + 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, + 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, + 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, + 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, + 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, + 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, + 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, + 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, + 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, + 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, + 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, + 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, + 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, + 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, + 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, + 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, + 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, + 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, + 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, + 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, + 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, + 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, + 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, + 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, + 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, + 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, + 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, + 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, + 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, + 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, + 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, + 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, + 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, }; constexpr uint32_t te1[256] = { - 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, - 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, - 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, - 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, - 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, - 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, - 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, - 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, - 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, - 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, - 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, - 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, - 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, - 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, - 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, - 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, - 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, - 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, - 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, - 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, - 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, - 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, - 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, - 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, - 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, - 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, - 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, - 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, - 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, - 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, - 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, - 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, - 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, - 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, - 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, - 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, - 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, - 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, - 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, - 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, - 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, - 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, - 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, + 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, + 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, + 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, + 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, + 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, + 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, + 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, + 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, + 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, + 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, + 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, + 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, + 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, + 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, + 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, + 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, + 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, + 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, + 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, + 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, + 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, + 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, + 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, + 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, + 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, + 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, + 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, + 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, + 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, + 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, + 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, + 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, + 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, + 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, + 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, + 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, + 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, + 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, + 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, + 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, + 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, + 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a, }; constexpr uint32_t te2[256] = { - 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, - 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, - 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, - 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, - 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, - 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, - 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, - 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, - 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, - 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, - 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, - 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, - 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, - 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, - 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, - 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, - 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, - 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, - 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, - 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, - 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, - 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, - 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, - 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, - 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, - 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, - 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, - 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, - 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, - 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, - 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, - 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, - 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, - 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, - 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, - 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, - 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, - 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, - 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, - 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, - 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, - 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, - 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, + 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, + 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, + 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, + 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, + 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, + 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, + 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, + 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, + 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, + 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, + 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, + 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, + 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, + 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, + 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, + 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, + 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, + 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, + 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, + 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, + 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, + 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, + 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, + 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, + 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, + 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, + 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, + 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, + 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, + 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, + 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, + 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, + 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, + 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, + 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, + 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, + 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, + 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, + 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, + 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, + 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, + 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16, }; constexpr uint32_t te3[256] = { - 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, - 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, - 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, - 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, - 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, - 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, - 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, - 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, - 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, - 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, - 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, - 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, - 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, - 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, - 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, - 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, - 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, - 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, - 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, - 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, - 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, - 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, - 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, - 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, - 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, - 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, - 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, - 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, - 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, - 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, - 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, - 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, - 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, - 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, - 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, - 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, - 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, - 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, - 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, - 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, - 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, - 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, - 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, + 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, + 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, + 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, + 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, + 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, + 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, + 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, + 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, + 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, + 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, + 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, + 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, + 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, + 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, + 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, + 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, + 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, + 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, + 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, + 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, + 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, + 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, + 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, + 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, + 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, + 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, + 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, + 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, + 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, + 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, + 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, + 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, + 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, + 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, + 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, + 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, + 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, + 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, + 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, + 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, + 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, + 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616, }; // Software implementation of the Vector128 class, using uint32_t @@ -235,45 +237,13 @@ struct alignas(16) Vector128 { inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 Vector128Load(const void* from) { Vector128 result; - const uint8_t* src = reinterpret_cast(from); - result.s[0] = static_cast(src[0]) << 24 | - static_cast(src[1]) << 16 | - static_cast(src[2]) << 8 | - static_cast(src[3]); - result.s[1] = static_cast(src[4]) << 24 | - static_cast(src[5]) << 16 | - static_cast(src[6]) << 8 | - static_cast(src[7]); - result.s[2] = static_cast(src[8]) << 24 | - static_cast(src[9]) << 16 | - static_cast(src[10]) << 8 | - static_cast(src[11]); - result.s[3] = static_cast(src[12]) << 24 | - static_cast(src[13]) << 16 | - static_cast(src[14]) << 8 | - static_cast(src[15]); + std::memcpy(result.s, from, sizeof(Vector128)); return result; } inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store( const Vector128& v, void* to) { - uint8_t* dst = reinterpret_cast(to); - dst[0] = static_cast(v.s[0] >> 24); - dst[1] = static_cast(v.s[0] >> 16); - dst[2] = static_cast(v.s[0] >> 8); - dst[3] = static_cast(v.s[0]); - dst[4] = static_cast(v.s[1] >> 24); - dst[5] = static_cast(v.s[1] >> 16); - dst[6] = static_cast(v.s[1] >> 8); - dst[7] = static_cast(v.s[1]); - dst[8] = static_cast(v.s[2] >> 24); - dst[9] = static_cast(v.s[2] >> 16); - dst[10] = static_cast(v.s[2] >> 8); - dst[11] = static_cast(v.s[2]); - dst[12] = static_cast(v.s[3] >> 24); - dst[13] = static_cast(v.s[3] >> 16); - dst[14] = static_cast(v.s[3] >> 8); - dst[15] = static_cast(v.s[3]); + std::memcpy(to, v.s, sizeof(Vector128)); } // One round of AES. "round_key" is a public constant for breaking the @@ -282,38 +252,33 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 AesRound(const Vector128& state, const Vector128& round_key) { Vector128 result; result.s[0] = round_key.s[0] ^ // - te0[uint8_t(state.s[0] >> 24)] ^ // - te1[uint8_t(state.s[1] >> 16)] ^ // - te2[uint8_t(state.s[2] >> 8)] ^ // - te3[uint8_t(state.s[3])]; + te0[uint8_t(state.s[0])] ^ // + te1[uint8_t(state.s[1] >> 8)] ^ // + te2[uint8_t(state.s[2] >> 16)] ^ // + te3[uint8_t(state.s[3] >> 24)]; result.s[1] = round_key.s[1] ^ // - te0[uint8_t(state.s[1] >> 24)] ^ // - te1[uint8_t(state.s[2] >> 16)] ^ // - te2[uint8_t(state.s[3] >> 8)] ^ // - te3[uint8_t(state.s[0])]; + te0[uint8_t(state.s[1])] ^ // + te1[uint8_t(state.s[2] >> 8)] ^ // + te2[uint8_t(state.s[3] >> 16)] ^ // + te3[uint8_t(state.s[0] >> 24)]; result.s[2] = round_key.s[2] ^ // - te0[uint8_t(state.s[2] >> 24)] ^ // - te1[uint8_t(state.s[3] >> 16)] ^ // - te2[uint8_t(state.s[0] >> 8)] ^ // - te3[uint8_t(state.s[1])]; + te0[uint8_t(state.s[2])] ^ // + te1[uint8_t(state.s[3] >> 8)] ^ // + te2[uint8_t(state.s[0] >> 16)] ^ // + te3[uint8_t(state.s[1] >> 24)]; result.s[3] = round_key.s[3] ^ // - te0[uint8_t(state.s[3] >> 24)] ^ // - te1[uint8_t(state.s[0] >> 16)] ^ // - te2[uint8_t(state.s[1] >> 8)] ^ // - te3[uint8_t(state.s[2])]; + te0[uint8_t(state.s[3])] ^ // + te1[uint8_t(state.s[0] >> 8)] ^ // + te2[uint8_t(state.s[1] >> 16)] ^ // + te3[uint8_t(state.s[2] >> 24)]; return result; } using ::absl::random_internal::RandenTraits; -// Randen operates on 128-bit vectors. -struct alignas(16) u64x2 { - uint64_t data[2]; -}; - // The improved Feistel block shuffle function for 16 blocks. inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( - u64x2* state) { + absl::uint128* state) { static_assert(RandenTraits::kFeistelBlocks == 16, "Feistel block shuffle only works for 16 blocks."); @@ -323,31 +288,31 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( // The fully unrolled loop without the memcpy improves the speed by about // 30% over the equivalent: #if 0 - u64x2 source[RandenTraits::kFeistelBlocks]; + absl::uint128 source[RandenTraits::kFeistelBlocks]; std::memcpy(source, state, sizeof(source)); for (size_t i = 0; i < RandenTraits::kFeistelBlocks; i++) { - const u64x2 v0 = source[shuffle[i]]; + const absl::uint128 v0 = source[shuffle[i]]; state[i] = v0; } return; #endif - const u64x2 v0 = state[shuffle[0]]; - const u64x2 v1 = state[shuffle[1]]; - const u64x2 v2 = state[shuffle[2]]; - const u64x2 v3 = state[shuffle[3]]; - const u64x2 v4 = state[shuffle[4]]; - const u64x2 v5 = state[shuffle[5]]; - const u64x2 v6 = state[shuffle[6]]; - const u64x2 v7 = state[shuffle[7]]; - const u64x2 w0 = state[shuffle[8]]; - const u64x2 w1 = state[shuffle[9]]; - const u64x2 w2 = state[shuffle[10]]; - const u64x2 w3 = state[shuffle[11]]; - const u64x2 w4 = state[shuffle[12]]; - const u64x2 w5 = state[shuffle[13]]; - const u64x2 w6 = state[shuffle[14]]; - const u64x2 w7 = state[shuffle[15]]; + const absl::uint128 v0 = state[shuffle[0]]; + const absl::uint128 v1 = state[shuffle[1]]; + const absl::uint128 v2 = state[shuffle[2]]; + const absl::uint128 v3 = state[shuffle[3]]; + const absl::uint128 v4 = state[shuffle[4]]; + const absl::uint128 v5 = state[shuffle[5]]; + const absl::uint128 v6 = state[shuffle[6]]; + const absl::uint128 v7 = state[shuffle[7]]; + const absl::uint128 w0 = state[shuffle[8]]; + const absl::uint128 w1 = state[shuffle[9]]; + const absl::uint128 w2 = state[shuffle[10]]; + const absl::uint128 w3 = state[shuffle[11]]; + const absl::uint128 w4 = state[shuffle[12]]; + const absl::uint128 w5 = state[shuffle[13]]; + const absl::uint128 w6 = state[shuffle[14]]; + const absl::uint128 w7 = state[shuffle[15]]; state[0] = v0; state[1] = v1; state[2] = v2; @@ -371,9 +336,9 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel // XORs are 'free' (included in the second AES instruction). -inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound( - u64x2* ABSL_RANDOM_INTERNAL_RESTRICT state, - const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const absl::uint128* +FeistelRound(absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { for (size_t branch = 0; branch < RandenTraits::kFeistelBlocks; branch += 4) { const Vector128 s0 = Vector128Load(state + branch); const Vector128 s1 = Vector128Load(state + branch + 1); @@ -398,7 +363,8 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound( // 2^64 queries if the round function is a PRF. This is similar to the b=8 case // of Simpira v2, but more efficient than its generic construction for b=16. inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute( - u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + absl::uint128* state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) { keys = FeistelRound(state, keys); BlockShuffle(state); @@ -437,19 +403,18 @@ void RandenSlow::Absorb(const void* seed_void, void* state_void) { } void RandenSlow::Generate(const void* keys_void, void* state_void) { - static_assert(RandenTraits::kCapacityBytes == sizeof(u64x2), + static_assert(RandenTraits::kCapacityBytes == sizeof(absl::uint128), "Capacity mismatch"); - auto* state = reinterpret_cast(state_void); - const auto* keys = reinterpret_cast(keys_void); + auto* state = reinterpret_cast(state_void); + const auto* keys = reinterpret_cast(keys_void); - const u64x2 prev_inner = state[0]; + const absl::uint128 prev_inner = state[0]; Permute(state, keys); // Ensure backtracking resistance. - state[0].data[0] ^= prev_inner.data[0]; - state[0].data[1] ^= prev_inner.data[1]; + *state ^= prev_inner; } } // namespace random_internal diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc index cc8fbaf9..b1787edf 100644 --- a/absl/strings/cord_ring_test.cc +++ b/absl/strings/cord_ring_test.cc @@ -223,7 +223,7 @@ CordRepExternal* MakeFakeExternal(size_t length) { std::string s; explicit Rep(size_t len) { this->tag = EXTERNAL; - this->base = this->storage; + this->base = reinterpret_cast(this->storage); this->length = len; this->releaser_invoker = [](CordRepExternal* self) { delete static_cast(self); diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index 905ffd0c..6a0b051c 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -25,6 +25,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { +ABSL_CONST_INIT std::atomic cord_btree_enabled(kCordEnableBtreeDefault); ABSL_CONST_INIT std::atomic cord_ring_buffer_enabled( kCordEnableRingBufferDefault); ABSL_CONST_INIT std::atomic shallow_subcords_enabled( diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index bddc9815..371a7f9c 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -37,13 +37,19 @@ class CordzInfo; // Default feature enable states for cord ring buffers enum CordFeatureDefaults { + kCordEnableBtreeDefault = false, kCordEnableRingBufferDefault = false, kCordShallowSubcordsDefault = false }; +extern std::atomic cord_btree_enabled; extern std::atomic cord_ring_buffer_enabled; extern std::atomic shallow_subcords_enabled; +inline void enable_cord_btree(bool enable) { + cord_btree_enabled.store(enable, std::memory_order_relaxed); +} + inline void enable_cord_ring_buffer(bool enable) { cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed); } @@ -193,7 +199,16 @@ struct CordRep { // If tag < FLAT, it represents CordRepKind and indicates the type of node. // Otherwise, the node type is CordRepFlat and the tag is the encoded size. uint8_t tag; - char storage[1]; // Starting point for flat array: MUST BE LAST FIELD + + // `storage` provides two main purposes: + // - the starting point for FlatCordRep.Data() [flexible-array-member] + // - 3 bytes of additional storage for use by derived classes. + // The latter is used by CordrepConcat and CordRepBtree. CordRepConcat stores + // a 'depth' value in storage[0], and the (future) CordRepBtree class stores + // `height`, `begin` and `end` in the 3 entries. Otherwise we would need to + // allocate room for these in the derived class, as not all compilers reuse + // padding space from the base class (clang and gcc do, MSVC does not, etc) + uint8_t storage[3]; inline CordRepRing* ring(); inline const CordRepRing* ring() const; @@ -228,8 +243,8 @@ struct CordRepConcat : public CordRep { CordRep* left; CordRep* right; - uint8_t depth() const { return static_cast(storage[0]); } - void set_depth(uint8_t depth) { storage[0] = static_cast(depth); } + uint8_t depth() const { return storage[0]; } + void set_depth(uint8_t depth) { storage[0] = depth; } }; struct CordRepSubstring : public CordRep { diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index cddb282f..4d0f9886 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -118,8 +118,8 @@ struct CordRepFlat : public CordRep { } // Returns a pointer to the data inside this flat rep. - char* Data() { return storage; } - const char* Data() const { return storage; } + char* Data() { return reinterpret_cast(storage); } + const char* Data() const { return reinterpret_cast(storage); } // Returns the maximum capacity (payload size) of this instance. size_t Capacity() const { return TagToLength(tag); } diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h index 01465107..4b05c70c 100644 --- a/absl/strings/str_format.h +++ b/absl/strings/str_format.h @@ -536,8 +536,7 @@ using FormatArg = str_format_internal::FormatArgImpl; // The arguments are provided in an `absl::Span`. // Each `absl::FormatArg` object binds to a single argument and keeps a // reference to it. The values used to create the `FormatArg` objects must -// outlive this function call. (See `str_format_arg.h` for information on -// the `FormatArg` class.)_ +// outlive this function call. // // Example: // -- cgit v1.2.3 From 33541e751039a8c4bd3a395dd1a3a0928885814a Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 12 Jul 2021 07:04:14 -0700 Subject: Export of internal Abseil changes -- c3b926ea986eea9d416ef57ee67a1041b70257fd by Martijn Vels : Remove internal absl_internal_cordz_disabled check. PiperOrigin-RevId: 384225993 -- 2863c56ad5c86dd9c207a796e65d5bc968f77755 by Benjamin Barenblat : Make randen_slow endian-correct Pay attention to the platform endianness when pulling bytes out of each AES block, and use platform-endian round keys. PiperOrigin-RevId: 383878281 GitOrigin-RevId: c3b926ea986eea9d416ef57ee67a1041b70257fd Change-Id: I0d48f4fd560b3e320260ef05790727756ffead02 --- absl/random/internal/BUILD.bazel | 1 + absl/random/internal/randen_slow.cc | 30 +++++++++++++++++++++++++++++- absl/strings/internal/cordz_functions.cc | 14 -------------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index cf6b5389..8420b5c5 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -297,6 +297,7 @@ cc_library( ":platform", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:endian", "//absl/numeric:int128", ], ) diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc index 56aeb030..d5c9347b 100644 --- a/absl/random/internal/randen_slow.cc +++ b/absl/random/internal/randen_slow.cc @@ -19,6 +19,7 @@ #include #include "absl/base/attributes.h" +#include "absl/base/internal/endian.h" #include "absl/numeric/int128.h" #include "absl/random/internal/platform.h" #include "absl/random/internal/randen_traits.h" @@ -40,7 +41,7 @@ namespace { // AES portions based on rijndael-alg-fst.c, // https://fastcrypto.org/front/misc/rijndael-alg-fst.c, and modified for -// little-endianness. +// platform-endianness. // // Implementation of // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf @@ -251,6 +252,7 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store( inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 AesRound(const Vector128& state, const Vector128& round_key) { Vector128 result; +#ifdef ABSL_IS_LITTLE_ENDIAN result.s[0] = round_key.s[0] ^ // te0[uint8_t(state.s[0])] ^ // te1[uint8_t(state.s[1] >> 8)] ^ // @@ -271,6 +273,28 @@ AesRound(const Vector128& state, const Vector128& round_key) { te1[uint8_t(state.s[0] >> 8)] ^ // te2[uint8_t(state.s[1] >> 16)] ^ // te3[uint8_t(state.s[2] >> 24)]; +#else + result.s[0] = round_key.s[0] ^ // + te0[uint8_t(state.s[0])] ^ // + te1[uint8_t(state.s[3] >> 8)] ^ // + te2[uint8_t(state.s[2] >> 16)] ^ // + te3[uint8_t(state.s[1] >> 24)]; + result.s[1] = round_key.s[1] ^ // + te0[uint8_t(state.s[1])] ^ // + te1[uint8_t(state.s[0] >> 8)] ^ // + te2[uint8_t(state.s[3] >> 16)] ^ // + te3[uint8_t(state.s[2] >> 24)]; + result.s[2] = round_key.s[2] ^ // + te0[uint8_t(state.s[2])] ^ // + te1[uint8_t(state.s[1] >> 8)] ^ // + te2[uint8_t(state.s[0] >> 16)] ^ // + te3[uint8_t(state.s[3] >> 24)]; + result.s[3] = round_key.s[3] ^ // + te0[uint8_t(state.s[3])] ^ // + te1[uint8_t(state.s[2] >> 8)] ^ // + te2[uint8_t(state.s[1] >> 16)] ^ // + te3[uint8_t(state.s[0] >> 24)]; +#endif return result; } @@ -380,7 +404,11 @@ namespace random_internal { const void* RandenSlow::GetKeys() { // Round keys for one AES per Feistel round and branch. // The canonical implementation uses first digits of Pi. +#ifdef ABSL_IS_LITTLE_ENDIAN return kRandenRoundKeys; +#else + return kRandenRoundKeysBE; +#endif } void RandenSlow::Absorb(const void* seed_void, void* state_void) { diff --git a/absl/strings/internal/cordz_functions.cc b/absl/strings/internal/cordz_functions.cc index f30080f8..48369933 100644 --- a/absl/strings/internal/cordz_functions.cc +++ b/absl/strings/internal/cordz_functions.cc @@ -24,13 +24,6 @@ #include "absl/base/internal/exponential_biased.h" #include "absl/base/internal/raw_logging.h" -// TODO(b/162942788): weak 'cordz_disabled' value. -// A strong version is in the 'cordz_disabled_hack_for_odr' library which can -// be linked in to disable cordz at compile time. -extern "C" { -bool absl_internal_cordz_disabled ABSL_ATTRIBUTE_WEAK = false; -} - namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { @@ -54,13 +47,6 @@ ABSL_CONST_INIT thread_local int64_t cordz_next_sample = kInitCordzNextSample; constexpr int64_t kIntervalIfDisabled = 1 << 16; ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() { - // TODO(b/162942788): check if profiling is disabled at compile time. - if (absl_internal_cordz_disabled) { - ABSL_RAW_LOG(WARNING, "Cordz info disabled at compile time"); - // We are permanently disabled: set counter to highest possible value. - cordz_next_sample = std::numeric_limits::max(); - return false; - } thread_local absl::base_internal::ExponentialBiased exponential_biased_generator; -- cgit v1.2.3 From d61843e5311041aa6b552dfcc42556615c5c5527 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 19 Jul 2021 17:41:36 -0700 Subject: Export of internal Abseil changes -- b20c539b8e21fee7d4d908a8a26a317a3de9d993 by Martijn Vels : Add CordRepBtree implementation PiperOrigin-RevId: 385679196 -- 96f7753b7af5fd964537d5794dd597bb6e698071 by Derek Mauro : Update Abseil dependencies PiperOrigin-RevId: 385643956 -- 67bdae4c686f0df09cc7155633c03218bf23d177 by Abseil Team : Fix up some small typos in error messages. PiperOrigin-RevId: 385625107 GitOrigin-RevId: b20c539b8e21fee7d4d908a8a26a317a3de9d993 Change-Id: I8f602cfe9f7878b0558359ab15efb048caefb3a5 --- CMake/AbseilDll.cmake | 2 + WORKSPACE | 25 +- absl/base/internal/thread_identity.h | 8 +- absl/strings/BUILD.bazel | 32 + absl/strings/CMakeLists.txt | 35 + absl/strings/internal/cord_internal.cc | 4 + absl/strings/internal/cord_rep_btree.cc | 947 ++++++++++++++++++ absl/strings/internal/cord_rep_btree.h | 851 ++++++++++++++++ absl/strings/internal/cord_rep_btree_test.cc | 1352 ++++++++++++++++++++++++++ absl/strings/internal/cord_rep_test_util.h | 185 ++++ ci/cmake_common.sh | 2 +- 11 files changed, 3426 insertions(+), 17 deletions(-) create mode 100644 absl/strings/internal/cord_rep_btree.cc create mode 100644 absl/strings/internal/cord_rep_btree.h create mode 100644 absl/strings/internal/cord_rep_btree_test.cc create mode 100644 absl/strings/internal/cord_rep_test_util.h diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 78ace82b..65014bfb 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -205,6 +205,8 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cord_internal.h" "strings/internal/cord_rep_consume.h" "strings/internal/cord_rep_consume.cc" + "strings/internal/cord_rep_btree.cc" + "strings/internal/cord_rep_btree.h" "strings/internal/cord_rep_flat.h" "strings/internal/cord_rep_ring.cc" "strings/internal/cord_rep_ring.h" diff --git a/WORKSPACE b/WORKSPACE index bc00ea3f..f4a5c476 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -15,29 +15,30 @@ # workspace(name = "com_google_absl") + load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( - name = "com_google_googletest", + name = "com_google_googletest", # 2021-07-09T13:28:13Z + sha256 = "12ef65654dc01ab40f6f33f9d02c04f2097d2cd9fbe48dc6001b29543583b0ad", + strip_prefix = "googletest-8d51ffdfab10b3fba636ae69bc03da4b54f8c235", # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. - urls = ["https://github.com/google/googletest/archive/5bcd8e3bb929714e031a542d303f818e5a5af45d.zip"], # 2021-06-08T22:36:38Z - strip_prefix = "googletest-5bcd8e3bb929714e031a542d303f818e5a5af45d", - sha256 = "3adecb6686ac7367452561dca518fad5a990fb09c5a961bfa1836f15eb774348", + urls = ["https://github.com/google/googletest/archive/8d51ffdfab10b3fba636ae69bc03da4b54f8c235.zip"], ) # Google benchmark. http_archive( - name = "com_github_google_benchmark", - urls = ["https://github.com/google/benchmark/archive/7d0d9061d83b663ce05d9de5da3d5865a3845b79.zip"], # 2021-05-11T11:56:00Z - strip_prefix = "benchmark-7d0d9061d83b663ce05d9de5da3d5865a3845b79", - sha256 = "a07789754963e3ea3a1e13fed3a4d48fac0c5f7f749c5065f6c30cd2c1529661", + name = "com_github_google_benchmark", # 2021-07-01T09:02:54Z + sha256 = "1cb4b97a90aa1fd9c8e412a6bc29fc13fc140162a4a0db3811af40befd8c9ea5", + strip_prefix = "benchmark-e451e50e9b8af453f076dec10bd6890847f1624e", + urls = ["https://github.com/google/benchmark/archive/e451e50e9b8af453f076dec10bd6890847f1624e.zip"], ) # C++ rules for Bazel. http_archive( - name = "rules_cc", - urls = ["https://github.com/bazelbuild/rules_cc/archive/ab5395627c80e025e824bd005d41f96b20618b9d.zip"], # 2021-05-10T15:35:04Z - strip_prefix = "rules_cc-ab5395627c80e025e824bd005d41f96b20618b9d", - sha256 = "f3908cb40a6577ab0d1ef9e00052739bf69b4313fa7d20b4d61d4521ea67abf3", + name = "rules_cc", # 2021-06-07T16:41:49Z + sha256 = "b295cad8c5899e371dde175079c0a2cdc0151f5127acc92366a8c986beb95c76", + strip_prefix = "rules_cc-daf6ace7cfeacd6a83e9ff2ed659f416537b6c74", + urls = ["https://github.com/bazelbuild/rules_cc/archive/daf6ace7cfeacd6a83e9ff2ed659f416537b6c74.zip"], ) diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index 6e25b92f..659694b3 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -188,25 +188,25 @@ void ClearCurrentThreadIdentity(); // May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE= #ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC -#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0 #endif #ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS -#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1 #endif #ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11 -#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2 #endif #ifdef ABSL_THREAD_IDENTITY_MODE -#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE cannot be directly set #elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE) #define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE #elif defined(_WIN32) && !defined(__MINGW32__) diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index b70aac3e..c686e05a 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -269,11 +269,13 @@ cc_library( name = "cord_internal", srcs = [ "internal/cord_internal.cc", + "internal/cord_rep_btree.cc", "internal/cord_rep_consume.cc", "internal/cord_rep_ring.cc", ], hdrs = [ "internal/cord_internal.h", + "internal/cord_rep_btree.h", "internal/cord_rep_consume.h", "internal/cord_rep_flat.h", "internal/cord_rep_ring.h", @@ -296,6 +298,23 @@ cc_library( "//absl/container:layout", "//absl/functional:function_ref", "//absl/meta:type_traits", + "//absl/types:span", + ], +) + +cc_test( + name = "cord_rep_btree_test", + size = "medium", + srcs = ["internal/cord_rep_btree_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":cord_rep_test_util", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "@com_google_googletest//:gtest_main", ], ) @@ -577,6 +596,19 @@ cc_library( ], ) +cc_library( + name = "cord_rep_test_util", + testonly = 1, + hdrs = ["internal/cord_rep_test_util.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord_internal", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + ], +) + cc_library( name = "cordz_test_helpers", testonly = 1, diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index d79ef712..2ddd3c48 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -555,12 +555,14 @@ absl_cc_library( cord_internal HDRS "internal/cord_internal.h" + "internal/cord_rep_btree.h" "internal/cord_rep_consume.h" "internal/cord_rep_flat.h" "internal/cord_rep_ring.h" "internal/cord_rep_ring_reader.h" SRCS "internal/cord_internal.cc" + "internal/cord_rep_btree.cc" "internal/cord_rep_consume.cc" "internal/cord_rep_ring.cc" COPTS @@ -847,6 +849,21 @@ absl_cc_library( PUBLIC ) +absl_cc_library( + NAME + cord_rep_test_util + HDRS + "internal/cord_rep_test_util.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::raw_logging_internal + absl::strings + TESTONLY +) + absl_cc_library( NAME cord_test_helpers @@ -922,6 +939,24 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + cord_rep_btree_test + SRCS + "internal/cord_rep_btree_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::cord_rep_test_util + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + absl_cc_test( NAME cord_ring_test diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index 6a0b051c..f79cb628 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -18,6 +18,7 @@ #include #include "absl/container/inlined_vector.h" +#include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" @@ -50,6 +51,9 @@ void CordRep::Destroy(CordRep* rep) { rep = left; continue; } + } else if (rep->tag == BTREE) { + CordRepBtree::Destroy(rep->btree()); + rep = nullptr; } else if (rep->tag == RING) { CordRepRing::Destroy(rep->ring()); rep = nullptr; diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc new file mode 100644 index 00000000..6978cfd2 --- /dev/null +++ b/absl/strings/internal/cord_rep_btree.cc @@ -0,0 +1,947 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_btree.h" + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_consume.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +namespace { + +using NodeStack = CordRepBtree * [CordRepBtree::kMaxDepth]; +using EdgeType = CordRepBtree::EdgeType; +using OpResult = CordRepBtree::OpResult; +using CopyResult = CordRepBtree::CopyResult; + +constexpr auto kFront = CordRepBtree::kFront; +constexpr auto kBack = CordRepBtree::kBack; + +// Implementation of the various 'Dump' functions. +// Prints the entire tree structure or 'rep'. External callers should +// not specify 'depth' and leave it to its default (0) value. +// Rep may be a CordRepBtree tree, or a SUBSTRING / EXTERNAL / FLAT node. +void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, + int depth = 0) { + // Allow for full height trees + substring -> flat / external nodes. + assert(depth <= CordRepBtree::kMaxDepth + 2); + std::string sharing = const_cast(rep)->refcount.IsOne() + ? std::string("Private") + : absl::StrCat("Shared(", rep->refcount.Get(), ")"); + std::string sptr = absl::StrCat("0x", absl::Hex(rep)); + + // Dumps the data contents of `rep` if `include_contents` is true. + // Always emits a new line character. + auto maybe_dump_data = [&stream, include_contents](const CordRep* r) { + if (include_contents) { + // Allow for up to 60 wide display of content data, which with some + // indentation and prefix / labels keeps us within roughly 80-100 wide. + constexpr size_t kMaxDataLength = 60; + stream << ", data = \"" + << CordRepBtree::EdgeData(r).substr(0, kMaxDataLength) + << (r->length > kMaxDataLength ? "\"..." : "\""); + } + stream << '\n'; + }; + + // For each level, we print the 'shared/private' state and the rep pointer, + // indented by two spaces per recursive depth. + stream << std::string(depth * 2, ' ') << sharing << " (" << sptr << ") "; + + if (rep->tag == BTREE) { + const CordRepBtree* node = rep->btree(); + std::string label = + node->height() ? absl::StrCat("Node(", node->height(), ")") : "Leaf"; + stream << label << ", len = " << node->length + << ", begin = " << node->begin() << ", end = " << node->end() + << "\n"; + for (CordRep* edge : node->Edges()) { + DumpAll(edge, include_contents, stream, depth + 1); + } + } else if (rep->tag == SUBSTRING) { + const CordRepSubstring* substring = rep->substring(); + stream << "Substring, len = " << rep->length + << ", start = " << substring->start; + maybe_dump_data(rep); + DumpAll(substring->child, include_contents, stream, depth + 1); + } else if (rep->tag >= FLAT) { + stream << "Flat, len = " << rep->length; + maybe_dump_data(rep); + } else if (rep->tag == EXTERNAL) { + stream << "Extn, len = " << rep->length; + maybe_dump_data(rep); + } +} + +// TODO(b/192061034): add 'bytes to copy' logic to avoid large slop on substring +// small data out of large reps, and general efficiency of 'always copy small +// data'. Consider making this a cord rep internal library function. +CordRepSubstring* CreateSubstring(CordRep* rep, size_t offset, size_t n) { + assert(n != 0); + assert(offset + n <= rep->length); + assert(offset != 0 || n != rep->length); + + if (rep->tag == SUBSTRING) { + CordRepSubstring* substring = rep->substring(); + offset += substring->start; + rep = CordRep::Ref(substring->child); + CordRep::Unref(substring); + } + CordRepSubstring* substring = new CordRepSubstring(); + substring->length = n; + substring->tag = SUBSTRING; + substring->start = offset; + substring->child = rep; + return substring; +} + +// TODO(b/192061034): consider making this a cord rep library function. +inline CordRep* MakeSubstring(CordRep* rep, size_t offset, size_t n) { + if (n == rep->length) return rep; + if (n == 0) return CordRep::Unref(rep), nullptr; + return CreateSubstring(rep, offset, n); +} + +// TODO(b/192061034): consider making this a cord rep library function. +inline CordRep* MakeSubstring(CordRep* rep, size_t offset) { + if (offset == 0) return rep; + return CreateSubstring(rep, offset, rep->length - offset); +} + +template +inline absl::string_view Consume(absl::string_view s, size_t n) { + return edge_type == kBack ? s.substr(n) : s.substr(0, s.size() - n); +} + +template +inline absl::string_view Consume(char* dst, absl::string_view s, size_t n) { + if (edge_type == kBack) { + memcpy(dst, s.data(), n); + return s.substr(n); + } else { + const size_t offset = s.size() - n; + memcpy(dst, s.data() + offset, n); + return s.substr(0, offset); + } +} + +// Known issue / optimization weirdness: the store associated with the +// decrement introduces traffic between cpus (even if the result of that +// traffic does nothing), making this faster than a single call to +// refcount.Decrement() checking the zero refcount condition. +template +inline void FastUnref(R* r, Fn&& fn) { + if (r->refcount.IsOne()) { + fn(r); + } else if (!r->refcount.DecrementExpectHighRefcount()) { + fn(r); + } +} + +// Deletes a leaf node data edge. Requires `rep` to be an EXTERNAL or FLAT +// node, or a SUBSTRING of an EXTERNAL or FLAT node. +void DeleteLeafEdge(CordRep* rep) { + for (;;) { + if (rep->tag >= FLAT) { + CordRepFlat::Delete(rep->flat()); + return; + } + if (rep->tag == EXTERNAL) { + CordRepExternal::Delete(rep->external()); + return; + } + assert(rep->tag == SUBSTRING); + CordRepSubstring* substring = rep->substring(); + rep = substring->child; + assert(rep->tag == EXTERNAL || rep->tag >= FLAT); + delete substring; + if (rep->refcount.Decrement()) return; + } +} + +// StackOperations contains the logic to build a left-most or right-most stack +// (leg) down to the leaf level of a btree, and 'unwind' / 'Finalize' methods to +// propagate node changes up the stack. +template +struct StackOperations { + // Returns true if the node at 'depth' is not shared, i.e. has a refcount + // of one and all of its parent nodes have a refcount of one. + inline bool owned(int depth) const { return depth < share_depth; } + + // Returns the node at 'depth'. + inline CordRepBtree* node(int depth) const { return stack[depth]; } + + // Builds a `depth` levels deep stack starting at `tree` recording which nodes + // are private in the form of the 'share depth' where nodes are shared. + inline CordRepBtree* BuildStack(CordRepBtree* tree, int depth) { + assert(depth <= tree->height()); + int current_depth = 0; + while (current_depth < depth && tree->refcount.IsOne()) { + stack[current_depth++] = tree; + tree = tree->Edge(edge_type)->btree(); + } + share_depth = current_depth + (tree->refcount.IsOne() ? 1 : 0); + while (current_depth < depth) { + stack[current_depth++] = tree; + tree = tree->Edge(edge_type)->btree(); + } + return tree; + } + + // Builds a stack with the invariant that all nodes are private owned / not + // shared. This is used in iterative updates where a previous propagation + // guaranteed all nodes are owned / private. + inline void BuildOwnedStack(CordRepBtree* tree, int height) { + assert(height <= CordRepBtree::kMaxHeight); + int depth = 0; + while (depth < height) { + assert(tree->refcount.IsOne()); + stack[depth++] = tree; + tree = tree->Edge(edge_type)->btree(); + } + assert(tree->refcount.IsOne()); + share_depth = depth + 1; + } + + // Processes the final 'top level' result action for the tree. + // See the 'Action' enum for the various action implications. + static inline CordRepBtree* Finalize(CordRepBtree* tree, OpResult result) { + switch (result.action) { + case CordRepBtree::kPopped: + if (ABSL_PREDICT_FALSE(tree->height() >= CordRepBtree::kMaxHeight)) { + ABSL_RAW_LOG(FATAL, "Max height exceeded"); + } + return edge_type == kBack ? CordRepBtree::New(tree, result.tree) + : CordRepBtree::New(result.tree, tree); + case CordRepBtree::kCopied: + CordRep::Unref(tree); + ABSL_FALLTHROUGH_INTENDED; + case CordRepBtree::kSelf: + return result.tree; + } + ABSL_INTERNAL_UNREACHABLE; + return result.tree; + } + + // Propagate the action result in 'result' up into all nodes of the stack + // starting at depth 'depth'. 'length' contains the extra length of data that + // was added at the lowest level, and is updated into all nodes of the stack. + // See the 'Action' enum for the various action implications. + // If 'propagate' is true, then any copied node values are updated into the + // stack, which is used for iterative processing on the same stack. + template + inline CordRepBtree* Unwind(CordRepBtree* tree, int depth, size_t length, + OpResult result) { + // TODO(mvels): revisit the below code to check if 3 loops with 3 + // (incremental) conditions is faster than 1 loop with a switch. + // Benchmarking and perf recordings indicate the loop with switch is + // fastest, likely because of indirect jumps on the tight case values and + // dense branches. But it's worth considering 3 loops, as the `action` + // transitions are mono directional. E.g.: + // while (action == kPopped) { + // ... + // } + // while (action == kCopied) { + // ... + // } + // ... + // We also found that an "if () do {}" loop here seems faster, possibly + // because it allows the branch predictor more granular heuristics on + // 'single leaf' (`depth` == 0) and 'single depth' (`depth` == 1) cases + // which appear to be the most common use cases. + if (depth != 0) { + do { + CordRepBtree* node = stack[--depth]; + const bool owned = depth < share_depth; + switch (result.action) { + case CordRepBtree::kPopped: + assert(!propagate); + result = node->AddEdge(owned, result.tree, length); + break; + case CordRepBtree::kCopied: + result = node->SetEdge(owned, result.tree, length); + if (propagate) stack[depth] = result.tree; + break; + case CordRepBtree::kSelf: + node->length += length; + while (depth > 0) { + node = stack[--depth]; + node->length += length; + } + return node; + } + } while (depth > 0); + } + return Finalize(tree, result); + } + + // Invokes `Unwind` with `propagate=true` to update the stack node values. + inline CordRepBtree* Propagate(CordRepBtree* tree, int depth, size_t length, + OpResult result) { + return Unwind(tree, depth, length, result); + } + + // `share_depth` contains the depth at which the nodes in the stack become + // shared. I.e., if the top most level is shared (i.e.: `!refcount.IsOne()`), + // then `share_depth` is 0. If the 2nd node is shared (and implicitly all + // nodes below that) then `share_depth` is 1, etc. A `share_depth` greater + // than the depth of the stack indicates that none of the nodes in the stack + // are shared. + int share_depth; + + NodeStack stack; +}; + +} // namespace + +void CordRepBtree::Dump(const CordRep* rep, absl::string_view label, + bool include_contents, std::ostream& stream) { + stream << "===================================\n"; + if (!label.empty()) { + stream << label << '\n'; + stream << "-----------------------------------\n"; + } + if (rep) { + DumpAll(rep, include_contents, stream); + } else { + stream << "NULL\n"; + } +} + +void CordRepBtree::Dump(const CordRep* rep, absl::string_view label, + std::ostream& stream) { + Dump(rep, label, false, stream); +} + +void CordRepBtree::Dump(const CordRep* rep, std::ostream& stream) { + Dump(rep, absl::string_view(), false, stream); +} + +void CordRepBtree::DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end) { + for (CordRep* edge : tree->Edges(begin, end)) { + FastUnref(edge, DeleteLeafEdge); + } + Delete(tree); +} + +void CordRepBtree::DestroyNonLeaf(CordRepBtree* tree, size_t begin, + size_t end) { + for (CordRep* edge : tree->Edges(begin, end)) { + FastUnref(edge->btree(), Destroy); + } + Delete(tree); +} + +bool CordRepBtree::IsValid(const CordRepBtree* tree) { +#define NODE_CHECK_VALID(x) \ + if (!(x)) { \ + ABSL_RAW_LOG(ERROR, "CordRepBtree::CheckValid() FAILED: %s", #x); \ + return false; \ + } +#define NODE_CHECK_EQ(x, y) \ + if ((x) != (y)) { \ + ABSL_RAW_LOG(ERROR, \ + "CordRepBtree::CheckValid() FAILED: %s != %s (%s vs %s)", #x, \ + #y, absl::StrCat(x).c_str(), absl::StrCat(y).c_str()); \ + return false; \ + } + + NODE_CHECK_VALID(tree != nullptr); + NODE_CHECK_EQ(tree->tag, BTREE); + NODE_CHECK_VALID(tree->height() <= kMaxHeight); + NODE_CHECK_VALID(tree->begin() < tree->capacity()); + NODE_CHECK_VALID(tree->end() <= tree->capacity()); + NODE_CHECK_VALID(tree->begin() <= tree->end()); + size_t child_length = 0; + for (CordRep* edge : tree->Edges()) { + NODE_CHECK_VALID(edge != nullptr); + if (tree->height() > 0) { + NODE_CHECK_VALID(edge->tag == BTREE); + NODE_CHECK_VALID(edge->btree()->height() == tree->height() - 1); + } else { + NODE_CHECK_VALID(IsDataEdge(edge)); + } + child_length += edge->length; + } + NODE_CHECK_EQ(child_length, tree->length); + if (tree->height() > 0) { + for (CordRep* edge : tree->Edges()) { + if (!IsValid(edge->btree())) return false; + } + } + return true; + +#undef NODE_CHECK_VALID +#undef NODE_CHECK_EQ +} + +#ifndef NDEBUG + +CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree) { + if (!IsValid(tree)) { + Dump(tree, "CordRepBtree validation failed:", false, std::cout); + ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); + } + return tree; +} + +const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree) { + if (!IsValid(tree)) { + Dump(tree, "CordRepBtree validation failed:", false, std::cout); + ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); + } + return tree; +} + +#endif // NDEBUG + +template +inline OpResult CordRepBtree::AddEdge(bool owned, CordRep* edge, size_t delta) { + if (size() >= kMaxCapacity) return {New(edge), kPopped}; + OpResult result = ToOpResult(owned); + result.tree->Add(edge); + result.tree->length += delta; + return result; +} + +template +OpResult CordRepBtree::SetEdge(bool owned, CordRep* edge, size_t delta) { + OpResult result; + const size_t idx = index(edge_type); + if (owned) { + result = {this, kSelf}; + CordRep::Unref(edges_[idx]); + } else { + // Create a copy containing all unchanged edges. Unchanged edges are the + // open interval [begin, back) or [begin + 1, end) depending on `edge_type`. + // We conveniently cover both case using a constexpr `shift` being 0 or 1 + // as `end :== back + 1`. + result = {CopyRaw(), kCopied}; + constexpr int shift = edge_type == kFront ? 1 : 0; + for (CordRep* r : Edges(begin() + shift, back() + shift)) { + CordRep::Ref(r); + } + } + result.tree->edges_[idx] = edge; + result.tree->length += delta; + return result; +} + +template +CordRepBtree* CordRepBtree::AddCordRep(CordRepBtree* tree, CordRep* rep) { + const int depth = tree->height(); + const size_t length = rep->length; + StackOperations ops; + CordRepBtree* leaf = ops.BuildStack(tree, depth); + const OpResult result = + leaf->AddEdge(ops.owned(depth), rep, length); + return ops.Unwind(tree, depth, length, result); +} + +template <> +CordRepBtree* CordRepBtree::NewLeaf(absl::string_view data, + size_t extra) { + CordRepBtree* leaf = CordRepBtree::New(0); + size_t length = 0; + size_t end = 0; + const size_t cap = leaf->capacity(); + while (!data.empty() && end != cap) { + auto* flat = CordRepFlat::New(data.length() + extra); + flat->length = (std::min)(data.length(), flat->Capacity()); + length += flat->length; + leaf->edges_[end++] = flat; + data = Consume(flat->Data(), data, flat->length); + } + leaf->length = length; + leaf->set_end(end); + return leaf; +} + +template <> +CordRepBtree* CordRepBtree::NewLeaf(absl::string_view data, + size_t extra) { + CordRepBtree* leaf = CordRepBtree::New(0); + size_t length = 0; + size_t begin = leaf->capacity(); + leaf->set_end(leaf->capacity()); + while (!data.empty() && begin != 0) { + auto* flat = CordRepFlat::New(data.length() + extra); + flat->length = (std::min)(data.length(), flat->Capacity()); + length += flat->length; + leaf->edges_[--begin] = flat; + data = Consume(flat->Data(), data, flat->length); + } + leaf->length = length; + leaf->set_begin(begin); + return leaf; +} + +template <> +absl::string_view CordRepBtree::AddData(absl::string_view data, + size_t extra) { + assert(!data.empty()); + assert(size() < capacity()); + AlignBegin(); + const size_t cap = capacity(); + do { + CordRepFlat* flat = CordRepFlat::New(data.length() + extra); + const size_t n = (std::min)(data.length(), flat->Capacity()); + flat->length = n; + edges_[fetch_add_end(1)] = flat; + data = Consume(flat->Data(), data, n); + } while (!data.empty() && end() != cap); + return data; +} + +template <> +absl::string_view CordRepBtree::AddData(absl::string_view data, + size_t extra) { + assert(!data.empty()); + assert(size() < capacity()); + AlignEnd(); + do { + CordRepFlat* flat = CordRepFlat::New(data.length() + extra); + const size_t n = (std::min)(data.length(), flat->Capacity()); + flat->length = n; + edges_[sub_fetch_begin(1)] = flat; + data = Consume(flat->Data(), data, n); + } while (!data.empty() && begin() != 0); + return data; +} + +template +CordRepBtree* CordRepBtree::AddData(CordRepBtree* tree, absl::string_view data, + size_t extra) { + if (ABSL_PREDICT_FALSE(data.empty())) return tree; + + const size_t original_data_size = data.size(); + int depth = tree->height(); + StackOperations ops; + CordRepBtree* leaf = ops.BuildStack(tree, depth); + + // If there is capacity in the last edge, append as much data + // as possible into this last edge. + if (leaf->size() < leaf->capacity()) { + OpResult result = leaf->ToOpResult(ops.owned(depth)); + data = result.tree->AddData(data, extra); + if (data.empty()) { + result.tree->length += original_data_size; + return ops.Unwind(tree, depth, original_data_size, result); + } + + // We added some data into this leaf, but not all. Propagate the added + // length to the top most node, and rebuild the stack with any newly copied + // or updated nodes. From this point on, the path (leg) from the top most + // node to the right-most node towards the leaf node is privately owned. + size_t delta = original_data_size - data.size(); + assert(delta > 0); + result.tree->length += delta; + tree = ops.Propagate(tree, depth, delta, result); + ops.share_depth = depth + 1; + } + + // We were unable to append all data into the existing right-most leaf node. + // This means all remaining data must be put into (a) new leaf node(s) which + // we append to the tree. To make this efficient, we iteratively build full + // leaf nodes from `data` until the created leaf contains all remaining data. + // We utilize the `Unwind` method to merge the created leaf into the first + // level towards root that has capacity. On each iteration with remaining + // data, we rebuild the stack in the knowledge that right-most nodes are + // privately owned after the first `Unwind` completes. + for (;;) { + OpResult result = {CordRepBtree::NewLeaf(data, extra), kPopped}; + if (result.tree->length == data.size()) { + return ops.Unwind(tree, depth, result.tree->length, result); + } + data = Consume(data, result.tree->length); + tree = ops.Unwind(tree, depth, result.tree->length, result); + depth = tree->height(); + ops.BuildOwnedStack(tree, depth); + } +} + +template +CordRepBtree* CordRepBtree::Merge(CordRepBtree* dst, CordRepBtree* src) { + assert(dst->height() >= src->height()); + + // Capture source length as we may consume / destroy `src`. + const size_t length = src->length; + + // We attempt to merge `src` at its corresponding height in `dst`. + const int depth = dst->height() - src->height(); + StackOperations ops; + CordRepBtree* merge_node = ops.BuildStack(dst, depth); + + // If there is enough space in `merge_node` for all edges from `src`, add all + // edges to this node, making a fresh copy as needed if not privately owned. + // If `merge_node` does not have capacity for `src`, we rely on `Unwind` and + // `Finalize` to merge `src` into the first level towards `root` where there + // is capacity for another edge, or create a new top level node. + OpResult result; + if (merge_node->size() + src->size() <= kMaxCapacity) { + result = merge_node->ToOpResult(ops.owned(depth)); + result.tree->Add(src->Edges()); + result.tree->length += src->length; + if (src->refcount.IsOne()) { + Delete(src); + } else { + for (CordRep* edge : src->Edges()) CordRep::Ref(edge); + CordRepBtree::Unref(src); + } + } else { + result = {src, kPopped}; + } + + // Unless we merged at the top level (i.e.: src and dst are equal height), + // unwind the result towards the top level, and finalize the result. + if (depth) { + return ops.Unwind(dst, depth, length, result); + } + return ops.Finalize(dst, result); +} + +CopyResult CordRepBtree::CopySuffix(size_t offset) { + assert(offset < this->length); + + // As long as `offset` starts inside the last edge, we can 'drop' the current + // depth. For the most extreme example: if offset references the last data + // edge in the tree, there is only a single edge / path from the top of the + // tree to that last edge, so we can drop all the nodes except that edge. + // The fast path check for this is `back->length >= length - offset`. + int height = this->height(); + CordRepBtree* node = this; + size_t len = node->length - offset; + CordRep* back = node->Edge(kBack); + while (back->length >= len) { + offset = back->length - len; + if (--height < 0) { + return {MakeSubstring(CordRep::Ref(back), offset), height}; + } + node = back->btree(); + back = node->Edge(kBack); + } + if (offset == 0) return {CordRep::Ref(node), height}; + + // Offset does not point into the last edge, so we span at least two edges. + // Find the index of offset with `IndexBeyond` which provides us the edge + // 'beyond' the offset if offset is not a clean starting point of an edge. + Position pos = node->IndexBeyond(offset); + CordRepBtree* sub = node->CopyToEndFrom(pos.index, len); + const CopyResult result = {sub, height}; + + // `pos.n` contains a non zero value if the offset is not an exact starting + // point of an edge. In this case, `pos.n` contains the 'trailing' amount of + // bytes of the edge preceding that in `pos.index`. We need to iteratively + // adjust the preceding edge with the 'broken' offset until we have a perfect + // start of the edge. + while (pos.n != 0) { + assert(pos.index >= 1); + const size_t begin = pos.index - 1; + sub->set_begin(begin); + CordRep* const edge = node->Edge(begin); + + len = pos.n; + offset = edge->length - len; + + if (--height < 0) { + sub->edges_[begin] = MakeSubstring(CordRep::Ref(edge), offset, len); + return result; + } + + node = edge->btree(); + pos = node->IndexBeyond(offset); + + CordRepBtree* nsub = node->CopyToEndFrom(pos.index, len); + sub->edges_[begin] = nsub; + sub = nsub; + } + sub->set_begin(pos.index); + return result; +} + +CopyResult CordRepBtree::CopyPrefix(size_t n) { + assert(n > 0); + assert(n <= this->length); + + // As long as `n` does not exceed the length of the first edge, we can 'drop' + // the current depth. For the most extreme example: if we'd copy a 1 byte + // prefix from a tree, there is only a single edge / path from the top of the + // tree to the single data edge containing this byte, so we can drop all the + // nodes except the data node. + int height = this->height(); + CordRepBtree* node = this; + CordRep* front = node->Edge(kFront); + while (front->length >= n) { + if (--height < 0) return {MakeSubstring(CordRep::Ref(front), 0, n), -1}; + node = front->btree(); + front = node->Edge(kFront); + } + if (node->length == n) return {CordRep::Ref(node), height}; + + // `n` spans at least two nodes, find the end point of the span. + Position pos = node->IndexOf(n); + + // Create a partial copy of the node up to `pos.index`, with a defined length + // of `n`. Any 'partial last edge' is added further below as needed. + CordRepBtree* sub = node->CopyBeginTo(pos.index, n); + const CopyResult result = {sub, height}; + + // `pos.n` contains the 'offset inside the edge for IndexOf(n)'. As long as + // this is not zero, we don't have a 'clean cut', so we need to make a + // (partial) copy of that last edge, and repeat this until pos.n is zero. + while (pos.n != 0) { + size_t end = pos.index; + n = pos.n; + + CordRep* edge = node->Edge(pos.index); + if (--height < 0) { + sub->edges_[end++] = MakeSubstring(CordRep::Ref(edge), 0, n); + sub->set_end(end); + AssertValid(result.edge->btree()); + return result; + } + + node = edge->btree(); + pos = node->IndexOf(n); + CordRepBtree* nsub = node->CopyBeginTo(pos.index, n); + sub->edges_[end++] = nsub; + sub->set_end(end); + sub = nsub; + } + sub->set_end(pos.index); + AssertValid(result.edge->btree()); + return result; +} + +CordRep* CordRepBtree::SubTree(size_t offset, size_t n) { + assert(n <= this->length); + assert(offset <= this->length - n); + if (ABSL_PREDICT_FALSE(n == 0)) return nullptr; + + CordRepBtree* node = this; + int height = node->height(); + Position front = node->IndexOf(offset); + CordRep* left = node->edges_[front.index]; + while (front.n + n <= left->length) { + if (--height < 0) return MakeSubstring(CordRep::Ref(left), front.n, n); + node = left->btree(); + front = node->IndexOf(front.n); + left = node->edges_[front.index]; + } + + const Position back = node->IndexBefore(front, n); + CordRep* const right = node->edges_[back.index]; + assert(back.index > front.index); + + // Get partial suffix and prefix entries. + CopyResult prefix; + CopyResult suffix; + if (height > 0) { + // Copy prefix and suffix of the boundary nodes. + prefix = left->btree()->CopySuffix(front.n); + suffix = right->btree()->CopyPrefix(back.n); + + // If there is an edge between the prefix and suffix edges, then the tree + // must remain at its previous (full) height. If we have no edges between + // prefix and suffix edges, then the tree must be as high as either the + // suffix or prefix edges (which are collapsed to their minimum heights). + if (front.index + 1 == back.index) { + height = (std::max)(prefix.height, suffix.height) + 1; + } + + // Raise prefix and suffixes to the new tree height. + for (int h = prefix.height + 1; h < height; ++h) { + prefix.edge = CordRepBtree::New(prefix.edge); + } + for (int h = suffix.height + 1; h < height; ++h) { + suffix.edge = CordRepBtree::New(suffix.edge); + } + } else { + // Leaf node, simply take substrings for prefix and suffix. + prefix = CopyResult{MakeSubstring(CordRep::Ref(left), front.n), -1}; + suffix = CopyResult{MakeSubstring(CordRep::Ref(right), 0, back.n), -1}; + } + + // Compose resulting tree. + CordRepBtree* sub = CordRepBtree::New(height); + size_t end = 0; + sub->edges_[end++] = prefix.edge; + for (CordRep* r : node->Edges(front.index + 1, back.index)) { + sub->edges_[end++] = CordRep::Ref(r); + } + sub->edges_[end++] = suffix.edge; + sub->set_end(end); + sub->length = n; + return AssertValid(sub); +} + +CordRepBtree* CordRepBtree::MergeTrees(CordRepBtree* left, + CordRepBtree* right) { + return left->height() >= right->height() ? Merge(left, right) + : Merge(right, left); +} + +bool CordRepBtree::IsFlat(absl::string_view* fragment) const { + if (height() == 0 && size() == 1) { + if (fragment) *fragment = Data(begin()); + return true; + } + return false; +} + +bool CordRepBtree::IsFlat(size_t offset, const size_t n, + absl::string_view* fragment) const { + assert(n <= this->length); + assert(offset <= this->length - n); + if (ABSL_PREDICT_FALSE(n == 0)) return false; + int height = this->height(); + const CordRepBtree* node = this; + for (;;) { + const Position front = node->IndexOf(offset); + const CordRep* edge = node->Edge(front.index); + if (edge->length < front.n + n) return false; + if (--height < 0) { + if (fragment) *fragment = EdgeData(edge).substr(front.n, n); + return true; + } + offset = front.n; + node = node->Edge(front.index)->btree(); + } +} + +char CordRepBtree::GetCharacter(size_t offset) const { + assert(offset < length); + const CordRepBtree* node = this; + int height = node->height(); + for (;;) { + Position front = node->IndexOf(offset); + if (--height < 0) return node->Data(front.index)[front.n]; + offset = front.n; + node = node->Edge(front.index)->btree(); + } +} + +Span CordRepBtree::GetAppendBufferSlow(size_t size) { + // The inlined version in `GetAppendBuffer()` deals with all heights <= 3. + assert(height() >= 4); + assert(refcount.IsOne()); + + // Build a stack of nodes we may potentially need to update if we find a + // non-shared FLAT with capacity at the leaf level. + const int depth = height(); + CordRepBtree* node = this; + CordRepBtree* stack[kMaxDepth]; + for (int i = 0; i < depth; ++i) { + node = node->Edge(kBack)->btree(); + if (!node->refcount.IsOne()) return {}; + stack[i] = node; + } + + // Must be a privately owned flat. + CordRep* const edge = node->Edge(kBack); + if (!edge->refcount.IsOne() || edge->tag < FLAT) return {}; + + // Must have capacity. + const size_t avail = edge->flat()->Capacity() - edge->length; + if (avail == 0) return {}; + + // Build span on remaining capacity. + size_t delta = (std::min)(size, avail); + Span span = {edge->flat()->Data() + edge->length, delta}; + edge->length += delta; + this->length += delta; + for (int i = 0; i < depth; ++i) { + stack[i]->length += delta; + } + return span; +} + +CordRepBtree* CordRepBtree::CreateSlow(CordRep* rep) { + if (rep->tag == BTREE) return rep->btree(); + + CordRepBtree* node = nullptr; + auto consume = [&node](CordRep* r, size_t offset, size_t length) { + r = MakeSubstring(r, offset, length); + if (node == nullptr) { + node = New(r); + } else { + node = CordRepBtree::AddCordRep(node, r); + } + }; + Consume(rep, consume); + return node; +} + +CordRepBtree* CordRepBtree::AppendSlow(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(rep->tag == BTREE)) { + return MergeTrees(tree, rep->btree()); + } + auto consume = [&tree](CordRep* r, size_t offset, size_t length) { + r = MakeSubstring(r, offset, length); + tree = CordRepBtree::AddCordRep(tree, r); + }; + Consume(rep, consume); + return tree; +} + +CordRepBtree* CordRepBtree::PrependSlow(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(rep->tag == BTREE)) { + return MergeTrees(rep->btree(), tree); + } + auto consume = [&tree](CordRep* r, size_t offset, size_t length) { + r = MakeSubstring(r, offset, length); + tree = CordRepBtree::AddCordRep(tree, r); + }; + ReverseConsume(rep, consume); + return tree; +} + +CordRepBtree* CordRepBtree::Append(CordRepBtree* tree, absl::string_view data, + size_t extra) { + return CordRepBtree::AddData(tree, data, extra); +} + +CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, absl::string_view data, + size_t extra) { + return CordRepBtree::AddData(tree, data, extra); +} + +template CordRepBtree* CordRepBtree::AddCordRep(CordRepBtree* tree, + CordRep* rep); +template CordRepBtree* CordRepBtree::AddCordRep(CordRepBtree* tree, + CordRep* rep); +template CordRepBtree* CordRepBtree::AddData(CordRepBtree* tree, + absl::string_view data, + size_t extra); +template CordRepBtree* CordRepBtree::AddData(CordRepBtree* tree, + absl::string_view data, + size_t extra); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h new file mode 100644 index 00000000..f4118fc0 --- /dev/null +++ b/absl/strings/internal/cord_rep_btree.h @@ -0,0 +1,851 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ + +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/optimization.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +class CordRepBtreeNavigator; + +// CordRepBtree is as the name implies a btree implementation of a Cordrep tree. +// Data is stored at the leaf level only, non leaf nodes contain down pointers +// only. Allowed types of data edges are FLAT, EXTERNAL and SUBSTRINGs of FLAT +// or EXTERNAL nodes. The implementation allows for data to be added to either +// end of the tree only, it does not provide any 'insert' logic. This has the +// benefit that we can expect good fill ratios: all nodes except the outer +// 'legs' will have 100% fill ratios for trees built using Append/Prepend +// methods. Merged trees will typically have a fill ratio well above 50% as in a +// similar fashion, one side of the merged tree will typically have a 100% fill +// ratio, and the 'open' end will average 50%. All operations are O(log(n)) or +// better, and the tree never needs balancing. +// +// All methods accepting a CordRep* or CordRepBtree* adopt a reference on that +// input unless explicitly stated otherwise. All functions returning a CordRep* +// or CordRepBtree* instance transfer a reference back to the caller. +// Simplified, callers both 'donate' and 'consume' a reference count on each +// call, simplifying the API. An example of building a tree: +// +// CordRepBtree* tree = CordRepBtree::Create(MakeFlat("Hello")); +// tree = CordRepBtree::Append(tree, MakeFlat("world")); +// +// In the above example, all inputs are consumed, making each call affecting +// `tree` reference count neutral. The returned `tree` value can be different +// from the input if the input is shared with other threads, or if the tree +// grows in height, but callers typically never have to concern themselves with +// that and trust that all methods DTRT at all times. +class CordRepBtree : public CordRep { + public: + // EdgeType identifies `front` and `back` enum values. + // Various implementations in CordRepBtree such as `Add` and `Edge` are + // generic and templated on operating on either of the boundary edges. + // For more information on the possible edges contained in a CordRepBtree + // instance see the documentation for `edges_`. + enum class EdgeType { kFront, kBack }; + + // Convenience constants into `EdgeType` + static constexpr EdgeType kFront = EdgeType::kFront; + static constexpr EdgeType kBack = EdgeType::kBack; + + // Maximum number of edges: based on experiments and performance data, we can + // pick suitable values resulting in optimum cacheline aligned values. The + // preferred values are based on 64-bit systems where we aim to align this + // class onto 64 bytes, i.e.: 6 = 64 bytes, 14 = 128 bytes, etc. + // TODO(b/192061034): experiment with alternative sizes. + static constexpr size_t kMaxCapacity = 6; + + // Reasonable maximum height of the btree. We can expect a fill ratio of at + // least 50%: trees are always expanded at the front or back. Concatenating + // trees will then typically fold at the top most node, where the lower nodes + // are at least at capacity on one side of joined inputs. At a lower fill + // rate of 4 edges per node, we have capacity for ~16 million leaf nodes. + // We will fail / abort if an application ever exceeds this height, which + // should be extremely rare (near impossible) and be an indication of an + // application error: we do not assume it reasonable for any application to + // operate correctly with such monster trees. + // Another compelling reason for the number `12` is that any contextual stack + // required for navigation or insertion requires 12 words and 12 bytes, which + // fits inside 2 cache lines with some room to spare, and is reasonable as a + // local stack variable compared to Cord's current near 400 bytes stack use. + // The maximum `height` value of a node is then `kMaxDepth - 1` as node height + // values start with a value of 0 for leaf nodes. + static constexpr int kMaxDepth = 12; + static constexpr int kMaxHeight = kMaxDepth - 1; + + // `Action` defines the action for unwinding changes done at the btree's leaf + // level that need to be propagated up to the parent node(s). Each operation + // on a node has an effect / action defined as follows: + // - kSelf + // The operation (add / update, etc) was performed directly on the node as + // the node is private to the current thread (i.e.: not shared directly or + // indirectly through a refcount > 1). Changes can be propagated directly to + // all parent nodes as all parent nodes are also then private to the current + // thread. + // - kCopied + // The operation (add / update, etc) was performed on a copy of the original + // node, as the node is (potentially) directly or indirectly shared with + // other threads. Changes need to be propagated into the parent nodes where + // the old down pointer must be unreffed and replaced with this new copy. + // Such changes to parent nodes may themselves require a copy if the parent + // node is also shared. A kCopied action can propagate all the way to the + // top node where we then must unref the `tree` input provided by the + // caller, and return the new copy. + // - kPopped + // The operation (typically add) could not be satisfied due to insufficient + // capacity in the targeted node, and a new 'leg' was created that needs to + // be added into the parent node. For example, adding a FLAT inside a leaf + // node that is at capacity will create a new leaf node containing that + // FLAT, that needs to be 'popped' up the btree. Such 'pop' actions can + // cascade up the tree if parent nodes are also at capacity. A 'Popped' + // action propagating all the way to the top of the tree will result in + // the tree becoming one level higher than the current tree through a final + // `CordRepBtree::New(tree, popped)` call, resulting in a new top node + // referencing the old tree and the new (fully popped upwards) 'leg'. + enum Action { kSelf, kCopied, kPopped }; + + // Result of an operation on a node. See the `Action` enum for details. + struct OpResult { + CordRepBtree* tree; + Action action; + }; + + // Return value of the CopyPrefix and CopySuffix methods which can + // return a node or data edge at any height inside the tree. + // A height of 0 defines the lowest (leaf) node, a height of -1 identifies + // `edge` as being a plain data node: EXTERNAL / FLAT or SUBSTRING thereof. + struct CopyResult { + CordRep* edge; + int height; + }; + + // Logical position inside a node: + // - index: index of the edge. + // - n: size or offset value depending on context. + struct Position { + size_t index; + size_t n; + }; + + // Creates a btree from the given input. Adopts a ref of `rep`. + // If the input `rep` is itself a btree, i.e., `tag == BTREE`, then this + // function immediately returns `rep->btree()`. If the input is a valid data + // edge (see IsDataEdge()), then a new leaf node is returned containing `rep` + // as the sole data edge. Else, the input is assumed to be a (legacy) concat + // tree, and the input is consumed and transformed into a btree(). + static CordRepBtree* Create(CordRep* rep); + + // Destroys the provided tree. Should only be called by cord internal API's, + // typically after a ref_count.Decrement() on the last reference count. + static void Destroy(CordRepBtree* tree); + + // Appends / Prepends an existing CordRep instance to this tree. + // The below methods accept three types of input: + // 1) `rep` is a data node (See `IsDataNode` for valid data edges). + // `rep` is appended or prepended to this tree 'as is'. + // 2) `rep` is a BTREE. + // `rep` is merged into `tree` respecting the Append/Prepend order. + // 3) `rep` is some other (legacy) type. + // `rep` is converted in place and added to `tree` + // Requires `tree` and `rep` to be not null. + static CordRepBtree* Append(CordRepBtree* tree, CordRep* rep); + static CordRepBtree* Prepend(CordRepBtree* tree, CordRep* rep); + + // Append/Prepend the data in `data` to this tree. + // The `extra` parameter defines how much extra capacity should be allocated + // for any additional FLAT being allocated. This is an optimization hint from + // the caller. For example, a caller may need to add 2 string_views of data + // "abc" and "defghi" which are not consecutive. The caller can in this case + // invoke `AddData(tree, "abc", 6)`, and any newly added flat is allocated + // where possible with at least 6 bytes of extra capacity beyond `length`. + // This helps avoiding data getting fragmented over multiple flats. + // There is no limit on the size of `data`. If `data` can not be stored inside + // a single flat, then the function will iteratively add flats until all data + // has been consumed and appended or prepended to the tree. + static CordRepBtree* Append(CordRepBtree* tree, string_view data, + size_t extra = 0); + static CordRepBtree* Prepend(CordRepBtree* tree, string_view data, + size_t extra = 0); + + // Returns a new tree, containing `n` bytes of data from this instance + // starting at offset `offset`. Where possible, the returned tree shares + // (re-uses) data edges and nodes with this instance to minimize the + // combined memory footprint of both trees. + // Requires `offset + n <= length`. Returns `nullptr` if `n` is zero. + CordRep* SubTree(size_t offset, size_t n); + + // Returns the character at the given offset. + char GetCharacter(size_t offset) const; + + // Returns true if this node holds a single data edge, and if so, sets + // `fragment` to reference the contained data. `fragment` is an optional + // output parameter and allowed to be null. + bool IsFlat(absl::string_view* fragment) const; + + // Returns true if the data of `n` bytes starting at offset `offset` + // is contained in a single data edge, and if so, sets fragment to reference + // the contained data. `fragment` is an optional output parameter and allowed + // to be null. + bool IsFlat(size_t offset, size_t n, absl::string_view* fragment) const; + + // Returns a span (mutable range of bytes) of up to `size` bytes into the + // last FLAT data edge inside this tree under the following conditions: + // - none of the nodes down into the FLAT node are shared. + // - the last data edge in this tree is a non-shared FLAT. + // - the referenced FLAT has additional capacity available. + // If all these conditions are met, a non-empty span is returned, and the + // length of the flat node and involved tree nodes have been increased by + // `span.length()`. The caller is responsible for immediately assigning values + // to all uninitialized data reference by the returned span. + // Requires `this->refcount.IsOne()`: this function forces the caller to do + // this fast path check on the top level node, as this is the most commonly + // shared node of a cord tree. + Span GetAppendBuffer(size_t size); + + // Returns the `height` of the tree. The height of a tree is limited to + // kMaxHeight. `height` is implemented as an `int` as in some places we + // use negative (-1) values for 'data edges'. + int height() const { return static_cast(storage[0]); } + + // Properties: begin, back, end, front/back boundary indexes. + size_t begin() const { return static_cast(storage[1]); } + size_t back() const { return static_cast(storage[2]) - 1; } + size_t end() const { return static_cast(storage[2]); } + size_t index(EdgeType edge) const { + return edge == kFront ? begin() : back(); + } + + // Properties: size and capacity. + // `capacity` contains the current capacity of this instance, where + // `kMaxCapacity` contains the maximum capacity of a btree node. + // For now, `capacity` and `kMaxCapacity` return the same value, but this may + // change in the future if we see benefit in dynamically sizing 'small' nodes + // to 'large' nodes for large data trees. + size_t size() const { return end() - begin(); } + size_t capacity() const { return kMaxCapacity; } + + // Edge access + inline CordRep* Edge(size_t index) const; + inline CordRep* Edge(EdgeType edge_type) const; + inline absl::Span Edges() const; + inline absl::Span Edges(size_t begin, size_t end) const; + + // Returns reference to the data edge at `index`. + // Requires this instance to be a leaf node, and `index` to be valid index. + inline absl::string_view Data(size_t index) const; + + static const char* EdgeDataPtr(const CordRep* r); + static absl::string_view EdgeData(const CordRep* r); + + // Returns true if the provided rep is a FLAT, EXTERNAL or a SUBSTRING node + // holding a FLAT or EXTERNAL child rep. + static bool IsDataEdge(const CordRep* rep); + + // Diagnostics + static bool IsValid(const CordRepBtree* tree); + static CordRepBtree* AssertValid(CordRepBtree* tree); + static const CordRepBtree* AssertValid(const CordRepBtree* tree); + static void Dump(const CordRep* rep, std::ostream& stream); + static void Dump(const CordRep* rep, absl::string_view label, + std::ostream& stream); + static void Dump(const CordRep* rep, absl::string_view label, + bool include_contents, std::ostream& stream); + + // Adds the edge `edge` to this node if possible. `owned` indicates if the + // current node is potentially shared or not with other threads. Returns: + // - {kSelf, } + // The edge was directly added to this node. + // - {kCopied, } + // The edge was added to a copy of this node. + // - {kPopped, New(edge, height())} + // A new leg with the edge was created as this node has no extra capacity. + template + inline OpResult AddEdge(bool owned, CordRep* edge, size_t delta); + + // Replaces the front or back edge with the provided new edge. Returns: + // - {kSelf, } + // The edge was directly set in this node. The old edge is unreffed. + // - {kCopied, } + // A copy of this node was created with the new edge value. + // In both cases, the function adopts a reference on `edge`. + template + OpResult SetEdge(bool owned, CordRep* edge, size_t delta); + + // Creates a new empty node at the specified height. + static CordRepBtree* New(int height = 0); + + // Creates a new node containing `rep`, with the height being computed + // automatically based on the type of `rep`. + static CordRepBtree* New(CordRep* rep); + + // Creates a new node containing both `front` and `back` at height + // `front.height() + 1`. Requires `back.height() == front.height()`. + static CordRepBtree* New(CordRepBtree* front, CordRepBtree* back); + + private: + CordRepBtree() = default; + ~CordRepBtree() = default; + + // Initializes the main properties `tag`, `begin`, `end`, `height`. + inline void InitInstance(int height, size_t begin = 0, size_t end = 0); + + // Direct property access begin / end + void set_begin(size_t begin) { storage[1] = static_cast(begin); } + void set_end(size_t end) { storage[2] = static_cast(end); } + + // Decreases the value of `begin` by `n`, and returns the new value. Notice + // how this returns the new value unlike atomic::fetch_add which returns the + // old value. This is because this is used to prepend edges at 'begin - 1'. + size_t sub_fetch_begin(size_t n) { + storage[1] -= static_cast(n); + return storage[1]; + } + + // Increases the value of `end` by `n`, and returns the previous value. This + // function is typically used to append edges at 'end'. + size_t fetch_add_end(size_t n) { + const uint8_t current = storage[2]; + storage[2] = static_cast(current + n); + return current; + } + + // Returns the index of the last edge starting on, or before `offset`, with + // `n` containing the relative offset of `offset` inside that edge. + // Requires `offset` < length. + Position IndexOf(size_t offset) const; + + // Returns the index of the last edge starting before `offset`, with `n` + // containing the relative offset of `offset` inside that edge. + // This function is useful to find the edges for some span of bytes ending at + // `offset` (i.e., `n` bytes). For example: + // + // Position pos = IndexBefore(n) + // edges = Edges(begin(), pos.index) // All full edges (may be empty) + // last = Sub(Edge(pos.index), 0, pos.n) // Last partial edge (may be empty) + // + // Requires 0 < `offset` <= length. + Position IndexBefore(size_t offset) const; + + // Identical to the above function except starting from the position `front`. + // This function is equivalent to `IndexBefore(front.n + offset)`, with + // the difference that this function is optimized to start at `front.index`. + Position IndexBefore(Position front, size_t offset) const; + + // Returns the index of the edge directly beyond the edge containing offset + // `offset`, with `n` containing the distance of that edge from `offset`. + // This function is useful for iteratively finding suffix nodes and remaining + // partial bytes in left-most suffix nodes as for example in CopySuffix. + // Requires `offset` < length. + Position IndexBeyond(size_t offset) const; + + // Destruction + static void DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end); + static void DestroyNonLeaf(CordRepBtree* tree, size_t begin, size_t end); + static void DestroyTree(CordRepBtree* tree, size_t begin, size_t end); + static void Delete(CordRepBtree* tree) { delete tree; } + + // Creates a new leaf node containing as much data as possible from `data`. + // The data is added either forwards or reversed depending on `edge_type`. + // Callers must check the length of the returned node to determine if all data + // was copied or not. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template + static CordRepBtree* NewLeaf(absl::string_view data, size_t extra); + + // Creates a raw copy of this Btree node, copying all properties, but + // without adding any references to existing edges. + CordRepBtree* CopyRaw() const; + + // Creates a full copy of this Btree node, adding a reference on all edges. + CordRepBtree* Copy() const; + + // Creates a partial copy of this Btree node, copying all edges up to `end`, + // adding a reference on each copied edge, and sets the length of the newly + // created copy to `new_length`. + CordRepBtree* CopyBeginTo(size_t end, size_t new_length) const; + + // Creates a partial copy of this Btree node, copying all edges starting at + // `begin`, adding a reference on each copied edge, and sets the length of + // the newly created copy to `new_length`. + CordRepBtree* CopyToEndFrom(size_t begin, size_t new_length) const; + + // Returns a tree containing the result of appending `right` to `left`. + static CordRepBtree* MergeTrees(CordRepBtree* left, CordRepBtree* right); + + // Fallback functions for `Create()`, `Append()` and `Prepend()` which + // deal with legacy / non conforming input, i.e.: CONCAT trees. + static CordRepBtree* CreateSlow(CordRep* rep); + static CordRepBtree* AppendSlow(CordRepBtree*, CordRep* rep); + static CordRepBtree* PrependSlow(CordRepBtree*, CordRep* rep); + + // Aligns existing edges to start at index 0, to allow for a new edge to be + // added to the back of the current edges. + inline void AlignBegin(); + + // Aligns existing edges to end at `capacity`, to allow for a new edge to be + // added in front of the current edges. + inline void AlignEnd(); + + // Adds the provided edge to this node. + // Requires this node to have capacity for the edge. Realigns / moves + // existing edges as needed to prepend or append the new edge. + template + inline void Add(CordRep* rep); + + // Adds the provided edges to this node. + // Requires this node to have capacity for the edges. Realigns / moves + // existing edges as needed to prepend or append the new edges. + template + inline void Add(absl::Span); + + // Adds data from `data` to this node until either all data has been consumed, + // or there is no more capacity for additional flat nodes inside this node. + // Requires the current node to be a leaf node, data to be non empty, and the + // current node to have capacity for at least one more data edge. + // Returns any remaining data from `data` that was not added, which is + // depending on the edge type (front / back) either the remaining prefix of + // suffix of the input. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template + absl::string_view AddData(absl::string_view data, size_t extra); + + // Replace the front or back edge with the provided value. + // Adopts a reference on `edge` and unrefs the old edge. + template + inline void SetEdge(CordRep* edge); + + // Returns a partial copy of the current tree containing the first `n` bytes + // of data. `CopyResult` contains both the resulting edge and its height. The + // resulting tree may be less high than the current tree, or even be a single + // matching data edge. For example, if `n == 1`, then the result will be the + // single data edge, and height will be set to -1 (one below the owning leaf + // node). If n == 0, this function returns null. + // Requires `n <= length` + CopyResult CopyPrefix(size_t n); + + // Returns a partial copy of the current tree containing all data starting + // after `offset`. `CopyResult` contains both the resulting edge and its + // height. The resulting tree may be less high than the current tree, or even + // be a single matching data edge. For example, if `n == length - 1`, then the + // result will be a single data edge, and height will be set to -1 (one below + // the owning leaf node). + // Requires `offset < length` + CopyResult CopySuffix(size_t offset); + + // Returns a OpResult value of {this, kSelf} or {Copy(), kCopied} + // depending on the value of `owned`. + inline OpResult ToOpResult(bool owned); + + // Adds `rep` to the specified tree, returning the modified tree. + template + static CordRepBtree* AddCordRep(CordRepBtree* tree, CordRep* rep); + + // Adds `data` to the specified tree, returning the modified tree. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template + static CordRepBtree* AddData(CordRepBtree* tree, absl::string_view data, + size_t extra = 0); + + // Merges `src` into `dst` with `src` being added either before (kFront) or + // after (kBack) `dst`. Requires the height of `dst` to be greater than or + // equal to the height of `src`. + template + static CordRepBtree* Merge(CordRepBtree* dst, CordRepBtree* src); + + // Fallback version of GetAppendBuffer for large trees: GetAppendBuffer() + // implements an inlined version for trees of limited height (3 levels), + // GetAppendBufferSlow implements the logic for large trees. + Span GetAppendBufferSlow(size_t size); + + // `edges_` contains all edges starting from this instance. + // These are explicitly `child` edges only, a cord btree (or any cord tree in + // that respect) does not store `parent` pointers anywhere: multiple trees / + // parents can reference the same shared child edge. The type of these edges + // depends on the height of the node. `Leaf nodes` (height == 0) contain `data + // edges` (external or flat nodes, or sub-strings thereof). All other nodes + // (height > 0) contain pointers to BTREE nodes with a height of `height - 1`. + CordRep* edges_[kMaxCapacity]; + + friend class CordRepBtreeTestPeer; + friend class CordRepBtreeNavigator; +}; + +inline CordRepBtree* CordRep::btree() { + assert(tag == BTREE); + return static_cast(this); +} + +inline const CordRepBtree* CordRep::btree() const { + assert(tag == BTREE); + return static_cast(this); +} + +inline void CordRepBtree::InitInstance(int height, size_t begin, size_t end) { + tag = BTREE; + storage[0] = height; + storage[1] = begin; + storage[2] = end; +} + +inline CordRep* CordRepBtree::Edge(size_t index) const { + assert(index >= begin()); + assert(index < end()); + return edges_[index]; +} + +inline CordRep* CordRepBtree::Edge(EdgeType edge_type) const { + return edges_[edge_type == kFront ? begin() : back()]; +} + +inline absl::Span CordRepBtree::Edges() const { + return {edges_ + begin(), size()}; +} + +inline absl::Span CordRepBtree::Edges(size_t begin, + size_t end) const { + assert(begin <= end); + assert(begin >= this->begin()); + assert(end <= this->end()); + return {edges_ + begin, static_cast(end - begin)}; +} + +inline const char* CordRepBtree::EdgeDataPtr(const CordRep* r) { + assert(IsDataEdge(r)); + size_t offset = 0; + if (r->tag == SUBSTRING) { + offset = r->substring()->start; + r = r->substring()->child; + } + return (r->tag >= FLAT ? r->flat()->Data() : r->external()->base) + offset; +} + +inline absl::string_view CordRepBtree::EdgeData(const CordRep* r) { + return absl::string_view(EdgeDataPtr(r), r->length); +} + +inline absl::string_view CordRepBtree::Data(size_t index) const { + assert(height() == 0); + return EdgeData(Edge(index)); +} + +inline bool CordRepBtree::IsDataEdge(const CordRep* rep) { + // The fast path is that `rep` is an EXTERNAL or FLAT node, making the below + // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL + // check in the slow path the SUBSTRING check to optimize for the hot path. + if (rep->tag == EXTERNAL || rep->tag >= FLAT) return true; + if (rep->tag == SUBSTRING) rep = rep->substring()->child; + return rep->tag == EXTERNAL || rep->tag >= FLAT; +} + +inline CordRepBtree* CordRepBtree::New(int height) { + CordRepBtree* tree = new CordRepBtree; + tree->length = 0; + tree->InitInstance(height); + return tree; +} + +inline CordRepBtree* CordRepBtree::New(CordRep* rep) { + CordRepBtree* tree = new CordRepBtree; + int height = rep->tag == BTREE ? rep->btree()->height() + 1 : 0; + tree->length = rep->length; + tree->InitInstance(height, /*begin=*/0, /*end=*/1); + tree->edges_[0] = rep; + return tree; +} + +inline CordRepBtree* CordRepBtree::New(CordRepBtree* front, + CordRepBtree* back) { + assert(front->height() == back->height()); + CordRepBtree* tree = new CordRepBtree; + tree->length = front->length + back->length; + tree->InitInstance(front->height() + 1, /*begin=*/0, /*end=*/2); + tree->edges_[0] = front; + tree->edges_[1] = back; + return tree; +} + +inline void CordRepBtree::DestroyTree(CordRepBtree* tree, size_t begin, + size_t end) { + if (tree->height() == 0) { + DestroyLeaf(tree, begin, end); + } else { + DestroyNonLeaf(tree, begin, end); + } +} + +inline void CordRepBtree::Destroy(CordRepBtree* tree) { + DestroyTree(tree, tree->begin(), tree->end()); +} + +inline CordRepBtree* CordRepBtree::CopyRaw() const { + auto* tree = static_cast(::operator new(sizeof(CordRepBtree))); + memcpy(static_cast(tree), this, sizeof(CordRepBtree)); + new (&tree->refcount) Refcount; + return tree; +} + +inline CordRepBtree* CordRepBtree::Copy() const { + CordRepBtree* tree = CopyRaw(); + for (CordRep* rep : Edges()) CordRep::Ref(rep); + return tree; +} + +inline CordRepBtree* CordRepBtree::CopyToEndFrom(size_t begin, + size_t new_length) const { + assert(begin >= this->begin()); + assert(begin <= this->end()); + CordRepBtree* tree = CopyRaw(); + tree->length = new_length; + tree->set_begin(begin); + for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); + return tree; +} + +inline CordRepBtree* CordRepBtree::CopyBeginTo(size_t end, + size_t new_length) const { + assert(end <= capacity()); + assert(end >= this->begin()); + CordRepBtree* tree = CopyRaw(); + tree->length = new_length; + tree->set_end(end); + for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); + return tree; +} + +inline void CordRepBtree::AlignBegin() { + // The below code itself does not need to be fast as typically we have + // mono-directional append/prepend calls, and `begin` / `end` are typically + // adjusted no more than once. But we want to avoid potential register clobber + // effects, making the compiler emit register save/store/spills, and minimize + // the size of code. + const size_t delta = begin(); + if (ABSL_PREDICT_FALSE(delta != 0)) { + const size_t new_end = end() - delta; + set_begin(0); + set_end(new_end); + // TODO(mvels): we can write this using 2 loads / 2 stores depending on + // total size for the kMaxCapacity = 6 case. I.e., we can branch (switch) on + // size, and then do overlapping load/store of up to 4 pointers (inlined as + // XMM, YMM or ZMM load/store) and up to 2 pointers (XMM / YMM), which is a) + // compact and b) not clobbering any registers. + ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity); +#ifdef __clang__ +#pragma unroll 1 +#endif + for (size_t i = 0; i < new_end; ++i) { + edges_[i] = edges_[i + delta]; + } + } +} + +inline void CordRepBtree::AlignEnd() { + // See comments in `AlignBegin` for motivation on the hand-rolled for loops. + const size_t delta = capacity() - end(); + if (delta != 0) { + const size_t new_begin = begin() + delta; + const size_t new_end = end() + delta; + set_begin(new_begin); + set_end(new_end); + ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity); +#ifdef __clang__ +#pragma unroll 1 +#endif + for (size_t i = new_end - 1; i >= new_begin; --i) { + edges_[i] = edges_[i - delta]; + } + } +} + +template <> +inline void CordRepBtree::Add(CordRep* rep) { + AlignBegin(); + edges_[fetch_add_end(1)] = rep; +} + +template <> +inline void CordRepBtree::Add( + absl::Span edges) { + AlignBegin(); + size_t new_end = end(); + for (CordRep* edge : edges) edges_[new_end++] = edge; + set_end(new_end); +} + +template <> +inline void CordRepBtree::Add(CordRep* rep) { + AlignEnd(); + edges_[sub_fetch_begin(1)] = rep; +} + +template <> +inline void CordRepBtree::Add( + absl::Span edges) { + AlignEnd(); + size_t new_begin = begin() - edges.size(); + set_begin(new_begin); + for (CordRep* edge : edges) edges_[new_begin++] = edge; +} + +template +inline void CordRepBtree::SetEdge(CordRep* edge) { + const int idx = edge_type == kFront ? begin() : back(); + CordRep::Unref(edges_[idx]); + edges_[idx] = edge; +} + +inline CordRepBtree::OpResult CordRepBtree::ToOpResult(bool owned) { + return owned ? OpResult{this, kSelf} : OpResult{Copy(), kCopied}; +} + +inline CordRepBtree::Position CordRepBtree::IndexOf(size_t offset) const { + assert(offset < length); + size_t index = begin(); + while (offset >= edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBefore(size_t offset) const { + assert(offset > 0); + assert(offset <= length); + size_t index = begin(); + while (offset > edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBefore(Position front, + size_t offset) const { + size_t index = front.index; + offset = offset + front.n; + while (offset > edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBeyond( + const size_t offset) const { + // We need to find the edge which `starting offset` is beyond (>=)`offset`. + // For this we can't use the `offset -= length` logic of IndexOf. Instead, we + // track the offset of the `current edge` in `off`, which we increase as we + // iterate over the edges until we find the matching edge. + size_t off = 0; + size_t index = begin(); + while (offset > off) off += edges_[index++]->length; + return {index, off - offset}; +} + +inline CordRepBtree* CordRepBtree::Create(CordRep* rep) { + if (IsDataEdge(rep)) return New(rep); + return CreateSlow(rep); +} + +inline Span CordRepBtree::GetAppendBuffer(size_t size) { + assert(refcount.IsOne()); + CordRepBtree* tree = this; + const int height = this->height(); + CordRepBtree* n1 = tree; + CordRepBtree* n2 = tree; + CordRepBtree* n3 = tree; + switch (height) { + case 3: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsOne()) return {}; + n2 = tree; + ABSL_FALLTHROUGH_INTENDED; + case 2: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsOne()) return {}; + n1 = tree; + ABSL_FALLTHROUGH_INTENDED; + case 1: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsOne()) return {}; + ABSL_FALLTHROUGH_INTENDED; + case 0: + CordRep* edge = tree->Edge(kBack); + if (!edge->refcount.IsOne()) return {}; + if (edge->tag < FLAT) return {}; + size_t avail = edge->flat()->Capacity() - edge->length; + if (avail == 0) return {}; + size_t delta = (std::min)(size, avail); + Span span = {edge->flat()->Data() + edge->length, delta}; + edge->length += delta; + switch (height) { + case 3: + n3->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 2: + n2->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 1: + n1->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 0: + tree->length += delta; + return span; + } + break; + } + return GetAppendBufferSlow(size); +} + +extern template CordRepBtree* CordRepBtree::AddCordRep( + CordRepBtree* tree, CordRep* rep); + +extern template CordRepBtree* CordRepBtree::AddCordRep( + CordRepBtree* tree, CordRep* rep); + +inline CordRepBtree* CordRepBtree::Append(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) { + return CordRepBtree::AddCordRep(tree, rep); + } + return AppendSlow(tree, rep); +} + +inline CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) { + return CordRepBtree::AddCordRep(tree, rep); + } + return PrependSlow(tree, rep); +} + +#ifdef NDEBUG + +inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree) { + return tree; +} + +inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree) { + return tree; +} + +#endif + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc new file mode 100644 index 00000000..7a0b7811 --- /dev/null +++ b/absl/strings/internal/cord_rep_btree_test.cc @@ -0,0 +1,1352 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_btree.h" + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_test_util.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +class CordRepBtreeTestPeer { + public: + static void SetEdge(CordRepBtree* node, size_t idx, CordRep* edge) { + node->edges_[idx] = edge; + } + static void AddEdge(CordRepBtree* node, CordRep* edge) { + node->edges_[node->fetch_add_end(1)] = edge; + } +}; + +namespace { + +using ::absl::cordrep_testing::AutoUnref; +using ::absl::cordrep_testing::CordToString; +using ::absl::cordrep_testing::CreateFlatsFromString; +using ::absl::cordrep_testing::CreateRandomString; +using ::absl::cordrep_testing::MakeConcat; +using ::absl::cordrep_testing::MakeExternal; +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::MakeSubstring; +using ::testing::_; +using ::testing::AllOf; +using ::testing::AnyOf; +using ::testing::Conditional; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::Not; +using ::testing::SizeIs; +using ::testing::TypedEq; + +MATCHER_P(EqFlatHolding, data, "Equals flat holding data") { + if (arg->tag < FLAT) { + *result_listener << "Expected FLAT, got tag " << static_cast(arg->tag); + return false; + } + std::string actual = CordToString(arg); + if (actual != data) { + *result_listener << "Expected flat holding \"" << data + << "\", got flat holding \"" << actual << "\""; + return false; + } + return true; +} + +MATCHER_P(IsNode, height, absl::StrCat("Is a valid node of height ", height)) { + if (arg == nullptr) { + *result_listener << "Expected NODE, got nullptr"; + return false; + } + if (arg->tag != BTREE) { + *result_listener << "Expected NODE, got " << static_cast(arg->tag); + return false; + } + if (!CordRepBtree::IsValid(arg->btree())) { + CordRepBtree::Dump(arg->btree(), "Expected valid NODE, got:", false, + *result_listener->stream()); + return false; + } + if (arg->btree()->height() != height) { + *result_listener << "Expected NODE of height " << height << ", got " + << arg->btree()->height(); + return false; + } + return true; +} + +MATCHER_P2(IsSubstring, start, length, + absl::StrCat("Is a substring(start = ", start, ", length = ", length, + ")")) { + if (arg == nullptr) { + *result_listener << "Expected substring, got nullptr"; + return false; + } + if (arg->tag != SUBSTRING) { + *result_listener << "Expected SUBSTRING, got " + << static_cast(arg->tag); + return false; + } + const CordRepSubstring* const substr = arg->substring(); + if (substr->start != start || substr->length != length) { + *result_listener << "Expected substring(" << start << ", " << length + << "), got substring(" << substr->start << ", " + << substr->length << ")"; + return false; + } + return true; +} + +// DataConsumer is a simple helper class used by tests to 'consume' string +// fragments from the provided input in forward or backward direction. +class DataConsumer { + public: + // Starts consumption of `data`. Caller must make sure `data` outlives this + // instance. Consumes data starting at the front if `forward` is true, else + // consumes data from the back. + DataConsumer(absl::string_view data, bool forward) + : data_(data), forward_(forward) {} + + // Return the next `n` bytes from referenced data. + absl::string_view Next(size_t n) { + assert(n <= data_.size() - consumed_); + consumed_ += n; + return data_.substr(forward_ ? consumed_ - n : data_.size() - consumed_, n); + } + + // Returns all data consumed so far. + absl::string_view Consumed() const { + return forward_ ? data_.substr(0, consumed_) + : data_.substr(data_.size() - consumed_); + } + + private: + absl::string_view data_; + size_t consumed_ = 0; + bool forward_; +}; + +// BtreeAdd returns either CordRepBtree::Append or CordRepBtree::Prepend. +CordRepBtree* BtreeAdd(CordRepBtree* node, bool append, + absl::string_view data) { + return append ? CordRepBtree::Append(node, data) + : CordRepBtree::Prepend(node, data); +} + +// Recursively collects all leaf edges from `tree` and appends them to `edges`. +void GetLeafEdges(const CordRepBtree* tree, std::vector& edges) { + if (tree->height() == 0) { + for (CordRep* edge : tree->Edges()) { + edges.push_back(edge); + } + } else { + for (CordRep* edge : tree->Edges()) { + GetLeafEdges(edge->btree(), edges); + } + } +} + +// Recursively collects and returns all leaf edges from `tree`. +std::vector GetLeafEdges(const CordRepBtree* tree) { + std::vector edges; + GetLeafEdges(tree, edges); + return edges; +} + +// Creates a flat containing the hexadecimal value of `i` zero padded +// to at least 4 digits prefixed with "0x", e.g.: "0x04AC". +CordRepFlat* MakeHexFlat(size_t i) { + return MakeFlat(absl::StrCat("0x", absl::Hex(i, absl::kZeroPad4))); +} + +CordRepBtree* MakeLeaf(size_t size = CordRepBtree::kMaxCapacity) { + assert(size <= CordRepBtree::kMaxCapacity); + CordRepBtree* leaf = CordRepBtree::Create(MakeHexFlat(0)); + for (size_t i = 1; i < size; ++i) { + leaf = CordRepBtree::Append(leaf, MakeHexFlat(i)); + } + return leaf; +} + +CordRepBtree* MakeTree(size_t size, bool append = true) { + CordRepBtree* tree = CordRepBtree::Create(MakeHexFlat(0)); + for (size_t i = 1; i < size; ++i) { + tree = append ? CordRepBtree::Append(tree, MakeHexFlat(i)) + : CordRepBtree::Prepend(tree, MakeHexFlat(i)); + } + return tree; +} + +CordRepBtree* CreateTree(absl::string_view data, size_t chunk_size) { + std::vector flats = CreateFlatsFromString(data, chunk_size); + auto it = flats.begin(); + CordRepBtree* tree = CordRepBtree::Create(*it); + while (++it != flats.end()) tree = CordRepBtree::Append(tree, *it); + return tree; +} + +CordRepBtree* CreateTreeReverse(absl::string_view data, size_t chunk_size) { + std::vector flats = CreateFlatsFromString(data, chunk_size); + auto rit = flats.rbegin(); + CordRepBtree* tree = CordRepBtree::Create(*rit); + while (++rit != flats.rend()) tree = CordRepBtree::Prepend(tree, *rit); + return tree; +} + +class CordRepBtreeTest : public testing::TestWithParam { + public: + bool shared() const { return GetParam(); } + + static std::string ToString(testing::TestParamInfo param) { + return param.param ? "Shared" : "Private"; + } +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordRepBtreeTest, testing::Bool(), + CordRepBtreeTest::ToString); + +class CordRepBtreeHeightTest : public testing::TestWithParam { + public: + int height() const { return GetParam(); } + + static std::string ToString(testing::TestParamInfo param) { + return absl::StrCat(param.param); + } +}; + +INSTANTIATE_TEST_SUITE_P(WithHeights, CordRepBtreeHeightTest, + testing::Range(0, CordRepBtree::kMaxHeight), + CordRepBtreeHeightTest::ToString); + +using TwoBools = testing::tuple; + +class CordRepBtreeDualTest : public testing::TestWithParam { + public: + bool first_shared() const { return std::get<0>(GetParam()); } + bool second_shared() const { return std::get<1>(GetParam()); } + + static std::string ToString(testing::TestParamInfo param) { + if (std::get<0>(param.param)) { + return std::get<1>(param.param) ? "BothShared" : "FirstShared"; + } + return std::get<1>(param.param) ? "SecondShared" : "Private"; + } +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordRepBtreeDualTest, + testing::Combine(testing::Bool(), testing::Bool()), + CordRepBtreeDualTest::ToString); + +TEST(CordRepBtreeTest, SizeIsMultipleOf64) { + // Only enforce for fully 64-bit platforms. + if (sizeof(size_t) == 8 && sizeof(void*) == 8) { + EXPECT_THAT(sizeof(CordRepBtree) % 64, Eq(0)) << "Should be multiple of 64"; + } +} + +TEST(CordRepBtreeTest, NewDestroyEmptyTree) { + auto* tree = CordRepBtree::New(); + EXPECT_THAT(tree->size(), Eq(0)); + EXPECT_THAT(tree->height(), Eq(0)); + EXPECT_THAT(tree->Edges(), ElementsAre()); + CordRepBtree::Destroy(tree); +} + +TEST(CordRepBtreeTest, NewDestroyEmptyTreeAtHeight) { + auto* tree = CordRepBtree::New(3); + EXPECT_THAT(tree->size(), Eq(0)); + EXPECT_THAT(tree->height(), Eq(3)); + EXPECT_THAT(tree->Edges(), ElementsAre()); + CordRepBtree::Destroy(tree); +} + +TEST(CordRepBtreeTest, Btree) { + CordRep* rep = CordRepBtree::New(); + EXPECT_THAT(rep->btree(), Eq(rep)); + EXPECT_THAT(static_cast(rep)->btree(), Eq(rep)); + CordRep::Unref(rep); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + rep = MakeFlat("Hello world"); + EXPECT_DEATH(rep->btree(), ".*"); + EXPECT_DEATH(static_cast(rep)->btree(), ".*"); + CordRep::Unref(rep); +#endif +} + +TEST(CordRepBtreeTest, EdgeData) { + CordRepFlat* flat = MakeFlat("Hello world"); + CordRepExternal* external = MakeExternal("Hello external"); + CordRep* substr1 = MakeSubstring(1, 6, CordRep::Ref(flat)); + CordRep* substr2 = MakeSubstring(1, 6, CordRep::Ref(external)); + CordRep* concat = MakeConcat(CordRep::Ref(flat), CordRep::Ref(external)); + CordRep* bad_substr = MakeSubstring(1, 2, CordRep::Ref(substr1)); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(flat)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(flat), + TypedEq(flat->Data())); + EXPECT_THAT(CordRepBtree::EdgeData(flat), Eq("Hello world")); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(external)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(external), + TypedEq(external->base)); + EXPECT_THAT(CordRepBtree::EdgeData(external), Eq("Hello external")); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(substr1)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr1), + TypedEq(flat->Data() + 1)); + EXPECT_THAT(CordRepBtree::EdgeData(substr1), Eq("ello w")); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(substr2)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr2), + TypedEq(external->base + 1)); + EXPECT_THAT(CordRepBtree::EdgeData(substr2), Eq("ello e")); + + EXPECT_FALSE(CordRepBtree::IsDataEdge(concat)); + EXPECT_FALSE(CordRepBtree::IsDataEdge(bad_substr)); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH(CordRepBtree::EdgeData(concat), ".*"); + EXPECT_DEATH(CordRepBtree::EdgeDataPtr(concat), ".*"); + EXPECT_DEATH(CordRepBtree::EdgeData(bad_substr), ".*"); + EXPECT_DEATH(CordRepBtree::EdgeDataPtr(bad_substr), ".*"); +#endif + + CordRep::Unref(bad_substr); + CordRep::Unref(concat); + CordRep::Unref(substr2); + CordRep::Unref(substr1); + CordRep::Unref(external); + CordRep::Unref(flat); +} + +TEST(CordRepBtreeTest, CreateUnrefLeaf) { + auto* flat = MakeFlat("a"); + auto* leaf = CordRepBtree::Create(flat); + EXPECT_THAT(leaf->size(), Eq(1)); + EXPECT_THAT(leaf->height(), Eq(0)); + EXPECT_THAT(leaf->Edges(), ElementsAre(flat)); + CordRepBtree::Unref(leaf); +} + +TEST(CordRepBtreeTest, NewUnrefNode) { + auto* leaf = CordRepBtree::Create(MakeFlat("a")); + CordRepBtree* tree = CordRepBtree::New(leaf); + EXPECT_THAT(tree->size(), Eq(1)); + EXPECT_THAT(tree->height(), Eq(1)); + EXPECT_THAT(tree->Edges(), ElementsAre(leaf)); + CordRepBtree::Unref(tree); +} + +TEST_P(CordRepBtreeTest, AppendToLeafToCapacity) { + AutoUnref refs; + std::vector flats; + flats.push_back(MakeHexFlat(0)); + auto* leaf = CordRepBtree::Create(flats.back()); + + for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { + refs.RefIf(shared(), leaf); + flats.push_back(MakeHexFlat(i)); + auto* result = CordRepBtree::Append(leaf, flats.back()); + EXPECT_THAT(result->height(), Eq(0)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(result->Edges(), ElementsAreArray(flats)); + leaf = result; + } + CordRep::Unref(leaf); +} + +TEST_P(CordRepBtreeTest, PrependToLeafToCapacity) { + AutoUnref refs; + std::deque flats; + flats.push_front(MakeHexFlat(0)); + auto* leaf = CordRepBtree::Create(flats.front()); + + for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { + refs.RefIf(shared(), leaf); + flats.push_front(MakeHexFlat(i)); + auto* result = CordRepBtree::Prepend(leaf, flats.front()); + EXPECT_THAT(result->height(), Eq(0)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(result->Edges(), ElementsAreArray(flats)); + leaf = result; + } + CordRep::Unref(leaf); +} + +// This test specifically aims at code aligning data at either the front or the +// back of the contained `edges[]` array, alternating Append and Prepend will +// move `begin()` and `end()` values as needed for each added value. +TEST_P(CordRepBtreeTest, AppendPrependToLeafToCapacity) { + AutoUnref refs; + std::deque flats; + flats.push_front(MakeHexFlat(0)); + auto* leaf = CordRepBtree::Create(flats.front()); + + for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { + refs.RefIf(shared(), leaf); + CordRepBtree* result; + if (i % 2 != 0) { + flats.push_front(MakeHexFlat(i)); + result = CordRepBtree::Prepend(leaf, flats.front()); + } else { + flats.push_back(MakeHexFlat(i)); + result = CordRepBtree::Append(leaf, flats.back()); + } + EXPECT_THAT(result->height(), Eq(0)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(result->Edges(), ElementsAreArray(flats)); + leaf = result; + } + CordRep::Unref(leaf); +} + +TEST_P(CordRepBtreeTest, AppendToLeafBeyondCapacity) { + AutoUnref refs; + auto* leaf = MakeLeaf(); + refs.RefIf(shared(), leaf); + CordRep* flat = MakeFlat("abc"); + auto* result = CordRepBtree::Append(leaf, flat); + ASSERT_THAT(result, IsNode(1)); + EXPECT_THAT(result, Ne(leaf)); + absl::Span edges = result->Edges(); + ASSERT_THAT(edges, ElementsAre(leaf, IsNode(0))); + EXPECT_THAT(edges[1]->btree()->Edges(), ElementsAre(flat)); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, PrependToLeafBeyondCapacity) { + AutoUnref refs; + auto* leaf = MakeLeaf(); + refs.RefIf(shared(), leaf); + CordRep* flat = MakeFlat("abc"); + auto* result = CordRepBtree::Prepend(leaf, flat); + ASSERT_THAT(result, IsNode(1)); + EXPECT_THAT(result, Ne(leaf)); + absl::Span edges = result->Edges(); + ASSERT_THAT(edges, ElementsAre(IsNode(0), leaf)); + EXPECT_THAT(edges[0]->btree()->Edges(), ElementsAre(flat)); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, AppendToTreeOneDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::vector flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap; ++i) { + flats.push_back(MakeHexFlat(i)); + tree = CordRepBtree::Append(tree, flats.back()); + } + ASSERT_THAT(tree, IsNode(1)); + + for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref leaf node once every 4 iterations, which should not have an + // observable effect other than that the leaf itself is copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 4 == 0, tree->Edges().back()); + + flats.push_back(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Append(tree, flats.back()); + ASSERT_THAT(result, IsNode(1)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, AppendToTreeTwoDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::vector flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap * max_cap; ++i) { + flats.push_back(MakeHexFlat(i)); + tree = CordRepBtree::Append(tree, flats.back()); + } + ASSERT_THAT(tree, IsNode(2)); + for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref child node once every 16 iterations, and leaf node every 4 + // iterrations which which should not have an observable effect other than + // the node and/or the leaf below it being copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 16 == 0, tree->Edges().back()); + refs.RefIf(i % 4 == 0, tree->Edges().back()->btree()->Edges().back()); + + flats.push_back(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Append(tree, flats.back()); + ASSERT_THAT(result, IsNode(2)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, PrependToTreeOneDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::deque flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap; ++i) { + flats.push_front(MakeHexFlat(i)); + tree = CordRepBtree::Prepend(tree, flats.front()); + } + ASSERT_THAT(tree, IsNode(1)); + + for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref leaf node once every 4 iterations which should not have an observable + // effect other than than the leaf itself is copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 4 == 0, tree->Edges().back()); + + flats.push_front(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Prepend(tree, flats.front()); + ASSERT_THAT(result, IsNode(1)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, PrependToTreeTwoDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::deque flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap * max_cap; ++i) { + flats.push_front(MakeHexFlat(i)); + tree = CordRepBtree::Prepend(tree, flats.front()); + } + ASSERT_THAT(tree, IsNode(2)); + for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref child node once every 16 iterations, and leaf node every 4 + // iterrations which which should not have an observable effect other than + // the node and/or the leaf below it being copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 16 == 0, tree->Edges().back()); + refs.RefIf(i % 4 == 0, tree->Edges().back()->btree()->Edges().back()); + + flats.push_front(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Prepend(tree, flats.front()); + ASSERT_THAT(result, IsNode(2)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeDualTest, MergeLeafsNotExceedingCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector flats; + + // Build `left` side leaf appending all contained flats to `flats` + CordRepBtree* left = MakeLeaf(3); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side leaf appending all contained flats to `flats` + CordRepBtree* right = MakeLeaf(2); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(0)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(tree->Edges(), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeLeafsExceedingCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + + // Build `left` side tree appending all contained flats to `flats` + CordRepBtree* left = MakeLeaf(CordRepBtree::kMaxCapacity - 2); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all contained flats to `flats` + CordRepBtree* right = MakeLeaf(CordRepBtree::kMaxCapacity - 1); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), ElementsAre(left, right)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeEqualHeightTrees) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector flats; + + // Build `left` side tree appending all contained flats to `flats` + CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 3); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all contained flats to `flats` + CordRepBtree* right = MakeTree(CordRepBtree::kMaxCapacity * 2); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), SizeIs(5)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeLeafWithTreeNotExceedingLeafCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector flats; + + // Build `left` side tree appending all added flats to `flats` + CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 2 + 2); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all added flats to `flats` + CordRepBtree* right = MakeTree(3); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), SizeIs(3)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeLeafWithTreeExceedingLeafCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector flats; + + // Build `left` side tree appending all added flats to `flats` + CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 3 - 2); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all added flats to `flats` + CordRepBtree* right = MakeTree(3); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), SizeIs(4)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +void RefEdgesAt(size_t depth, AutoUnref& refs, CordRepBtree* tree) { + absl::Span edges = tree->Edges(); + if (depth == 0) { + refs.Ref(edges.front()); + refs.Ref(edges.back()); + } else { + assert(tree->height() > 0); + RefEdgesAt(depth - 1, refs, edges.front()->btree()); + RefEdgesAt(depth - 1, refs, edges.back()->btree()); + } +} + +TEST(CordRepBtreeTest, MergeFuzzTest) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + std::minstd_rand rnd; + std::uniform_int_distribution coin_flip(0, 1); + std::uniform_int_distribution dice_throw(1, 6); + + auto random_leaf_count = [&]() { + std::uniform_int_distribution dist_height(0, 3); + std::uniform_int_distribution dist_leaf(0, max_cap - 1); + const size_t height = dist_height(rnd); + return (height ? pow(max_cap, height) : 0) + dist_leaf(rnd); + }; + + for (int i = 0; i < 10000; ++i) { + AutoUnref refs; + std::vector flats; + + CordRepBtree* left = MakeTree(random_leaf_count(), coin_flip(rnd)); + GetLeafEdges(left, flats); + if (dice_throw(rnd) == 1) { + std::uniform_int_distribution dist(0, left->height()); + RefEdgesAt(dist(rnd), refs, left); + } + + CordRepBtree* right = MakeTree(random_leaf_count(), coin_flip(rnd)); + GetLeafEdges(right, flats); + if (dice_throw(rnd) == 1) { + std::uniform_int_distribution dist(0, right->height()); + RefEdgesAt(dist(rnd), refs, right); + } + + CordRepBtree* tree = CordRepBtree::Append(left, right); + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST(CordRepBtreeTest, SubTree) { + // Create tree of at least 2 levels high + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * 2; + const std::string data = CreateRandomString(n * 3); + std::vector flats; + for (absl::string_view s = data; !s.empty(); s.remove_prefix(3)) { + flats.push_back(MakeFlat(s.substr(0, 3))); + } + CordRepBtree* node = CordRepBtree::Create(CordRep::Ref(flats[0])); + for (size_t i = 1; i < flats.size(); ++i) { + node = CordRepBtree::Append(node, CordRep::Ref(flats[i])); + } + + for (int offset = 0; offset < data.length(); ++offset) { + for (int length = 1; length <= data.length() - offset; ++length) { + CordRep* rep = node->SubTree(offset, length); + EXPECT_THAT(CordToString(rep), Eq(data.substr(offset, length))); + CordRep::Unref(rep); + } + } + CordRepBtree::Unref(node); + for (CordRep* rep : flats) { + CordRep::Unref(rep); + } +} + +TEST(CordRepBtreeTest, SubTreeOnExistingSubstring) { + // This test verifies that a SubTree call on a pre-existing (large) substring + // adjusts the existing substring if not shared, and else rewrites the + // existing substring. + AutoUnref refs; + std::string data = CreateRandomString(1000); + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc")); + CordRep* flat = MakeFlat(data); + leaf = CordRepBtree::Append(leaf, flat); + + // Setup tree containing substring. + CordRep* result = leaf->SubTree(0, 3 + 990); + ASSERT_THAT(result->tag, Eq(BTREE)); + CordRep::Unref(leaf); + leaf = result->btree(); + ASSERT_THAT(leaf->Edges(), ElementsAre(_, IsSubstring(0, 990))); + EXPECT_THAT(leaf->Edges()[1]->substring()->child, Eq(flat)); + + // Verify substring of substring. + result = leaf->SubTree(3 + 5, 970); + ASSERT_THAT(result, IsSubstring(5, 970)); + EXPECT_THAT(result->substring()->child, Eq(flat)); + CordRep::Unref(result); + + CordRep::Unref(leaf); +} + +TEST_P(CordRepBtreeTest, AddDataToLeaf) { + const size_t n = CordRepBtree::kMaxCapacity; + const std::string data = CreateRandomString(n * 3); + + for (bool append : {true, false}) { + AutoUnref refs; + DataConsumer consumer(data, append); + SCOPED_TRACE(append ? "Append" : "Prepend"); + + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat(consumer.Next(3))); + for (size_t i = 1; i < n; ++i) { + refs.RefIf(shared(), leaf); + CordRepBtree* result = BtreeAdd(leaf, append, consumer.Next(3)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(CordToString(result), Eq(consumer.Consumed())); + leaf = result; + } + CordRep::Unref(leaf); + } +} + +TEST_P(CordRepBtreeTest, AppendDataToTree) { + AutoUnref refs; + size_t n = CordRepBtree::kMaxCapacity + CordRepBtree::kMaxCapacity / 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = refs.RefIf(shared(), CreateTree(data, 3)); + CordRepBtree* leaf0 = tree->Edges()[0]->btree(); + CordRepBtree* leaf1 = tree->Edges()[1]->btree(); + CordRepBtree* result = CordRepBtree::Append(tree, "123456789"); + EXPECT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + EXPECT_THAT(result->Edges(), + ElementsAre(leaf0, Conditional(shared(), Ne(leaf1), Eq(leaf1)))); + EXPECT_THAT(CordToString(result), Eq(data + "123456789")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, PrependDataToTree) { + AutoUnref refs; + size_t n = CordRepBtree::kMaxCapacity + CordRepBtree::kMaxCapacity / 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = refs.RefIf(shared(), CreateTreeReverse(data, 3)); + CordRepBtree* leaf0 = tree->Edges()[0]->btree(); + CordRepBtree* leaf1 = tree->Edges()[1]->btree(); + CordRepBtree* result = CordRepBtree::Prepend(tree, "123456789"); + EXPECT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + EXPECT_THAT(result->Edges(), + ElementsAre(Conditional(shared(), Ne(leaf0), Eq(leaf0)), leaf1)); + EXPECT_THAT(CordToString(result), Eq("123456789" + data)); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, AddDataToTreeThreeLevelsDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * max_cap; + const std::string data = CreateRandomString(n * 3); + + for (bool append : {true, false}) { + AutoUnref refs; + DataConsumer consumer(data, append); + SCOPED_TRACE(append ? "Append" : "Prepend"); + + // Fill leaf + CordRepBtree* tree = CordRepBtree::Create(MakeFlat(consumer.Next(3))); + for (size_t i = 1; i < max_cap; ++i) { + tree = BtreeAdd(tree, append, consumer.Next(3)); + } + ASSERT_THAT(CordToString(tree), Eq(consumer.Consumed())); + + // Fill to maximum at one deep + refs.RefIf(shared(), tree); + CordRepBtree* result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, IsNode(1)); + ASSERT_THAT(result, Ne(tree)); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) { + refs.RefIf(shared(), tree); + result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + } + + // Fill to maximum at two deep + refs.RefIf(shared(), tree); + result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, IsNode(2)); + ASSERT_THAT(result, Ne(tree)); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; + ++i) { + refs.RefIf(shared(), tree); + result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + } + + CordRep::Unref(tree); + } +} + +TEST_P(CordRepBtreeTest, AddLargeDataToLeaf) { + const size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * max_cap * 3 + 2; + const std::string data = CreateRandomString(n * kMaxFlatLength); + + for (bool append : {true, false}) { + AutoUnref refs; + SCOPED_TRACE(append ? "Append" : "Prepend"); + + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc")); + refs.RefIf(shared(), leaf); + CordRepBtree* result = BtreeAdd(leaf, append, data); + EXPECT_THAT(CordToString(result), Eq(append ? "abc" + data : data + "abc")); + CordRep::Unref(result); + } +} + +TEST_P(CordRepBtreeDualTest, CreateFromConcat) { + AutoUnref refs; + CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; + auto* left = MakeConcat(flats[0], flats[1]); + auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); + auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); + CordRepBtree* result = CordRepBtree::Create(concat); + ASSERT_TRUE(CordRepBtree::IsValid(result)); + EXPECT_THAT(result->length, Eq(26)); + EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeDualTest, AppendConcat) { + AutoUnref refs; + CordRep* flats[] = {MakeFlat("defgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; + auto* left = MakeConcat(flats[0], flats[1]); + auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); + auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); + CordRepBtree* result = CordRepBtree::Create(MakeFlat("abc")); + result = CordRepBtree::Append(result, concat); + ASSERT_TRUE(CordRepBtree::IsValid(result)); + EXPECT_THAT(result->length, Eq(26)); + EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeDualTest, PrependConcat) { + AutoUnref refs; + CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wx")}; + auto* left = MakeConcat(flats[0], flats[1]); + auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); + auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); + CordRepBtree* result = CordRepBtree::Create(MakeFlat("yz")); + result = CordRepBtree::Prepend(result, concat); + ASSERT_TRUE(CordRepBtree::IsValid(result)); + EXPECT_THAT(result->length, Eq(26)); + EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, CreateFromTreeReturnsTree) { + AutoUnref refs; + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("Hello world")); + refs.RefIf(shared(), leaf); + CordRepBtree* result = CordRepBtree::Create(leaf); + EXPECT_THAT(result, Eq(leaf)); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, ExceedMaxHeight) { + AutoUnref refs; + CordRepBtree* tree = MakeLeaf(); + for (int h = 1; h <= CordRepBtree::kMaxHeight; ++h) { + CordRepBtree* newtree = CordRepBtree::New(tree); + for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { + newtree = CordRepBtree::Append(newtree, CordRep::Ref(tree)); + } + tree = newtree; + } + refs.RefIf(shared(), tree); +#if defined(GTEST_HAS_DEATH_TEST) + EXPECT_DEATH(tree = CordRepBtree::Append(tree, MakeFlat("Boom")), ".*"); +#endif + CordRep::Unref(tree); +} + +TEST(CordRepBtreeTest, GetCharacter) { + size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = CreateTree(data, 3); + // Add a substring node for good measure. + tree = tree->Append(tree, MakeSubstring(4, 5, MakeFlat("abcdefghijklm"))); + data += "efghi"; + for (size_t i = 0; i < data.length(); ++i) { + ASSERT_THAT(tree->GetCharacter(i), Eq(data[i])); + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, IsFlatSingleFlat) { + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("Hello world")); + + absl::string_view fragment; + EXPECT_TRUE(leaf->IsFlat(nullptr)); + EXPECT_TRUE(leaf->IsFlat(&fragment)); + EXPECT_THAT(fragment, Eq("Hello world")); + fragment = ""; + EXPECT_TRUE(leaf->IsFlat(0, 11, nullptr)); + EXPECT_TRUE(leaf->IsFlat(0, 11, &fragment)); + EXPECT_THAT(fragment, Eq("Hello world")); + + // Arbitrary ranges must check true as well. + EXPECT_TRUE(leaf->IsFlat(1, 4, &fragment)); + EXPECT_THAT(fragment, Eq("ello")); + EXPECT_TRUE(leaf->IsFlat(6, 5, &fragment)); + EXPECT_THAT(fragment, Eq("world")); + + CordRep::Unref(leaf); +} + +TEST(CordRepBtreeTest, IsFlatMultiFlat) { + size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = CreateTree(data, 3); + // Add substring nodes for good measure. + tree = tree->Append(tree, MakeSubstring(4, 3, MakeFlat("abcdefghijklm"))); + tree = tree->Append(tree, MakeSubstring(8, 3, MakeFlat("abcdefghijklm"))); + data += "efgijk"; + + EXPECT_FALSE(tree->IsFlat(nullptr)); + absl::string_view fragment = "Can't touch this"; + EXPECT_FALSE(tree->IsFlat(&fragment)); + EXPECT_THAT(fragment, Eq("Can't touch this")); + + for (size_t offset = 0; offset < data.size(); offset += 3) { + EXPECT_TRUE(tree->IsFlat(offset, 3, nullptr)); + EXPECT_TRUE(tree->IsFlat(offset, 3, &fragment)); + EXPECT_THAT(fragment, Eq(data.substr(offset, 3))); + + fragment = "Can't touch this"; + if (offset > 0) { + EXPECT_FALSE(tree->IsFlat(offset - 1, 4, nullptr)); + EXPECT_FALSE(tree->IsFlat(offset - 1, 4, &fragment)); + EXPECT_THAT(fragment, Eq("Can't touch this")); + } + if (offset < data.size() - 4) { + EXPECT_FALSE(tree->IsFlat(offset, 4, nullptr)); + EXPECT_FALSE(tree->IsFlat(offset, 4, &fragment)); + EXPECT_THAT(fragment, Eq("Can't touch this")); + } + } + + CordRep::Unref(tree); +} + +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferNotPrivate) { + CordRepBtree* tree = CordRepBtree::Create(MakeExternal("Foo")); + CordRepBtree::Ref(tree); + EXPECT_DEATH(tree->GetAppendBuffer(1), ".*"); + CordRepBtree::Unref(tree); + CordRepBtree::Unref(tree); +} + +#endif // defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferNotFlat) { + CordRepBtree* tree = CordRepBtree::Create(MakeExternal("Foo")); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatNotPrivate) { + CordRepFlat* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(CordRep::Ref(flat)); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); + CordRep::Unref(flat); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferTreeNotPrivate) { + if (height() == 0) return; + AutoUnref refs; + CordRepFlat* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(CordRep::Ref(flat)); + for (int i = 1; i <= height(); ++i) { + if (i == (height() + 1) / 2) refs.Ref(tree); + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); + CordRep::Unref(flat); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatNoCapacity) { + CordRepFlat* flat = MakeFlat("abc"); + flat->length = flat->Capacity(); + CordRepBtree* tree = CordRepBtree::Create(flat); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatWithCapacity) { + CordRepFlat* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(flat); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + absl::Span span = tree->GetAppendBuffer(2); + EXPECT_THAT(span, SizeIs(2)); + EXPECT_THAT(span.data(), TypedEq(flat->Data() + 3)); + EXPECT_THAT(tree->length, Eq(5)); + + size_t avail = flat->Capacity() - 5; + span = tree->GetAppendBuffer(avail + 100); + EXPECT_THAT(span, SizeIs(avail)); + EXPECT_THAT(span.data(), TypedEq(flat->Data() + 5)); + EXPECT_THAT(tree->length, Eq(5 + avail)); + + CordRepBtree::Unref(tree); +} + +TEST(CordRepBtreeTest, Dump) { + // Handles nullptr + std::stringstream ss; + CordRepBtree::Dump(nullptr, ss); + CordRepBtree::Dump(nullptr, "Once upon a label", ss); + CordRepBtree::Dump(nullptr, "Once upon a label", false, ss); + CordRepBtree::Dump(nullptr, "Once upon a label", true, ss); + + // Cover legal edges + CordRepFlat* flat = MakeFlat("Hello world"); + CordRepExternal* external = MakeExternal("Hello external"); + CordRep* substr_flat = MakeSubstring(1, 6, CordRep::Ref(flat)); + CordRep* substr_external = MakeSubstring(2, 7, CordRep::Ref(external)); + + // Build tree + CordRepBtree* tree = CordRepBtree::Create(flat); + tree = CordRepBtree::Append(tree, external); + tree = CordRepBtree::Append(tree, substr_flat); + tree = CordRepBtree::Append(tree, substr_external); + + // Repeat until we have a tree + while (tree->height() == 0) { + tree = CordRepBtree::Append(tree, CordRep::Ref(flat)); + tree = CordRepBtree::Append(tree, CordRep::Ref(external)); + tree = CordRepBtree::Append(tree, CordRep::Ref(substr_flat)); + tree = CordRepBtree::Append(tree, CordRep::Ref(substr_external)); + } + + for (int api = 0; api <= 3; ++api) { + absl::string_view api_scope; + std::stringstream ss; + switch (api) { + case 0: + api_scope = "Bare"; + CordRepBtree::Dump(tree, ss); + break; + case 1: + api_scope = "Label only"; + CordRepBtree::Dump(tree, "Once upon a label", ss); + break; + case 2: + api_scope = "Label no content"; + CordRepBtree::Dump(tree, "Once upon a label", false, ss); + break; + default: + api_scope = "Label and content"; + CordRepBtree::Dump(tree, "Once upon a label", true, ss); + break; + } + SCOPED_TRACE(api_scope); + std::string str = ss.str(); + + // Contains Node(depth) / Leaf and private / shared indicators + EXPECT_THAT(str, AllOf(HasSubstr("Node(1)"), HasSubstr("Leaf"), + HasSubstr("Private"), HasSubstr("Shared"))); + + // Contains length and start offset of all data edges + EXPECT_THAT(str, AllOf(HasSubstr("len = 11"), HasSubstr("len = 14"), + HasSubstr("len = 6"), HasSubstr("len = 7"), + HasSubstr("start = 1"), HasSubstr("start = 2"))); + + // Contains address of all data edges + EXPECT_THAT( + str, AllOf(HasSubstr(absl::StrCat("0x", absl::Hex(flat))), + HasSubstr(absl::StrCat("0x", absl::Hex(external))), + HasSubstr(absl::StrCat("0x", absl::Hex(substr_flat))), + HasSubstr(absl::StrCat("0x", absl::Hex(substr_external))))); + + if (api != 0) { + // Contains label + EXPECT_THAT(str, HasSubstr("Once upon a label")); + } + + if (api != 3) { + // Does not contain contents + EXPECT_THAT(str, Not(AnyOf((HasSubstr("data = \"Hello world\""), + HasSubstr("data = \"Hello external\""), + HasSubstr("data = \"ello w\""), + HasSubstr("data = \"llo ext\""))))); + } else { + // Contains contents + EXPECT_THAT(str, AllOf((HasSubstr("data = \"Hello world\""), + HasSubstr("data = \"Hello external\""), + HasSubstr("data = \"ello w\""), + HasSubstr("data = \"llo ext\"")))); + } + } + + CordRep::Unref(tree); +} + +TEST(CordRepBtreeTest, IsValid) { + EXPECT_FALSE(CordRepBtree::IsValid(nullptr)); + + CordRepBtree* empty = CordRepBtree::New(0); + EXPECT_TRUE(CordRepBtree::IsValid(empty)); + CordRep::Unref(empty); + + for (bool as_tree : {false, true}) { + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc")); + CordRepBtree* tree = as_tree ? CordRepBtree::New(leaf) : nullptr; + CordRepBtree* check = as_tree ? tree : leaf; + + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->length--; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->length++; + + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->tag--; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->tag++; + + // Height + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->storage[0] = static_cast(CordRepBtree::kMaxHeight + 1); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[0] = 1; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[0] = 0; + + // Begin + ASSERT_TRUE(CordRepBtree::IsValid(check)); + const uint8_t begin = leaf->storage[1]; + leaf->storage[1] = static_cast(CordRepBtree::kMaxCapacity); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[1] = 2; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[1] = begin; + + // End + ASSERT_TRUE(CordRepBtree::IsValid(check)); + const uint8_t end = leaf->storage[2]; + leaf->storage[2] = static_cast(CordRepBtree::kMaxCapacity + 1); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[2] = end; + + // DataEdge tag and value + ASSERT_TRUE(CordRepBtree::IsValid(check)); + CordRep* const edge = leaf->Edges()[0]; + const uint8_t tag = edge->tag; + CordRepBtreeTestPeer::SetEdge(leaf, begin, nullptr); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + CordRepBtreeTestPeer::SetEdge(leaf, begin, edge); + edge->tag = BTREE; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + edge->tag = tag; + + if (as_tree) { + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->length--; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->length++; + + // Height + ASSERT_TRUE(CordRepBtree::IsValid(check)); + tree->storage[0] = static_cast(2); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + tree->storage[0] = 1; + + // Btree edge + ASSERT_TRUE(CordRepBtree::IsValid(check)); + CordRep* const edge = tree->Edges()[0]; + const uint8_t tag = edge->tag; + edge->tag = FLAT; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + edge->tag = tag; + } + + ASSERT_TRUE(CordRepBtree::IsValid(check)); + CordRep::Unref(check); + } +} + +TEST(CordRepBtreeTest, AssertValid) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + const CordRepBtree* ctree = tree; + EXPECT_THAT(CordRepBtree::AssertValid(tree), Eq(tree)); + EXPECT_THAT(CordRepBtree::AssertValid(ctree), Eq(ctree)); + +#if defined(GTEST_HAS_DEATH_TEST) + CordRepBtree* nulltree = nullptr; + const CordRepBtree* cnulltree = nullptr; + EXPECT_DEBUG_DEATH( + EXPECT_THAT(CordRepBtree::AssertValid(nulltree), Eq(nulltree)), ".*"); + EXPECT_DEBUG_DEATH( + EXPECT_THAT(CordRepBtree::AssertValid(cnulltree), Eq(cnulltree)), ".*"); + + tree->length--; + EXPECT_DEBUG_DEATH(EXPECT_THAT(CordRepBtree::AssertValid(tree), Eq(tree)), + ".*"); + EXPECT_DEBUG_DEATH(EXPECT_THAT(CordRepBtree::AssertValid(ctree), Eq(ctree)), + ".*"); + tree->length++; +#endif + CordRep::Unref(tree); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_test_util.h b/absl/strings/internal/cord_rep_test_util.h new file mode 100644 index 00000000..bc500064 --- /dev/null +++ b/absl/strings/internal/cord_rep_test_util.h @@ -0,0 +1,185 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cordrep_testing { + +inline cord_internal::CordRepSubstring* MakeSubstring( + size_t start, size_t len, cord_internal::CordRep* rep) { + auto* sub = new cord_internal::CordRepSubstring; + sub->tag = cord_internal::SUBSTRING; + sub->start = start; + sub->length = len <= 0 ? rep->length - start + len : len; + sub->child = rep; + return sub; +} + +inline cord_internal::CordRepConcat* MakeConcat(cord_internal::CordRep* left, + cord_internal::CordRep* right, + int depth = 0) { + auto* concat = new cord_internal::CordRepConcat; + concat->tag = cord_internal::CONCAT; + concat->length = left->length + right->length; + concat->left = left; + concat->right = right; + concat->set_depth(depth); + return concat; +} + +inline cord_internal::CordRepFlat* MakeFlat(absl::string_view value) { + assert(value.length() <= cord_internal::kMaxFlatLength); + auto* flat = cord_internal::CordRepFlat::New(value.length()); + flat->length = value.length(); + memcpy(flat->Data(), value.data(), value.length()); + return flat; +} + +// Creates an external node for testing +inline cord_internal::CordRepExternal* MakeExternal(absl::string_view s) { + struct Rep : public cord_internal::CordRepExternal { + std::string s; + explicit Rep(absl::string_view sv) : s(sv) { + this->tag = cord_internal::EXTERNAL; + this->base = s.data(); + this->length = s.length(); + this->releaser_invoker = [](cord_internal::CordRepExternal* self) { + delete static_cast(self); + }; + } + }; + return new Rep(s); +} + +inline std::string CreateRandomString(size_t n) { + absl::string_view data = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789~!@#$%^&*()_+=-<>?:\"{}[]|"; + std::minstd_rand rnd; + std::uniform_int_distribution dist(0, data.size() - 1); + std::string s(n, ' '); + for (size_t i = 0; i < n; ++i) { + s[i] = data[dist(rnd)]; + } + return s; +} + +// Creates an array of flats from the provided string, chopping +// the provided string up into flats of size `chunk_size` characters +// resulting in roughly `data.size() / chunk_size` total flats. +inline std::vector CreateFlatsFromString( + absl::string_view data, size_t chunk_size) { + assert(chunk_size > 0); + std::vector flats; + for (absl::string_view s = data; !s.empty(); s.remove_prefix(chunk_size)) { + flats.push_back(MakeFlat(s.substr(0, chunk_size))); + } + return flats; +} + +inline cord_internal::CordRepBtree* CordRepBtreeFromFlats( + absl::Span flats) { + assert(!flats.empty()); + auto* node = cord_internal::CordRepBtree::Create(flats[0]); + for (size_t i = 1; i < flats.size(); ++i) { + node = cord_internal::CordRepBtree::Append(node, flats[i]); + } + return node; +} + +inline void CordToString(cord_internal::CordRep* rep, std::string& s) { + size_t offset = 0; + size_t length = rep->length; + while (rep->tag == cord_internal::SUBSTRING) { + offset += rep->substring()->start; + rep = rep->substring()->child; + } + if (rep->tag == cord_internal::BTREE) { + for (cord_internal::CordRep* edge : rep->btree()->Edges()) { + CordToString(edge, s); + } + } else if (rep->tag >= cord_internal::FLAT) { + s.append(rep->flat()->Data() + offset, length); + } else if (rep->tag == cord_internal::EXTERNAL) { + s.append(rep->external()->base + offset, length); + } else { + ABSL_RAW_LOG(FATAL, "Unsupported tag %d", rep->tag); + } +} + +inline std::string CordToString(cord_internal::CordRep* rep) { + std::string s; + s.reserve(rep->length); + CordToString(rep, s); + return s; +} + +// RAII Helper class to automatically unref reps on destruction. +class AutoUnref { + public: + ~AutoUnref() { + for (CordRep* rep : unrefs_) CordRep::Unref(rep); + } + + // Adds `rep` to the list of reps to be unreffed at destruction. + template + CordRepType* Add(CordRepType* rep) { + unrefs_.push_back(rep); + return rep; + } + + // Increments the reference count of `rep` by one, and adds it to + // the list of reps to be unreffed at destruction. + template + CordRepType* Ref(CordRepType* rep) { + unrefs_.push_back(CordRep::Ref(rep)); + return rep; + } + + // Increments the reference count of `rep` by one if `condition` is true, + // and adds it to the list of reps to be unreffed at destruction. + template + CordRepType* RefIf(bool condition, CordRepType* rep) { + if (condition) unrefs_.push_back(CordRep::Ref(rep)); + return rep; + } + + private: + using CordRep = absl::cord_internal::CordRep; + + std::vector unrefs_; +}; + +} // namespace cordrep_testing +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index 626fed0d..51f31069 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="5bcd8e3bb929714e031a542d303f818e5a5af45d" +readonly ABSL_GOOGLETEST_COMMIT="8d51ffdfab10b3fba636ae69bc03da4b54f8c235" # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then -- cgit v1.2.3 From 2e94e5b6e152df9fa9c2fe8c1b96e1393973d32c Mon Sep 17 00:00:00 2001 From: "Bc. Martin Kubovčík" <74611856+markub3327@users.noreply.github.com> Date: Tue, 20 Jul 2021 18:58:46 +0200 Subject: Fix Bazel build on aarch64 (#984) --- absl/copts/configure_copts.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl index 669a9060..40d5849a 100644 --- a/absl/copts/configure_copts.bzl +++ b/absl/copts/configure_copts.bzl @@ -50,6 +50,7 @@ ABSL_RANDOM_RANDEN_COPTS = select({ ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, ":cpu_k8": ABSL_RANDOM_HWAES_X64_FLAGS, ":cpu_ppc": ["-mcrypto"], + ":cpu_aarch64": ABSL_RANDOM_HWAES_ARM64_FLAGS, # Supported by default or unsupported. "//conditions:default": [], @@ -70,6 +71,7 @@ def absl_random_randen_copts_init(): "darwin", "x64_windows_msvc", "x64_windows", + "aarch64", ] for cpu in cpu_configs: native.config_setting( -- cgit v1.2.3 From 0ce3ca95fd56d1ff32ec6cec69a28f589064f8e6 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 21 Jul 2021 12:45:51 -0700 Subject: Export of internal Abseil changes -- 50f4b71699a116d95b72e8cc4c0a98433fac51a0 by Evan Brown : Split reset_ctrl and set_ctrl out of raw_hash_set to avoid redundant memory accesses. In the previous member functions, the compiler has to assume that `ctrl_` can alias `this` so it has to reload the other member variables unnecessarily. Note: I tried adding __restrict__ to the ctrl arguments in the new functions, but it didn't result in further binary changes. PiperOrigin-RevId: 386074009 -- 47dfd737cc484492f9af9452e3a49adf81fe2ad2 by Abseil Team : rehash_and_grow_if_necessary in-place more aggressively. It previously resized too eagerly, resulting in low load factors. PiperOrigin-RevId: 386008094 -- 05321b4841ffe685813897a1ec86abc8fdf54093 by Derek Mauro : Internal change PiperOrigin-RevId: 385810140 GitOrigin-RevId: 50f4b71699a116d95b72e8cc4c0a98433fac51a0 Change-Id: I9c9571564adc1f9ccfa9b29d3aac238389397d1b --- absl/container/internal/raw_hash_set.h | 117 +++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index a9034b0b..675c9148 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -588,6 +588,31 @@ inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash, } } +// Reset all ctrl bytes back to kEmpty, except the sentinel. +inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, + size_t slot_size) { + std::memset(ctrl, kEmpty, capacity + 1 + NumClonedBytes()); + ctrl[capacity] = kSentinel; + SanitizerPoisonMemoryRegion(slot, slot_size * capacity); +} + +// Sets the control byte, and if `i < NumClonedBytes()`, set the cloned byte +// at the end too. +inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl, + const void* slot, size_t slot_size) { + assert(i < capacity); + + auto* slot_i = static_cast(slot) + i * slot_size; + if (IsFull(h)) { + SanitizerUnpoisonMemoryRegion(slot_i, slot_size); + } else { + SanitizerPoisonMemoryRegion(slot_i, slot_size); + } + + ctrl[i] = h; + ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h; +} + // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface // of policy). @@ -925,7 +950,8 @@ class raw_hash_set { for (const auto& v : that) { const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v); auto target = find_first_non_full(ctrl_, hash, capacity_); - set_ctrl(target.offset, H2(hash)); + SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, + sizeof(slot_type)); emplace_at(target.offset, v); infoz().RecordInsert(hash, target.probe_length); } @@ -1028,7 +1054,7 @@ class raw_hash_set { } } size_ = 0; - reset_ctrl(); + ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); reset_growth_left(); } assert(empty()); @@ -1549,7 +1575,8 @@ class raw_hash_set { static_cast(empty_after.TrailingZeros() + empty_before.LeadingZeros()) < Group::kWidth; - set_ctrl(index, was_never_full ? kEmpty : kDeleted); + SetCtrl(index, was_never_full ? kEmpty : kDeleted, capacity_, ctrl_, slots_, + sizeof(slot_type)); growth_left() += was_never_full; infoz().RecordErase(); } @@ -1576,7 +1603,7 @@ class raw_hash_set { Allocate(&alloc_ref(), layout.AllocSize())); ctrl_ = layout.template Pointer<0>(mem); slots_ = layout.template Pointer<1>(mem); - reset_ctrl(); + ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); reset_growth_left(); infoz().RecordStorageChanged(size_, capacity_); } @@ -1615,7 +1642,7 @@ class raw_hash_set { auto target = find_first_non_full(ctrl_, hash, capacity_); size_t new_i = target.offset; total_probe_length += target.probe_length; - set_ctrl(new_i, H2(hash)); + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); } } @@ -1670,19 +1697,19 @@ class raw_hash_set { // Element doesn't move. if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { - set_ctrl(i, H2(hash)); + SetCtrl(i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); continue; } if (IsEmpty(ctrl_[new_i])) { // Transfer element to the empty spot. - // set_ctrl poisons/unpoisons the slots so we have to call it at the + // SetCtrl poisons/unpoisons the slots so we have to call it at the // right time. - set_ctrl(new_i, H2(hash)); + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); - set_ctrl(i, kEmpty); + SetCtrl(i, kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type)); } else { assert(IsDeleted(ctrl_[new_i])); - set_ctrl(new_i, H2(hash)); + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); // Until we are done rehashing, DELETED marks previously FULL slots. // Swap i and new_i elements. PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i); @@ -1698,8 +1725,50 @@ class raw_hash_set { void rehash_and_grow_if_necessary() { if (capacity_ == 0) { resize(1); - } else if (size() <= CapacityToGrowth(capacity()) / 2) { + } else if (capacity_ > Group::kWidth && + // Do these calcuations in 64-bit to avoid overflow. + size() * uint64_t{32} <= capacity_ * uint64_t{25}) { // Squash DELETED without growing if there is enough capacity. + // + // Rehash in place if the current size is <= 25/32 of capacity_. + // Rationale for such a high factor: 1) drop_deletes_without_resize() is + // faster than resize, and 2) it takes quite a bit of work to add + // tombstones. In the worst case, seems to take approximately 4 + // insert/erase pairs to create a single tombstone and so if we are + // rehashing because of tombstones, we can afford to rehash-in-place as + // long as we are reclaiming at least 1/8 the capacity without doing more + // than 2X the work. (Where "work" is defined to be size() for rehashing + // or rehashing in place, and 1 for an insert or erase.) But rehashing in + // place is faster per operation than inserting or even doubling the size + // of the table, so we actually afford to reclaim even less space from a + // resize-in-place. The decision is to rehash in place if we can reclaim + // at about 1/8th of the usable capacity (specifically 3/28 of the + // capacity) which means that the total cost of rehashing will be a small + // fraction of the total work. + // + // Here is output of an experiment using the BM_CacheInSteadyState + // benchmark running the old case (where we rehash-in-place only if we can + // reclaim at least 7/16*capacity_) vs. this code (which rehashes in place + // if we can recover 3/32*capacity_). + // + // Note that although in the worst-case number of rehashes jumped up from + // 15 to 190, but the number of operations per second is almost the same. + // + // Abridged output of running BM_CacheInSteadyState benchmark from + // raw_hash_set_benchmark. N is the number of insert/erase operations. + // + // | OLD (recover >= 7/16 | NEW (recover >= 3/32) + // size | N/s LoadFactor NRehashes | N/s LoadFactor NRehashes + // 448 | 145284 0.44 18 | 140118 0.44 19 + // 493 | 152546 0.24 11 | 151417 0.48 28 + // 538 | 151439 0.26 11 | 151152 0.53 38 + // 583 | 151765 0.28 11 | 150572 0.57 50 + // 628 | 150241 0.31 11 | 150853 0.61 66 + // 672 | 149602 0.33 12 | 150110 0.66 90 + // 717 | 149998 0.35 12 | 149531 0.70 129 + // 762 | 149836 0.37 13 | 148559 0.74 190 + // 807 | 149736 0.39 14 | 151107 0.39 14 + // 852 | 150204 0.42 15 | 151019 0.42 15 drop_deletes_without_resize(); } else { // Otherwise grow the container. @@ -1765,7 +1834,8 @@ class raw_hash_set { } ++size_; growth_left() -= IsEmpty(ctrl_[target.offset]); - set_ctrl(target.offset, H2(hash)); + SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, + sizeof(slot_type)); infoz().RecordInsert(hash, target.probe_length); return target.offset; } @@ -1794,33 +1864,10 @@ class raw_hash_set { private: friend struct RawHashSetTestOnlyAccess; - // Reset all ctrl bytes back to kEmpty, except the sentinel. - void reset_ctrl() { - std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth); - ctrl_[capacity_] = kSentinel; - SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); - } - void reset_growth_left() { growth_left() = CapacityToGrowth(capacity()) - size_; } - // Sets the control byte, and if `i < Group::kWidth - 1`, set the cloned byte - // at the end too. - void set_ctrl(size_t i, ctrl_t h) { - assert(i < capacity_); - - if (IsFull(h)) { - SanitizerUnpoisonObject(slots_ + i); - } else { - SanitizerPoisonObject(slots_ + i); - } - - ctrl_[i] = h; - ctrl_[((i - NumClonedBytes()) & capacity_) + - (NumClonedBytes() & capacity_)] = h; - } - size_t& growth_left() { return settings_.template get<0>(); } HashtablezInfoHandle& infoz() { return settings_.template get<1>(); } -- cgit v1.2.3 From 4bb739310c0257bedc41bfe2824c3f2860398a65 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 27 Jul 2021 11:15:36 -0700 Subject: Export of internal Abseil changes -- 69b5d0b2a5adb49a53e51f9da6848eaa484242fe by Derek Mauro : Changes the absl::Duration factory functions to disallow types that are convertible to int or double, instead requiring that the argument itself is indeed an integer or a floating-point number. This will prevent callers from passing arguments, such as std::atomic. This change is an API break. Information and a tool to fix issues can be found at https://abseil.io/docs/cpp/tools/upgrades/duration-conversions PiperOrigin-RevId: 387153494 -- 786063e438ab6a55ac4baa88ad4d20a8293be52a by Evan Brown : Make ctrl_t be an enum class. This adds type safety, and also when strict aliasing is enabled, the compiler will know that control bytes can't alias non-control bytes. Also make H2() return h2_t. PiperOrigin-RevId: 387120717 -- 7e537aabec1c255d6e7c9d21232c597c1c2077bf by Evan Brown : Add some missing `const` keywords to ctrl_t* function parameters. PiperOrigin-RevId: 386976062 -- da53ac6d91cabd951e81dd0a145e1e52b918955f by Martijn Vels : Change Seek and InitOffset to return nullptr instead of assert / fail. This makes it consistent with the rest of the API (Next, Previous, Skip) and hardens it against invariants that are harder (or less likey) to be upheld correctly by the caller. PiperOrigin-RevId: 386963283 -- a4d1faac020d5025edf53ce81808e5db68da7d89 by Abseil Team : PC / Backtrace / Symbolization for Emscripten. PiperOrigin-RevId: 386957724 -- 97f2c47d83ba9d3ac89e1f55bd06897686ffd063 by Martijn Vels : Fix static casts ([-Wimplicit-int-conversion]) PiperOrigin-RevId: 386951646 -- 9530c795248543817cbc4013953baa09c35f5e1a by Abseil Team : Fix incorrect header guard in cord_rep_btree_navigator.h PiperOrigin-RevId: 386907904 -- 90ce5872406df2b7f4c428683741dc13a572267e by Abseil Team : Small grammar fixes for some StatusCode descriptions. PiperOrigin-RevId: 386906217 -- b30a2fd777f12a04a4d512f37a34614b0d05ce99 by Derek Mauro : Skip length checking when constructing absl::string_view from std::string. The length check causes unnecessary code bloat. PiperOrigin-RevId: 386857974 -- fa171536c359bfa2a1b80297e844519bb9ee7791 by Martijn Vels : Introduce CordRepBtreeNavigator CordRepBtreeNavigator implements bi-directional navigation over all data edges stored inside a Cord Btree. PiperOrigin-RevId: 386519102 GitOrigin-RevId: 69b5d0b2a5adb49a53e51f9da6848eaa484242fe Change-Id: I1b35188d66133f8cb73d346bc5564aac4e0b3e80 --- CMake/AbseilDll.cmake | 2 + absl/container/internal/raw_hash_set.cc | 10 +- absl/container/internal/raw_hash_set.h | 106 ++++--- absl/container/internal/raw_hash_set_benchmark.cc | 25 +- absl/container/internal/raw_hash_set_test.cc | 57 ++-- absl/debugging/BUILD.bazel | 2 + absl/debugging/CMakeLists.txt | 2 + absl/debugging/internal/stacktrace_config.h | 5 +- .../internal/stacktrace_emscripten-inl.inc | 110 +++++++ absl/debugging/internal/symbolize.h | 6 + absl/debugging/stacktrace.cc | 1 + absl/debugging/symbolize.cc | 2 + absl/debugging/symbolize_emscripten.inc | 72 +++++ absl/debugging/symbolize_test.cc | 31 +- absl/status/status.h | 23 +- absl/strings/BUILD.bazel | 18 ++ absl/strings/CMakeLists.txt | 20 ++ absl/strings/internal/cord_rep_btree.h | 6 +- absl/strings/internal/cord_rep_btree_navigator.cc | 185 ++++++++++++ absl/strings/internal/cord_rep_btree_navigator.h | 265 +++++++++++++++++ .../internal/cord_rep_btree_navigator_test.cc | 325 +++++++++++++++++++++ absl/strings/string_view.h | 10 +- absl/time/time.h | 68 +++-- 23 files changed, 1215 insertions(+), 136 deletions(-) create mode 100644 absl/debugging/internal/stacktrace_emscripten-inl.inc create mode 100644 absl/debugging/symbolize_emscripten.inc create mode 100644 absl/strings/internal/cord_rep_btree_navigator.cc create mode 100644 absl/strings/internal/cord_rep_btree_navigator.h create mode 100644 absl/strings/internal/cord_rep_btree_navigator_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 65014bfb..66c9e395 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -207,6 +207,8 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cord_rep_consume.cc" "strings/internal/cord_rep_btree.cc" "strings/internal/cord_rep_btree.h" + "strings/internal/cord_rep_btree_navigator.cc" + "strings/internal/cord_rep_btree_navigator.h" "strings/internal/cord_rep_flat.h" "strings/internal/cord_rep_ring.cc" "strings/internal/cord_rep_ring.h" diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index f5a9ce68..a5dee6aa 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -37,25 +37,23 @@ inline size_t RandomSeed() { return value ^ static_cast(reinterpret_cast(&counter)); } -bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) { +bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) { // To avoid problems with weak hashes and single bit tests, we use % 13. // TODO(kfm,sbenza): revisit after we do unconditional mixing return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6; } -void ConvertDeletedToEmptyAndFullToDeleted( - ctrl_t* ctrl, size_t capacity) { - assert(ctrl[capacity] == kSentinel); +void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) { + assert(ctrl[capacity] == ctrl_t::kSentinel); assert(IsValidCapacity(capacity)); for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) { Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); } // Copy the cloned ctrl bytes. std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes()); - ctrl[capacity] = kSentinel; + ctrl[capacity] = ctrl_t::kSentinel; } - } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 675c9148..d7783263 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -252,48 +252,57 @@ class BitMask { T mask_; }; -using ctrl_t = signed char; using h2_t = uint8_t; // The values here are selected for maximum performance. See the static asserts -// below for details. -enum Ctrl : ctrl_t { +// below for details. We use an enum class so that when strict aliasing is +// enabled, the compiler knows ctrl_t doesn't alias other types. +enum class ctrl_t : int8_t { kEmpty = -128, // 0b10000000 kDeleted = -2, // 0b11111110 kSentinel = -1, // 0b11111111 }; static_assert( - kEmpty & kDeleted & kSentinel & 0x80, + (static_cast(ctrl_t::kEmpty) & + static_cast(ctrl_t::kDeleted) & + static_cast(ctrl_t::kSentinel) & 0x80) != 0, "Special markers need to have the MSB to make checking for them efficient"); -static_assert(kEmpty < kSentinel && kDeleted < kSentinel, - "kEmpty and kDeleted must be smaller than kSentinel to make the " - "SIMD test of IsEmptyOrDeleted() efficient"); -static_assert(kSentinel == -1, - "kSentinel must be -1 to elide loading it from memory into SIMD " - "registers (pcmpeqd xmm, xmm)"); -static_assert(kEmpty == -128, - "kEmpty must be -128 to make the SIMD check for its " +static_assert( + ctrl_t::kEmpty < ctrl_t::kSentinel && ctrl_t::kDeleted < ctrl_t::kSentinel, + "ctrl_t::kEmpty and ctrl_t::kDeleted must be smaller than " + "ctrl_t::kSentinel to make the SIMD test of IsEmptyOrDeleted() efficient"); +static_assert( + ctrl_t::kSentinel == static_cast(-1), + "ctrl_t::kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(ctrl_t::kEmpty == static_cast(-128), + "ctrl_t::kEmpty must be -128 to make the SIMD check for its " "existence efficient (psignb xmm, xmm)"); -static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F, - "kEmpty and kDeleted must share an unset bit that is not shared " - "by kSentinel to make the scalar test for MatchEmptyOrDeleted() " - "efficient"); -static_assert(kDeleted == -2, - "kDeleted must be -2 to make the implementation of " +static_assert( + (~static_cast(ctrl_t::kEmpty) & + ~static_cast(ctrl_t::kDeleted) & + static_cast(ctrl_t::kSentinel) & 0x7F) != 0, + "ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not " + "shared by ctrl_t::kSentinel to make the scalar test for " + "MatchEmptyOrDeleted() efficient"); +static_assert(ctrl_t::kDeleted == static_cast(-2), + "ctrl_t::kDeleted must be -2 to make the implementation of " "ConvertSpecialToEmptyAndFullToDeleted efficient"); // A single block of empty control bytes for tables without any slots allocated. // This enables removing a branch in the hot path of find(). inline ctrl_t* EmptyGroup() { alignas(16) static constexpr ctrl_t empty_group[] = { - kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, - kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty}; + ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; return const_cast(empty_group); } // Mixes a randomly generated per-process seed with `hash` and `ctrl` to // randomize insertion order within groups. -bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl); +bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl); // Returns a hash seed. // @@ -309,12 +318,12 @@ inline size_t HashSeed(const ctrl_t* ctrl) { inline size_t H1(size_t hash, const ctrl_t* ctrl) { return (hash >> 7) ^ HashSeed(ctrl); } -inline ctrl_t H2(size_t hash) { return hash & 0x7F; } +inline h2_t H2(size_t hash) { return hash & 0x7F; } -inline bool IsEmpty(ctrl_t c) { return c == kEmpty; } -inline bool IsFull(ctrl_t c) { return c >= 0; } -inline bool IsDeleted(ctrl_t c) { return c == kDeleted; } -inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; } +inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; } +inline bool IsFull(ctrl_t c) { return c >= static_cast(0); } +inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 @@ -351,24 +360,24 @@ struct GroupSse2Impl { // Returns a bitmask representing the positions of empty slots. BitMask MatchEmpty() const { #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 - // This only works because kEmpty is -128. + // This only works because ctrl_t::kEmpty is -128. return BitMask( _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); #else - return Match(static_cast(kEmpty)); + return Match(static_cast(ctrl_t::kEmpty)); #endif } // Returns a bitmask representing the positions of empty or deleted slots. BitMask MatchEmptyOrDeleted() const { - auto special = _mm_set1_epi8(kSentinel); + auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); return BitMask( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))); } // Returns the number of trailing empty or deleted elements in the group. uint32_t CountLeadingEmptyOrDeleted() const { - auto special = _mm_set1_epi8(kSentinel); + auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); return TrailingZeros(static_cast( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1)); } @@ -403,7 +412,7 @@ struct GroupPortableImpl { // // Caveat: there are false positives but: // - they only occur if there is a real match - // - they never occur on kEmpty, kDeleted, kSentinel + // - they never occur on ctrl_t::kEmpty, ctrl_t::kDeleted, ctrl_t::kSentinel // - they will be handled gracefully by subsequent checks in code // // Example: @@ -459,8 +468,8 @@ inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } // PRECONDITION: // IsValidCapacity(capacity) -// ctrl[capacity] == kSentinel -// ctrl[i] != kSentinel for all i < capacity +// ctrl[capacity] == ctrl_t::kSentinel +// ctrl[i] != ctrl_t::kSentinel for all i < capacity // Applies mapping for every byte in ctrl: // DELETED -> EMPTY // EMPTY -> EMPTY @@ -545,27 +554,28 @@ struct FindInfo { // This is important to make 1 a valid capacity. // // - In small mode only the first `capacity()` control bytes after the -// sentinel are valid. The rest contain dummy kEmpty values that do not +// sentinel are valid. The rest contain dummy ctrl_t::kEmpty values that do not // represent a real slot. This is important to take into account on // find_first_non_full(), where we never try ShouldInsertBackwards() for // small tables. inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; } -inline probe_seq probe(ctrl_t* ctrl, size_t hash, +inline probe_seq probe(const ctrl_t* ctrl, size_t hash, size_t capacity) { return probe_seq(H1(hash, ctrl), capacity); } // Probes the raw_hash_set with the probe sequence for hash and returns the // pointer to the first empty or deleted slot. -// NOTE: this function must work with tables having both kEmpty and kDelete -// in one group. Such tables appears during drop_deletes_without_resize. +// NOTE: this function must work with tables having both ctrl_t::kEmpty and +// ctrl_t::kDeleted in one group. Such tables appears during +// drop_deletes_without_resize. // // This function is very useful when insertions happen and: // - the input is already a set // - there are enough slots // - the element with the hash is not in the table -inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash, +inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, size_t capacity) { auto seq = probe(ctrl, hash, capacity); while (true) { @@ -588,11 +598,12 @@ inline FindInfo find_first_non_full(ctrl_t* ctrl, size_t hash, } } -// Reset all ctrl bytes back to kEmpty, except the sentinel. +// Reset all ctrl bytes back to ctrl_t::kEmpty, except the sentinel. inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, size_t slot_size) { - std::memset(ctrl, kEmpty, capacity + 1 + NumClonedBytes()); - ctrl[capacity] = kSentinel; + std::memset(ctrl, static_cast(ctrl_t::kEmpty), + capacity + 1 + NumClonedBytes()); + ctrl[capacity] = ctrl_t::kSentinel; SanitizerPoisonMemoryRegion(slot, slot_size * capacity); } @@ -613,6 +624,11 @@ inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl, ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h; } +inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl, + const void* slot, size_t slot_size) { + SetCtrl(i, static_cast(h), capacity, ctrl, slot, slot_size); +} + // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface // of policy). @@ -780,7 +796,7 @@ class raw_hash_set { ctrl_ += shift; slot_ += shift; } - if (ABSL_PREDICT_FALSE(*ctrl_ == kSentinel)) ctrl_ = nullptr; + if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr; } ctrl_t* ctrl_ = nullptr; @@ -1575,8 +1591,8 @@ class raw_hash_set { static_cast(empty_after.TrailingZeros() + empty_before.LeadingZeros()) < Group::kWidth; - SetCtrl(index, was_never_full ? kEmpty : kDeleted, capacity_, ctrl_, slots_, - sizeof(slot_type)); + SetCtrl(index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted, + capacity_, ctrl_, slots_, sizeof(slot_type)); growth_left() += was_never_full; infoz().RecordErase(); } @@ -1706,7 +1722,7 @@ class raw_hash_set { // right time. SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); - SetCtrl(i, kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type)); + SetCtrl(i, ctrl_t::kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type)); } else { assert(IsDeleted(ctrl_[new_i])); SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc index 977ff4c0..c886d3ad 100644 --- a/absl/container/internal/raw_hash_set_benchmark.cc +++ b/absl/container/internal/raw_hash_set_benchmark.cc @@ -315,9 +315,17 @@ void BM_ReserveStringTable(benchmark::State& state) { } BENCHMARK(BM_ReserveStringTable)->Range(128, 4096); +// Like std::iota, except that ctrl_t doesn't support operator++. +template +void Iota(CtrlIter begin, CtrlIter end, int value) { + for (; begin != end; ++begin, ++value) { + *begin = static_cast(value); + } +} + void BM_Group_Match(benchmark::State& state) { std::array group; - std::iota(group.begin(), group.end(), -4); + Iota(group.begin(), group.end(), -4); Group g{group.data()}; h2_t h = 1; for (auto _ : state) { @@ -329,7 +337,7 @@ BENCHMARK(BM_Group_Match); void BM_Group_MatchEmpty(benchmark::State& state) { std::array group; - std::iota(group.begin(), group.end(), -4); + Iota(group.begin(), group.end(), -4); Group g{group.data()}; for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmpty()); } @@ -337,7 +345,7 @@ BENCHMARK(BM_Group_MatchEmpty); void BM_Group_MatchEmptyOrDeleted(benchmark::State& state) { std::array group; - std::iota(group.begin(), group.end(), -4); + Iota(group.begin(), group.end(), -4); Group g{group.data()}; for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted()); } @@ -345,7 +353,7 @@ BENCHMARK(BM_Group_MatchEmptyOrDeleted); void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) { std::array group; - std::iota(group.begin(), group.end(), -2); + Iota(group.begin(), group.end(), -2); Group g{group.data()}; for (auto _ : state) ::benchmark::DoNotOptimize(g.CountLeadingEmptyOrDeleted()); @@ -354,7 +362,7 @@ BENCHMARK(BM_Group_CountLeadingEmptyOrDeleted); void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) { std::array group; - std::iota(group.begin(), group.end(), -2); + Iota(group.begin(), group.end(), -2); Group g{group.data()}; for (auto _ : state) ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted()); } @@ -363,8 +371,11 @@ BENCHMARK(BM_Group_MatchFirstEmptyOrDeleted); void BM_DropDeletes(benchmark::State& state) { constexpr size_t capacity = (1 << 20) - 1; std::vector ctrl(capacity + 1 + Group::kWidth); - ctrl[capacity] = kSentinel; - std::vector pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted}; + ctrl[capacity] = ctrl_t::kSentinel; + std::vector pattern = {ctrl_t::kEmpty, static_cast(2), + ctrl_t::kDeleted, static_cast(2), + ctrl_t::kEmpty, static_cast(1), + ctrl_t::kDeleted}; for (size_t i = 0; i != capacity; ++i) { ctrl[i] = pattern[i % pattern.size()]; } diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index a191bff9..4fb31fad 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -58,6 +58,9 @@ using ::testing::Lt; using ::testing::Pair; using ::testing::UnorderedElementsAre; +// Convenience function to static cast to ctrl_t. +ctrl_t CtrlT(int i) { return static_cast(i); } + TEST(Util, NormalizeCapacity) { EXPECT_EQ(1, NormalizeCapacity(0)); EXPECT_EQ(1, NormalizeCapacity(1)); @@ -170,15 +173,19 @@ TEST(Group, EmptyGroup) { TEST(Group, Match) { if (Group::kWidth == 16) { - ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, - 7, 5, 3, 1, 1, 1, 1, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), + ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), + CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), + CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; EXPECT_THAT(Group{group}.Match(0), ElementsAre()); EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15)); EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10)); EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9)); EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8)); } else if (Group::kWidth == 8) { - ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), + ctrl_t::kDeleted, CtrlT(2), CtrlT(1), + ctrl_t::kSentinel, CtrlT(1)}; EXPECT_THAT(Group{group}.Match(0), ElementsAre()); EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7)); EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4)); @@ -189,11 +196,15 @@ TEST(Group, Match) { TEST(Group, MatchEmpty) { if (Group::kWidth == 16) { - ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, - 7, 5, 3, 1, 1, 1, 1, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), + ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), + CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), + CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4)); } else if (Group::kWidth == 8) { - ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), + ctrl_t::kDeleted, CtrlT(2), CtrlT(1), + ctrl_t::kSentinel, CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0)); } else { FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; @@ -202,11 +213,15 @@ TEST(Group, MatchEmpty) { TEST(Group, MatchEmptyOrDeleted) { if (Group::kWidth == 16) { - ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, - 7, 5, 3, 1, 1, 1, 1, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), + ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), + CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), + CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4)); } else if (Group::kWidth == 8) { - ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), + ctrl_t::kDeleted, CtrlT(2), CtrlT(1), + ctrl_t::kSentinel, CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3)); } else { FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; @@ -217,28 +232,32 @@ TEST(Batch, DropDeletes) { constexpr size_t kCapacity = 63; constexpr size_t kGroupWidth = container_internal::Group::kWidth; std::vector ctrl(kCapacity + 1 + kGroupWidth); - ctrl[kCapacity] = kSentinel; - std::vector pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted}; + ctrl[kCapacity] = ctrl_t::kSentinel; + std::vector pattern = { + ctrl_t::kEmpty, CtrlT(2), ctrl_t::kDeleted, CtrlT(2), + ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted}; for (size_t i = 0; i != kCapacity; ++i) { ctrl[i] = pattern[i % pattern.size()]; if (i < kGroupWidth - 1) ctrl[i + kCapacity + 1] = pattern[i % pattern.size()]; } ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity); - ASSERT_EQ(ctrl[kCapacity], kSentinel); + ASSERT_EQ(ctrl[kCapacity], ctrl_t::kSentinel); for (size_t i = 0; i < kCapacity + kGroupWidth; ++i) { ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()]; - if (i == kCapacity) expected = kSentinel; - if (expected == kDeleted) expected = kEmpty; - if (IsFull(expected)) expected = kDeleted; + if (i == kCapacity) expected = ctrl_t::kSentinel; + if (expected == ctrl_t::kDeleted) expected = ctrl_t::kEmpty; + if (IsFull(expected)) expected = ctrl_t::kDeleted; EXPECT_EQ(ctrl[i], expected) - << i << " " << int{pattern[i % pattern.size()]}; + << i << " " << static_cast(pattern[i % pattern.size()]); } } TEST(Group, CountLeadingEmptyOrDeleted) { - const std::vector empty_examples = {kEmpty, kDeleted}; - const std::vector full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel}; + const std::vector empty_examples = {ctrl_t::kEmpty, ctrl_t::kDeleted}; + const std::vector full_examples = { + CtrlT(0), CtrlT(1), CtrlT(2), CtrlT(3), + CtrlT(5), CtrlT(9), CtrlT(127), ctrl_t::kSentinel}; for (ctrl_t empty : empty_examples) { std::vector e(Group::kWidth, empty); @@ -871,7 +890,7 @@ TEST(Table, RehashWithNoResize) { const size_t capacity = t.capacity(); // Remove elements from all groups except the first and the last one. - // All elements removed from full groups will be marked as kDeleted. + // All elements removed from full groups will be marked as ctrl_t::kDeleted. const size_t erase_begin = Group::kWidth / 2; const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth; for (size_t i = erase_begin; i < erase_end; ++i) { diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index 2aac0f66..a8c51a5c 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -34,6 +34,7 @@ cc_library( "internal/stacktrace_aarch64-inl.inc", "internal/stacktrace_arm-inl.inc", "internal/stacktrace_config.h", + "internal/stacktrace_emscripten-inl.inc", "internal/stacktrace_generic-inl.inc", "internal/stacktrace_powerpc-inl.inc", "internal/stacktrace_unimplemented-inl.inc", @@ -57,6 +58,7 @@ cc_library( "symbolize.cc", "symbolize_darwin.inc", "symbolize_elf.inc", + "symbolize_emscripten.inc", "symbolize_unimplemented.inc", "symbolize_win32.inc", ], diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index bb4d4c92..760772f3 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -22,6 +22,7 @@ absl_cc_library( "internal/stacktrace_aarch64-inl.inc" "internal/stacktrace_arm-inl.inc" "internal/stacktrace_config.h" + "internal/stacktrace_emscripten-inl.inc" "internal/stacktrace_generic-inl.inc" "internal/stacktrace_powerpc-inl.inc" "internal/stacktrace_unimplemented-inl.inc" @@ -48,6 +49,7 @@ absl_cc_library( "symbolize.cc" "symbolize_darwin.inc" "symbolize_elf.inc" + "symbolize_emscripten.inc" "symbolize_unimplemented.inc" "symbolize_win32.inc" COPTS diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h index cca410d4..6cceef26 100644 --- a/absl/debugging/internal/stacktrace_config.h +++ b/absl/debugging/internal/stacktrace_config.h @@ -37,6 +37,10 @@ "absl/debugging/internal/stacktrace_generic-inl.inc" #endif +#elif defined(__EMSCRIPTEN__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_emscripten-inl.inc" + #elif defined(__linux__) && !defined(__ANDROID__) #if defined(NO_FRAME_POINTER) && \ @@ -68,7 +72,6 @@ "absl/debugging/internal/stacktrace_generic-inl.inc" #endif #endif - #endif // Fallback to the empty implementation. diff --git a/absl/debugging/internal/stacktrace_emscripten-inl.inc b/absl/debugging/internal/stacktrace_emscripten-inl.inc new file mode 100644 index 00000000..0f444514 --- /dev/null +++ b/absl/debugging/internal/stacktrace_emscripten-inl.inc @@ -0,0 +1,110 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Portable implementation - just use glibc +// +// Note: The glibc implementation may cause a call to malloc. +// This can cause a deadlock in HeapProfiler. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ + +#include + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/stacktrace.h" + +extern "C" { +uintptr_t emscripten_stack_snapshot(); +uint32_t emscripten_stack_unwind_buffer(uintptr_t pc, void *buffer, + uint32_t depth); +} + +// Sometimes, we can try to get a stack trace from within a stack +// trace, which can cause a self-deadlock. +// Protect against such reentrant call by failing to get a stack trace. +// +// We use __thread here because the code here is extremely low level -- it is +// called while collecting stack traces from within malloc and mmap, and thus +// can not call anything which might call malloc or mmap itself. +static __thread int recursive = 0; + +// The stack trace function might be invoked very early in the program's +// execution (e.g. from the very first malloc). +// As such, we suppress usage of backtrace during this early stage of execution. +static std::atomic disable_stacktraces(true); // Disabled until healthy. +// Waiting until static initializers run seems to be late enough. +// This file is included into stacktrace.cc so this will only run once. +ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() { + // Check if we can even create stacktraces. If not, bail early and leave + // disable_stacktraces set as-is. + // clang-format off + if (!EM_ASM_INT({ return (typeof wasmOffsetConverter !== 'undefined'); })) { + return 0; + } + // clang-format on + disable_stacktraces.store(false, std::memory_order_relaxed); + return 0; +}(); + +template +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { + return 0; + } + ++recursive; + + static_cast(ucp); // Unused. + constexpr int kStackLength = 64; + void *stack[kStackLength]; + + int size; + uintptr_t pc = emscripten_stack_snapshot(); + size = emscripten_stack_unwind_buffer(pc, stack, kStackLength); + + int result_count = size - skip_count; + if (result_count < 0) result_count = 0; + if (result_count > max_depth) result_count = max_depth; + for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count]; + + if (IS_STACK_FRAMES) { + // No implementation for finding out the stack frame sizes yet. + memset(sizes, 0, sizeof(*sizes) * result_count); + } + if (min_dropped_frames != nullptr) { + if (size - skip_count - max_depth > 0) { + *min_dropped_frames = size - skip_count - max_depth; + } else { + *min_dropped_frames = 0; + } + } + + --recursive; + + return result_count; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { return true; } +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h index 4f26130f..8c296f89 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h @@ -68,6 +68,12 @@ ABSL_NAMESPACE_END #define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1 #endif +#ifdef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE +#error ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE cannot be directly set +#elif defined(__EMSCRIPTEN__) +#define ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE 1 +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc index 1f7c7d82..5358b942 100644 --- a/absl/debugging/stacktrace.cc +++ b/absl/debugging/stacktrace.cc @@ -49,6 +49,7 @@ # include "absl/debugging/internal/stacktrace_aarch64-inl.inc" # include "absl/debugging/internal/stacktrace_arm-inl.inc" +# include "absl/debugging/internal/stacktrace_emscripten-inl.inc" # include "absl/debugging/internal/stacktrace_generic-inl.inc" # include "absl/debugging/internal/stacktrace_powerpc-inl.inc" # include "absl/debugging/internal/stacktrace_unimplemented-inl.inc" diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index 5e4a25d6..f1abdfda 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc @@ -31,6 +31,8 @@ #include "absl/debugging/symbolize_win32.inc" #elif defined(__APPLE__) #include "absl/debugging/symbolize_darwin.inc" +#elif defined(__EMSCRIPTEN__) +#include "absl/debugging/symbolize_emscripten.inc" #else #include "absl/debugging/symbolize_unimplemented.inc" #endif diff --git a/absl/debugging/symbolize_emscripten.inc b/absl/debugging/symbolize_emscripten.inc new file mode 100644 index 00000000..c226c456 --- /dev/null +++ b/absl/debugging/symbolize_emscripten.inc @@ -0,0 +1,72 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include + +#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +extern "C" { +const char* emscripten_pc_get_function(const void* pc); +} + +// clang-format off +EM_JS(bool, HaveOffsetConverter, (), + { return typeof wasmOffsetConverter !== 'undefined'; }); +// clang-format on + +namespace absl { +ABSL_NAMESPACE_BEGIN + +void InitializeSymbolizer(const char*) { + if (!HaveOffsetConverter()) { + ABSL_RAW_LOG(INFO, + "Symbolization unavailable. Rebuild with -sWASM=1 " + "and -sUSE_OFFSET_CONVERTER=1."); + } +} + +bool Symbolize(const void* pc, char* out, int out_size) { + // Check if we have the offset converter necessary for pc_get_function. + // Without it, the program will abort(). + if (!HaveOffsetConverter()) { + return false; + } + const char* func_name = emscripten_pc_get_function(pc); + if (func_name == nullptr) { + return false; + } + + strncpy(out, func_name, out_size); + + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = std::min(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + + return true; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 35de02e2..c710a3da 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -146,8 +146,22 @@ static const char *TrySymbolize(void *pc) { return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer)); } -#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \ - defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE) +#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \ + defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE) || \ + defined(ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE) + +// Test with a return address. +void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { +#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) + void *return_address = __builtin_return_address(0); + const char *symbol = TrySymbolize(return_address); + ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed"); + ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed"); + std::cout << "TestWithReturnAddress passed" << std::endl; +#endif +} + +#ifndef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE TEST(Symbolize, Cached) { // Compilers should give us pointers to them. @@ -418,6 +432,7 @@ TEST(Symbolize, ForEachSection) { close(fd); } #endif // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE +#endif // !ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE // x86 specific tests. Uses some inline assembler. extern "C" { @@ -466,17 +481,6 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() { } } -// Test with a return address. -void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { -#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) - void *return_address = __builtin_return_address(0); - const char *symbol = TrySymbolize(return_address); - ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed"); - ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed"); - std::cout << "TestWithReturnAddress passed" << std::endl; -#endif -} - #if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) // Test that we correctly identify bounds of Thumb functions on ARM. // @@ -559,7 +563,6 @@ TEST(Symbolize, SymbolizeWithDemangling) { #endif // !defined(ABSL_CONSUME_DLL) #else // Symbolizer unimplemented - TEST(Symbolize, Unimplemented) { char buf[64]; EXPECT_FALSE(absl::Symbolize((void *)(&nonstatic_func), buf, sizeof(buf))); diff --git a/absl/status/status.h b/absl/status/status.h index 2e05f46e..c5fe0a70 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -80,7 +80,7 @@ ABSL_NAMESPACE_BEGIN // `kFailedPrecondition` if both codes apply. Similarly prefer `kNotFound` or // `kAlreadyExists` over `kFailedPrecondition`. // -// Because these errors may travel RPC boundaries, these codes are tied to the +// Because these errors may cross RPC boundaries, these codes are tied to the // `google.rpc.Code` definitions within // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto // The string value of these RPC codes is denoted within each enum below. @@ -114,10 +114,10 @@ enum class StatusCode : int { // StatusCode::kInvalidArgument // // kInvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller - // specified an invalid argument, such a malformed filename. Note that such - // errors should be narrowly limited to indicate to the invalid nature of the - // arguments themselves. Errors with validly formed arguments that may cause - // errors with the state of the receiving system should be denoted with + // specified an invalid argument, such as a malformed filename. Note that use + // of such errors should be narrowly limited to indicate the invalid nature of + // the arguments themselves. Errors with validly formed arguments that may + // cause errors with the state of the receiving system should be denoted with // `kFailedPrecondition` instead. kInvalidArgument = 3, @@ -137,14 +137,15 @@ enum class StatusCode : int { // // `kNotFound` is useful if a request should be denied for an entire class of // users, such as during a gradual feature rollout or undocumented allow list. - // If, instead, a request should be denied for specific sets of users, such as - // through user-based access control, use `kPermissionDenied` instead. + // If a request should be denied for specific sets of users, such as through + // user-based access control, use `kPermissionDenied` instead. kNotFound = 5, // StatusCode::kAlreadyExists // - // kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates the entity that a - // caller attempted to create (such as file or directory) is already present. + // kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates that the entity a + // caller attempted to create (such as a file or directory) is already + // present. kAlreadyExists = 6, // StatusCode::kPermissionDenied @@ -183,7 +184,7 @@ enum class StatusCode : int { // level (such as when a client-specified test-and-set fails, indicating // the client should restart a read-modify-write sequence). // (c) Use `kFailedPrecondition` if the client should not retry until - // the system state has been explicitly fixed. For example, if an "rmdir" + // the system state has been explicitly fixed. For example, if a "rmdir" // fails because the directory is non-empty, `kFailedPrecondition` // should be returned since the client should not retry unless // the files are deleted from the directory. @@ -283,7 +284,7 @@ std::ostream& operator<<(std::ostream& os, StatusCode code); // absl::StatusToStringMode // // An `absl::StatusToStringMode` is an enumerated type indicating how -// `absl::Status::ToString()` should construct the output string for an non-ok +// `absl::Status::ToString()` should construct the output string for a non-ok // status. enum class StatusToStringMode : int { // ToString will not contain any extra data (such as payloads). It will only diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index c686e05a..7e0245a3 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -270,12 +270,14 @@ cc_library( srcs = [ "internal/cord_internal.cc", "internal/cord_rep_btree.cc", + "internal/cord_rep_btree_navigator.cc", "internal/cord_rep_consume.cc", "internal/cord_rep_ring.cc", ], hdrs = [ "internal/cord_internal.h", "internal/cord_rep_btree.h", + "internal/cord_rep_btree_navigator.h", "internal/cord_rep_consume.h", "internal/cord_rep_flat.h", "internal/cord_rep_ring.h", @@ -318,6 +320,22 @@ cc_test( ], ) +cc_test( + name = "cord_rep_btree_navigator_test", + size = "medium", + srcs = ["internal/cord_rep_btree_navigator_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":cord_rep_test_util", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "cordz_update_tracker", hdrs = ["internal/cordz_update_tracker.h"], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 2ddd3c48..e55c035d 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -556,6 +556,7 @@ absl_cc_library( HDRS "internal/cord_internal.h" "internal/cord_rep_btree.h" + "internal/cord_rep_btree_navigator.h" "internal/cord_rep_consume.h" "internal/cord_rep_flat.h" "internal/cord_rep_ring.h" @@ -563,6 +564,7 @@ absl_cc_library( SRCS "internal/cord_internal.cc" "internal/cord_rep_btree.cc" + "internal/cord_rep_btree_navigator.cc" "internal/cord_rep_consume.cc" "internal/cord_rep_ring.cc" COPTS @@ -957,6 +959,24 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + cord_rep_btree_navigator_test + SRCS + "internal/cord_rep_btree_navigator_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::cord_rep_test_util + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + absl_cc_test( NAME cord_ring_test diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index f4118fc0..7d854731 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -507,9 +507,9 @@ inline const CordRepBtree* CordRep::btree() const { inline void CordRepBtree::InitInstance(int height, size_t begin, size_t end) { tag = BTREE; - storage[0] = height; - storage[1] = begin; - storage[2] = end; + storage[0] = static_cast(height); + storage[1] = static_cast(begin); + storage[2] = static_cast(end); } inline CordRep* CordRepBtree::Edge(size_t index) const { diff --git a/absl/strings/internal/cord_rep_btree_navigator.cc b/absl/strings/internal/cord_rep_btree_navigator.cc new file mode 100644 index 00000000..d1f9995d --- /dev/null +++ b/absl/strings/internal/cord_rep_btree_navigator.cc @@ -0,0 +1,185 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_btree_navigator.h" + +#include + +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +using ReadResult = CordRepBtreeNavigator::ReadResult; + +namespace { + +// Returns a `CordRepSubstring` from `rep` starting at `offset` of size `n`. +// If `rep` is already a `CordRepSubstring` instance, an adjusted instance is +// created based on the old offset and new offset. +// Adopts a reference on `rep`. Rep must be a valid data edge. Returns +// nullptr if `n == 0`, `rep` if `n == rep->length`. +// Requires `offset < rep->length` and `offset + n <= rep->length`. +// TODO(192061034): move to utility library in internal and optimize for small +// substrings of larger reps. +inline CordRep* Substring(CordRep* rep, size_t offset, size_t n) { + assert(n <= rep->length); + assert(offset < rep->length); + assert(offset <= rep->length - n); + assert(CordRepBtree::IsDataEdge(rep)); + + if (n == 0) return nullptr; + if (n == rep->length) return CordRep::Ref(rep); + + if (rep->tag == SUBSTRING) { + offset += rep->substring()->start; + rep = rep->substring()->child; + } + + CordRepSubstring* substring = new CordRepSubstring(); + substring->length = n; + substring->tag = SUBSTRING; + substring->start = offset; + substring->child = CordRep::Ref(rep); + return substring; +} + +inline CordRep* Substring(CordRep* rep, size_t offset) { + return Substring(rep, offset, rep->length - offset); +} + +} // namespace + +CordRepBtreeNavigator::Position CordRepBtreeNavigator::Skip(size_t n) { + int height = 0; + size_t index = index_[0]; + CordRepBtree* node = node_[0]; + CordRep* edge = node->Edge(index); + + // Overall logic: Find an edge of at least the length we need to skip. + // We consume all edges which are smaller (i.e., must be 100% skipped). + // If we exhausted all edges on the current level, we move one level + // up the tree, and repeat until we either find the edge, or until we hit + // the top of the tree meaning the skip exceeds tree->length. + while (n >= edge->length) { + n -= edge->length; + while (++index == node->end()) { + if (++height > height_) return {nullptr, n}; + node = node_[height]; + index = index_[height]; + } + edge = node->Edge(index); + } + + // If we moved up the tree, descend down to the leaf level, consuming all + // edges that must be skipped. + while (height > 0) { + node = edge->btree(); + index_[height] = index; + node_[--height] = node; + index = node->begin(); + edge = node->Edge(index); + while (n >= edge->length) { + n -= edge->length; + ++index; + assert(index != node->end()); + edge = node->Edge(index); + } + } + index_[0] = index; + return {edge, n}; +} + +ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) { + int height = 0; + size_t length = edge_offset + n; + size_t index = index_[0]; + CordRepBtree* node = node_[0]; + CordRep* edge = node->Edge(index); + assert(edge_offset < edge->length); + + if (length < edge->length) { + return {Substring(edge, edge_offset, n), length}; + } + + // Similar to 'Skip', we consume all edges that are inside the 'length' of + // data that needs to be read. If we exhaust the current level, we move one + // level up the tree and repeat until we hit the final edge that must be + // (partially) read. We consume all edges into `subtree`. + CordRepBtree* subtree = CordRepBtree::New(Substring(edge, edge_offset)); + size_t subtree_end = 1; + do { + length -= edge->length; + while (++index == node->end()) { + index_[height] = index; + if (++height > height_) { + subtree->set_end(subtree_end); + if (length == 0) return {subtree, 0}; + CordRep::Unref(subtree); + return {nullptr, length}; + } + if (length != 0) { + subtree->set_end(subtree_end); + subtree = CordRepBtree::New(subtree); + subtree_end = 1; + } + node = node_[height]; + index = index_[height]; + } + edge = node->Edge(index); + if (length >= edge->length) { + subtree->length += edge->length; + subtree->edges_[subtree_end++] = CordRep::Ref(edge); + } + } while (length >= edge->length); + CordRepBtree* tree = subtree; + subtree->length += length; + + // If we moved up the tree, descend down to the leaf level, consuming all + // edges that must be read, adding 'down' nodes to `subtree`. + while (height > 0) { + node = edge->btree(); + index_[height] = index; + node_[--height] = node; + index = node->begin(); + edge = node->Edge(index); + + if (length != 0) { + CordRepBtree* right = CordRepBtree::New(height); + right->length = length; + subtree->edges_[subtree_end++] = right; + subtree->set_end(subtree_end); + subtree = right; + subtree_end = 0; + while (length >= edge->length) { + subtree->edges_[subtree_end++] = CordRep::Ref(edge); + length -= edge->length; + edge = node->Edge(++index); + } + } + } + // Add any (partial) edge still remaining at the leaf level. + if (length != 0) { + subtree->edges_[subtree_end++] = Substring(edge, 0, length); + } + subtree->set_end(subtree_end); + index_[0] = index; + return {tree, length}; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_btree_navigator.h b/absl/strings/internal/cord_rep_btree_navigator.h new file mode 100644 index 00000000..971b92ed --- /dev/null +++ b/absl/strings/internal/cord_rep_btree_navigator.h @@ -0,0 +1,265 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ + +#include +#include + +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepBtreeNavigator is a bi-directional navigator allowing callers to +// navigate all the (leaf) data edges in a CordRepBtree instance. +// +// A CordRepBtreeNavigator instance is by default empty. Callers initialize a +// navigator instance by calling one of `InitFirst()`, `InitLast()` or +// `InitOffset()`, which establishes a current position. Callers can then +// navigate using the `Next`, `Previous`, `Skip` and `Seek` methods. +// +// The navigator instance does not take or adopt a reference on the provided +// `tree` on any of the initialization calls. Callers are responsible for +// guaranteeing the lifecycle of the provided tree. A navigator instance can +// be reset to the empty state by calling `Reset`. +// +// A navigator only keeps positional state on the 'current data edge', it does +// explicitly not keep any 'offset' state. The class does accept and return +// offsets in the `Read()`, `Skip()` and 'Seek()` methods as these would +// otherwise put a big burden on callers. Callers are expected to maintain +// (returned) offset info if they require such granular state. +class CordRepBtreeNavigator { + public: + // The logical position as returned by the Seek() and Skip() functions. + // Returns the current leaf edge for the desired seek or skip position and + // the offset of that position inside that edge. + struct Position { + CordRep* edge; + size_t offset; + }; + + // The read result as returned by the Read() function. + // `tree` contains the resulting tree which is identical to the result + // of calling CordRepBtree::SubTree(...) on the tree being navigated. + // `n` contains the number of bytes used from the last navigated to + // edge of the tree. + struct ReadResult { + CordRep* tree; + size_t n; + }; + + // Returns true if this instance is not empty. + explicit operator bool() const; + + // Returns the tree for this instance or nullptr if empty. + CordRepBtree* btree() const; + + // Returns the data edge of the current position. + // Requires this instance to not be empty. + CordRep* Current() const; + + // Resets this navigator to `tree`, returning the first data edge in the tree. + CordRep* InitFirst(CordRepBtree* tree); + + // Resets this navigator to `tree`, returning the last data edge in the tree. + CordRep* InitLast(CordRepBtree* tree); + + // Resets this navigator to `tree` returning the data edge at position + // `offset` and the relative offset of `offset` into that data edge. + // Returns `Position.edge = nullptr` if the provided offset is greater + // than or equal to the length of the tree, in which case the state of + // the navigator instance remains unchanged. + Position InitOffset(CordRepBtree* tree, size_t offset); + + // Navigates to the next data edge. + // Returns the next data edge or nullptr if there is no next data edge, in + // which case the current position remains unchanged. + CordRep* Next(); + + // Navigates to the previous data edge. + // Returns the previous data edge or nullptr if there is no previous data + // edge, in which case the current position remains unchanged. + CordRep* Previous(); + + // Navigates to the data edge at position `offset`. Returns the navigated to + // data edge in `Position.edge` and the relative offset of `offset` into that + // data edge in `Position.offset`. Returns `Position.edge = nullptr` if the + // provide offset is greater than or equal to the tree's length. + Position Seek(size_t offset); + + // Reads `n` bytes of data starting at offset `edge_offset` of the current + // data edge, and returns the result in `ReadResult.tree`. `ReadResult.n` + // contains the 'bytes used` from the last / current data edge in the tree. + // This allows users that mix regular navigation (using string views) and + // 'read into cord' navigation to keep track of the current state, and which + // bytes have been consumed from a navigator. + // This function returns `ReadResult.tree = nullptr` if the requested length + // exceeds the length of the tree starting at the current data edge. + ReadResult Read(size_t edge_offset, size_t n); + + // Skips `n` bytes forward from the current data edge, returning the navigated + // to data edge in `Position.edge` and `Position.offset` containing the offset + // inside that data edge. Note that the state of the navigator is left + // unchanged if `n` is smaller than the length of the current data edge. + Position Skip(size_t n); + + // Resets this instance to the default / empty state. + void Reset(); + + private: + // Slow path for Next() if Next() reached the end of a leaf node. Backtracks + // up the stack until it finds a node that has a 'next' position available, + // and then does a 'front dive' towards the next leaf node. + CordRep* NextUp(); + + // Slow path for Previous() if Previous() reached the beginning of a leaf + // node. Backtracks up the stack until it finds a node that has a 'previous' + // position available, and then does a 'back dive' towards the previous leaf + // node. + CordRep* PreviousUp(); + + // Generic implementation of InitFirst() and InitLast(). + template + CordRep* Init(CordRepBtree* tree); + + // `height_` contains the height of the current tree, or -1 if empty. + int height_ = -1; + + // `index_` and `node_` contain the navigation state as the 'path' to the + // current data edge which is at `node_[0]->Edge(index_[0])`. The contents + // of these are undefined until the instance is initialized (`height_ >= 0`). + uint8_t index_[CordRepBtree::kMaxHeight]; + CordRepBtree* node_[CordRepBtree::kMaxHeight]; +}; + +// Returns true if this instance is not empty. +inline CordRepBtreeNavigator::operator bool() const { return height_ >= 0; } + +inline CordRepBtree* CordRepBtreeNavigator::btree() const { + return height_ >= 0 ? node_[height_] : nullptr; +} + +inline CordRep* CordRepBtreeNavigator::Current() const { + assert(height_ >= 0); + return node_[0]->Edge(index_[0]); +} + +inline void CordRepBtreeNavigator::Reset() { height_ = -1; } + +inline CordRep* CordRepBtreeNavigator::InitFirst(CordRepBtree* tree) { + return Init(tree); +} + +inline CordRep* CordRepBtreeNavigator::InitLast(CordRepBtree* tree) { + return Init(tree); +} + +template +inline CordRep* CordRepBtreeNavigator::Init(CordRepBtree* tree) { + assert(tree != nullptr); + assert(tree->size() > 0); + int height = height_ = tree->height(); + size_t index = tree->index(edge_type); + node_[height] = tree; + index_[height] = static_cast(index); + while (--height >= 0) { + tree = tree->Edge(index)->btree(); + node_[height] = tree; + index = tree->index(edge_type); + index_[height] = static_cast(index); + } + return node_[0]->Edge(index); +} + +inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::Seek( + size_t offset) { + assert(btree() != nullptr); + int height = height_; + CordRepBtree* edge = node_[height]; + if (ABSL_PREDICT_FALSE(offset >= edge->length)) return {nullptr, 0}; + CordRepBtree::Position index = edge->IndexOf(offset); + index_[height] = static_cast(index.index); + while (--height >= 0) { + edge = edge->Edge(index.index)->btree(); + node_[height] = edge; + index = edge->IndexOf(index.n); + index_[height] = static_cast(index.index); + } + return {edge->Edge(index.index), index.n}; +} + +inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::InitOffset( + CordRepBtree* tree, size_t offset) { + assert(tree != nullptr); + if (ABSL_PREDICT_FALSE(offset >= tree->length)) return {nullptr, 0}; + height_ = tree->height(); + node_[height_] = tree; + return Seek(offset); +} + +inline CordRep* CordRepBtreeNavigator::Next() { + CordRepBtree* edge = node_[0]; + return index_[0] == edge->back() ? NextUp() : edge->Edge(++index_[0]); +} + +inline CordRep* CordRepBtreeNavigator::Previous() { + CordRepBtree* edge = node_[0]; + return index_[0] == edge->begin() ? PreviousUp() : edge->Edge(--index_[0]); +} + +inline CordRep* CordRepBtreeNavigator::NextUp() { + assert(index_[0] == node_[0]->back()); + CordRepBtree* edge; + size_t index; + int height = 0; + do { + if (++height > height_) return nullptr; + edge = node_[height]; + index = index_[height] + 1; + } while (index == edge->end()); + index_[height] = static_cast(index); + do { + node_[--height] = edge = edge->Edge(index)->btree(); + index_[height] = static_cast(index = edge->begin()); + } while (height > 0); + return edge->Edge(index); +} + +inline CordRep* CordRepBtreeNavigator::PreviousUp() { + assert(index_[0] == node_[0]->begin()); + CordRepBtree* edge; + size_t index; + int height = 0; + do { + if (++height > height_) return nullptr; + edge = node_[height]; + index = index_[height]; + } while (index == edge->begin()); + index_[height] = static_cast(--index); + do { + node_[--height] = edge = edge->Edge(index)->btree(); + index_[height] = static_cast(index = edge->back()); + } while (height > 0); + return edge->Edge(index); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ diff --git a/absl/strings/internal/cord_rep_btree_navigator_test.cc b/absl/strings/internal/cord_rep_btree_navigator_test.cc new file mode 100644 index 00000000..ce09b199 --- /dev/null +++ b/absl/strings/internal/cord_rep_btree_navigator_test.cc @@ -0,0 +1,325 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_btree_navigator.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_test_util.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::Eq; +using ::testing::Ne; + +using ::absl::cordrep_testing::CordRepBtreeFromFlats; +using ::absl::cordrep_testing::CordToString; +using ::absl::cordrep_testing::CreateFlatsFromString; +using ::absl::cordrep_testing::CreateRandomString; +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::MakeSubstring; + +using ReadResult = CordRepBtreeNavigator::ReadResult; +using Position = CordRepBtreeNavigator::Position; + +// CordRepBtreeNavigatorTest is a test fixture which automatically creates a +// tree to test navigation logic on. The parameter `count' defines the number of +// data edges in the test tree. +class CordRepBtreeNavigatorTest : public testing::TestWithParam { + public: + using Flats = std::vector; + static constexpr size_t kCharsPerFlat = 3; + + CordRepBtreeNavigatorTest() { + data_ = CreateRandomString(count() * kCharsPerFlat); + flats_ = CreateFlatsFromString(data_, kCharsPerFlat); + + // Turn flat 0 or 1 into a substring to cover partial reads on substrings. + if (count() > 1) { + CordRep::Unref(flats_[1]); + flats_[1] = MakeSubstring(kCharsPerFlat, kCharsPerFlat, MakeFlat(data_)); + } else { + CordRep::Unref(flats_[0]); + flats_[0] = MakeSubstring(0, kCharsPerFlat, MakeFlat(data_)); + } + + tree_ = CordRepBtreeFromFlats(flats_); + } + + ~CordRepBtreeNavigatorTest() override { CordRep::Unref(tree_); } + + int count() const { return GetParam(); } + CordRepBtree* tree() { return tree_; } + const std::string& data() const { return data_; } + const std::vector& flats() const { return flats_; } + + static std::string ToString(testing::TestParamInfo param) { + return absl::StrCat(param.param, "_Flats"); + } + + private: + std::string data_; + Flats flats_; + CordRepBtree* tree_; +}; + +INSTANTIATE_TEST_SUITE_P( + WithParam, CordRepBtreeNavigatorTest, + testing::Values(1, CordRepBtree::kMaxCapacity - 1, + CordRepBtree::kMaxCapacity, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity - 1, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity + 1, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity * 2 + + 17), + CordRepBtreeNavigatorTest::ToString); + +TEST(CordRepBtreeNavigatorTest, Uninitialized) { + CordRepBtreeNavigator nav; + EXPECT_FALSE(nav); + EXPECT_THAT(nav.btree(), Eq(nullptr)); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH(nav.Current(), ".*"); +#endif +} + +TEST_P(CordRepBtreeNavigatorTest, InitFirst) { + CordRepBtreeNavigator nav; + CordRep* edge = nav.InitFirst(tree()); + EXPECT_TRUE(nav); + EXPECT_THAT(nav.btree(), Eq(tree())); + EXPECT_THAT(nav.Current(), Eq(flats().front())); + EXPECT_THAT(edge, Eq(flats().front())); +} + +TEST_P(CordRepBtreeNavigatorTest, InitLast) { + CordRepBtreeNavigator nav; + CordRep* edge = nav.InitLast(tree()); + EXPECT_TRUE(nav); + EXPECT_THAT(nav.btree(), Eq(tree())); + EXPECT_THAT(nav.Current(), Eq(flats().back())); + EXPECT_THAT(edge, Eq(flats().back())); +} + +TEST_P(CordRepBtreeNavigatorTest, NextPrev) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + const Flats& flats = this->flats(); + + EXPECT_THAT(nav.Previous(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.front())); + for (int i = 1; i < flats.size(); ++i) { + ASSERT_THAT(nav.Next(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Next(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.back())); + for (int i = static_cast(flats.size()) - 2; i >= 0; --i) { + ASSERT_THAT(nav.Previous(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Previous(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.front())); +} + +TEST_P(CordRepBtreeNavigatorTest, PrevNext) { + CordRepBtreeNavigator nav; + nav.InitLast(tree()); + const Flats& flats = this->flats(); + + EXPECT_THAT(nav.Next(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.back())); + for (int i = static_cast(flats.size()) - 2; i >= 0; --i) { + ASSERT_THAT(nav.Previous(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Previous(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.front())); + for (int i = 1; i < flats.size(); ++i) { + ASSERT_THAT(nav.Next(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Next(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.back())); +} + +TEST(CordRepBtreeNavigatorTest, Reset) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + CordRepBtreeNavigator nav; + nav.InitFirst(tree); + nav.Reset(); + EXPECT_FALSE(nav); + EXPECT_THAT(nav.btree(), Eq(nullptr)); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH(nav.Current(), ".*"); +#endif + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeNavigatorTest, Skip) { + int count = this->count(); + const Flats& flats = this->flats(); + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + Position pos = nav.Skip(char_offset); + EXPECT_THAT(pos.edge, Eq(nav.Current())); + EXPECT_THAT(pos.edge, Eq(flats[0])); + EXPECT_THAT(pos.offset, Eq(char_offset)); + } + + for (int index1 = 0; index1 < count; ++index1) { + for (int index2 = index1; index2 < count; ++index2) { + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + size_t length1 = index1 * kCharsPerFlat; + Position pos1 = nav.Skip(length1 + char_offset); + ASSERT_THAT(pos1.edge, Eq(flats[index1])); + ASSERT_THAT(pos1.edge, Eq(nav.Current())); + ASSERT_THAT(pos1.offset, Eq(char_offset)); + + size_t length2 = index2 * kCharsPerFlat; + Position pos2 = nav.Skip(length2 - length1 + char_offset); + ASSERT_THAT(pos2.edge, Eq(flats[index2])); + ASSERT_THAT(pos2.edge, Eq(nav.Current())); + ASSERT_THAT(pos2.offset, Eq(char_offset)); + } + } + } +} + +TEST_P(CordRepBtreeNavigatorTest, Seek) { + int count = this->count(); + const Flats& flats = this->flats(); + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + Position pos = nav.Seek(char_offset); + EXPECT_THAT(pos.edge, Eq(nav.Current())); + EXPECT_THAT(pos.edge, Eq(flats[0])); + EXPECT_THAT(pos.offset, Eq(char_offset)); + } + + for (int index = 0; index < count; ++index) { + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + size_t offset = index * kCharsPerFlat + char_offset; + Position pos1 = nav.Seek(offset); + ASSERT_THAT(pos1.edge, Eq(flats[index])); + ASSERT_THAT(pos1.edge, Eq(nav.Current())); + ASSERT_THAT(pos1.offset, Eq(char_offset)); + } + } +} + +TEST(CordRepBtreeNavigatorTest, InitOffset) { + // Whitebox: InitOffset() is implemented in terms of Seek() which is + // exhaustively tested. Only test it initializes / forwards properly.. + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + tree = CordRepBtree::Append(tree, MakeFlat("def")); + CordRepBtreeNavigator nav; + Position pos = nav.InitOffset(tree, 5); + EXPECT_TRUE(nav); + EXPECT_THAT(nav.btree(), Eq(tree)); + EXPECT_THAT(pos.edge, Eq(tree->Edges()[1])); + EXPECT_THAT(pos.edge, Eq(nav.Current())); + EXPECT_THAT(pos.offset, Eq(2)); + CordRep::Unref(tree); +} + +TEST(CordRepBtreeNavigatorTest, InitOffsetAndSeekBeyondLength) { + CordRepBtree* tree1 = CordRepBtree::Create(MakeFlat("abc")); + CordRepBtree* tree2 = CordRepBtree::Create(MakeFlat("def")); + + CordRepBtreeNavigator nav; + nav.InitFirst(tree1); + EXPECT_THAT(nav.Seek(3).edge, Eq(nullptr)); + EXPECT_THAT(nav.Seek(100).edge, Eq(nullptr)); + EXPECT_THAT(nav.btree(), Eq(tree1)); + EXPECT_THAT(nav.Current(), Eq(tree1->Edges().front())); + + EXPECT_THAT(nav.InitOffset(tree2, 3).edge, Eq(nullptr)); + EXPECT_THAT(nav.InitOffset(tree2, 100).edge, Eq(nullptr)); + EXPECT_THAT(nav.btree(), Eq(tree1)); + EXPECT_THAT(nav.Current(), Eq(tree1->Edges().front())); + + CordRep::Unref(tree1); + CordRep::Unref(tree2); +} + +TEST_P(CordRepBtreeNavigatorTest, Read) { + const Flats& flats = this->flats(); + const std::string& data = this->data(); + + for (size_t offset = 0; offset < data.size(); ++offset) { + for (size_t length = 1; length <= data.size() - offset; ++length) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + // Skip towards edge holding offset + size_t edge_offset = nav.Skip(offset).offset; + + // Read node + ReadResult result = nav.Read(edge_offset, length); + ASSERT_THAT(result.tree, Ne(nullptr)); + EXPECT_THAT(result.tree->length, Eq(length)); + if (result.tree->tag == BTREE) { + ASSERT_TRUE(CordRepBtree::IsValid(result.tree->btree())); + } + + // Verify contents + std::string value = CordToString(result.tree); + EXPECT_THAT(value, Eq(data.substr(offset, length))); + + // Verify 'partial last edge' reads. + size_t partial = (offset + length) % kCharsPerFlat; + ASSERT_THAT(result.n, Eq(partial)); + + // Verify ending position if not EOF + if (offset + length < data.size()) { + size_t index = (offset + length) / kCharsPerFlat; + EXPECT_THAT(nav.Current(), Eq(flats[index])); + } + + CordRep::Unref(result.tree); + } + } +} + +TEST_P(CordRepBtreeNavigatorTest, ReadBeyondLengthOfTree) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + ReadResult result = nav.Read(2, tree()->length); + ASSERT_THAT(result.tree, Eq(nullptr)); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 968549be..ec6c431f 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -191,7 +191,9 @@ class string_view { 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()) {} + // The length check is also skipped since it is unnecessary and causes + // code bloat. + : string_view(str.data(), str.size(), SkipCheckLengthTag{}) {} // Implicit constructor of a `string_view` from NUL-terminated `str`. When // accepting possibly null strings, use `absl::NullSafeStringView(str)` @@ -594,6 +596,12 @@ class string_view { } private: + // The constructor from std::string delegates to this constuctor. + // See the comment on that constructor for the rationale. + struct SkipCheckLengthTag {}; + string_view(const char* data, size_type len, SkipCheckLengthTag) noexcept + : ptr_(data), length_(len) {} + static constexpr size_type kMaxSize = (std::numeric_limits::max)(); diff --git a/absl/time/time.h b/absl/time/time.h index 7fa0f5c6..e9cbce84 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -182,18 +182,29 @@ class Duration { // Overloads that forward to either the int64_t or double overloads above. // Integer operands must be representable as int64_t. - template + template = 0> Duration& operator*=(T r) { int64_t x = r; return *this *= x; } - template + + template = 0> Duration& operator/=(T r) { int64_t x = r; return *this /= x; } - Duration& operator*=(float r) { return *this *= static_cast(r); } - Duration& operator/=(float r) { return *this /= static_cast(r); } + + template = 0> + Duration& operator*=(T r) { + double x = r; + return *this *= x; + } + + template = 0> + Duration& operator/=(T r) { + double x = r; + return *this /= x; + } template friend H AbslHashValue(H h, Duration d) { @@ -392,12 +403,30 @@ constexpr Duration InfiniteDuration(); // // absl::Duration a = absl::Seconds(60); // absl::Duration b = absl::Minutes(1); // b == a -constexpr Duration Nanoseconds(int64_t n); -constexpr Duration Microseconds(int64_t n); -constexpr Duration Milliseconds(int64_t n); -constexpr Duration Seconds(int64_t n); -constexpr Duration Minutes(int64_t n); -constexpr Duration Hours(int64_t n); +template = 0> +constexpr Duration Nanoseconds(T n) { + return time_internal::FromInt64(n, std::nano{}); +} +template = 0> +constexpr Duration Microseconds(T n) { + return time_internal::FromInt64(n, std::micro{}); +} +template = 0> +constexpr Duration Milliseconds(T n) { + return time_internal::FromInt64(n, std::milli{}); +} +template = 0> +constexpr Duration Seconds(T n) { + return time_internal::FromInt64(n, std::ratio<1>{}); +} +template = 0> +constexpr Duration Minutes(T n) { + return time_internal::FromInt64(n, std::ratio<60>{}); +} +template = 0> +constexpr Duration Hours(T n) { + return time_internal::FromInt64(n, std::ratio<3600>{}); +} // Factory overloads for constructing `Duration` values from a floating-point // number of the unit indicated by the factory function's name. These functions @@ -1496,25 +1525,6 @@ T ToChronoDuration(Duration d) { } // namespace time_internal -constexpr Duration Nanoseconds(int64_t n) { - return time_internal::FromInt64(n, std::nano{}); -} -constexpr Duration Microseconds(int64_t n) { - return time_internal::FromInt64(n, std::micro{}); -} -constexpr Duration Milliseconds(int64_t n) { - return time_internal::FromInt64(n, std::milli{}); -} -constexpr Duration Seconds(int64_t n) { - return time_internal::FromInt64(n, std::ratio<1>{}); -} -constexpr Duration Minutes(int64_t n) { - return time_internal::FromInt64(n, std::ratio<60>{}); -} -constexpr Duration Hours(int64_t n) { - return time_internal::FromInt64(n, std::ratio<3600>{}); -} - constexpr bool operator<(Duration lhs, Duration rhs) { return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs) ? time_internal::GetRepHi(lhs) < time_internal::GetRepHi(rhs) -- cgit v1.2.3 From 89c531c1e0d7372e2e7029f072a35495c5447d61 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 29 Jul 2021 08:04:56 -0700 Subject: Export of internal Abseil changes -- e1a0989213908927f05002ab7697955ad7dc5632 by Martijn Vels : Introduce CordRepBtreeReader CordRepBtreeReader provides forward navigation on cord btrees with absolute positional (offset) context, iterating over btree data in absl::string_view chunks. PiperOrigin-RevId: 387585161 -- 206d298e2bccb998731995cb05717b31fa9d90ec by Abseil Team : Internal change PiperOrigin-RevId: 387577465 -- f07fafe8a400a4f5dfef186d1a3b61fb7f709fe5 by Abseil Team : This change adds debug-build enforcement that the inputs to absl::c_set_intersection are sorted, which is a prerequisite of std::set_intersection and required for correct operation of the algorithm. PiperOrigin-RevId: 387446657 -- 2ca15c6361bb758be7fb88cae82bf8489b4d3364 by Abseil Team : Change BadStatusOrAccess::what() to contain status_.ToString() This ensures that on uncaught exception propagation that would cause program termination, the message contains information on the error which caused the failure. Lazy initialization of what_ is a value judgement: if most callers are expected to call status() not what(), lazy initialization is correct. If most callers are expected to call what(), it should be initialized on construction to avoid atomic operation overhead. PiperOrigin-RevId: 387402243 -- 3e855084e104dc972a0c4385395e6d8e8465127f by Gennadiy Rozental : LSC: Standardize access to GoogleTest flags on GTEST_FLAG_GET/GTEST_FLAG_SET This change is necessary to move Googletest flags out of the testing:: namespace without breaking code. These new macros will continue to be required for code that needs to work both inside Google's monorepo and outside in OSS, but can be used anywhere inside the monorepo. PiperOrigin-RevId: 387396025 -- 1ccf5895a15059ef689af5c4817d7b84f73190be by Gennadiy Rozental : Import of CCTZ from GitHub. PiperOrigin-RevId: 387388496 GitOrigin-RevId: e1a0989213908927f05002ab7697955ad7dc5632 Change-Id: I3606d9ce29d909a3555e662e9df564202cf5068d --- CMake/AbseilDll.cmake | 2 + absl/algorithm/container.h | 12 +- absl/container/btree_test.cc | 4 +- absl/status/BUILD.bazel | 2 + absl/status/CMakeLists.txt | 1 + absl/status/statusor.cc | 36 ++- absl/status/statusor.h | 28 +- absl/status/statusor_test.cc | 70 +++-- absl/strings/BUILD.bazel | 19 ++ absl/strings/CMakeLists.txt | 20 ++ absl/strings/cord_ring_test.cc | 2 +- absl/strings/cord_test.cc | 18 +- absl/strings/internal/cord_rep_btree_reader.cc | 68 +++++ absl/strings/internal/cord_rep_btree_reader.h | 219 ++++++++++++++++ .../strings/internal/cord_rep_btree_reader_test.cc | 285 +++++++++++++++++++++ absl/time/internal/cctz/src/time_zone_info.cc | 39 ++- 16 files changed, 763 insertions(+), 62 deletions(-) create mode 100644 absl/strings/internal/cord_rep_btree_reader.cc create mode 100644 absl/strings/internal/cord_rep_btree_reader.h create mode 100644 absl/strings/internal/cord_rep_btree_reader_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 66c9e395..8bdf5a50 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -209,6 +209,8 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cord_rep_btree.h" "strings/internal/cord_rep_btree_navigator.cc" "strings/internal/cord_rep_btree_navigator.h" + "strings/internal/cord_rep_btree_reader.cc" + "strings/internal/cord_rep_btree_reader.h" "strings/internal/cord_rep_flat.h" "strings/internal/cord_rep_ring.cc" "strings/internal/cord_rep_ring.h" diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 1652e7b0..c38a4a63 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -1262,7 +1262,7 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, // c_set_intersection() // // Container-based version of the `std::set_intersection()` function -// to return an iterator containing the intersection of two containers. +// to return an iterator containing the intersection of two sorted containers. template ::value, @@ -1272,6 +1272,11 @@ template ::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using operator<. + assert(absl::c_is_sorted(c1)); + assert(absl::c_is_sorted(c2)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1289,6 +1294,11 @@ template ::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output, LessThan&& comp) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using the same comparator. + assert(absl::c_is_sorted(c1, comp)); + assert(absl::c_is_sorted(c2, comp)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index d5d79151..d27cf271 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -595,7 +595,7 @@ void BtreeTest() { using V = typename remove_pair_const::type; const std::vector random_values = GenerateValuesWithSeed( absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values), - testing::GTEST_FLAG(random_seed)); + GTEST_FLAG_GET(random_seed)); unique_checker container; @@ -619,7 +619,7 @@ void BtreeMultiTest() { using V = typename remove_pair_const::type; const std::vector random_values = GenerateValuesWithSeed( absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values), - testing::GTEST_FLAG(random_seed)); + GTEST_FLAG_GET(random_seed)); multi_checker container; diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 189bd73d..30df22ae 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -78,6 +78,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":status", + "//absl/base", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/meta:type_traits", @@ -96,6 +97,7 @@ cc_test( ":statusor", "//absl/base", "//absl/memory", + "//absl/strings", "//absl/types:any", "//absl/utility", "@com_google_googletest//:gtest_main", diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index 1248dff0..43898564 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt @@ -64,6 +64,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::status absl::core_headers absl::raw_logging_internal diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc index b954b45e..96642b34 100644 --- a/absl/status/statusor.cc +++ b/absl/status/statusor.cc @@ -16,6 +16,7 @@ #include #include +#include "absl/base/call_once.h" #include "absl/base/internal/raw_logging.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" @@ -26,13 +27,44 @@ ABSL_NAMESPACE_BEGIN BadStatusOrAccess::BadStatusOrAccess(absl::Status status) : status_(std::move(status)) {} -BadStatusOrAccess::~BadStatusOrAccess() = default; +BadStatusOrAccess::BadStatusOrAccess(const BadStatusOrAccess& other) + : status_(other.status_) {} + +BadStatusOrAccess& BadStatusOrAccess::operator=( + const BadStatusOrAccess& other) { + // Ensure assignment is correct regardless of whether this->InitWhat() has + // already been called. + other.InitWhat(); + status_ = other.status_; + what_ = other.what_; + return *this; +} + +BadStatusOrAccess& BadStatusOrAccess::operator=(BadStatusOrAccess&& other) { + // Ensure assignment is correct regardless of whether this->InitWhat() has + // already been called. + other.InitWhat(); + status_ = std::move(other.status_); + what_ = std::move(other.what_); + return *this; +} + +BadStatusOrAccess::BadStatusOrAccess(BadStatusOrAccess&& other) + : status_(std::move(other.status_)) {} + const char* BadStatusOrAccess::what() const noexcept { - return "Bad StatusOr access"; + InitWhat(); + return what_.c_str(); } const absl::Status& BadStatusOrAccess::status() const { return status_; } +void BadStatusOrAccess::InitWhat() const { + absl::call_once(init_what_, [this] { + what_ = absl::StrCat("Bad StatusOr access: ", status_.ToString()); + }); +} + namespace internal_statusor { void Helper::HandleInvalidStatusCtorArg(absl::Status* status) { diff --git a/absl/status/statusor.h b/absl/status/statusor.h index b7c55cc8..235a3433 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -44,6 +44,7 @@ #include #include "absl/base/attributes.h" +#include "absl/base/call_once.h" #include "absl/meta/type_traits.h" #include "absl/status/internal/statusor_internal.h" #include "absl/status/status.h" @@ -72,13 +73,18 @@ ABSL_NAMESPACE_BEGIN class BadStatusOrAccess : public std::exception { public: explicit BadStatusOrAccess(absl::Status status); - ~BadStatusOrAccess() override; + ~BadStatusOrAccess() override = default; + + BadStatusOrAccess(const BadStatusOrAccess& other); + BadStatusOrAccess& operator=(const BadStatusOrAccess& other); + BadStatusOrAccess(BadStatusOrAccess&& other); + BadStatusOrAccess& operator=(BadStatusOrAccess&& other); // BadStatusOrAccess::what() // // Returns the associated explanatory string of the `absl::StatusOr` - // object's error code. This function only returns the string literal "Bad - // StatusOr Access" for cases when evaluating general exceptions. + // object's error code. This function contains information about the failing + // status, but its exact formatting may change and should not be depended on. // // The pointer of this string is guaranteed to be valid until any non-const // function is invoked on the exception object. @@ -91,7 +97,11 @@ class BadStatusOrAccess : public std::exception { const absl::Status& status() const; private: + void InitWhat() const; + absl::Status status_; + mutable absl::once_flag init_what_; + mutable std::string what_; }; // Returned StatusOr objects may not be ignored. @@ -437,8 +447,7 @@ class StatusOr : private internal_statusor::StatusOrData, T, U&&>>>>>::value, int> = 0> StatusOr(U&& u) // NOLINT - : StatusOr(absl::in_place, std::forward(u)) { - } + : StatusOr(absl::in_place, std::forward(u)) {} template < typename U = T, @@ -457,8 +466,7 @@ class StatusOr : private internal_statusor::StatusOrData, absl::negation>>::value, int> = 0> explicit StatusOr(U&& u) // NOLINT - : StatusOr(absl::in_place, std::forward(u)) { - } + : StatusOr(absl::in_place, std::forward(u)) {} // StatusOr::ok() // @@ -481,7 +489,7 @@ class StatusOr : private internal_statusor::StatusOrData, // Returns a reference to the current `absl::Status` contained within the // `absl::StatusOr`. If `absl::StatusOr` contains a `T`, then this // function returns `absl::OkStatus()`. - const Status& status() const &; + const Status& status() const&; Status status() &&; // StatusOr::value() @@ -661,7 +669,9 @@ StatusOr::StatusOr(absl::in_place_t, std::initializer_list ilist, : Base(absl::in_place, ilist, std::forward(args)...) {} template -const Status& StatusOr::status() const & { return this->status_; } +const Status& StatusOr::status() const& { + return this->status_; +} template Status StatusOr::status() && { return ok() ? OkStatus() : std::move(this->status_); diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc index c2e8fb7e..7cae90e1 100644 --- a/absl/status/statusor_test.cc +++ b/absl/status/statusor_test.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,7 @@ #include "absl/base/casts.h" #include "absl/memory/memory.h" #include "absl/status/status.h" +#include "absl/strings/string_view.h" #include "absl/types/any.h" #include "absl/utility/utility.h" @@ -34,6 +36,7 @@ using ::testing::AllOf; using ::testing::AnyWith; using ::testing::ElementsAre; using ::testing::Field; +using ::testing::HasSubstr; using ::testing::Ne; using ::testing::Not; using ::testing::Pointee; @@ -257,9 +260,9 @@ TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) { TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) { static_assert( - std::is_same&&>().value())>(), + std::is_same< + const int&&, + decltype(std::declval&&>().value())>(), "value() for const temporaries should return const T&&"); } @@ -303,20 +306,57 @@ TEST(StatusOr, StatusCtorForwards) { EXPECT_NE(status.message(), "Some error"); } +TEST(BadStatusOrAccessTest, CopyConstructionWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{e1}; + EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, CopyAssignmentWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{absl::InternalError("other")}; + e2 = e1; + EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, MoveConstructionWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{std::move(e1)}; + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, MoveAssignmentWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{absl::InternalError("other")}; + e2 = std::move(e1); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + // Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`, // which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether // exceptions are enabled. #ifdef ABSL_HAVE_EXCEPTIONS -#define EXPECT_DEATH_OR_THROW(statement, status_) \ - EXPECT_THROW( \ - { \ - try { \ - statement; \ - } catch (const absl::BadStatusOrAccess& e) { \ - EXPECT_EQ(e.status(), status_); \ - throw; \ - } \ - }, \ +#define EXPECT_DEATH_OR_THROW(statement, status_) \ + EXPECT_THROW( \ + { \ + try { \ + statement; \ + } catch (const absl::BadStatusOrAccess& e) { \ + EXPECT_EQ(e.status(), status_); \ + EXPECT_THAT(e.what(), HasSubstr(e.status().ToString())); \ + throw; \ + } \ + }, \ absl::BadStatusOrAccess); #else // ABSL_HAVE_EXCEPTIONS #define EXPECT_DEATH_OR_THROW(statement, status) \ @@ -412,8 +452,6 @@ TEST(StatusOr, TestStatusCtor) { EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); } - - TEST(StatusOr, TestValueCtor) { const int kI = 4; const absl::StatusOr thing(kI); @@ -1300,8 +1338,6 @@ TEST(StatusOr, TestPointerDefaultCtor) { EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); } - - TEST(StatusOr, TestPointerStatusCtor) { absl::StatusOr thing(absl::CancelledError()); EXPECT_FALSE(thing.ok()); diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 7e0245a3..9720b683 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -271,6 +271,7 @@ cc_library( "internal/cord_internal.cc", "internal/cord_rep_btree.cc", "internal/cord_rep_btree_navigator.cc", + "internal/cord_rep_btree_reader.cc", "internal/cord_rep_consume.cc", "internal/cord_rep_ring.cc", ], @@ -278,6 +279,7 @@ cc_library( "internal/cord_internal.h", "internal/cord_rep_btree.h", "internal/cord_rep_btree_navigator.h", + "internal/cord_rep_btree_reader.h", "internal/cord_rep_consume.h", "internal/cord_rep_flat.h", "internal/cord_rep_ring.h", @@ -336,6 +338,23 @@ cc_test( ], ) +cc_test( + name = "cord_rep_btree_reader_test", + size = "medium", + srcs = ["internal/cord_rep_btree_reader_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord", + ":cord_internal", + ":cord_rep_test_util", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "cordz_update_tracker", hdrs = ["internal/cordz_update_tracker.h"], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index e55c035d..88f076a8 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -557,6 +557,7 @@ absl_cc_library( "internal/cord_internal.h" "internal/cord_rep_btree.h" "internal/cord_rep_btree_navigator.h" + "internal/cord_rep_btree_reader.h" "internal/cord_rep_consume.h" "internal/cord_rep_flat.h" "internal/cord_rep_ring.h" @@ -565,6 +566,7 @@ absl_cc_library( "internal/cord_internal.cc" "internal/cord_rep_btree.cc" "internal/cord_rep_btree_navigator.cc" + "internal/cord_rep_btree_reader.cc" "internal/cord_rep_consume.cc" "internal/cord_rep_ring.cc" COPTS @@ -977,6 +979,24 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + cord_rep_btree_reader_test + SRCS + "internal/cord_rep_btree_reader_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::cord_rep_test_util + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + absl_cc_test( NAME cord_ring_test diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc index b1787edf..f1318595 100644 --- a/absl/strings/cord_ring_test.cc +++ b/absl/strings/cord_ring_test.cc @@ -275,7 +275,7 @@ CordRepConcat* MakeConcat(CordRep* left, CordRep* right, int depth = 0) { enum Composition { kMix, kAppend, kPrepend }; Composition RandomComposition() { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); return (rng() & 1) ? kMix : ((rng() & 1) ? kAppend : kPrepend); } diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 14eca155..597378cc 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -361,7 +361,7 @@ TEST(Cord, StartsEndsWith) { } TEST(Cord, Subcord) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); const std::string s = RandomLowercaseString(&rng, 1024); absl::Cord a; @@ -570,7 +570,7 @@ TEST(Cord, Flatten) { VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"})); // Test with a cord that is longer than the largest flat buffer - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192))); } @@ -937,7 +937,7 @@ static void TestCompare(const absl::Cord& c, const absl::Cord& d, } TEST(Compare, ComparisonIsUnsigned) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); std::uniform_int_distribution uniform_uint8(0, 255); char x = static_cast(uniform_uint8(rng)); TestCompare( @@ -947,7 +947,7 @@ TEST(Compare, ComparisonIsUnsigned) { TEST(Compare, RandomComparisons) { const int kIters = 5000; - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); int n = GetUniformRandomUpTo(&rng, 5000); absl::Cord a[] = {MakeExternalCord(n), @@ -1082,7 +1082,7 @@ TEST(ConstructFromExternal, ReleaserInvoked) { } TEST(ConstructFromExternal, CompareContents) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); for (int length = 1; length <= 2048; length *= 2) { std::string data = RandomLowercaseString(&rng, length); @@ -1098,7 +1098,7 @@ TEST(ConstructFromExternal, CompareContents) { } TEST(ConstructFromExternal, LargeReleaser) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); constexpr size_t kLength = 256; std::string data = RandomLowercaseString(&rng, kLength); std::array data_array; @@ -1323,7 +1323,7 @@ TEST(Cord, DiabolicalGrowth) { // 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)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); const std::string expected = RandomLowercaseString(&rng, 5000); absl::Cord cord; for (char c : expected) { @@ -1483,7 +1483,7 @@ TEST(CordChunkIterator, Operations) { VerifyChunkIterator(reused_nodes_cord, expected_chunks); } - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); absl::Cord flat_cord(RandomLowercaseString(&rng, 256)); absl::Cord subcords; for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128)); @@ -1621,7 +1621,7 @@ TEST(CordCharIterator, Operations) { VerifyCharIterator(reused_nodes_cord); } - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); absl::Cord flat_cord(RandomLowercaseString(&rng, 256)); absl::Cord subcords; for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128)); diff --git a/absl/strings/internal/cord_rep_btree_reader.cc b/absl/strings/internal/cord_rep_btree_reader.cc new file mode 100644 index 00000000..3ba43144 --- /dev/null +++ b/absl/strings/internal/cord_rep_btree_reader.cc @@ -0,0 +1,68 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_btree_reader.h" + +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_navigator.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +absl::string_view CordRepBtreeReader::Read(size_t n, size_t chunk_size, + CordRep*& tree) { + assert(chunk_size <= navigator_.Current()->length); + + // If chunk_size is non-zero, we need to start inside last returned edge. + // Else we start reading at the next data edge of the tree. + CordRep* edge = chunk_size ? navigator_.Current() : navigator_.Next(); + const size_t offset = chunk_size ? edge->length - chunk_size : 0; + + // Read the sub tree and verify we got what we wanted. + ReadResult result = navigator_.Read(offset, n); + tree = result.tree; + + // If the data returned in `tree` was covered entirely by `chunk_size`, i.e., + // read from the 'previous' edge, we did not consume any additional data, and + // can directly return the substring into the current data edge as the next + // chunk. We can easily establish from the above code that `navigator_.Next()` + // has not been called as that requires `chunk_size` to be zero. + if (n < chunk_size) return CordRepBtree::EdgeData(edge).substr(result.n); + + // The amount of data taken from the last edge is `chunk_size` and `result.n` + // contains the offset into the current edge trailing the read data (which can + // be 0). As the call to `navigator_.Read()` could have consumed all remaining + // data, calling `navigator_.Current()` is not safe before checking if we + // already consumed all remaining data. + const size_t consumed_by_read = n - chunk_size - result.n; + if (consumed_ + consumed_by_read >= length()) { + consumed_ = length(); + return {}; + } + + // We did not read all data, return remaining data from current edge. + edge = navigator_.Current(); + consumed_ += consumed_by_read + edge->length; + return CordRepBtree::EdgeData(edge).substr(result.n); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_btree_reader.h b/absl/strings/internal/cord_rep_btree_reader.h new file mode 100644 index 00000000..c19fa43d --- /dev/null +++ b/absl/strings/internal/cord_rep_btree_reader.h @@ -0,0 +1,219 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ + +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_navigator.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepBtreeReader implements logic to iterate over cord btrees. +// References to the underlying data are returned as absl::string_view values. +// The most typical use case is a forward only iteration over tree data. +// The class also provides `Skip()`, `Seek()` and `Read()` methods similar to +// CordRepBtreeNavigator that allow more advanced navigation. The class provides +// a `consumed` property which contains the end offset of the chunk last +// returned to the user which is useful in cord iteration logic. +// +// Example: iterate over all data inside a cord btree: +// +// CordRepBtreeReader reader; +// for (string_view sv = reader.Init(tree); !sv.Empty(); sv = sv.Next()) { +// DoSomethingWithDataIn(sv); +// } +// +// All navigation methods always return the next 'chunk' of data. The class +// assumes that all data is directly 'consumed' by the caller. For example: +// invoking `Skip()` will skip the desired number of bytes, and directly +// read and return the next chunk of data directly after the skipped bytes. +// +// Example: iterate over all data inside a btree skipping the first 100 bytes: +// +// CordRepBtreeReader reader; +// absl::string_view sv = reader.Init(tree); +// if (sv.length() > 100) { +// sv.RemovePrefix(100); +// } else { +// sv = reader.Skip(100 - sv.length()); +// } +// while (!sv.empty()) { +// DoSomethingWithDataIn(sv); +// absl::string_view sv = reader.Next(); +// } +// +// It is important to notice that `consumed` represents the end position of the +// last data edge returned to the caller, not the cumulative data returned to +// the caller which can be less in cases of skipping or seeking over data. +// +// For example, consider a cord btree with five data edges: "abc", "def", "ghi", +// "jkl" and "mno": +// +// absl::string_view sv; +// CordRepBtreeReader reader; +// +// sv = reader.Init(tree); // sv = "abc", reader.consumed() = 3 +// sv = reader.Skip(4); // sv = "hi", reader.consumed() = 9 +// sv = reader.Skip(2); // sv = "l", reader.consumed() = 12 +// sv = reader.Next(); // sv = "mno", reader.consumed() = 15 +// +// In the above example, `reader.consumed()` reflects the data edges iterated +// over or skipped by the reader, not the amount of data 'consumed' by the +// caller. +class CordRepBtreeReader { + public: + using ReadResult = CordRepBtreeNavigator::ReadResult; + using Position = CordRepBtreeNavigator::Position; + + // Returns true if this instance is not empty. + explicit operator bool() const { return navigator_.btree() != nullptr; } + + // Returns the tree referenced by this instance or nullptr if empty. + CordRepBtree* btree() const { return navigator_.btree(); } + + // Returns the current data edge inside the referenced btree. + // Requires that the current instance is not empty. + CordRep* node() const { return navigator_.Current(); } + + // Returns the length of the referenced tree. + // Requires that the current instance is not empty. + size_t length() const; + + // Returns the end offset of the last navigated to chunk, which represents the + // total bytes 'consumed' relative to the start of the tree. The returned + // value is never zero. For example, initializing a reader with a tree with a + // first data edge of 19 bytes will return `consumed() = 19`. See also the + // class comments on the meaning of `consumed`. + // Requires that the current instance is not empty. + size_t consumed() const; + + // Resets this instance to an empty value. + void Reset() { navigator_.Reset(); } + + // Initializes this instance with `tree`. `tree` must not be null. + // Returns a reference to the first data edge of the provided tree. + absl::string_view Init(CordRepBtree* tree); + + // Navigates to and returns the next data edge of the referenced tree. + // Returns an empty string_view if an attempt is made to read beyond the end + // of the tree, i.e.: if `remaining()` is zero indicating an EOF condition. + // Requires that the current instance is not empty. + absl::string_view Next(); + + // Skips the provided amount of bytes and returns a reference to the data + // directly following the skipped bytes. + absl::string_view Skip(size_t skip); + + // Reads `n` bytes into `tree`. + // If `chunk_size` is zero, starts reading at the next data edge. If + // `chunk_size` is non zero, the read starts at the last `chunk_size` bytes of + // the last returned data edge. Effectively, this means that the read starts + // at offset `consumed() - chunk_size`. + // Requires that `chunk_size` is less than or equal to the length of the + // last returned data edge. The purpose of `chunk_size` is to simplify code + // partially consuming a returned chunk and wanting to include the remaining + // bytes in the Read call. For example, the below code will read 1000 bytes of + // data into a cord tree if the first chunk starts with "big:": + // + // CordRepBtreeReader reader; + // absl::string_view sv = reader.Init(tree); + // if (absl::StartsWith(sv, "big:")) { + // CordRepBtree tree; + // sv = reader.Read(1000, sv.size() - 4 /* "big:" */, &tree); + // } + // + // This method will return an empty string view if all remaining data was + // read. If `n` exceeded the amount of remaining data this function will + // return an empty string view and `tree` will be set to nullptr. + // In both cases, `consumed` will be set to `length`. + absl::string_view Read(size_t n, size_t chunk_size, CordRep*& tree); + + // Navigates to the chunk at offset `offset`. + // Returns a reference into the navigated to chunk, adjusted for the relative + // position of `offset` into that chunk. For example, calling `Seek(13)` on a + // cord tree containing 2 chunks of 10 and 20 bytes respectively will return + // a string view into the second chunk starting at offset 3 with a size of 17. + // Returns an empty string view if `offset` is equal to or greater than the + // length of the referenced tree. + absl::string_view Seek(size_t offset); + + private: + size_t consumed_; + CordRepBtreeNavigator navigator_; +}; + +inline size_t CordRepBtreeReader::length() const { + assert(btree() != nullptr); + return btree()->length; +} + +inline size_t CordRepBtreeReader::consumed() const { + assert(btree() != nullptr); + return consumed_; +} + +inline absl::string_view CordRepBtreeReader::Init(CordRepBtree* tree) { + assert(tree != nullptr); + const CordRep* edge = navigator_.InitFirst(tree); + consumed_ = edge->length; + return CordRepBtree::EdgeData(edge); +} + +inline absl::string_view CordRepBtreeReader::Next() { + assert(consumed() < length()); + const CordRep* edge = navigator_.Next(); + assert(edge != nullptr); + consumed_ += edge->length; + return CordRepBtree::EdgeData(edge); +} + +inline absl::string_view CordRepBtreeReader::Skip(size_t skip) { + // As we are always positioned on the last 'consumed' edge, we + // need to skip the current edge as well as `skip`. + const size_t edge_length = navigator_.Current()->length; + CordRepBtreeNavigator::Position pos = navigator_.Skip(skip + edge_length); + if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { + consumed_ = length(); + return {}; + } + // The combined length of all edges skipped before `pos.edge` is `skip - + // pos.offset`, all of which are 'consumed', as well as the current edge. + consumed_ += skip - pos.offset + pos.edge->length; + return CordRepBtree::EdgeData(pos.edge).substr(pos.offset); +} + +inline absl::string_view CordRepBtreeReader::Seek(size_t offset) { + const CordRepBtreeNavigator::Position pos = navigator_.Seek(offset); + if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { + consumed_ = length(); + return {}; + } + absl::string_view chunk = CordRepBtree::EdgeData(pos.edge).substr(pos.offset); + consumed_ = offset + chunk.length(); + return chunk; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ diff --git a/absl/strings/internal/cord_rep_btree_reader_test.cc b/absl/strings/internal/cord_rep_btree_reader_test.cc new file mode 100644 index 00000000..44d3365f --- /dev/null +++ b/absl/strings/internal/cord_rep_btree_reader_test.cc @@ -0,0 +1,285 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_btree_reader.h" + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_test_util.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::Ne; +using ::testing::Not; + +using ::absl::cordrep_testing::CordRepBtreeFromFlats; +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::CordToString; +using ::absl::cordrep_testing::CreateFlatsFromString; +using ::absl::cordrep_testing::CreateRandomString; + +using ReadResult = CordRepBtreeReader::ReadResult; + +TEST(CordRepBtreeReaderTest, Next) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + CordRepBtreeReader reader; + absl::string_view chunk = reader.Init(node); + EXPECT_THAT(chunk, Eq(data.substr(0, chunk.length()))); + + size_t consumed = chunk.length(); + EXPECT_THAT(reader.consumed(), Eq(consumed)); + + while (consumed < data.length()) { + chunk = reader.Next(); + EXPECT_THAT(chunk, Eq(data.substr(consumed, chunk.length()))); + + consumed += chunk.length(); + EXPECT_THAT(reader.consumed(), Eq(consumed)); + } + + EXPECT_THAT(consumed, Eq(data.length())); + EXPECT_THAT(reader.consumed(), Eq(data.length())); + + CordRep::Unref(node); + } +} + +TEST(CordRepBtreeReaderTest, Skip) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + for (size_t skip1 = 0; skip1 < data.length() - kChars; ++skip1) { + for (size_t skip2 = 0; skip2 < data.length() - kChars; ++skip2) { + CordRepBtreeReader reader; + absl::string_view chunk = reader.Init(node); + size_t consumed = chunk.length(); + + chunk = reader.Skip(skip1); + ASSERT_THAT(chunk, Eq(data.substr(consumed + skip1, chunk.length()))); + consumed += chunk.length() + skip1; + ASSERT_THAT(reader.consumed(), Eq(consumed)); + + if (consumed >= data.length()) continue; + + size_t skip = std::min(data.length() - consumed - 1, skip2); + chunk = reader.Skip(skip); + ASSERT_THAT(chunk, Eq(data.substr(consumed + skip, chunk.length()))); + } + } + + CordRep::Unref(node); + } +} + +TEST(CordRepBtreeReaderTest, SkipBeyondLength) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + tree = CordRepBtree::Append(tree, MakeFlat("def")); + CordRepBtreeReader reader; + reader.Init(tree); + EXPECT_THAT(reader.Skip(100), IsEmpty()); + EXPECT_THAT(reader.consumed(), Eq(6)); + CordRep::Unref(tree); +} + +TEST(CordRepBtreeReaderTest, Seek) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + for (size_t seek = 0; seek < data.length() - 1; ++seek) { + CordRepBtreeReader reader; + reader.Init(node); + absl::string_view chunk = reader.Seek(seek); + ASSERT_THAT(chunk, Not(IsEmpty())); + ASSERT_THAT(chunk, Eq(data.substr(seek, chunk.length()))); + ASSERT_THAT(reader.consumed(), Eq(seek + chunk.length())); + } + + CordRep::Unref(node); + } +} + +TEST(CordRepBtreeReaderTest, SeekBeyondLength) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + tree = CordRepBtree::Append(tree, MakeFlat("def")); + CordRepBtreeReader reader; + reader.Init(tree); + EXPECT_THAT(reader.Seek(6), IsEmpty()); + EXPECT_THAT(reader.consumed(), Eq(6)); + EXPECT_THAT(reader.Seek(100), IsEmpty()); + EXPECT_THAT(reader.consumed(), Eq(6)); + CordRep::Unref(tree); +} + +TEST(CordRepBtreeReaderTest, Read) { + std::string data = "abcdefghijklmno"; + std::vector flats = CreateFlatsFromString(data, 5); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + CordRep* tree; + CordRepBtreeReader reader; + absl::string_view chunk; + + // Read zero bytes + chunk = reader.Init(node); + chunk = reader.Read(0, chunk.length(), tree); + EXPECT_THAT(tree, Eq(nullptr)); + EXPECT_THAT(chunk, Eq("abcde")); + EXPECT_THAT(reader.consumed(), Eq(5)); + EXPECT_THAT(reader.Next(), Eq("fghij")); + + // Read in full + chunk = reader.Init(node); + chunk = reader.Read(15, chunk.length(), tree); + EXPECT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("abcdefghijklmno")); + EXPECT_THAT(chunk, Eq("")); + EXPECT_THAT(reader.consumed(), Eq(15)); + CordRep::Unref(tree); + + // Read < chunk bytes + chunk = reader.Init(node); + chunk = reader.Read(3, chunk.length(), tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("abc")); + EXPECT_THAT(chunk, Eq("de")); + EXPECT_THAT(reader.consumed(), Eq(5)); + EXPECT_THAT(reader.Next(), Eq("fghij")); + CordRep::Unref(tree); + + // Read < chunk bytes at offset + chunk = reader.Init(node); + chunk = reader.Read(2, chunk.length() - 2, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("cd")); + EXPECT_THAT(chunk, Eq("e")); + EXPECT_THAT(reader.consumed(), Eq(5)); + EXPECT_THAT(reader.Next(), Eq("fghij")); + CordRep::Unref(tree); + + // Read from consumed chunk + chunk = reader.Init(node); + chunk = reader.Read(3, 0, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("fgh")); + EXPECT_THAT(chunk, Eq("ij")); + EXPECT_THAT(reader.consumed(), Eq(10)); + EXPECT_THAT(reader.Next(), Eq("klmno")); + CordRep::Unref(tree); + + // Read across chunks + chunk = reader.Init(node); + chunk = reader.Read(12, chunk.length() - 2, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("cdefghijklmn")); + EXPECT_THAT(chunk, Eq("o")); + EXPECT_THAT(reader.consumed(), Eq(15)); + CordRep::Unref(tree); + + // Read across chunks landing on exact edge boundary + chunk = reader.Init(node); + chunk = reader.Read(10 - 2, chunk.length() - 2, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("cdefghij")); + EXPECT_THAT(chunk, Eq("klmno")); + EXPECT_THAT(reader.consumed(), Eq(15)); + CordRep::Unref(tree); + + CordRep::Unref(node); +} + +TEST(CordRepBtreeReaderTest, ReadExhaustive) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap + 1, cap * cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + for (size_t read_size : {kChars - 1, kChars, kChars + 7, cap * cap}) { + CordRepBtreeReader reader; + absl::string_view chunk = reader.Init(node); + + // `consumed` tracks the end of last consumed chunk which is the start of + // the next chunk: we always read with `chunk_size = chunk.length()`. + size_t consumed = 0; + size_t remaining = data.length(); + while (remaining > 0) { + CordRep* tree; + size_t n = (std::min)(remaining, read_size); + chunk = reader.Read(n, chunk.length(), tree); + EXPECT_THAT(tree, Ne(nullptr)); + if (tree) { + EXPECT_THAT(CordToString(tree), Eq(data.substr(consumed, n))); + CordRep::Unref(tree); + } + + consumed += n; + remaining -= n; + EXPECT_THAT(reader.consumed(), Eq(consumed + chunk.length())); + + if (remaining > 0) { + ASSERT_FALSE(chunk.empty()); + ASSERT_THAT(chunk, Eq(data.substr(consumed, chunk.length()))); + } else { + ASSERT_TRUE(chunk.empty()) << chunk; + } + } + } + + CordRep::Unref(node); + } +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc index 8039353e..f2777d91 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -43,6 +43,7 @@ #include #include #include +#include #include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" @@ -576,14 +577,17 @@ bool TimeZoneInfo::Load(ZoneInfoSource* zip) { namespace { +using FilePtr = std::unique_ptr; + // fopen(3) adaptor. -inline FILE* FOpen(const char* path, const char* mode) { +inline FilePtr FOpen(const char* path, const char* mode) { #if defined(_MSC_VER) FILE* fp; if (fopen_s(&fp, path, mode) != 0) fp = nullptr; - return fp; + return FilePtr(fp, fclose); #else - return fopen(path, mode); // TODO: Enable the close-on-exec flag. + // TODO: Enable the close-on-exec flag. + return FilePtr(fopen(path, mode), fclose); #endif } @@ -611,11 +615,11 @@ class FileZoneInfoSource : public ZoneInfoSource { protected: explicit FileZoneInfoSource( - FILE* fp, std::size_t len = std::numeric_limits::max()) - : fp_(fp, fclose), len_(len) {} + FilePtr fp, std::size_t len = std::numeric_limits::max()) + : fp_(std::move(fp)), len_(len) {} private: - std::unique_ptr fp_; + FilePtr fp_; std::size_t len_; }; @@ -644,17 +648,9 @@ std::unique_ptr FileZoneInfoSource::Open( path.append(name, pos, std::string::npos); // Open the zoneinfo file. - FILE* fp = FOpen(path.c_str(), "rb"); - if (fp == nullptr) return nullptr; - std::size_t length = 0; - if (fseek(fp, 0, SEEK_END) == 0) { - long offset = ftell(fp); - if (offset >= 0) { - length = static_cast(offset); - } - rewind(fp); - } - return std::unique_ptr(new FileZoneInfoSource(fp, length)); + auto fp = FOpen(path.c_str(), "rb"); + if (fp.get() == nullptr) return nullptr; + return std::unique_ptr(new FileZoneInfoSource(std::move(fp))); } class AndroidZoneInfoSource : public FileZoneInfoSource { @@ -663,8 +659,9 @@ class AndroidZoneInfoSource : public FileZoneInfoSource { std::string Version() const override { return version_; } private: - explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers) - : FileZoneInfoSource(fp, len), version_(vers) {} + explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len, + std::string version) + : FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {} std::string version_; }; @@ -676,7 +673,7 @@ std::unique_ptr AndroidZoneInfoSource::Open( // See Android's libc/tzcode/bionic.cpp for additional information. for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", "/system/usr/share/zoneinfo/tzdata"}) { - std::unique_ptr fp(FOpen(tzdata, "rb"), fclose); + auto fp = FOpen(tzdata, "rb"); if (fp.get() == nullptr) continue; char hbuf[24]; // covers header.zonetab_offset too @@ -703,7 +700,7 @@ std::unique_ptr AndroidZoneInfoSource::Open( if (strcmp(name.c_str() + pos, ebuf) == 0) { if (fseek(fp.get(), static_cast(start), SEEK_SET) != 0) break; return std::unique_ptr(new AndroidZoneInfoSource( - fp.release(), static_cast(length), vers)); + std::move(fp), static_cast(length), vers)); } } } -- cgit v1.2.3 From ee0ebdae4a9e789b92f5abbe8573ddeeaead4864 Mon Sep 17 00:00:00 2001 From: Eric Barndollar Date: Thu, 29 Jul 2021 21:33:51 -0400 Subject: CMake: option to use cxx_std_11 (minimum) that propagates. (#986) See https://github.com/abseil/abseil-cpp/issues/259 for context. This change introduces the ABSL_PROPAGATE_CXX_STD option (default to OFF for now, though it prints a warning in CMake 2.8+ builds that a future Abseil release will default to ON). When enabled, all Abseil CMake targets will set the cxx_std_11 target meta feature (which will then propagate to targets that depend upon Abseil) rather than setting the target-level CXX_STANDARD property to CMAKE_CXX_STANDARD (which is the default value anyway), which doesn't propagate. Updates README documentation to clarify behavior and with a different example recommendation for library projects (which should generally leave CMAKE_CXX_STANDARD to the root application project). See https://crascit.com/2015/03/28/enabling-cxx11-in-cmake/ for a useful overview of these different CMake features surrounding C++ standard version configuration. --- CMake/AbseilHelpers.cmake | 49 ++++++++++++++++++++++++++++++++++++++++++----- CMake/README.md | 45 ++++++++++++++++++++++++++++++++++++++++--- CMakeLists.txt | 7 +++++++ FAQ.md | 5 ++++- 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 6a64a2c7..17c4f449 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -254,9 +254,23 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n") set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal) endif() - # INTERFACE libraries can't have the CXX_STANDARD property set - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++11 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} PUBLIC cxx_std_11) + else() + # Note: This is legacy (before CMake 3.8) behavior. Setting the + # target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is + # initialized by CMAKE_CXX_STANDARD) should have no real effect, since + # that is the default value anyway. + # + # CXX_STANDARD_REQUIRED does guard against the top-level CMake project + # not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents + # "decaying" to an older standard if the requested one isn't available). + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + endif() # When being installed, we lose the absl_ prefix. We want to put it back # to have properly named lib files. This is a no-op when we are not being @@ -287,6 +301,16 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n") ${ABSL_DEFAULT_LINKOPTS} ) target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES}) + + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++11 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} INTERFACE cxx_std_11) + + # (INTERFACE libraries can't have the CXX_STANDARD property set, so there + # is no legacy behavior else case). + endif() endif() # TODO currently we don't install googletest alongside abseil sources, so @@ -390,8 +414,23 @@ function(absl_cc_test) # Add all Abseil targets to a folder in the IDE for organization. set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++11 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} PUBLIC cxx_std_11) + else() + # Note: This is legacy (before CMake 3.8) behavior. Setting the + # target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is + # initialized by CMAKE_CXX_STANDARD) should have no real effect, since + # that is the default value anyway. + # + # CXX_STANDARD_REQUIRED does guard against the top-level CMake project + # not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents + # "decaying" to an older standard if the requested one isn't available). + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + endif() add_test(NAME ${_NAME} COMMAND ${_NAME}) endfunction() diff --git a/CMake/README.md b/CMake/README.md index fa8e8d30..f8b27e63 100644 --- a/CMake/README.md +++ b/CMake/README.md @@ -34,15 +34,16 @@ to include Abseil directly in your CMake project. 4. Add the **absl::** target you wish to use to the [`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) section of your executable or of your library.
-Here is a short CMakeLists.txt example of a project file using Abseil. +Here is a short CMakeLists.txt example of an application project using Abseil. ```cmake -cmake_minimum_required(VERSION 3.5) -project(my_project) +cmake_minimum_required(VERSION 3.8.2) +project(my_app_project) # Pick the C++ standard to compile with. # Abseil currently supports C++11, C++14, and C++17. set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) add_subdirectory(abseil-cpp) @@ -50,6 +51,44 @@ add_executable(my_exe source.cpp) target_link_libraries(my_exe absl::base absl::synchronization absl::strings) ``` +Note that if you are developing a library designed for use by other clients, you +should instead leave `CMAKE_CXX_STANDARD` unset (or only set if being built as +the current top-level CMake project) and configure the minimum required C++ +standard at the target level. If you require a later minimum C++ standard than +Abseil does, it's a good idea to also enforce that `CMAKE_CXX_STANDARD` (which +will control Abseil library targets) is set to at least that minimum. For +example: + +```cmake +cmake_minimum_required(VERSION 3.8.2) +project(my_lib_project) + +# Leave C++ standard up to the root application, so set it only if this is the +# current top-level CMake project. +if(CMAKE_SOURCE_DIR STREQUAL my_lib_project_SOURCE_DIR) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +add_subdirectory(abseil-cpp) + +add_library(my_lib source.cpp) +target_link_libraries(my_lib absl::base absl::synchronization absl::strings) + +# Enforce that my_lib requires C++17. Important to document for clients that they +# must set CMAKE_CXX_STANDARD to 17 or higher for proper Abseil ABI compatibility +# (since otherwise, Abseil library targets could be compiled with a lower C++ +# standard than my_lib). +target_compile_features(my_lib PUBLIC cxx_std_17) +if(CMAKE_CXX_STANDARD LESS 17) + message(FATAL_ERROR + "my_lib_project requires CMAKE_CXX_STANDARD >= 17 (got: ${CMAKE_CXX_STANDARD})") +endif() +``` + +Then the top-level application project that uses your library is responsible for +setting a consistent `CMAKE_CXX_STANDARD` that is sufficiently high. + ### Running Abseil Tests with CMake Use the `-DBUILD_TESTING=ON` flag to run Abseil tests. diff --git a/CMakeLists.txt b/CMakeLists.txt index 42bcbe10..7c8bfff4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,13 @@ else() option(ABSL_ENABLE_INSTALL "Enable install rule" ON) endif() +option(ABSL_PROPAGATE_CXX_STD + "Use CMake C++ standard meta features (e.g. cxx_std_11) that propagate to targets that link to Abseil" + OFF) # TODO: Default to ON for CMake 3.8 and greater. +if((${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.8) AND (NOT ABSL_PROPAGATE_CXX_STD)) + message(WARNING "A future Abseil release will default ABSL_PROPAGATE_CXX_STD to ON for CMake 3.8 and up. We recommend enabling this option to ensure your project still builds correctly.") +endif() + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake ${CMAKE_CURRENT_LIST_DIR}/absl/copts diff --git a/FAQ.md b/FAQ.md index 78028fc0..fbd92ce9 100644 --- a/FAQ.md +++ b/FAQ.md @@ -27,7 +27,10 @@ compiler, there several ways to do this: file](https://docs.bazel.build/versions/master/guide.html#bazelrc) If you are using CMake as the build system, you'll need to add a line like -`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. See the +`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you +are developing a library designed to be used by other clients, you should +instead leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard +required by each of your library targets via `target_compile_features`. See the [CMake build instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md) for more information. -- cgit v1.2.3 From ab01e0403a40813857a8e580d9cf1580ba0c4139 Mon Sep 17 00:00:00 2001 From: Leonhard Markert Date: Fri, 6 Aug 2021 15:21:35 +0200 Subject: Simplifies the construction of the value returned by GenerateRealFromBits() (#994) * GenerateRealFromBits: sign is already set If std::is_same::value then sign is already set to zero thanks to: uint_type sign = std::is_same::value ? (static_cast(1) << (kUintBits - 1)) : 0; // <- here So the conditional is unnecessary. * Update generate_real.h Remove extra parenthesis Co-authored-by: Derek Mauro <761129+derekmauro@users.noreply.github.com> --- absl/random/internal/generate_real.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h index 4f62873d..d5fbb44c 100644 --- a/absl/random/internal/generate_real.h +++ b/absl/random/internal/generate_real.h @@ -127,10 +127,8 @@ inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { // Construct the 32-bit or 64-bit IEEE 754 floating-point value from // the individual fields: sign, exp, mantissa(bits). - uint_type val = - (std::is_same::value ? 0u : sign) | - (static_cast(exp) << kExp) | - (static_cast(bits) & kMask); + uint_type val = sign | (static_cast(exp) << kExp) | + (static_cast(bits) & kMask); // bit_cast to the output-type real_type result; -- cgit v1.2.3 From bf31a10b65d945665cecfb9d8807702ae4a7fde1 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 6 Aug 2021 06:19:26 -0700 Subject: Export of internal Abseil changes -- 93c607726d663800b4bfa472cba043fd3f5d0e97 by Derek Mauro : Internal change PiperOrigin-RevId: 389158822 -- 55b3bb50bbc168567c6ba25d07df2c2c39e864af by Martijn Vels : Change CordRepRing alternative implementation to CordRepBtree alternative. This changes makes CordRepBtree (BTREE) the alternative to CordRepConcat (CONCAT) trees, enabled through the internal / experimental 'cord_btree_enabled' latch. PiperOrigin-RevId: 389030571 -- d6fc346143606c096bca8eb5029e4c429ac6e305 by Todd Lipcon : Fix a small typo in SequenceLock doc comment PiperOrigin-RevId: 388972936 -- e46f9245dce8b4150e3ca2664e0cf42b75f90a83 by Martijn Vels : Add 'shallow' validation mode to CordRepBtree which will be the default for internal assertions. PiperOrigin-RevId: 388753606 -- b5e74f163b490beb006f848ace67bb650433fe13 by Martijn Vels : Add btree statistics to CordzInfo, and reduce rounding errors PiperOrigin-RevId: 388715878 -- 105bcbf80de649937e693b29b18220f9e6841a51 by Evan Brown : Skip length checking when constructing absl::string_view from `const char*`. The length check causes unnecessary code bloat. PiperOrigin-RevId: 388271741 -- bed595158f24839efe49c65ae483f797d79fe0ae by Derek Mauro : Internal change PiperOrigin-RevId: 387713428 GitOrigin-RevId: 93c607726d663800b4bfa472cba043fd3f5d0e97 Change-Id: I2a4840f5ffcd7f70b7d7d45cce66f23c42cf565f --- absl/flags/internal/sequence_lock.h | 2 +- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 1 + absl/strings/cord.cc | 180 +++++++++++---------- absl/strings/cord.h | 37 ++--- absl/strings/cord_test.cc | 2 +- absl/strings/internal/cord_internal.cc | 1 + absl/strings/internal/cord_internal.h | 6 + absl/strings/internal/cord_rep_btree.cc | 21 ++- absl/strings/internal/cord_rep_btree.h | 32 +++- absl/strings/internal/cord_rep_btree_test.cc | 43 +++++ absl/strings/internal/cordz_info.cc | 51 +++--- .../strings/internal/cordz_info_statistics_test.cc | 133 ++++++++++++++- absl/strings/internal/cordz_statistics.h | 3 + absl/strings/string_view.h | 4 +- 15 files changed, 369 insertions(+), 148 deletions(-) diff --git a/absl/flags/internal/sequence_lock.h b/absl/flags/internal/sequence_lock.h index 807b2a73..36318ab9 100644 --- a/absl/flags/internal/sequence_lock.h +++ b/absl/flags/internal/sequence_lock.h @@ -49,7 +49,7 @@ inline constexpr size_t AlignUp(size_t x, size_t align) { // The memory reads and writes protected by this lock must use the provided // `TryRead()` and `Write()` functions. These functions behave similarly to // `memcpy()`, with one oddity: the protected data must be an array of -// `std::atomic`. This is to comply with the C++ standard, which +// `std::atomic`. This is to comply with the C++ standard, which // considers data races on non-atomic objects to be undefined behavior. See "Can // Seqlocks Get Along With Programming Language Memory Models?"[1] by Hans J. // Boehm for more details. diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 9720b683..f9735b4b 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -318,6 +318,7 @@ cc_test( ":strings", "//absl/base:config", "//absl/base:raw_logging_internal", + "//absl/cleanup", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 88f076a8..8ad5f9c7 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -952,6 +952,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base + absl::cleanup absl::config absl::cord_internal absl::cord_rep_test_util diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index f5aa6e47..ecbd2dbd 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -36,8 +36,8 @@ #include "absl/container/inlined_vector.h" #include "absl/strings/escaping.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_flat.h" -#include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_update_scope.h" #include "absl/strings/internal/cordz_update_tracker.h" @@ -51,20 +51,20 @@ namespace absl { ABSL_NAMESPACE_BEGIN using ::absl::cord_internal::CordRep; +using ::absl::cord_internal::CordRepBtree; using ::absl::cord_internal::CordRepConcat; using ::absl::cord_internal::CordRepExternal; using ::absl::cord_internal::CordRepFlat; -using ::absl::cord_internal::CordRepRing; using ::absl::cord_internal::CordRepSubstring; using ::absl::cord_internal::CordzUpdateTracker; using ::absl::cord_internal::InlineData; using ::absl::cord_internal::kMaxFlatLength; using ::absl::cord_internal::kMinFlatLength; +using ::absl::cord_internal::BTREE; using ::absl::cord_internal::CONCAT; using ::absl::cord_internal::EXTERNAL; using ::absl::cord_internal::FLAT; -using ::absl::cord_internal::RING; using ::absl::cord_internal::SUBSTRING; using ::absl::cord_internal::kInlinedVectorSize; @@ -100,8 +100,8 @@ static constexpr uint64_t min_length[] = { static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length); -static inline bool cord_ring_enabled() { - return cord_internal::cord_ring_buffer_enabled.load( +static inline bool btree_enabled() { + return cord_internal::cord_btree_enabled.load( std::memory_order_relaxed); } @@ -218,27 +218,25 @@ static CordRepFlat* CreateFlat(const char* data, size_t length, return flat; } -// Creates a new flat or ringbuffer out of the specified array. +// Creates a new flat or Btree out of the specified array. // The returned node has a refcount of 1. -static CordRep* RingNewTree(const char* data, size_t length, - size_t alloc_hint) { +static CordRep* NewBtree(const char* data, size_t length, size_t alloc_hint) { if (length <= kMaxFlatLength) { return CreateFlat(data, length, alloc_hint); } CordRepFlat* flat = CreateFlat(data, kMaxFlatLength, 0); data += kMaxFlatLength; length -= kMaxFlatLength; - size_t extra = (length - 1) / kMaxFlatLength + 1; - auto* root = CordRepRing::Create(flat, extra); - return CordRepRing::Append(root, {data, length}, alloc_hint); + auto* root = CordRepBtree::Create(flat); + return CordRepBtree::Append(root, {data, length}, alloc_hint); } // Create a new tree out of the specified array. // The returned node has a refcount of 1. static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) { if (length == 0) return nullptr; - if (cord_ring_enabled()) { - return RingNewTree(data, length, alloc_hint); + if (btree_enabled()) { + return NewBtree(data, length, alloc_hint); } absl::FixedArray reps((length - 1) / kMaxFlatLength + 1); size_t n = 0; @@ -346,10 +344,10 @@ inline void Cord::InlineRep::remove_prefix(size_t n) { reduce_size(n); } -// Returns `rep` converted into a CordRepRing. -// Directly returns `rep` if `rep` is already a CordRepRing. -static CordRepRing* ForceRing(CordRep* rep, size_t extra) { - return (rep->tag == RING) ? rep->ring() : CordRepRing::Create(rep, extra); +// Returns `rep` converted into a CordRepBtree. +// Directly returns `rep` if `rep` is already a CordRepBtree. +static CordRepBtree* ForceBtree(CordRep* rep) { + return rep->tag == BTREE ? rep->btree() : CordRepBtree::Create(rep); } void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, @@ -357,8 +355,8 @@ void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, assert(!is_tree()); if (!data_.is_empty()) { CordRepFlat* flat = MakeFlatWithExtraCapacity(0); - if (cord_ring_enabled()) { - tree = CordRepRing::Append(CordRepRing::Create(flat, 1), tree); + if (btree_enabled()) { + tree = CordRepBtree::Append(CordRepBtree::Create(flat), tree); } else { tree = Concat(flat, tree); } @@ -369,8 +367,8 @@ void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) { assert(is_tree()); const CordzUpdateScope scope(data_.cordz_info(), method); - if (cord_ring_enabled()) { - tree = CordRepRing::Append(ForceRing(data_.as_tree(), 1), tree); + if (btree_enabled()) { + tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree); } else { tree = Concat(data_.as_tree(), tree); } @@ -391,8 +389,8 @@ void Cord::InlineRep::PrependTreeToInlined(CordRep* tree, assert(!is_tree()); if (!data_.is_empty()) { CordRepFlat* flat = MakeFlatWithExtraCapacity(0); - if (cord_ring_enabled()) { - tree = CordRepRing::Prepend(CordRepRing::Create(flat, 1), tree); + if (btree_enabled()) { + tree = CordRepBtree::Prepend(CordRepBtree::Create(flat), tree); } else { tree = Concat(tree, flat); } @@ -404,8 +402,8 @@ void Cord::InlineRep::PrependTreeToTree(CordRep* tree, MethodIdentifier method) { assert(is_tree()); const CordzUpdateScope scope(data_.cordz_info(), method); - if (cord_ring_enabled()) { - tree = CordRepRing::Prepend(ForceRing(data_.as_tree(), 1), tree); + if (btree_enabled()) { + tree = CordRepBtree::Prepend(ForceBtree(data_.as_tree()), tree); } else { tree = Concat(tree, data_.as_tree()); } @@ -427,8 +425,8 @@ void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { // written to region and the actual size increase will be written to size. static inline bool PrepareAppendRegion(CordRep* root, char** region, size_t* size, size_t max_length) { - if (root->tag == RING && root->refcount.IsOne()) { - Span span = root->ring()->GetAppendBuffer(max_length); + if (root->tag == BTREE && root->refcount.IsOne()) { + Span span = root->btree()->GetAppendBuffer(max_length); if (!span.empty()) { *region = span.data(); *size = span.size(); @@ -500,8 +498,8 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, *region = new_node->Data(); *size = new_node->length; - if (cord_ring_enabled()) { - rep = CordRepRing::Append(ForceRing(rep, 1), new_node); + if (btree_enabled()) { + rep = CordRepBtree::Append(ForceBtree(rep), new_node); } else { rep = Concat(rep, new_node); } @@ -511,8 +509,13 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, // If the rep is a leaf, this will increment the value at total_mem_usage and // will return true. static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { + size_t maybe_sub_size = 0; + if (rep->tag == SUBSTRING) { + maybe_sub_size = sizeof(cord_internal::CordRepSubstring); + rep = rep->substring()->child; + } if (rep->tag >= FLAT) { - *total_mem_usage += rep->flat()->AllocatedSize(); + *total_mem_usage += maybe_sub_size + rep->flat()->AllocatedSize(); return true; } if (rep->tag == EXTERNAL) { @@ -520,8 +523,9 @@ static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { // assume it is 'at least' a word / pointer to data. In the future we may // choose to use the 'data' byte as a tag to identify the types of some // well-known externals, such as a std::string instance. - *total_mem_usage += - sizeof(cord_internal::CordRepExternalImpl) + rep->length; + *total_mem_usage += maybe_sub_size + + sizeof(cord_internal::CordRepExternalImpl) + + rep->length; return true; } return false; @@ -690,9 +694,11 @@ void Cord::InlineRep::AppendArray(absl::string_view src, return; } - if (cord_ring_enabled()) { - rep = ForceRing(rep, (src.size() - 1) / kMaxFlatLength + 1); - rep = CordRepRing::Append(rep->ring(), src); + if (btree_enabled()) { + // TODO(b/192061034): keep legacy 10% growth rate: consider other rates. + rep = ForceBtree(rep); + const size_t alloc_hint = (std::min)(kMaxFlatLength, rep->length / 10); + rep = CordRepBtree::Append(rep->btree(), src, alloc_hint); } else { // Use new block(s) for any remaining bytes that were not handled above. // Alloc extra memory only if the right child of the root of the new tree @@ -769,9 +775,13 @@ inline void Cord::AppendImpl(C&& src) { contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord); } -void Cord::Append(const Cord& src) { AppendImpl(src); } +void Cord::Append(const Cord& src) { + AppendImpl(src); +} -void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); } +void Cord::Append(Cord&& src) { + AppendImpl(std::move(src)); +} template > void Cord::Append(T&& src) { @@ -923,8 +933,10 @@ void Cord::RemovePrefix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemovePrefix; CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag == RING) { - tree = CordRepRing::RemovePrefix(tree->ring(), n); + if (tree->tag == BTREE) { + CordRep* old = tree; + tree = tree->btree()->SubTree(n, tree->length - n); + CordRep::Unref(old); } else { CordRep* newrep = RemovePrefixFrom(tree, n); CordRep::Unref(tree); @@ -944,8 +956,10 @@ void Cord::RemoveSuffix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemoveSuffix; CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag == RING) { - tree = CordRepRing::RemoveSuffix(tree->ring(), n); + if (tree->tag == BTREE) { + CordRep* old = tree; + tree = tree->btree()->SubTree(0, tree->length - n); + CordRep::Unref(old); } else { CordRep* newrep = RemoveSuffixFrom(tree, n); CordRep::Unref(tree); @@ -1037,9 +1051,8 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { return sub_cord; } - if (tree->tag == RING) { - CordRepRing* ring = CordRep::Ref(tree)->ring(); - tree = CordRepRing::SubRing(ring, pos, new_size); + if (tree->tag == BTREE) { + tree = tree->btree()->SubTree(pos, new_size); } else { tree = NewSubRange(tree, pos, new_size); } @@ -1227,8 +1240,8 @@ bool ComputeCompareResult(int memcmp_res) { } // namespace -// Helper routine. Locates the first flat chunk of the Cord without -// initializing the iterator. +// Helper routine. Locates the first flat or external chunk of the Cord without +// initializing the iterator, and returns a string_view referencing the data. inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { if (!is_tree()) { return absl::string_view(data_.as_chars(), data_.inline_size()); @@ -1243,8 +1256,13 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { return absl::string_view(node->external()->base, node->length); } - if (node->tag == RING) { - return node->ring()->entry_data(node->ring()->head()); + if (node->tag == BTREE) { + CordRepBtree* tree = node->btree(); + int height = tree->height(); + while (--height >= 0) { + tree = tree->Edge(CordRepBtree::kFront)->btree(); + } + return tree->Data(tree->begin()); } // Walk down the left branches until we hit a non-CONCAT node. @@ -1506,22 +1524,21 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { return subcord; } - if (ring_reader_) { + if (btree_reader_) { size_t chunk_size = current_chunk_.size(); if (n <= chunk_size && n <= kMaxBytesToCopy) { subcord = Cord(current_chunk_.substr(0, n), method); + if (n < chunk_size) { + current_chunk_.remove_prefix(n); + } else { + current_chunk_ = btree_reader_.Next(); + } } else { - auto* ring = CordRep::Ref(ring_reader_.ring())->ring(); - size_t offset = ring_reader_.length() - bytes_remaining_; - CordRep* rep = CordRepRing::SubRing(ring, offset, n); + CordRep* rep; + current_chunk_ = btree_reader_.Read(n, chunk_size, rep); subcord.contents_.EmplaceTree(rep, method); } - if (n < chunk_size) { - bytes_remaining_ -= n; - current_chunk_.remove_prefix(n); - } else { - AdvanceBytesRing(n); - } + bytes_remaining_ -= n; return subcord; } @@ -1698,8 +1715,8 @@ char Cord::operator[](size_t i) const { if (rep->tag >= FLAT) { // Get the "i"th character directly from the flat array. return rep->flat()->Data()[offset]; - } else if (rep->tag == RING) { - return rep->ring()->GetCharacter(offset); + } else if (rep->tag == BTREE) { + return rep->btree()->GetCharacter(offset); } else if (rep->tag == EXTERNAL) { // Get the "i"th character from the external array. return rep->external()->base[offset]; @@ -1758,8 +1775,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 == BTREE) { + return rep->btree()->IsFlat(fragment); } else if (rep->tag == SUBSTRING) { CordRep* child = rep->substring()->child; if (child->tag >= FLAT) { @@ -1770,9 +1787,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); + } else if (child->tag == BTREE) { + return child->btree()->IsFlat(rep->substring()->start, rep->length, + fragment); } } return false; @@ -1781,7 +1798,7 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ void Cord::ForEachChunkAux( absl::cord_internal::CordRep* rep, absl::FunctionRef callback) { - if (rep->tag == RING) { + if (rep->tag == BTREE) { ChunkIterator it(rep), end; while (it != end) { callback(*it); @@ -1866,15 +1883,7 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length)); *os << "]\n"; } else { - assert(rep->tag == RING); - auto* ring = rep->ring(); - *os << "RING, entries = " << ring->entries() << "\n"; - CordRepRing::index_type head = ring->head(); - do { - DumpNode(ring->entry_child(head), include_data, os, - indent + kIndentStep); - head = ring->advance(head); - } while (head != ring->tail()); + CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os); } if (stack.empty()) break; rep = stack.back(); @@ -1967,15 +1976,18 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, } next_node = right; } - } else if (cur_node->tag == RING) { - total_mem_usage += CordRepRing::AllocSize(cur_node->ring()->capacity()); - const CordRepRing* ring = cur_node->ring(); - CordRepRing::index_type pos = ring->head(), tail = ring->tail(); - do { - CordRep* node = ring->entry_child(pos); - assert(node->tag >= FLAT || node->tag == EXTERNAL); - RepMemoryUsageLeaf(node, &total_mem_usage); - } while ((pos = ring->advance(pos)) != tail); + } else if (cur_node->tag == BTREE) { + total_mem_usage += sizeof(CordRepBtree); + const CordRepBtree* node = cur_node->btree(); + if (node->height() == 0) { + for (const CordRep* edge : node->Edges()) { + RepMemoryUsageLeaf(edge, &total_mem_usage); + } + } else { + for (const CordRep* edge : node->Edges()) { + tree_stack.push_back(edge); + } + } } else { // Since cur_node is not a leaf or a concat node it must be a substring. assert(cur_node->tag == SUBSTRING); diff --git a/absl/strings/cord.h b/absl/strings/cord.h index e758f1cd..ac1832f0 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -79,8 +79,9 @@ #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_reader.h" #include "absl/strings/internal/cord_rep_ring.h" -#include "absl/strings/internal/cord_rep_ring_reader.h" #include "absl/strings/internal/cordz_functions.h" #include "absl/strings/internal/cordz_info.h" #include "absl/strings/internal/cordz_statistics.h" @@ -370,8 +371,8 @@ class Cord { private: using CordRep = absl::cord_internal::CordRep; - using CordRepRing = absl::cord_internal::CordRepRing; - using CordRepRingReader = absl::cord_internal::CordRepRingReader; + using CordRepBtree = absl::cord_internal::CordRepBtree; + using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader; // Stack of right children of concat nodes that we have to visit. // Keep this at the end of the structure to avoid cache-thrashing. @@ -397,9 +398,9 @@ class Cord { // Stack specific operator++ ChunkIterator& AdvanceStack(); - // Ring buffer specific operator++ - ChunkIterator& AdvanceRing(); - void AdvanceBytesRing(size_t n); + // Btree specific operator++ + ChunkIterator& AdvanceBtree(); + void AdvanceBytesBtree(size_t n); // Iterates `n` bytes, where `n` is expected to be greater than or equal to // `current_chunk_.size()`. @@ -415,8 +416,8 @@ class Cord { // The number of bytes left in the `Cord` over which we are iterating. size_t bytes_remaining_ = 0; - // Cord reader for ring buffers. Empty if not traversing a ring buffer. - CordRepRingReader ring_reader_; + // Cord reader for cord btrees. Empty if not traversing a btree. + CordRepBtreeReader btree_reader_; // See 'Stack' alias definition. Stack stack_of_right_children_; @@ -1247,8 +1248,8 @@ inline bool Cord::StartsWith(absl::string_view rhs) const { } inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) { - if (tree->tag == cord_internal::RING) { - current_chunk_ = ring_reader_.Reset(tree->ring()); + if (tree->tag == cord_internal::BTREE) { + current_chunk_ = btree_reader_.Init(tree->btree()); return; } @@ -1271,20 +1272,20 @@ inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) } } -inline Cord::ChunkIterator& Cord::ChunkIterator::AdvanceRing() { - current_chunk_ = ring_reader_.Next(); +inline Cord::ChunkIterator& Cord::ChunkIterator::AdvanceBtree() { + current_chunk_ = btree_reader_.Next(); return *this; } -inline void Cord::ChunkIterator::AdvanceBytesRing(size_t n) { +inline void Cord::ChunkIterator::AdvanceBytesBtree(size_t n) { assert(n >= current_chunk_.size()); bytes_remaining_ -= n; if (bytes_remaining_) { if (n == current_chunk_.size()) { - current_chunk_ = ring_reader_.Next(); + current_chunk_ = btree_reader_.Next(); } else { - size_t offset = ring_reader_.length() - bytes_remaining_; - current_chunk_ = ring_reader_.Seek(offset); + size_t offset = btree_reader_.length() - bytes_remaining_; + current_chunk_ = btree_reader_.Seek(offset); } } else { current_chunk_ = {}; @@ -1297,7 +1298,7 @@ inline Cord::ChunkIterator& Cord::ChunkIterator::operator++() { assert(bytes_remaining_ >= current_chunk_.size()); bytes_remaining_ -= current_chunk_.size(); if (bytes_remaining_ > 0) { - return ring_reader_ ? AdvanceRing() : AdvanceStack(); + return btree_reader_ ? AdvanceBtree() : AdvanceStack(); } else { current_chunk_ = {}; } @@ -1339,7 +1340,7 @@ inline void Cord::ChunkIterator::AdvanceBytes(size_t n) { if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) { RemoveChunkPrefix(n); } else if (n != 0) { - ring_reader_ ? AdvanceBytesRing(n) : AdvanceBytesSlowPath(n); + btree_reader_ ? AdvanceBytesBtree(n) : AdvanceBytesSlowPath(n); } } diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 597378cc..0c0a8a7a 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -366,7 +366,7 @@ TEST(Cord, Subcord) { absl::Cord a; AppendWithFragments(s, &rng, &a); - ASSERT_EQ(s.size(), a.size()); + ASSERT_EQ(s, std::string(a)); // Check subcords of a, from a variety of interesting points. std::set positions; diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index f79cb628..1767e6fc 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -31,6 +31,7 @@ ABSL_CONST_INIT std::atomic cord_ring_buffer_enabled( kCordEnableRingBufferDefault); ABSL_CONST_INIT std::atomic shallow_subcords_enabled( kCordShallowSubcordsDefault); +ABSL_CONST_INIT std::atomic cord_btree_exhaustive_validation(false); void CordRep::Destroy(CordRep* rep) { assert(rep != nullptr); diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 371a7f9c..8ae644ba 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -46,6 +46,12 @@ extern std::atomic cord_btree_enabled; extern std::atomic cord_ring_buffer_enabled; extern std::atomic shallow_subcords_enabled; +// `cord_btree_exhaustive_validation` can be set to force exhaustive validation +// in debug assertions, and code that calls `IsValid()` explicitly. By default, +// assertions should be relatively cheap and AssertValid() can easily lead to +// O(n^2) complexity as recursive / full tree validation is O(n). +extern std::atomic cord_btree_exhaustive_validation; + inline void enable_cord_btree(bool enable) { cord_btree_enabled.store(enable, std::memory_order_relaxed); } diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 6978cfd2..fd3a0045 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -32,6 +32,8 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { +constexpr size_t CordRepBtree::kMaxCapacity; // NOLINT: needed for c++ < c++17 + namespace { using NodeStack = CordRepBtree * [CordRepBtree::kMaxDepth]; @@ -42,6 +44,10 @@ using CopyResult = CordRepBtree::CopyResult; constexpr auto kFront = CordRepBtree::kFront; constexpr auto kBack = CordRepBtree::kBack; +inline bool exhaustive_validation() { + return cord_btree_exhaustive_validation.load(std::memory_order_relaxed); +} + // Implementation of the various 'Dump' functions. // Prints the entire tree structure or 'rep'. External callers should // not specify 'depth' and leave it to its default (0) value. @@ -357,7 +363,7 @@ void CordRepBtree::DestroyNonLeaf(CordRepBtree* tree, size_t begin, Delete(tree); } -bool CordRepBtree::IsValid(const CordRepBtree* tree) { +bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { #define NODE_CHECK_VALID(x) \ if (!(x)) { \ ABSL_RAW_LOG(ERROR, "CordRepBtree::CheckValid() FAILED: %s", #x); \ @@ -389,9 +395,9 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree) { child_length += edge->length; } NODE_CHECK_EQ(child_length, tree->length); - if (tree->height() > 0) { + if ((!shallow || exhaustive_validation()) && tree->height() > 0) { for (CordRep* edge : tree->Edges()) { - if (!IsValid(edge->btree())) return false; + if (!IsValid(edge->btree(), shallow)) return false; } } return true; @@ -402,16 +408,17 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree) { #ifndef NDEBUG -CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree) { - if (!IsValid(tree)) { +CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, bool shallow) { + if (!IsValid(tree, shallow)) { Dump(tree, "CordRepBtree validation failed:", false, std::cout); ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); } return tree; } -const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree) { - if (!IsValid(tree)) { +const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree, + bool shallow) { + if (!IsValid(tree, shallow)) { Dump(tree, "CordRepBtree validation failed:", false, std::cout); ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); } diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 7d854731..56e1e4af 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -266,10 +266,28 @@ class CordRepBtree : public CordRep { // holding a FLAT or EXTERNAL child rep. static bool IsDataEdge(const CordRep* rep); - // Diagnostics - static bool IsValid(const CordRepBtree* tree); - static CordRepBtree* AssertValid(CordRepBtree* tree); - static const CordRepBtree* AssertValid(const CordRepBtree* tree); + // Diagnostics: returns true if `tree` is valid and internally consistent. + // If `shallow` is false, then the provided top level node and all child nodes + // below it are recursively checked. If `shallow` is true, only the provided + // node in `tree` and the cumulative length, type and height of the direct + // child nodes of `tree` are checked. The value of `shallow` is ignored if the + // internal `cord_btree_exhaustive_validation` diagnostics variable is true, + // in which case the performed validations works as if `shallow` were false. + // This function is intended for debugging and testing purposes only. + static bool IsValid(const CordRepBtree* tree, bool shallow = false); + + // Diagnostics: asserts that the provided tree is valid. + // `AssertValid()` performs a shallow validation by default. `shallow` can be + // set to false in which case an exhaustive validation is performed. This + // function is implemented in terms of calling `IsValid()` and asserting the + // return value to be true. See `IsValid()` for more information. + // This function is intended for debugging and testing purposes only. + static CordRepBtree* AssertValid(CordRepBtree* tree, bool shallow = true); + static const CordRepBtree* AssertValid(const CordRepBtree* tree, + bool shallow = true); + + // Diagnostics: dump the contents of this tree to `stream`. + // This function is intended for debugging and testing purposes only. static void Dump(const CordRep* rep, std::ostream& stream); static void Dump(const CordRep* rep, absl::string_view label, std::ostream& stream); @@ -834,11 +852,13 @@ inline CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, CordRep* rep) { #ifdef NDEBUG -inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree) { +inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, + bool /* shallow */) { return tree; } -inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree) { +inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree, + bool /* shallow */) { return tree; } diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc index 7a0b7811..073a7d45 100644 --- a/absl/strings/internal/cord_rep_btree_test.cc +++ b/absl/strings/internal/cord_rep_btree_test.cc @@ -24,6 +24,7 @@ #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" +#include "absl/cleanup/cleanup.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_test_util.h" #include "absl/strings/str_cat.h" @@ -1346,6 +1347,48 @@ TEST(CordRepBtreeTest, AssertValid) { CordRep::Unref(tree); } +TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) { + // Restore exhaustive validation on any exit. + const bool exhaustive_validation = cord_btree_exhaustive_validation.load(); + auto cleanup = absl::MakeCleanup([exhaustive_validation] { + cord_btree_exhaustive_validation.store(exhaustive_validation); + }); + + // Create a tree of at least 2 levels, and mess with the original flat, which + // should go undetected in shallow mode as the flat is too far away, but + // should be detected in forced non-shallow mode. + CordRep* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(flat); + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * 2; + for (size_t i = 0; i < n; ++i) { + tree = CordRepBtree::Append(tree, MakeFlat("Hello world")); + } + flat->length = 100; + + cord_btree_exhaustive_validation.store(false); + EXPECT_FALSE(CordRepBtree::IsValid(tree)); + EXPECT_TRUE(CordRepBtree::IsValid(tree, true)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, false)); + CordRepBtree::AssertValid(tree); + CordRepBtree::AssertValid(tree, true); +#if defined(GTEST_HAS_DEATH_TEST) + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, false), ".*"); +#endif + + cord_btree_exhaustive_validation.store(true); + EXPECT_FALSE(CordRepBtree::IsValid(tree)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, true)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, false)); +#if defined(GTEST_HAS_DEATH_TEST) + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree), ".*"); + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, true), ".*"); +#endif + + flat->length = 3; + CordRep::Unref(tree); +} + } // namespace } // namespace cord_internal ABSL_NAMESPACE_END diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index a3a0b9c0..5c18bbc5 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -19,6 +19,7 @@ #include "absl/container/inlined_vector.h" #include "absl/debugging/stacktrace.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" @@ -83,19 +84,23 @@ class CordRepAnalyzer { // Process all top level linear nodes (substrings and flats). repref = CountLinearReps(repref, memory_usage_); - // We should have have either a concat or ring node node if not null. if (repref.rep != nullptr) { - assert(repref.rep->tag == RING || repref.rep->tag == CONCAT); if (repref.rep->tag == RING) { AnalyzeRing(repref); + } else if (repref.rep->tag == BTREE) { + AnalyzeBtree(repref); } else if (repref.rep->tag == CONCAT) { AnalyzeConcat(repref); + } else { + // We should have either a concat, btree, or ring node if not null. + assert(false); } } // Adds values to output statistics_.estimated_memory_usage += memory_usage_.total; - statistics_.estimated_fair_share_memory_usage += memory_usage_.fair_share; + statistics_.estimated_fair_share_memory_usage += + static_cast(memory_usage_.fair_share); } private: @@ -117,13 +122,13 @@ class CordRepAnalyzer { // Memory usage values struct MemoryUsage { size_t total = 0; - size_t fair_share = 0; + double fair_share = 0.0; // Adds 'size` memory usage to this class, with a cumulative (recursive) // reference count of `refcount` void Add(size_t size, size_t refcount) { total += size; - fair_share += size / refcount; + fair_share += static_cast(size) / refcount; } }; @@ -215,28 +220,32 @@ class CordRepAnalyzer { } } - // Counts the provided ring buffer child into `child_usage`. - void CountRingChild(const CordRep* child, MemoryUsage& child_usage) { - RepRef rep{child, static_cast(child->refcount.Get())}; - rep = CountLinearReps(rep, child_usage); - assert(rep.rep == nullptr); - } - - // Analyzes the provided ring. As ring buffers can have many child nodes, the - // effect of rounding errors can become non trivial, so we compute the totals - // first at the ring level, and then divide the fair share of the total - // including children fair share totals. + // Analyzes the provided ring. void AnalyzeRing(RepRef rep) { statistics_.node_count++; statistics_.node_counts.ring++; - MemoryUsage ring_usage; const CordRepRing* ring = rep.rep->ring(); - ring_usage.Add(CordRepRing::AllocSize(ring->capacity()), 1); + memory_usage_.Add(CordRepRing::AllocSize(ring->capacity()), rep.refcount); ring->ForEach([&](CordRepRing::index_type pos) { - CountRingChild(ring->entry_child(pos), ring_usage); + CountLinearReps(rep.Child(ring->entry_child(pos)), memory_usage_); }); - memory_usage_.total += ring_usage.total; - memory_usage_.fair_share += ring_usage.fair_share / rep.refcount; + } + + // Analyzes the provided btree. + void AnalyzeBtree(RepRef rep) { + statistics_.node_count++; + statistics_.node_counts.btree++; + memory_usage_.Add(sizeof(CordRepBtree), rep.refcount); + const CordRepBtree* tree = rep.rep->btree(); + if (tree->height() > 0) { + for (CordRep* edge : tree->Edges()) { + AnalyzeBtree(rep.Child(edge)); + } + } else { + for (CordRep* edge : tree->Edges()) { + CountLinearReps(rep.Child(edge), memory_usage_); + } + } } CordzStatistics& statistics_; diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc index 9f2842d9..7430d281 100644 --- a/absl/strings/internal/cordz_info_statistics_test.cc +++ b/absl/strings/internal/cordz_info_statistics_test.cc @@ -21,6 +21,7 @@ #include "absl/base/config.h" #include "absl/strings/cord.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_info.h" @@ -42,6 +43,8 @@ inline void PrintTo(const CordzStatistics& stats, std::ostream* s) { namespace { +using ::testing::Ge; + // Creates a flat of the specified allocated size CordRepFlat* Flat(size_t size) { // Round up to a tag size, as we are going to poke an exact tag size back into @@ -134,8 +137,8 @@ size_t SizeOf(const CordRepRing* rep) { } // Computes fair share memory used in a naive 'we dare to recurse' way. -size_t FairShare(CordRep* rep, size_t ref = 1) { - size_t self = 0, children = 0; +double FairShareImpl(CordRep* rep, size_t ref) { + double self = 0.0, children = 0.0; ref *= rep->refcount.Get(); if (rep->tag >= FLAT) { self = SizeOf(rep->flat()); @@ -143,22 +146,32 @@ size_t FairShare(CordRep* rep, size_t ref = 1) { self = SizeOf(rep->external()); } else if (rep->tag == SUBSTRING) { self = SizeOf(rep->substring()); - children = FairShare(rep->substring()->child, ref); + children = FairShareImpl(rep->substring()->child, ref); + } else if (rep->tag == BTREE) { + self = SizeOf(rep->btree()); + for (CordRep*edge : rep->btree()->Edges()) { + children += FairShareImpl(edge, ref); + } } else if (rep->tag == RING) { self = SizeOf(rep->ring()); rep->ring()->ForEach([&](CordRepRing::index_type i) { - self += FairShare(rep->ring()->entry_child(i)); + self += FairShareImpl(rep->ring()->entry_child(i), 1); }); } else if (rep->tag == CONCAT) { self = SizeOf(rep->concat()); - children = FairShare(rep->concat()->left, ref) + - FairShare(rep->concat()->right, ref); + children = FairShareImpl(rep->concat()->left, ref) + + FairShareImpl(rep->concat()->right, ref); } else { assert(false); } return self / ref + children; } +// Returns the fair share memory size from `ShareFhareImpl()` as a size_t. +size_t FairShare(CordRep* rep, size_t ref = 1) { + return static_cast(FairShareImpl(rep, ref)); +} + // Samples the cord and returns CordzInfo::GetStatistics() CordzStatistics SampleCord(CordRep* rep) { InlineData cord(rep); @@ -191,6 +204,7 @@ MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { STATS_MATCHER_EXPECT_EQ(node_counts.concat); STATS_MATCHER_EXPECT_EQ(node_counts.substring); STATS_MATCHER_EXPECT_EQ(node_counts.ring); + STATS_MATCHER_EXPECT_EQ(node_counts.btree); STATS_MATCHER_EXPECT_EQ(estimated_memory_usage); STATS_MATCHER_EXPECT_EQ(estimated_fair_share_memory_usage); @@ -424,6 +438,103 @@ TEST(CordzInfoStatisticsTest, SharedSubstringRing) { EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); } +TEST(CordzInfoStatisticsTest, BtreeLeaf) { + ASSERT_THAT(CordRepBtree::kMaxCapacity, Ge(3)); + RefHelper ref; + auto* flat1 = Flat(2000); + auto* flat2 = Flat(200); + auto* substr = Substring(flat2); + auto* external = External(3000); + + CordRepBtree* tree = CordRepBtree::Create(flat1); + tree = CordRepBtree::Append(tree, substr); + tree = CordRepBtree::Append(tree, external); + size_t flat3_count = CordRepBtree::kMaxCapacity - 3; + size_t flat3_size = 0; + for (size_t i = 0; i < flat3_count; ++i) { + auto* flat3 = Flat(70); + flat3_size += SizeOf(flat3); + tree = CordRepBtree::Append(tree, flat3); + } + ref.NeedsUnref(tree); + + CordzStatistics expected; + expected.size = tree->length; + expected.estimated_memory_usage = SizeOf(tree) + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(substr) + + flat3_size + SizeOf(external); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 1 + 3 + 1 + flat3_count; + expected.node_counts.flat = 2 + flat3_count; + expected.node_counts.flat_128 = flat3_count; + expected.node_counts.flat_256 = 1; + expected.node_counts.external = 1; + expected.node_counts.substring = 1; + expected.node_counts.btree = 1; + + EXPECT_THAT(SampleCord(tree), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, BtreeNodeShared) { + RefHelper ref; + static constexpr int leaf_count = 3; + const size_t flat3_count = CordRepBtree::kMaxCapacity - 3; + ASSERT_THAT(flat3_count, Ge(0)); + + CordRepBtree* tree = nullptr; + size_t mem_size = 0; + for (int i = 0; i < leaf_count; ++i) { + auto* flat1 = ref.Ref(Flat(2000), 9); + mem_size += SizeOf(flat1); + if (i == 0) { + tree = CordRepBtree::Create(flat1); + } else { + tree = CordRepBtree::Append(tree, flat1); + } + + auto* flat2 = Flat(200); + auto* substr = Substring(flat2); + mem_size += SizeOf(flat2) + SizeOf(substr); + tree = CordRepBtree::Append(tree, substr); + + auto* external = External(30); + mem_size += SizeOf(external); + tree = CordRepBtree::Append(tree, external); + + for (size_t i = 0; i < flat3_count; ++i) { + auto* flat3 = Flat(70); + mem_size += SizeOf(flat3); + tree = CordRepBtree::Append(tree, flat3); + } + + if (i == 0) { + mem_size += SizeOf(tree); + } else { + mem_size += SizeOf(tree->Edges().back()->btree()); + } + } + ref.NeedsUnref(tree); + + // Ref count: 2 for top (add 1), 5 for leaf 0 (add 4). + ref.Ref(tree, 1); + ref.Ref(tree->Edges().front(), 4); + + CordzStatistics expected; + expected.size = tree->length; + expected.estimated_memory_usage = SizeOf(tree) + mem_size; + expected.estimated_fair_share_memory_usage = FairShare(tree); + + expected.node_count = 1 + leaf_count * (1 + 3 + 1 + flat3_count); + expected.node_counts.flat = leaf_count * (2 + flat3_count); + expected.node_counts.flat_128 = leaf_count * flat3_count; + expected.node_counts.flat_256 = leaf_count; + expected.node_counts.external = leaf_count; + expected.node_counts.substring = leaf_count; + expected.node_counts.btree = 1 + leaf_count; + + EXPECT_THAT(SampleCord(tree), EqStatistics(expected)); +} + TEST(CordzInfoStatisticsTest, ThreadSafety) { Notification stop; static constexpr int kNumThreads = 8; @@ -471,9 +582,15 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) { CordRep::Unref(cord.as_tree()); cord.set_inline_size(0); } else { - // 50/50 Ring or Flat coin toss + // Coin toss to 25% ring, 25% btree, and 50% flat. CordRep* rep = Flat(256); - rep = (coin_toss(gen) != 0) ? CordRepRing::Create(rep) : rep; + if (coin_toss(gen) != 0) { + if (coin_toss(gen) != 0) { + rep = CordRepRing::Create(rep); + } else { + rep = CordRepBtree::Create(rep); + } + } cord.make_tree(rep); // 50/50 sample diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h index e03c651e..da4c7dbb 100644 --- a/absl/strings/internal/cordz_statistics.h +++ b/absl/strings/internal/cordz_statistics.h @@ -40,6 +40,7 @@ struct CordzStatistics { size_t substring = 0; // #substring reps size_t concat = 0; // #concat reps size_t ring = 0; // #ring buffer reps + size_t btree = 0; // #btree reps }; // The size of the cord in bytes. This matches the result of Cord::size(). @@ -61,6 +62,8 @@ struct CordzStatistics { // The total number of nodes referenced by this cord. // For ring buffer Cords, this includes the 'ring buffer' node. + // For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all + // the substring, flat and external nodes referenced by the tree. // A value of 0 implies the property has not been recorded. int64_t node_count = 0; diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index ec6c431f..ea760526 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -198,9 +198,9 @@ class string_view { // Implicit constructor of a `string_view` from NUL-terminated `str`. When // accepting possibly null strings, use `absl::NullSafeStringView(str)` // instead (see below). + // The length check is skipped since it is unnecessary and causes code bloat. constexpr string_view(const char* str) // NOLINT(runtime/explicit) - : ptr_(str), - length_(str ? CheckLengthInternal(StrlenInternal(str)) : 0) {} + : ptr_(str), length_(str ? StrlenInternal(str) : 0) {} // Implicit constructor of a `string_view` from a `const char*` and length. constexpr string_view(const char* data, size_type len) -- cgit v1.2.3 From 8e088c5f3c290c5ac53dd5010fd501d80b483115 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 10 Aug 2021 08:52:44 -0700 Subject: Export of internal Abseil changes -- 77cd6291781bc39e8472c706163d6951fe2ae573 by Derek Mauro : absl::uint128: Use intrinsics for more operations when available This change also inlines the division and modulus operators when intrinsics are available for better code generation. Fixes #987 PiperOrigin-RevId: 389895706 -- fa23339584599e07ebcb4d0a857e2553b017757c by Abseil Team : only hide retired flags if human-readable output is requested PiperOrigin-RevId: 389835452 -- f1111f2b88359d4b253d4d81681c8a488458a36e by Martijn Vels : Add helpers IsFlat(), IsExternal(), etc to improve readability PiperOrigin-RevId: 389779333 -- 785b8712261e41695ebeeb64b4317f93b37adc11 by Martijn Vels : Split off 'concat' and 'btree' RepMemoryUsageLeaf and RepMemoryUsageDataEdge PiperOrigin-RevId: 389701120 -- 5264bffebffc2b377bf7e18f0ce69a3ed38c6629 by CJ Johnson : Eagerly destroy `Callback` in `absl::Cleanup` PiperOrigin-RevId: 389678813 -- a05312f0668458e97c50ca932c8f974c1508ebf2 by Abseil Team : Have one instance of empty_group per program, rather than one per translation unit. https://stackoverflow.com/questions/185624/static-variables-in-an-inlined-function PiperOrigin-RevId: 389185845 GitOrigin-RevId: 77cd6291781bc39e8472c706163d6951fe2ae573 Change-Id: Iac8d9cb27707a9562c831c77a552d1fb4bb0405f --- absl/cleanup/cleanup.h | 8 +- absl/cleanup/cleanup_test.cc | 44 ++++++++ absl/cleanup/internal/cleanup.h | 39 +++++-- absl/container/internal/raw_hash_set.cc | 6 + absl/container/internal/raw_hash_set.h | 8 +- absl/flags/internal/usage.cc | 17 ++- absl/flags/internal/usage_test.cc | 3 + absl/numeric/int128.cc | 13 +-- absl/numeric/int128.h | 73 +++++++++++-- absl/strings/cord.cc | 182 +++++++++++++++++-------------- absl/strings/cord_test.cc | 188 ++++++++++++++++++++------------ absl/strings/internal/cord_internal.h | 25 +++-- absl/strings/internal/cord_rep_btree.cc | 12 +- absl/strings/internal/cord_rep_btree.h | 8 +- absl/strings/internal/cord_rep_ring.cc | 12 +- absl/strings/internal/cord_rep_ring.h | 4 +- 16 files changed, 418 insertions(+), 224 deletions(-) diff --git a/absl/cleanup/cleanup.h b/absl/cleanup/cleanup.h index 61b53d55..960ccd08 100644 --- a/absl/cleanup/cleanup.h +++ b/absl/cleanup/cleanup.h @@ -86,25 +86,25 @@ class ABSL_MUST_USE_RESULT Cleanup final { "Callbacks that return values are not supported."); public: - Cleanup(Callback callback) // NOLINT - : storage_(std::move(callback), /* is_callback_engaged = */ true) {} + Cleanup(Callback callback) : storage_(std::move(callback)) {} // NOLINT Cleanup(Cleanup&& other) = default; void Cancel() && { ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); - storage_.DisengageCallback(); + storage_.DestroyCallback(); } void Invoke() && { ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); - storage_.DisengageCallback(); storage_.InvokeCallback(); + storage_.DestroyCallback(); } ~Cleanup() { if (storage_.IsCallbackEngaged()) { storage_.InvokeCallback(); + storage_.DestroyCallback(); } } diff --git a/absl/cleanup/cleanup_test.cc b/absl/cleanup/cleanup_test.cc index 792595d6..46b88589 100644 --- a/absl/cleanup/cleanup_test.cc +++ b/absl/cleanup/cleanup_test.cc @@ -264,4 +264,48 @@ TYPED_TEST(CleanupTest, Move) { EXPECT_FALSE(called); // Destructor shouldn't invoke the callback } +int DestructionCount = 0; + +struct DestructionCounter { + void operator()() {} + + ~DestructionCounter() { ++DestructionCount; } +}; + +TYPED_TEST(CleanupTest, DestructorDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + } + + EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys +} + +TYPED_TEST(CleanupTest, CancelDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + + std::move(cleanup).Cancel(); + EXPECT_EQ(DestructionCount, 1); // Cancel destroys + } + + EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy +} + +TYPED_TEST(CleanupTest, InvokeDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + + std::move(cleanup).Invoke(); + EXPECT_EQ(DestructionCount, 1); // Invoke destroys + } + + EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy +} + } // namespace diff --git a/absl/cleanup/internal/cleanup.h b/absl/cleanup/internal/cleanup.h index b4c40737..2783fcb7 100644 --- a/absl/cleanup/internal/cleanup.h +++ b/absl/cleanup/internal/cleanup.h @@ -15,10 +15,12 @@ #ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_ #define ABSL_CLEANUP_INTERNAL_CLEANUP_H_ +#include #include #include #include "absl/base/internal/invoke.h" +#include "absl/base/macros.h" #include "absl/base/thread_annotations.h" #include "absl/utility/utility.h" @@ -45,14 +47,22 @@ class Storage { public: Storage() = delete; - Storage(Callback callback, bool is_callback_engaged) - : callback_(std::move(callback)), - is_callback_engaged_(is_callback_engaged) {} + explicit Storage(Callback callback) { + // Placement-new into a character buffer is used for eager destruction when + // the cleanup is invoked or cancelled. To ensure this optimizes well, the + // behavior is implemented locally instead of using an absl::optional. + ::new (GetCallbackBuffer()) Callback(std::move(callback)); + is_callback_engaged_ = true; + } + + Storage(Storage&& other) { + ABSL_HARDENING_ASSERT(other.IsCallbackEngaged()); - Storage(Storage&& other) - : callback_(std::move(other.callback_)), - is_callback_engaged_( - absl::exchange(other.is_callback_engaged_, false)) {} + ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback())); + is_callback_engaged_ = true; + + other.DestroyCallback(); + } Storage(const Storage& other) = delete; @@ -60,17 +70,26 @@ class Storage { Storage& operator=(const Storage& other) = delete; + void* GetCallbackBuffer() { return static_cast(+callback_buffer_); } + + Callback& GetCallback() { + return *reinterpret_cast(GetCallbackBuffer()); + } + bool IsCallbackEngaged() const { return is_callback_engaged_; } - void DisengageCallback() { is_callback_engaged_ = false; } + void DestroyCallback() { + is_callback_engaged_ = false; + GetCallback().~Callback(); + } void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS { - std::move(callback_)(); + std::move(GetCallback())(); } private: - Callback callback_; bool is_callback_engaged_; + alignas(Callback) char callback_buffer_[sizeof(Callback)]; }; } // namespace cleanup_internal diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index a5dee6aa..e219956f 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -23,6 +23,12 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +ABSL_CONST_INIT ABSL_DLL alignas(16) const ctrl_t kEmptyGroup[16] = { + ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; + constexpr size_t Group::kWidth; // Returns "random" seed. diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index d7783263..bafafd46 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -291,13 +291,9 @@ static_assert(ctrl_t::kDeleted == static_cast(-2), // A single block of empty control bytes for tables without any slots allocated. // This enables removing a branch in the hot path of find(). +ABSL_DLL extern const ctrl_t kEmptyGroup[16]; inline ctrl_t* EmptyGroup() { - alignas(16) static constexpr ctrl_t empty_group[] = { - ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, - ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, - ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, - ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; - return const_cast(empty_group); + return const_cast(kEmptyGroup); } // Mixes a randomly generated per-process seed with `hash` and `ctrl` to diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index 949709e8..a883567f 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -255,9 +256,6 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, matching_flags; flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) { - // Ignore retired flags. - if (flag.IsRetired()) return; - // If the flag has been stripped, pretend that it doesn't exist. if (flag.Help() == flags_internal::kStrippedFlagHelp) return; @@ -275,6 +273,14 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, absl::string_view file_separator; // controls blank lines between files for (auto& package : matching_flags) { if (format == HelpFormat::kHumanReadable) { + // Hide packages with only retired flags + bool all_package_flags_are_retired = true; + for (const auto& flags_in_file : package.second) { + for (const auto* flag : flags_in_file.second) { + all_package_flags_are_retired &= flag->IsRetired(); + } + } + if (all_package_flags_are_retired) continue; out << package_separator; package_separator = "\n\n"; } @@ -334,8 +340,11 @@ void FlagsHelpImpl(std::ostream& out, // Produces the help message describing specific flag. void FlagHelp(std::ostream& out, const CommandLineFlag& flag, HelpFormat format) { - if (format == HelpFormat::kHumanReadable) + if (format == HelpFormat::kHumanReadable) { + // Ignore retired flags + if (flag.IsRetired()) return; flags_internal::FlagHelpHumanReadable(flag, out); + } } // -------------------------------------------------------------------- diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 044d71c8..5055640a 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -61,6 +61,9 @@ ABSL_FLAG( "Even more long long long long long long long long long long long long " "help message."); +ABSL_RETIRED_FLAG(int64_t, usage_reporting_test_flag_07, 1, + "usage_reporting_test_flag_07 help message"); + namespace { namespace flags = absl::flags_internal; diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 5160df79..17d88744 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -138,28 +138,21 @@ uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {} uint128::uint128(double v) : uint128(MakeUint128FromFloat(v)) {} uint128::uint128(long double v) : uint128(MakeUint128FromFloat(v)) {} +#if !defined(ABSL_HAVE_INTRINSIC_INT128) uint128 operator/(uint128 lhs, uint128 rhs) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return static_cast(lhs) / - static_cast(rhs); -#else // ABSL_HAVE_INTRINSIC_INT128 uint128 quotient = 0; uint128 remainder = 0; DivModImpl(lhs, rhs, "ient, &remainder); return quotient; -#endif // ABSL_HAVE_INTRINSIC_INT128 } + uint128 operator%(uint128 lhs, uint128 rhs) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return static_cast(lhs) % - static_cast(rhs); -#else // ABSL_HAVE_INTRINSIC_INT128 uint128 quotient = 0; uint128 remainder = 0; DivModImpl(lhs, rhs, "ient, &remainder); return remainder; -#endif // ABSL_HAVE_INTRINSIC_INT128 } +#endif // !defined(ABSL_HAVE_INTRINSIC_INT128) namespace { diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index 198aa195..a9cbe489 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -18,6 +18,10 @@ // ----------------------------------------------------------------------------- // // This header file defines 128-bit integer types, `uint128` and `int128`. +// +// TODO(absl-team): This module is inconsistent as many inline `uint128` methods +// are defined in this file, while many inline `int128` methods are defined in +// the `int128_*_intrinsic.inc` files. #ifndef ABSL_NUMERIC_INT128_H_ #define ABSL_NUMERIC_INT128_H_ @@ -783,8 +787,13 @@ inline uint128::operator long double() const { // Comparison operators. inline bool operator==(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) == + static_cast(rhs); +#else return (Uint128Low64(lhs) == Uint128Low64(rhs) && Uint128High64(lhs) == Uint128High64(rhs)); +#endif } inline bool operator!=(uint128 lhs, uint128 rhs) { @@ -819,52 +828,76 @@ constexpr inline int128 operator+(int128 val) { } inline uint128 operator-(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return -static_cast(val); +#else uint64_t hi = ~Uint128High64(val); uint64_t lo = ~Uint128Low64(val) + 1; if (lo == 0) ++hi; // carry return MakeUint128(hi, lo); +#endif } constexpr inline bool operator!(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return !static_cast(val); +#else return !Uint128High64(val) && !Uint128Low64(val); +#endif } // Logical operators. constexpr inline uint128 operator~(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return ~static_cast(val); +#else return MakeUint128(~Uint128High64(val), ~Uint128Low64(val)); +#endif } constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) | + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs), - Uint128Low64(lhs) | Uint128Low64(rhs)); + Uint128Low64(lhs) | Uint128Low64(rhs)); +#endif } constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) & + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs), - Uint128Low64(lhs) & Uint128Low64(rhs)); + Uint128Low64(lhs) & Uint128Low64(rhs)); +#endif } constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) ^ + static_cast(rhs); +#else return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs), - Uint128Low64(lhs) ^ Uint128Low64(rhs)); + Uint128Low64(lhs) ^ Uint128Low64(rhs)); +#endif } inline uint128& uint128::operator|=(uint128 other) { - hi_ |= other.hi_; - lo_ |= other.lo_; + *this = *this | other; return *this; } inline uint128& uint128::operator&=(uint128 other) { - hi_ &= other.hi_; - lo_ &= other.lo_; + *this = *this & other; return *this; } inline uint128& uint128::operator^=(uint128 other) { - hi_ ^= other.hi_; - lo_ ^= other.lo_; + *this = *this ^ other; return *this; } @@ -907,21 +940,31 @@ inline uint128 operator>>(uint128 lhs, int amount) { } inline uint128 operator+(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) + + static_cast(rhs); +#else uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), Uint128Low64(lhs) + Uint128Low64(rhs)); if (Uint128Low64(result) < Uint128Low64(lhs)) { // check for carry return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)); } return result; +#endif } inline uint128 operator-(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast(lhs) - + static_cast(rhs); +#else uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), Uint128Low64(lhs) - Uint128Low64(rhs)); if (Uint128Low64(lhs) < Uint128Low64(rhs)) { // check for carry return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)); } return result; +#endif } inline uint128 operator*(uint128 lhs, uint128 rhs) { @@ -951,6 +994,18 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { #endif // ABSL_HAVE_INTRINSIC128 } +#if defined(ABSL_HAVE_INTRINSIC_INT128) +inline uint128 operator/(uint128 lhs, uint128 rhs) { + return static_cast(lhs) / + static_cast(rhs); +} + +inline uint128 operator%(uint128 lhs, uint128 rhs) { + return static_cast(lhs) % + static_cast(rhs); +} +#endif + // Increment/decrement operators. inline uint128 uint128::operator++(int) { diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index ecbd2dbd..e9d72fa8 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -61,12 +61,6 @@ using ::absl::cord_internal::InlineData; using ::absl::cord_internal::kMaxFlatLength; using ::absl::cord_internal::kMinFlatLength; -using ::absl::cord_internal::BTREE; -using ::absl::cord_internal::CONCAT; -using ::absl::cord_internal::EXTERNAL; -using ::absl::cord_internal::FLAT; -using ::absl::cord_internal::SUBSTRING; - using ::absl::cord_internal::kInlinedVectorSize; using ::absl::cord_internal::kMaxBytesToCopy; @@ -106,7 +100,7 @@ static inline bool btree_enabled() { } static inline bool IsRootBalanced(CordRep* node) { - if (node->tag != CONCAT) { + if (!node->IsConcat()) { return true; } else if (node->concat()->depth() <= 15) { return true; @@ -143,7 +137,7 @@ static inline CordRep* VerifyTree(CordRep* node) { // Return the depth of a node static int Depth(const CordRep* rep) { - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { return rep->concat()->depth(); } else { return 0; @@ -176,7 +170,7 @@ static CordRep* RawConcat(CordRep* left, CordRep* right) { } CordRepConcat* rep = new CordRepConcat(); - rep->tag = CONCAT; + rep->tag = cord_internal::CONCAT; SetConcatChildren(rep, left, right); return rep; @@ -273,7 +267,7 @@ static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) { CordRepSubstring* rep = new CordRepSubstring(); assert((offset + length) <= child->length); rep->length = length; - rep->tag = SUBSTRING; + rep->tag = cord_internal::SUBSTRING; rep->start = offset; rep->child = child; return VerifyTree(rep); @@ -347,7 +341,7 @@ inline void Cord::InlineRep::remove_prefix(size_t n) { // Returns `rep` converted into a CordRepBtree. // Directly returns `rep` if `rep` is already a CordRepBtree. static CordRepBtree* ForceBtree(CordRep* rep) { - return rep->tag == BTREE ? rep->btree() : CordRepBtree::Create(rep); + return rep->IsBtree() ? rep->btree() : CordRepBtree::Create(rep); } void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, @@ -425,7 +419,7 @@ void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { // written to region and the actual size increase will be written to size. static inline bool PrepareAppendRegion(CordRep* root, char** region, size_t* size, size_t max_length) { - if (root->tag == BTREE && root->refcount.IsOne()) { + if (root->IsBtree() && root->refcount.IsOne()) { Span span = root->btree()->GetAppendBuffer(max_length); if (!span.empty()) { *region = span.data(); @@ -436,11 +430,11 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, // Search down the right-hand path for a non-full FLAT node. CordRep* dst = root; - while (dst->tag == CONCAT && dst->refcount.IsOne()) { + while (dst->IsConcat() && dst->refcount.IsOne()) { dst = dst->concat()->right; } - if (dst->tag < FLAT || !dst->refcount.IsOne()) { + if (!dst->IsFlat() || !dst->refcount.IsOne()) { *region = nullptr; *size = 0; return false; @@ -506,19 +500,20 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, CommitTree(root, rep, scope, method); } -// If the rep is a leaf, this will increment the value at total_mem_usage and -// will return true. -static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { +// Computes the memory side of the provided edge which must be a valid data edge +// for a btrtee, i.e., a FLAT, EXTERNAL or SUBSTRING of a FLAT or EXTERNAL node. +static bool RepMemoryUsageDataEdge(const CordRep* rep, + size_t* total_mem_usage) { size_t maybe_sub_size = 0; - if (rep->tag == SUBSTRING) { + if (ABSL_PREDICT_FALSE(rep->IsSubstring())) { maybe_sub_size = sizeof(cord_internal::CordRepSubstring); rep = rep->substring()->child; } - if (rep->tag >= FLAT) { + if (rep->IsFlat()) { *total_mem_usage += maybe_sub_size + rep->flat()->AllocatedSize(); return true; } - if (rep->tag == EXTERNAL) { + if (rep->IsExternal()) { // We don't know anything about the embedded / bound data, but we can safely // assume it is 'at least' a word / pointer to data. In the future we may // choose to use the 'data' byte as a tag to identify the types of some @@ -531,6 +526,25 @@ static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { return false; } +// If the rep is a leaf, this will increment the value at total_mem_usage and +// will return true. +static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { + if (rep->IsFlat()) { + *total_mem_usage += rep->flat()->AllocatedSize(); + return true; + } + if (rep->IsExternal()) { + // We don't know anything about the embedded / bound data, but we can safely + // assume it is 'at least' a word / pointer to data. In the future we may + // choose to use the 'data' byte as a tag to identify the types of some + // well-known externals, such as a std::string instance. + *total_mem_usage += + sizeof(cord_internal::CordRepExternalImpl) + rep->length; + return true; + } + return false; +} + void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { assert(&src != this); assert(is_tree() || src.is_tree()); @@ -634,7 +648,7 @@ Cord& Cord::operator=(absl::string_view src) { } if (tree != nullptr) { CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag >= FLAT && tree->flat()->Capacity() >= length && + if (tree->IsFlat() && tree->flat()->Capacity() >= length && tree->refcount.IsOne()) { // Copy in place if the existing FLAT node is reusable. memmove(tree->flat()->Data(), data, length); @@ -753,7 +767,7 @@ inline void Cord::AppendImpl(C&& src) { contents_.AppendArray({src.contents_.data(), src_size}, method); return; } - if (src_tree->tag >= FLAT) { + if (src_tree->IsFlat()) { // src tree just has one flat node. contents_.AppendArray({src_tree->flat()->Data(), src_size}, method); return; @@ -843,7 +857,7 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { if (n == 0) return CordRep::Ref(node); absl::InlinedVector rhs_stack; - while (node->tag == CONCAT) { + while (node->IsConcat()) { assert(n <= node->length); if (n < node->concat()->left->length) { // Push right to stack, descend left. @@ -862,7 +876,7 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { } else { size_t start = n; size_t len = node->length - n; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { // Consider in-place update of node, similar to in RemoveSuffixFrom(). start += node->substring()->start; node = node->substring()->child; @@ -885,7 +899,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { absl::InlinedVector lhs_stack; bool inplace_ok = node->refcount.IsOne(); - while (node->tag == CONCAT) { + while (node->IsConcat()) { assert(n <= node->length); if (n < node->concat()->right->length) { // Push left to stack, descend right. @@ -902,7 +916,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { if (n == 0) { CordRep::Ref(node); - } else if (inplace_ok && node->tag != EXTERNAL) { + } else if (inplace_ok && !node->IsExternal()) { // Consider making a new buffer if the current node capacity is much // larger than the new length. CordRep::Ref(node); @@ -910,7 +924,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { } else { size_t start = 0; size_t len = node->length - n; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { start = node->substring()->start; node = node->substring()->child; } @@ -933,7 +947,7 @@ void Cord::RemovePrefix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemovePrefix; CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag == BTREE) { + if (tree->IsBtree()) { CordRep* old = tree; tree = tree->btree()->SubTree(n, tree->length - n); CordRep::Unref(old); @@ -956,7 +970,7 @@ void Cord::RemoveSuffix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemoveSuffix; CordzUpdateScope scope(contents_.cordz_info(), method); - if (tree->tag == BTREE) { + if (tree->IsBtree()) { CordRep* old = tree; tree = tree->btree()->SubTree(0, tree->length - n); CordRep::Unref(old); @@ -998,8 +1012,8 @@ static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { results.push_back(Concat(left, right)); } else if (pos == 0 && n == node->length) { results.push_back(CordRep::Ref(node)); - } else if (node->tag != CONCAT) { - if (node->tag == SUBSTRING) { + } else if (!node->IsConcat()) { + if (node->IsSubstring()) { pos += node->substring()->start; node = node->substring()->child; } @@ -1051,7 +1065,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { return sub_cord; } - if (tree->tag == BTREE) { + if (tree->IsBtree()) { tree = tree->btree()->SubTree(pos, new_size); } else { tree = NewSubRange(tree, pos, new_size); @@ -1076,7 +1090,7 @@ class CordForest { CordRep* node = pending.back(); pending.pop_back(); CheckNode(node); - if (ABSL_PREDICT_FALSE(node->tag != CONCAT)) { + if (ABSL_PREDICT_FALSE(!node->IsConcat())) { AddNode(node); continue; } @@ -1170,7 +1184,7 @@ class CordForest { static void CheckNode(CordRep* node) { ABSL_INTERNAL_CHECK(node->length != 0u, ""); - if (node->tag == CONCAT) { + if (node->IsConcat()) { ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ""); ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, ""); ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length + @@ -1190,7 +1204,7 @@ class CordForest { static CordRep* Rebalance(CordRep* node) { VerifyTree(node); - assert(node->tag == CONCAT); + assert(node->IsConcat()); if (node->length == 0) { return nullptr; @@ -1248,15 +1262,15 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { } CordRep* node = tree(); - if (node->tag >= FLAT) { + if (node->IsFlat()) { return absl::string_view(node->flat()->Data(), node->length); } - if (node->tag == EXTERNAL) { + if (node->IsExternal()) { return absl::string_view(node->external()->base, node->length); } - if (node->tag == BTREE) { + if (node->IsBtree()) { CordRepBtree* tree = node->btree(); int height = tree->height(); while (--height >= 0) { @@ -1266,7 +1280,7 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { } // Walk down the left branches until we hit a non-CONCAT node. - while (node->tag == CONCAT) { + while (node->IsConcat()) { node = node->concat()->left; } @@ -1275,16 +1289,16 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { size_t length = node->length; assert(length != 0); - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - if (node->tag >= FLAT) { + if (node->IsFlat()) { return absl::string_view(node->flat()->Data() + offset, length); } - assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here"); + assert(node->IsExternal() && "Expect FLAT or EXTERNAL node here"); return absl::string_view(node->external()->base + offset, length); } @@ -1478,7 +1492,7 @@ Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() { // Walk down the left branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { stack_of_right_children.push_back(node->concat()->right); node = node->concat()->left; } @@ -1486,15 +1500,15 @@ Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length != 0); const char* data = - node->tag == EXTERNAL ? node->external()->base : node->flat()->Data(); + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset, length); current_leaf_ = node; return *this; @@ -1547,8 +1561,8 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Range to read is a proper subrange of the current chunk. assert(current_leaf_ != nullptr); CordRep* subnode = CordRep::Ref(current_leaf_); - const char* data = subnode->tag == EXTERNAL ? subnode->external()->base - : subnode->flat()->Data(); + const char* data = subnode->IsExternal() ? subnode->external()->base + : subnode->flat()->Data(); subnode = NewSubstring(subnode, current_chunk_.data() - data, n); subcord.contents_.EmplaceTree(VerifyTree(subnode), method); RemoveChunkPrefix(n); @@ -1560,8 +1574,8 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { assert(current_leaf_ != nullptr); CordRep* subnode = CordRep::Ref(current_leaf_); if (current_chunk_.size() < subnode->length) { - const char* data = subnode->tag == EXTERNAL ? subnode->external()->base - : subnode->flat()->Data(); + const char* data = subnode->IsExternal() ? subnode->external()->base + : subnode->flat()->Data(); subnode = NewSubstring(subnode, current_chunk_.data() - data, current_chunk_.size()); } @@ -1599,7 +1613,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Walk down the appropriate branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { if (node->concat()->left->length > n) { // Push right, descend left. stack_of_right_children.push_back(node->concat()->right); @@ -1616,20 +1630,20 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } // Range to read ends with a proper (possibly empty) subrange of the current // chunk. - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length > n); if (n > 0) { subnode = Concat(subnode, NewSubstring(CordRep::Ref(node), offset, n)); } const char* data = - node->tag == EXTERNAL ? node->external()->base : node->flat()->Data(); + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset + n, length - n); current_leaf_ = node; bytes_remaining_ -= n; @@ -1672,7 +1686,7 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { // Walk down the appropriate branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { if (node->concat()->left->length > n) { // Push right, descend left. stack_of_right_children.push_back(node->concat()->right); @@ -1688,15 +1702,15 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length > n); const char* data = - node->tag == EXTERNAL ? node->external()->base : node->flat()->Data(); + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset + n, length - n); current_leaf_ = node; bytes_remaining_ -= n; @@ -1712,15 +1726,15 @@ char Cord::operator[](size_t i) const { while (true) { assert(rep != nullptr); assert(offset < rep->length); - if (rep->tag >= FLAT) { + if (rep->IsFlat()) { // Get the "i"th character directly from the flat array. return rep->flat()->Data()[offset]; - } else if (rep->tag == BTREE) { + } else if (rep->IsBtree()) { return rep->btree()->GetCharacter(offset); - } else if (rep->tag == EXTERNAL) { + } else if (rep->IsExternal()) { // Get the "i"th character from the external array. return rep->external()->base[offset]; - } else if (rep->tag == CONCAT) { + } else if (rep->IsConcat()) { // Recursively branch to the side of the concatenation that the "i"th // character is on. size_t left_length = rep->concat()->left->length; @@ -1732,7 +1746,7 @@ char Cord::operator[](size_t i) const { } } else { // This must be a substring a node, so bypass it to get to the child. - assert(rep->tag == SUBSTRING); + assert(rep->IsSubstring()); offset += rep->substring()->start; rep = rep->substring()->child; } @@ -1769,25 +1783,25 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) { assert(rep != nullptr); - if (rep->tag >= FLAT) { + if (rep->IsFlat()) { *fragment = absl::string_view(rep->flat()->Data(), rep->length); return true; - } else if (rep->tag == EXTERNAL) { + } else if (rep->IsExternal()) { *fragment = absl::string_view(rep->external()->base, rep->length); return true; - } else if (rep->tag == BTREE) { + } else if (rep->IsBtree()) { return rep->btree()->IsFlat(fragment); - } else if (rep->tag == SUBSTRING) { + } else if (rep->IsSubstring()) { CordRep* child = rep->substring()->child; - if (child->tag >= FLAT) { + if (child->IsFlat()) { *fragment = absl::string_view( child->flat()->Data() + rep->substring()->start, rep->length); return true; - } else if (child->tag == EXTERNAL) { + } else if (child->IsExternal()) { *fragment = absl::string_view( child->external()->base + rep->substring()->start, rep->length); return true; - } else if (child->tag == BTREE) { + } else if (child->IsBtree()) { return child->btree()->IsFlat(rep->substring()->start, rep->length, fragment); } @@ -1798,7 +1812,7 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ void Cord::ForEachChunkAux( absl::cord_internal::CordRep* rep, absl::FunctionRef callback) { - if (rep->tag == BTREE) { + if (rep->IsBtree()) { ChunkIterator it(rep), end; while (it != end) { callback(*it); @@ -1814,7 +1828,7 @@ absl::string_view Cord::FlattenSlowPath() { absl::cord_internal::CordRep* stack[stack_max]; absl::cord_internal::CordRep* current_node = rep; while (true) { - if (current_node->tag == CONCAT) { + if (current_node->IsConcat()) { if (stack_pos == stack_max) { // There's no more room on our stack array to add another right branch, // and the idea is to avoid allocations, so call this function @@ -1861,23 +1875,23 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, *os << "]"; *os << " " << (IsRootBalanced(rep) ? 'b' : 'u'); *os << " " << std::setw(indent) << ""; - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { *os << "CONCAT depth=" << Depth(rep) << "\n"; indent += kIndentStep; indents.push_back(indent); stack.push_back(rep->concat()->right); rep = rep->concat()->left; - } else if (rep->tag == SUBSTRING) { + } else if (rep->IsSubstring()) { *os << "SUBSTRING @ " << rep->substring()->start << "\n"; indent += kIndentStep; rep = rep->substring()->child; } else { // Leaf or ring - if (rep->tag == EXTERNAL) { + if (rep->IsExternal()) { *os << "EXTERNAL ["; if (include_data) *os << absl::CEscape(std::string(rep->external()->base, rep->length)); *os << "]\n"; - } else if (rep->tag >= FLAT) { + } else if (rep->IsFlat()) { *os << "FLAT cap=" << rep->flat()->Capacity() << " ["; if (include_data) *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length)); @@ -1915,7 +1929,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node)); } - if (node->tag == CONCAT) { + if (node->IsConcat()) { ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ReportError(root, node)); ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, @@ -1927,13 +1941,13 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, worklist.push_back(node->concat()->right); worklist.push_back(node->concat()->left); } - } else if (node->tag >= FLAT) { + } else if (node->IsFlat()) { ABSL_INTERNAL_CHECK(node->length <= node->flat()->Capacity(), ReportError(root, node)); - } else if (node->tag == EXTERNAL) { + } else if (node->IsExternal()) { ABSL_INTERNAL_CHECK(node->external()->base != nullptr, ReportError(root, node)); - } else if (node->tag == SUBSTRING) { + } else if (node->IsSubstring()) { ABSL_INTERNAL_CHECK( node->substring()->start < node->substring()->child->length, ReportError(root, node)); @@ -1962,7 +1976,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, while (true) { const CordRep* next_node = nullptr; - if (cur_node->tag == CONCAT) { + if (cur_node->IsConcat()) { total_mem_usage += sizeof(CordRepConcat); const CordRep* left = cur_node->concat()->left; if (!RepMemoryUsageLeaf(left, &total_mem_usage)) { @@ -1976,12 +1990,12 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, } next_node = right; } - } else if (cur_node->tag == BTREE) { + } else if (cur_node->IsBtree()) { total_mem_usage += sizeof(CordRepBtree); const CordRepBtree* node = cur_node->btree(); if (node->height() == 0) { for (const CordRep* edge : node->Edges()) { - RepMemoryUsageLeaf(edge, &total_mem_usage); + RepMemoryUsageDataEdge(edge, &total_mem_usage); } } else { for (const CordRep* edge : node->Edges()) { @@ -1990,7 +2004,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, } } else { // Since cur_node is not a leaf or a concat node it must be a substring. - assert(cur_node->tag == SUBSTRING); + assert(cur_node->IsSubstring()); total_mem_usage += sizeof(CordRepSubstring); next_node = cur_node->substring()->child; if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) { diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 0c0a8a7a..d0296338 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -206,7 +206,32 @@ class CordTestPeer { ABSL_NAMESPACE_END } // namespace absl -TEST(Cord, AllFlatSizes) { +// The CordTest fixture runs all tests with and without Cord Btree enabled. +class CordTest : public testing::TestWithParam { + public: + CordTest() : was_btree_(absl::cord_internal::cord_btree_enabled.load()) { + absl::cord_internal::cord_btree_enabled.store(UseBtree()); + } + ~CordTest() override { + absl::cord_internal::cord_btree_enabled.store(was_btree_); + } + + // Returns true if test is running with btree enabled. + bool UseBtree() const { return GetParam(); } + + // Returns human readable string representation of the test parameter. + static std::string ToString(testing::TestParamInfo param) { + return param.param ? "Btree" : "Concat"; + } + + private: + const bool was_btree_; +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Bool(), + CordTest::ToString); + +TEST_P(CordTest, AllFlatSizes) { using absl::strings_internal::CordTestAccess; for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) { @@ -224,7 +249,7 @@ TEST(Cord, AllFlatSizes) { // We create a Cord at least 128GB in size using the fact that Cords can // internally reference-count; thus the Cord is enormous without actually // consuming very much memory. -TEST(GigabyteCord, FromExternal) { +TEST_P(CordTest, GigabyteCordFromExternal) { const size_t one_gig = 1024U * 1024U * 1024U; size_t max_size = 2 * one_gig; if (sizeof(max_size) > 4) max_size = 128 * one_gig; @@ -273,7 +298,7 @@ static absl::Cord MakeExternalCord(int size) { extern bool my_unique_true_boolean; bool my_unique_true_boolean = true; -TEST(Cord, Assignment) { +TEST_P(CordTest, Assignment) { absl::Cord x(absl::string_view("hi there")); absl::Cord y(x); ASSERT_EQ(std::string(x), "hi there"); @@ -327,7 +352,7 @@ TEST(Cord, Assignment) { } } -TEST(Cord, StartsEndsWith) { +TEST_P(CordTest, StartsEndsWith) { absl::Cord x(absl::string_view("abcde")); absl::Cord empty(""); @@ -360,7 +385,7 @@ TEST(Cord, StartsEndsWith) { ASSERT_TRUE(!empty.EndsWith("xyz")); } -TEST(Cord, Subcord) { +TEST_P(CordTest, Subcord) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); const std::string s = RandomLowercaseString(&rng, 1024); @@ -421,7 +446,7 @@ TEST(Cord, Subcord) { EXPECT_TRUE(sa.empty()); } -TEST(Cord, Swap) { +TEST_P(CordTest, Swap) { absl::string_view a("Dexter"); absl::string_view b("Mandark"); absl::Cord x(a); @@ -453,7 +478,7 @@ static void VerifyCopyToString(const absl::Cord& cord) { } } -TEST(Cord, CopyToString) { +TEST_P(CordTest, CopyToString) { VerifyCopyToString(absl::Cord()); VerifyCopyToString(absl::Cord("small cord")); VerifyCopyToString( @@ -461,45 +486,45 @@ TEST(Cord, CopyToString) { "copying ", "to ", "a ", "string."})); } -TEST(TryFlat, Empty) { +TEST_P(CordTest, TryFlatEmpty) { absl::Cord c; EXPECT_EQ(c.TryFlat(), ""); } -TEST(TryFlat, Flat) { +TEST_P(CordTest, TryFlatFlat) { absl::Cord c("hello"); EXPECT_EQ(c.TryFlat(), "hello"); } -TEST(TryFlat, SubstrInlined) { +TEST_P(CordTest, TryFlatSubstrInlined) { absl::Cord c("hello"); c.RemovePrefix(1); EXPECT_EQ(c.TryFlat(), "ello"); } -TEST(TryFlat, SubstrFlat) { +TEST_P(CordTest, TryFlatSubstrFlat) { absl::Cord c("longer 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) { +TEST_P(CordTest, TryFlatConcat) { absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"}); EXPECT_EQ(c.TryFlat(), absl::nullopt); } -TEST(TryFlat, External) { +TEST_P(CordTest, TryFlatExternal) { absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); EXPECT_EQ(c.TryFlat(), "hell"); } -TEST(TryFlat, SubstrExternal) { +TEST_P(CordTest, TryFlatSubstrExternal) { absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); EXPECT_EQ(sub.TryFlat(), "ell"); } -TEST(TryFlat, SubstrConcat) { +TEST_P(CordTest, TryFlatSubstrConcat) { absl::Cord c = absl::MakeFragmentedCord({"hello", " world"}); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); EXPECT_EQ(sub.TryFlat(), absl::nullopt); @@ -507,7 +532,7 @@ TEST(TryFlat, SubstrConcat) { EXPECT_EQ(c.TryFlat(), absl::nullopt); } -TEST(TryFlat, CommonlyAssumedInvariants) { +TEST_P(CordTest, TryFlatCommonlyAssumedInvariants) { // 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. @@ -563,7 +588,7 @@ static void VerifyFlatten(absl::Cord c) { EXPECT_TRUE(IsFlat(c)); } -TEST(Cord, Flatten) { +TEST_P(CordTest, Flatten) { VerifyFlatten(absl::Cord()); VerifyFlatten(absl::Cord("small cord")); VerifyFlatten(absl::Cord("larger than small buffer optimization")); @@ -617,7 +642,7 @@ class TestData { }; } // namespace -TEST(Cord, MultipleLengths) { +TEST_P(CordTest, MultipleLengths) { TestData d; for (size_t i = 0; i < d.size(); i++) { std::string a = d.data(i); @@ -693,7 +718,7 @@ TEST(Cord, MultipleLengths) { namespace { -TEST(Cord, RemoveSuffixWithExternalOrSubstring) { +TEST_P(CordTest, RemoveSuffixWithExternalOrSubstring) { absl::Cord cord = absl::MakeCordFromExternal( "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); }); @@ -708,7 +733,7 @@ TEST(Cord, RemoveSuffixWithExternalOrSubstring) { EXPECT_EQ("foo", std::string(cord)); } -TEST(Cord, RemoveSuffixMakesZeroLengthNode) { +TEST_P(CordTest, RemoveSuffixMakesZeroLengthNode) { absl::Cord c; c.Append(absl::Cord(std::string(100, 'x'))); absl::Cord other_ref = c; // Prevent inplace appends @@ -735,7 +760,7 @@ absl::Cord CordWithZedBlock(size_t size) { } // Establish that ZedBlock does what we think it does. -TEST(CordSpliceTest, ZedBlock) { +TEST_P(CordTest, CordSpliceTestZedBlock) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); std::string s; @@ -743,7 +768,7 @@ TEST(CordSpliceTest, ZedBlock) { EXPECT_EQ("zzzzzzzzzz", s); } -TEST(CordSpliceTest, ZedBlock0) { +TEST_P(CordTest, CordSpliceTestZedBlock0) { absl::Cord blob = CordWithZedBlock(0); EXPECT_EQ(0, blob.size()); std::string s; @@ -751,7 +776,7 @@ TEST(CordSpliceTest, ZedBlock0) { EXPECT_EQ("", s); } -TEST(CordSpliceTest, ZedBlockSuffix1) { +TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); @@ -763,7 +788,7 @@ TEST(CordSpliceTest, ZedBlockSuffix1) { } // Remove all of a prefix block -TEST(CordSpliceTest, ZedBlockSuffix0) { +TEST_P(CordTest, CordSpliceTestZedBlockSuffix0) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); @@ -795,7 +820,7 @@ absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset, } // Taking an empty suffix of a block breaks appending. -TEST(CordSpliceTest, RemoveEntireBlock1) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) { absl::Cord zero = CordWithZedBlock(10); absl::Cord suffix(zero); suffix.RemovePrefix(10); @@ -803,7 +828,7 @@ TEST(CordSpliceTest, RemoveEntireBlock1) { result.Append(suffix); } -TEST(CordSpliceTest, RemoveEntireBlock2) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) { absl::Cord zero = CordWithZedBlock(10); absl::Cord prefix(zero); prefix.RemoveSuffix(10); @@ -813,7 +838,7 @@ TEST(CordSpliceTest, RemoveEntireBlock2) { result.Append(suffix); } -TEST(CordSpliceTest, RemoveEntireBlock3) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock3) { absl::Cord blob = CordWithZedBlock(10); absl::Cord block = BigCord(10, 'b'); blob = SpliceCord(blob, 0, block); @@ -844,7 +869,7 @@ void VerifyComparison(const CordCompareTestCase& test_case) { << "LHS=" << rhs_string << "; RHS=" << lhs_string; } -TEST(Cord, Compare) { +TEST_P(CordTest, Compare) { absl::Cord subcord("aaaaaBBBBBcccccDDDDD"); subcord = subcord.Subcord(3, 10); @@ -907,7 +932,7 @@ TEST(Cord, Compare) { } } -TEST(Cord, CompareAfterAssign) { +TEST_P(CordTest, CompareAfterAssign) { absl::Cord a("aaaaaa1111111"); absl::Cord b("aaaaaa2222222"); a = "cccccc"; @@ -936,7 +961,7 @@ static void TestCompare(const absl::Cord& c, const absl::Cord& d, EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d; } -TEST(Compare, ComparisonIsUnsigned) { +TEST_P(CordTest, CompareComparisonIsUnsigned) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); std::uniform_int_distribution uniform_uint8(0, 255); char x = static_cast(uniform_uint8(rng)); @@ -945,7 +970,7 @@ TEST(Compare, ComparisonIsUnsigned) { absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng); } -TEST(Compare, RandomComparisons) { +TEST_P(CordTest, CompareRandomComparisons) { const int kIters = 5000; RandomEngine rng(GTEST_FLAG_GET(random_seed)); @@ -1003,43 +1028,43 @@ void CompareOperators() { EXPECT_FALSE(b <= a); } -TEST(ComparisonOperators, Cord_Cord) { +TEST_P(CordTest, ComparisonOperators_Cord_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_StringPiece) { +TEST_P(CordTest, ComparisonOperators_Cord_StringPiece) { CompareOperators(); } -TEST(ComparisonOperators, StringPiece_Cord) { +TEST_P(CordTest, ComparisonOperators_StringPiece_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_string) { +TEST_P(CordTest, ComparisonOperators_Cord_string) { CompareOperators(); } -TEST(ComparisonOperators, string_Cord) { +TEST_P(CordTest, ComparisonOperators_string_Cord) { CompareOperators(); } -TEST(ComparisonOperators, stdstring_Cord) { +TEST_P(CordTest, ComparisonOperators_stdstring_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_stdstring) { +TEST_P(CordTest, ComparisonOperators_Cord_stdstring) { CompareOperators(); } -TEST(ComparisonOperators, charstar_Cord) { +TEST_P(CordTest, ComparisonOperators_charstar_Cord) { CompareOperators(); } -TEST(ComparisonOperators, Cord_charstar) { +TEST_P(CordTest, ComparisonOperators_Cord_charstar) { CompareOperators(); } -TEST(ConstructFromExternal, ReleaserInvoked) { +TEST_P(CordTest, ConstructFromExternalReleaserInvoked) { // Empty external memory means the releaser should be called immediately. { bool invoked = false; @@ -1081,7 +1106,7 @@ TEST(ConstructFromExternal, ReleaserInvoked) { } } -TEST(ConstructFromExternal, CompareContents) { +TEST_P(CordTest, ConstructFromExternalCompareContents) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); for (int length = 1; length <= 2048; length *= 2) { @@ -1097,7 +1122,7 @@ TEST(ConstructFromExternal, CompareContents) { } } -TEST(ConstructFromExternal, LargeReleaser) { +TEST_P(CordTest, ConstructFromExternalLargeReleaser) { RandomEngine rng(GTEST_FLAG_GET(random_seed)); constexpr size_t kLength = 256; std::string data = RandomLowercaseString(&rng, kLength); @@ -1112,7 +1137,7 @@ TEST(ConstructFromExternal, LargeReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, FunctionPointerReleaser) { +TEST_P(CordTest, ConstructFromExternalFunctionPointerReleaser) { static absl::string_view data("hello world"); static bool invoked; auto* releaser = @@ -1129,7 +1154,7 @@ TEST(ConstructFromExternal, FunctionPointerReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, MoveOnlyReleaser) { +TEST_P(CordTest, ConstructFromExternalMoveOnlyReleaser) { struct Releaser { explicit Releaser(bool* invoked) : invoked(invoked) {} Releaser(Releaser&& other) noexcept : invoked(other.invoked) {} @@ -1143,20 +1168,20 @@ TEST(ConstructFromExternal, MoveOnlyReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, NoArgLambda) { +TEST_P(CordTest, ConstructFromExternalNoArgLambda) { bool invoked = false; (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; }); EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, StringViewArgLambda) { +TEST_P(CordTest, ConstructFromExternalStringViewArgLambda) { bool invoked = false; (void)absl::MakeCordFromExternal( "dummy", [&invoked](absl::string_view) { invoked = true; }); EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { +TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) { struct Releaser { explicit Releaser(bool* destroyed) : destroyed(destroyed) {} ~Releaser() { *destroyed = true; } @@ -1171,7 +1196,7 @@ TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { EXPECT_TRUE(destroyed); } -TEST(ConstructFromExternal, ReferenceQualifierOverloads) { +TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) { struct Releaser { void operator()(absl::string_view) & { *lvalue_invoked = true; } void operator()(absl::string_view) && { *rvalue_invoked = true; } @@ -1199,7 +1224,7 @@ TEST(ConstructFromExternal, ReferenceQualifierOverloads) { EXPECT_TRUE(rvalue_invoked); } -TEST(ExternalMemory, BasicUsage) { +TEST_P(CordTest, ExternalMemoryBasicUsage) { static const char* strings[] = {"", "hello", "there"}; for (const char* str : strings) { absl::Cord dst("(prefix)"); @@ -1210,7 +1235,7 @@ TEST(ExternalMemory, BasicUsage) { } } -TEST(ExternalMemory, RemovePrefixSuffix) { +TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) { // Exhaustively try all sub-strings. absl::Cord cord = MakeComposite(); std::string s = std::string(cord); @@ -1225,7 +1250,7 @@ TEST(ExternalMemory, RemovePrefixSuffix) { } } -TEST(ExternalMemory, Get) { +TEST_P(CordTest, ExternalMemoryGet) { absl::Cord cord("hello"); AddExternalMemory(" world!", &cord); AddExternalMemory(" how are ", &cord); @@ -1244,16 +1269,16 @@ TEST(ExternalMemory, Get) { // Additionally we have some whiteboxed expectations based on our knowledge of // the layout and size of empty and inlined cords, and flat nodes. -TEST(CordMemoryUsage, Empty) { +TEST_P(CordTest, CordMemoryUsageEmpty) { EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage()); } -TEST(CordMemoryUsage, Embedded) { +TEST_P(CordTest, CordMemoryUsageEmbedded) { absl::Cord a("hello"); EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); } -TEST(CordMemoryUsage, EmbeddedAppend) { +TEST_P(CordTest, CordMemoryUsageEmbeddedAppend) { absl::Cord a("a"); absl::Cord b("bcd"); EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord)); @@ -1261,7 +1286,7 @@ TEST(CordMemoryUsage, EmbeddedAppend) { EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); } -TEST(CordMemoryUsage, ExternalMemory) { +TEST_P(CordTest, CordMemoryUsageExternalMemory) { static const int kLength = 1000; absl::Cord cord; AddExternalMemory(std::string(kLength, 'x'), &cord); @@ -1269,14 +1294,14 @@ TEST(CordMemoryUsage, ExternalMemory) { EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5); } -TEST(CordMemoryUsage, Flat) { +TEST_P(CordTest, CordMemoryUsageFlat) { static const int kLength = 125; absl::Cord a(std::string(kLength, 'a')); EXPECT_GT(a.EstimatedMemoryUsage(), kLength); EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5); } -TEST(CordMemoryUsage, AppendFlat) { +TEST_P(CordTest, CordMemoryUsageAppendFlat) { using absl::strings_internal::CordTestAccess; absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); size_t length = a.EstimatedMemoryUsage(); @@ -1286,9 +1311,32 @@ TEST(CordMemoryUsage, AppendFlat) { EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5); } +TEST_P(CordTest, CordMemoryUsageAppendExternal) { + static const int kLength = 1000; + using absl::strings_internal::CordTestAccess; + absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); + size_t length = a.EstimatedMemoryUsage(); + AddExternalMemory(std::string(kLength, 'b'), &a); + size_t delta = a.EstimatedMemoryUsage() - length; + EXPECT_GT(delta, kLength); + EXPECT_LE(delta, kLength * 1.5); +} + +TEST_P(CordTest, CordMemoryUsageSubString) { + static const int kLength = 2000; + using absl::strings_internal::CordTestAccess; + absl::Cord a(std::string(kLength, 'a')); + size_t length = a.EstimatedMemoryUsage(); + AddExternalMemory(std::string(kLength, 'b'), &a); + absl::Cord b = a.Subcord(0, kLength + kLength / 2); + size_t delta = b.EstimatedMemoryUsage() - length; + EXPECT_GT(delta, kLength); + EXPECT_LE(delta, kLength * 1.5); +} + // Regtest for a change that had to be rolled back because it expanded out // of the InlineRep too soon, which was observable through MemoryUsage(). -TEST(CordMemoryUsage, InlineRep) { +TEST_P(CordTest, CordMemoryUsageInlineRep) { constexpr size_t kMaxInline = 15; // Cord::InlineRep::N const std::string small_string(kMaxInline, 'x'); absl::Cord c1(small_string); @@ -1302,7 +1350,7 @@ TEST(CordMemoryUsage, InlineRep) { } // namespace // Regtest for 7510292 (fix a bug introduced by 7465150) -TEST(Cord, Concat_Append) { +TEST_P(CordTest, Concat_Append) { // Create a rep of type CONCAT absl::Cord s1("foobarbarbarbarbar"); s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg"); @@ -1317,7 +1365,7 @@ TEST(Cord, Concat_Append) { EXPECT_EQ(s2.size(), size + 1); } -TEST(Cord, DiabolicalGrowth) { +TEST_P(CordTest, DiabolicalGrowth) { // This test exercises a diabolical Append() on a cord, making the // cord shared before each Append call resulting in a terribly fragmented // resulting cord. @@ -1337,7 +1385,7 @@ TEST(Cord, DiabolicalGrowth) { cord.EstimatedMemoryUsage()); } -TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) { +TEST_P(CordTest, MakeFragmentedCordFromInitializerList) { absl::Cord fragmented = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); @@ -1357,7 +1405,7 @@ TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) { ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); } -TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) { +TEST_P(CordTest, MakeFragmentedCordFromVector) { std::vector chunks = {"A ", "fragmented ", "Cord"}; absl::Cord fragmented = absl::MakeFragmentedCord(chunks); @@ -1377,7 +1425,7 @@ TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) { ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); } -TEST(CordChunkIterator, Traits) { +TEST_P(CordTest, CordChunkIteratorTraits) { static_assert(std::is_copy_constructible::value, ""); static_assert(std::is_copy_assignable::value, ""); @@ -1458,7 +1506,7 @@ static void VerifyChunkIterator(const absl::Cord& cord, EXPECT_TRUE(post_iter == cord.chunk_end()); // NOLINT } -TEST(CordChunkIterator, Operations) { +TEST_P(CordTest, CordChunkIteratorOperations) { absl::Cord empty_cord; VerifyChunkIterator(empty_cord, 0); @@ -1628,7 +1676,7 @@ TEST(CordCharIterator, Operations) { VerifyCharIterator(subcords); } -TEST(Cord, StreamingOutput) { +TEST_P(CordTest, StreamingOutput) { absl::Cord c = absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."}); std::stringstream output; @@ -1636,7 +1684,7 @@ TEST(Cord, StreamingOutput) { EXPECT_EQ("A small fragmented Cord.", output.str()); } -TEST(Cord, ForEachChunk) { +TEST_P(CordTest, ForEachChunk) { for (int num_elements : {1, 10, 200}) { SCOPED_TRACE(num_elements); std::vector cord_chunks; @@ -1654,7 +1702,7 @@ TEST(Cord, ForEachChunk) { } } -TEST(Cord, SmallBufferAssignFromOwnData) { +TEST_P(CordTest, SmallBufferAssignFromOwnData) { constexpr size_t kMaxInline = 15; std::string contents = "small buff cord"; EXPECT_EQ(contents.size(), kMaxInline); @@ -1669,7 +1717,7 @@ TEST(Cord, SmallBufferAssignFromOwnData) { } } -TEST(Cord, Format) { +TEST_P(CordTest, Format) { absl::Cord c; absl::Format(&c, "There were %04d little %s.", 3, "pigs"); EXPECT_EQ(c, "There were 0003 little pigs."); @@ -1770,7 +1818,7 @@ struct LongView { }; -TEST(Cord, ConstinitConstructor) { +TEST_P(CordTest, ConstinitConstructor) { TestConstinitConstructor( absl::strings_internal::MakeStringConstant(ShortView{})); TestConstinitConstructor( diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 8ae644ba..b7f3f4c0 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -216,6 +216,14 @@ struct CordRep { // padding space from the base class (clang and gcc do, MSVC does not, etc) uint8_t storage[3]; + // Returns true if this instance's tag matches the requested type. + constexpr bool IsRing() const { return tag == RING; } + constexpr bool IsConcat() const { return tag == CONCAT; } + constexpr bool IsSubstring() const { return tag == SUBSTRING; } + constexpr bool IsExternal() const { return tag == EXTERNAL; } + constexpr bool IsFlat() const { return tag >= FLAT; } + constexpr bool IsBtree() const { return tag == BTREE; } + inline CordRepRing* ring(); inline const CordRepRing* ring() const; inline CordRepConcat* concat(); @@ -226,7 +234,6 @@ struct CordRep { inline const CordRepExternal* external() const; inline CordRepFlat* flat(); inline const CordRepFlat* flat() const; - inline CordRepBtree* btree(); inline const CordRepBtree* btree() const; @@ -277,7 +284,7 @@ struct CordRepExternal : public CordRep { ExternalReleaserInvoker releaser_invoker; // Deletes (releases) the external rep. - // Requires rep != nullptr and rep->tag == EXTERNAL + // Requires rep != nullptr and rep->IsExternal() static void Delete(CordRep* rep); }; @@ -320,7 +327,7 @@ struct CordRepExternalImpl }; inline void CordRepExternal::Delete(CordRep* rep) { - assert(rep != nullptr && rep->tag == EXTERNAL); + assert(rep != nullptr && rep->IsExternal()); auto* rep_external = static_cast(rep); assert(rep_external->releaser_invoker != nullptr); rep_external->releaser_invoker(rep_external); @@ -531,32 +538,32 @@ class InlineData { static_assert(sizeof(InlineData) == kMaxInline + 1, ""); inline CordRepConcat* CordRep::concat() { - assert(tag == CONCAT); + assert(IsConcat()); return static_cast(this); } inline const CordRepConcat* CordRep::concat() const { - assert(tag == CONCAT); + assert(IsConcat()); return static_cast(this); } inline CordRepSubstring* CordRep::substring() { - assert(tag == SUBSTRING); + assert(IsSubstring()); return static_cast(this); } inline const CordRepSubstring* CordRep::substring() const { - assert(tag == SUBSTRING); + assert(IsSubstring()); return static_cast(this); } inline CordRepExternal* CordRep::external() { - assert(tag == EXTERNAL); + assert(IsExternal()); return static_cast(this); } inline const CordRepExternal* CordRep::external() const { - assert(tag == EXTERNAL); + assert(IsExternal()); return static_cast(this); } diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index fd3a0045..8fe589fa 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -79,7 +79,7 @@ void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, // indented by two spaces per recursive depth. stream << std::string(depth * 2, ' ') << sharing << " (" << sptr << ") "; - if (rep->tag == BTREE) { + if (rep->IsBtree()) { const CordRepBtree* node = rep->btree(); std::string label = node->height() ? absl::StrCat("Node(", node->height(), ")") : "Leaf"; @@ -378,7 +378,7 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { } NODE_CHECK_VALID(tree != nullptr); - NODE_CHECK_EQ(tree->tag, BTREE); + NODE_CHECK_VALID(tree->IsBtree()); NODE_CHECK_VALID(tree->height() <= kMaxHeight); NODE_CHECK_VALID(tree->begin() < tree->capacity()); NODE_CHECK_VALID(tree->end() <= tree->capacity()); @@ -387,7 +387,7 @@ bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { for (CordRep* edge : tree->Edges()) { NODE_CHECK_VALID(edge != nullptr); if (tree->height() > 0) { - NODE_CHECK_VALID(edge->tag == BTREE); + NODE_CHECK_VALID(edge->IsBtree()); NODE_CHECK_VALID(edge->btree()->height() == tree->height() - 1); } else { NODE_CHECK_VALID(IsDataEdge(edge)); @@ -889,7 +889,7 @@ Span CordRepBtree::GetAppendBufferSlow(size_t size) { } CordRepBtree* CordRepBtree::CreateSlow(CordRep* rep) { - if (rep->tag == BTREE) return rep->btree(); + if (rep->IsBtree()) return rep->btree(); CordRepBtree* node = nullptr; auto consume = [&node](CordRep* r, size_t offset, size_t length) { @@ -905,7 +905,7 @@ CordRepBtree* CordRepBtree::CreateSlow(CordRep* rep) { } CordRepBtree* CordRepBtree::AppendSlow(CordRepBtree* tree, CordRep* rep) { - if (ABSL_PREDICT_TRUE(rep->tag == BTREE)) { + if (ABSL_PREDICT_TRUE(rep->IsBtree())) { return MergeTrees(tree, rep->btree()); } auto consume = [&tree](CordRep* r, size_t offset, size_t length) { @@ -917,7 +917,7 @@ CordRepBtree* CordRepBtree::AppendSlow(CordRepBtree* tree, CordRep* rep) { } CordRepBtree* CordRepBtree::PrependSlow(CordRepBtree* tree, CordRep* rep) { - if (ABSL_PREDICT_TRUE(rep->tag == BTREE)) { + if (ABSL_PREDICT_TRUE(rep->IsBtree())) { return MergeTrees(rep->btree(), tree); } auto consume = [&tree](CordRep* r, size_t offset, size_t length) { diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 56e1e4af..8f000cab 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -153,7 +153,7 @@ class CordRepBtree : public CordRep { }; // Creates a btree from the given input. Adopts a ref of `rep`. - // If the input `rep` is itself a btree, i.e., `tag == BTREE`, then this + // If the input `rep` is itself a btree, i.e., `IsBtree()`, then this // function immediately returns `rep->btree()`. If the input is a valid data // edge (see IsDataEdge()), then a new leaf node is returned containing `rep` // as the sole data edge. Else, the input is assumed to be a (legacy) concat @@ -514,12 +514,12 @@ class CordRepBtree : public CordRep { }; inline CordRepBtree* CordRep::btree() { - assert(tag == BTREE); + assert(IsBtree()); return static_cast(this); } inline const CordRepBtree* CordRep::btree() const { - assert(tag == BTREE); + assert(IsBtree()); return static_cast(this); } @@ -589,7 +589,7 @@ inline CordRepBtree* CordRepBtree::New(int height) { inline CordRepBtree* CordRepBtree::New(CordRep* rep) { CordRepBtree* tree = new CordRepBtree; - int height = rep->tag == BTREE ? rep->btree()->height() + 1 : 0; + int height = rep->IsBtree() ? rep->btree()->height() + 1 : 0; tree->length = rep->length; tree->InitInstance(height, /*begin=*/0, /*end=*/1); tree->edges_[0] = rep; diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index 20a6fc2b..db1f63fa 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -40,7 +40,7 @@ using index_type = CordRepRing::index_type; enum class Direction { kForward, kReversed }; inline bool IsFlatOrExternal(CordRep* rep) { - return rep->tag >= FLAT || rep->tag == EXTERNAL; + return rep->IsFlat() || rep->IsExternal(); } // Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise. @@ -229,7 +229,7 @@ void CordRepRing::SetCapacityForTesting(size_t capacity) { } void CordRepRing::Delete(CordRepRing* rep) { - assert(rep != nullptr && rep->tag == RING); + assert(rep != nullptr && rep->IsRing()); #if defined(__cpp_sized_deallocation) size_t size = AllocSize(rep->capacity_); rep->~CordRepRing(); @@ -360,7 +360,7 @@ CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) { if (IsFlatOrExternal(child)) { return CreateFromLeaf(child, 0, length, extra); } - if (child->tag == RING) { + if (child->IsRing()) { return Mutable(child->ring(), extra); } return CreateSlow(child, extra); @@ -433,7 +433,7 @@ CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring, CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) { Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) { - if (child_arg->tag == RING) { + if (child_arg->IsRing()) { rep = AddRing(rep, child_arg->ring(), offset, len); } else { rep = AppendLeaf(rep, child_arg, offset, len); @@ -460,7 +460,7 @@ CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) { if (IsFlatOrExternal(child)) { return AppendLeaf(rep, child, 0, length); } - if (child->tag == RING) { + if (child->IsRing()) { return AddRing(rep, child->ring(), 0, length); } return AppendSlow(rep, child); @@ -496,7 +496,7 @@ CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) { if (IsFlatOrExternal(child)) { return PrependLeaf(rep, child, 0, length); } - if (child->tag == RING) { + if (child->IsRing()) { return AddRing(rep, child->ring(), 0, length); } return PrependSlow(rep, child); diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h index 2082a565..44db8494 100644 --- a/absl/strings/internal/cord_rep_ring.h +++ b/absl/strings/internal/cord_rep_ring.h @@ -570,12 +570,12 @@ inline CordRepRing::Position CordRepRing::FindTail(index_type head, // Now that CordRepRing is defined, we can define CordRep's helper casts: inline CordRepRing* CordRep::ring() { - assert(tag == RING); + assert(IsRing()); return static_cast(this); } inline const CordRepRing* CordRep::ring() const { - assert(tag == RING); + assert(IsRing()); return static_cast(this); } -- cgit v1.2.3 From b699707f0bfeae034e36cdfd909b66b0fcab696c Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 11 Aug 2021 11:57:03 -0700 Subject: Export of internal Abseil changes -- 228b5878d7a994656f383666cfaee34e33e0b09b by Abseil Team : Add element_type typedef to match std::span PiperOrigin-RevId: 390186160 -- c91d96c88c60be793c525158f76dfcaa5e32d161 by Abseil Team : Rollback change to only hide retired flags if human-readable output is requested PiperOrigin-RevId: 390183146 -- 170192c10ef8d513de80f29298ce93eeccc3712c by Abseil Team : Move alignas(16) before ABSL_CONST_INIT and ABSL_DLL. PiperOrigin-RevId: 390182845 -- 77a5ee5081c81cef625fac7bbcf993cc028b32ed by Evan Brown : Use simple SlotOffset and AllocSize logic instead of container_internal::Layout in order to save linker input size for non-opt and sanitizer builds. In opt mode, all of this logic is inlined so we don't save linker input size in opt mode. PiperOrigin-RevId: 389914594 GitOrigin-RevId: 228b5878d7a994656f383666cfaee34e33e0b09b Change-Id: I3b904068687574931d8390071b322c0c3c083283 --- absl/container/BUILD.bazel | 1 - absl/container/CMakeLists.txt | 1 - absl/container/internal/raw_hash_set.cc | 2 +- absl/container/internal/raw_hash_set.h | 53 ++++++++++++++++++--------------- absl/flags/internal/usage.cc | 17 +++-------- absl/flags/internal/usage_test.cc | 3 -- absl/types/span.h | 2 +- absl/types/span_test.cc | 2 ++ 8 files changed, 37 insertions(+), 44 deletions(-) diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index f22fdc60..cf55f4bb 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -598,7 +598,6 @@ cc_library( ":hashtable_debug_hooks", ":hashtablez_sampler", ":have_sse", - ":layout", "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 91c40154..691a05c0 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -666,7 +666,6 @@ absl_cc_library( absl::hash_policy_traits absl::hashtable_debug_hooks absl::have_sse - absl::layout absl::memory absl::meta absl::optional diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index e219956f..3c72c41a 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -23,7 +23,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { -ABSL_CONST_INIT ABSL_DLL alignas(16) const ctrl_t kEmptyGroup[16] = { +alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[16] = { ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index bafafd46..ade2f584 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -112,7 +112,6 @@ #include "absl/container/internal/hashtable_debug_hooks.h" #include "absl/container/internal/hashtablez_sampler.h" #include "absl/container/internal/have_sse.h" -#include "absl/container/internal/layout.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" #include "absl/numeric/bits.h" @@ -625,6 +624,20 @@ inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl, SetCtrl(i, static_cast(h), capacity, ctrl, slot, slot_size); } +// The allocated block consists of `capacity + 1 + NumClonedBytes()` control +// bytes followed by `capacity` slots, which must be aligned to `slot_align`. +// SlotOffset returns the offset of the slots into the allocated block. +inline size_t SlotOffset(size_t capacity, size_t slot_align) { + assert(IsValidCapacity(capacity)); + const size_t num_control_bytes = capacity + 1 + NumClonedBytes(); + return (num_control_bytes + slot_align - 1) & (~slot_align + 1); +} + +// Returns the size of the allocated block. See also above comment. +inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) { + return SlotOffset(capacity, slot_align) + capacity * slot_size; +} + // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface // of policy). @@ -681,15 +694,6 @@ class raw_hash_set { auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k)); auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k)); - using Layout = absl::container_internal::Layout; - - static Layout MakeLayout(size_t capacity) { - assert(IsValidCapacity(capacity)); - // The extra control bytes are for 1 sentinel byte followed by - // NumClonedBytes() bytes that are cloned from the beginning. - return Layout(capacity + 1 + NumClonedBytes(), capacity); - } - using AllocTraits = absl::allocator_traits; using SlotAlloc = typename absl::allocator_traits< allocator_type>::template rebind_alloc; @@ -1610,11 +1614,12 @@ class raw_hash_set { infoz() = Sample(); } - auto layout = MakeLayout(capacity_); - char* mem = static_cast( - Allocate(&alloc_ref(), layout.AllocSize())); - ctrl_ = layout.template Pointer<0>(mem); - slots_ = layout.template Pointer<1>(mem); + char* mem = static_cast(Allocate( + &alloc_ref(), + AllocSize(capacity_, sizeof(slot_type), alignof(slot_type)))); + ctrl_ = reinterpret_cast(mem); + slots_ = reinterpret_cast( + mem + SlotOffset(capacity_, alignof(slot_type))); ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); reset_growth_left(); infoz().RecordStorageChanged(size_, capacity_); @@ -1627,10 +1632,11 @@ class raw_hash_set { PolicyTraits::destroy(&alloc_ref(), slots_ + i); } } - auto layout = MakeLayout(capacity_); // Unpoison before returning the memory to the allocator. SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); - Deallocate(&alloc_ref(), ctrl_, layout.AllocSize()); + Deallocate( + &alloc_ref(), ctrl_, + AllocSize(capacity_, sizeof(slot_type), alignof(slot_type))); ctrl_ = EmptyGroup(); slots_ = nullptr; size_ = 0; @@ -1661,9 +1667,9 @@ class raw_hash_set { if (old_capacity) { SanitizerUnpoisonMemoryRegion(old_slots, sizeof(slot_type) * old_capacity); - auto layout = MakeLayout(old_capacity); - Deallocate(&alloc_ref(), old_ctrl, - layout.AllocSize()); + Deallocate( + &alloc_ref(), old_ctrl, + AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type))); } infoz().RecordRehash(total_probe_length); } @@ -1948,8 +1954,7 @@ struct HashtableDebugAccess> { static size_t AllocatedByteSize(const Set& c) { size_t capacity = c.capacity_; if (capacity == 0) return 0; - auto layout = Set::MakeLayout(capacity); - size_t m = layout.AllocSize(); + size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot)); size_t per_slot = Traits::space_used(static_cast(nullptr)); if (per_slot != ~size_t{}) { @@ -1967,8 +1972,8 @@ struct HashtableDebugAccess> { static size_t LowerBoundAllocatedByteSize(size_t size) { size_t capacity = GrowthToLowerboundCapacity(size); if (capacity == 0) return 0; - auto layout = Set::MakeLayout(NormalizeCapacity(capacity)); - size_t m = layout.AllocSize(); + size_t m = + AllocSize(NormalizeCapacity(capacity), sizeof(Slot), alignof(Slot)); size_t per_slot = Traits::space_used(static_cast(nullptr)); if (per_slot != ~size_t{}) { m += per_slot * size; diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index a883567f..949709e8 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -17,7 +17,6 @@ #include -#include #include #include #include @@ -256,6 +255,9 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, matching_flags; flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) { + // Ignore retired flags. + if (flag.IsRetired()) return; + // If the flag has been stripped, pretend that it doesn't exist. if (flag.Help() == flags_internal::kStrippedFlagHelp) return; @@ -273,14 +275,6 @@ void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, absl::string_view file_separator; // controls blank lines between files for (auto& package : matching_flags) { if (format == HelpFormat::kHumanReadable) { - // Hide packages with only retired flags - bool all_package_flags_are_retired = true; - for (const auto& flags_in_file : package.second) { - for (const auto* flag : flags_in_file.second) { - all_package_flags_are_retired &= flag->IsRetired(); - } - } - if (all_package_flags_are_retired) continue; out << package_separator; package_separator = "\n\n"; } @@ -340,11 +334,8 @@ void FlagsHelpImpl(std::ostream& out, // Produces the help message describing specific flag. void FlagHelp(std::ostream& out, const CommandLineFlag& flag, HelpFormat format) { - if (format == HelpFormat::kHumanReadable) { - // Ignore retired flags - if (flag.IsRetired()) return; + if (format == HelpFormat::kHumanReadable) flags_internal::FlagHelpHumanReadable(flag, out); - } } // -------------------------------------------------------------------- diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 5055640a..044d71c8 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -61,9 +61,6 @@ ABSL_FLAG( "Even more long long long long long long long long long long long long " "help message."); -ABSL_RETIRED_FLAG(int64_t, usage_reporting_test_flag_07, 1, - "usage_reporting_test_flag_07 help message"); - namespace { namespace flags = absl::flags_internal; diff --git a/absl/types/span.h b/absl/types/span.h index 41db3420..6272bb7a 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -40,7 +40,6 @@ // * `absl::Span` has compiler-provided move and copy constructors and // assignment. This is due to them being specified as `constexpr`, but that // implies const in C++11. -// * `absl::Span` has no `element_type` typedef // * A read-only `absl::Span` can be implicitly constructed from an // initializer list. // * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or @@ -170,6 +169,7 @@ class Span { typename std::enable_if::value, U>::type; public: + using element_type = T; using value_type = absl::remove_cv_t; using pointer = T*; using const_pointer = const T*; diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc index 2584339b..13264aae 100644 --- a/absl/types/span_test.cc +++ b/absl/types/span_test.cc @@ -661,6 +661,8 @@ TEST(IntSpan, ExposesContainerTypesAndConsts) { CheckType::const_reverse_iterator>(slice.crend()); testing::StaticAssertTypeEq::value_type>(); testing::StaticAssertTypeEq::value_type>(); + testing::StaticAssertTypeEq::element_type>(); + testing::StaticAssertTypeEq::element_type>(); testing::StaticAssertTypeEq::pointer>(); testing::StaticAssertTypeEq::pointer>(); testing::StaticAssertTypeEq::reference>(); -- cgit v1.2.3 From f04a0408623d728c5c1bf929b2bd97a71d063695 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 11 Aug 2021 16:16:04 -0700 Subject: Export of internal Abseil changes -- 667d6502169b645de44d5aa79ff7524c329ef136 by Abseil Team : Improve the error messages for absl::Substitute to clarify that an unescaped $ symbol is an error. The existing functionality enforces that unescaped $ results in a compile-time error. However, the current error messages do not mention this case and make it easier to miss while looking for an incorrect numerical substitution argument. PiperOrigin-RevId: 390242485 GitOrigin-RevId: 667d6502169b645de44d5aa79ff7524c329ef136 Change-Id: Iad1f1c18ab9686be11d40772e3d51dd233cfc9bc --- absl/strings/substitute.h | 173 ++++++++++++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 74 deletions(-) diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index c6da4dc6..151c56f5 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -361,43 +361,49 @@ inline void SubstituteAndAppend( // This body of functions catches cases where the number of placeholders // doesn't match the number of data arguments. void SubstituteAndAppend(std::string* output, const char* format) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0, - "There were no substitution arguments " - "but this format string has a $[0-9] in it"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 0, + "There were no substitution arguments " + "but this format string either has a $[0-9] in it or contains " + "an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1, "There was 1 substitution argument given, but " - "this format string is either missing its $0, or " - "contains one of $1-$9"); + "this format string is missing its $0, contains " + "one of $1-$9, or contains an unescaped $ character (use " + "$$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3, - "There were 2 substitution arguments given, but " - "this format string is either missing its $0/$1, or " - "contains one of $2-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 3, + "There were 2 substitution arguments given, but this format string is " + "missing its $0/$1, contains one of $2-$9, or contains an " + "unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7, - "There were 3 substitution arguments given, but " - "this format string is either missing its $0/$1/$2, or " - "contains one of $3-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 7, + "There were 3 substitution arguments given, but " + "this format string is missing its $0/$1/$2, contains one of " + "$3-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15, - "There were 4 substitution arguments given, but " - "this format string is either missing its $0-$3, or " - "contains one of $4-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 15, + "There were 4 substitution arguments given, but " + "this format string is missing its $0-$3, contains one of " + "$4-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -405,10 +411,11 @@ void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31, - "There were 5 substitution arguments given, but " - "this format string is either missing its $0-$4, or " - "contains one of $5-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 31, + "There were 5 substitution arguments given, but " + "this format string is missing its $0-$4, contains one of " + "$5-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -417,20 +424,22 @@ void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63, - "There were 6 substitution arguments given, but " - "this format string is either missing its $0-$5, or " - "contains one of $6-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 63, + "There were 6 substitution arguments given, but " + "this format string is missing its $0-$5, contains one of " + "$6-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127, - "There were 7 substitution arguments given, but " - "this format string is either missing its $0-$6, or " - "contains one of $7-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 127, + "There were 7 substitution arguments given, but " + "this format string is missing its $0-$6, contains one of " + "$7-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -438,10 +447,11 @@ void SubstituteAndAppend( const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255, - "There were 8 substitution arguments given, but " - "this format string is either missing its $0-$7, or " - "contains one of $8-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 255, + "There were 8 substitution arguments given, but " + "this format string is missing its $0-$7, contains one of " + "$8-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -452,7 +462,8 @@ void SubstituteAndAppend( ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 511, "There were 9 substitution arguments given, but " - "this format string is either missing its $0-$8, or contains a $9"); + "this format string is missing its $0-$8, contains a $9, or " + "contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -461,9 +472,11 @@ void SubstituteAndAppend( const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023, - "There were 10 substitution arguments given, but this " - "format string doesn't contain all of $0 through $9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1023, + "There were 10 substitution arguments given, but this " + "format string either doesn't contain all of $0 through $9 or " + "contains an unescaped $ character (use $$ instead)"); #endif // ABSL_BAD_CALL_IF // Substitute() @@ -589,47 +602,53 @@ ABSL_MUST_USE_RESULT inline std::string Substitute( std::string Substitute(const char* format) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0, "There were no substitution arguments " - "but this format string has a $[0-9] in it"); + "but this format string either has a $[0-9] in it or " + "contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1, - "There was 1 substitution argument given, but " - "this format string is either missing its $0, or " - "contains one of $1-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1, + "There was 1 substitution argument given, but " + "this format string is missing its $0, contains one of $1-$9, " + "or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3, - "There were 2 substitution arguments given, but " - "this format string is either missing its $0/$1, or " - "contains one of $2-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 3, + "There were 2 substitution arguments given, but " + "this format string is missing its $0/$1, contains one of " + "$2-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7, - "There were 3 substitution arguments given, but " - "this format string is either missing its $0/$1/$2, or " - "contains one of $3-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 7, + "There were 3 substitution arguments given, but " + "this format string is missing its $0/$1/$2, contains one of " + "$3-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15, - "There were 4 substitution arguments given, but " - "this format string is either missing its $0-$3, or " - "contains one of $4-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 15, + "There were 4 substitution arguments given, but " + "this format string is missing its $0-$3, contains one of " + "$4-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31, - "There were 5 substitution arguments given, but " - "this format string is either missing its $0-$4, or " - "contains one of $5-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 31, + "There were 5 substitution arguments given, but " + "this format string is missing its $0-$4, contains one of " + "$5-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -637,10 +656,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63, - "There were 6 substitution arguments given, but " - "this format string is either missing its $0-$5, or " - "contains one of $6-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 63, + "There were 6 substitution arguments given, but " + "this format string is missing its $0-$5, contains one of " + "$6-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -649,10 +669,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127, - "There were 7 substitution arguments given, but " - "this format string is either missing its $0-$6, or " - "contains one of $7-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 127, + "There were 7 substitution arguments given, but " + "this format string is missing its $0-$6, contains one of " + "$7-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -662,10 +683,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255, - "There were 8 substitution arguments given, but " - "this format string is either missing its $0-$7, or " - "contains one of $8-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 255, + "There were 8 substitution arguments given, but " + "this format string is missing its $0-$7, contains one of " + "$8-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute( const char* format, const substitute_internal::Arg& a0, @@ -676,7 +698,8 @@ std::string Substitute( ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 511, "There were 9 substitution arguments given, but " - "this format string is either missing its $0-$8, or contains a $9"); + "this format string is missing its $0-$8, contains a $9, or " + "contains an unescaped $ character (use $$ instead)"); std::string Substitute( const char* format, const substitute_internal::Arg& a0, @@ -685,9 +708,11 @@ std::string Substitute( const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023, - "There were 10 substitution arguments given, but this " - "format string doesn't contain all of $0 through $9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1023, + "There were 10 substitution arguments given, but this " + "format string either doesn't contain all of $0 through $9 or " + "contains an unescaped $ character (use $$ instead)"); #endif // ABSL_BAD_CALL_IF ABSL_NAMESPACE_END -- cgit v1.2.3 From 8910297baf87e1777c4fd30fb0693eecf9f2c134 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 13 Aug 2021 10:38:41 -0700 Subject: Export of internal Abseil changes -- 3a9b4e8e5ecba532db5cc4ac12d12660307ce9fb by Derek Mauro : Use the Bazel @platforms repository for platform constraints Fixes #1000 PiperOrigin-RevId: 390644226 -- b34e4d2f8a86b54bd483ec4c9c3dd781ad2d8b68 by Abseil Team : debugging: add some handling for RISC-V The RISC-V architecture uses a downward growing stack and can host Linux using ELF files. Adjust a few sites accordingly to indicate how to handle the RISC-V architecture. PiperOrigin-RevId: 390631894 -- 5fa3a0961bf3dd0799c048956a0128f7b8113f1e by Samuel Benzaquen : Rename the buffer hash function to LowLevelHash. Although it started as wyhash, it will depart from it so it does not make sense to keep the name. PiperOrigin-RevId: 390483506 -- 2e7867a2301d58ad4cd5abcaa5fd6f0db973ae7b by Abseil Team : This is an internal change. PiperOrigin-RevId: 390349746 GitOrigin-RevId: 3a9b4e8e5ecba532db5cc4ac12d12660307ce9fb Change-Id: I322c3762552a2107e6c6b108c25c01e5efa8aecd --- CMake/AbseilDll.cmake | 4 +- WORKSPACE | 8 + absl/BUILD.bazel | 4 +- absl/debugging/internal/stack_consumption.cc | 2 +- absl/debugging/internal/stack_consumption.h | 2 +- absl/debugging/internal/symbolize.h | 4 +- absl/hash/BUILD.bazel | 14 +- absl/hash/CMakeLists.txt | 14 +- absl/hash/internal/hash.cc | 13 +- absl/hash/internal/hash.h | 14 +- absl/hash/internal/low_level_hash.cc | 111 ++++++ absl/hash/internal/low_level_hash.h | 50 +++ absl/hash/internal/low_level_hash_test.cc | 486 +++++++++++++++++++++++++++ absl/hash/internal/wyhash.cc | 111 ------ absl/hash/internal/wyhash.h | 48 --- absl/hash/internal/wyhash_test.cc | 486 --------------------------- absl/time/internal/cctz/BUILD.bazel | 4 +- 17 files changed, 693 insertions(+), 682 deletions(-) create mode 100644 absl/hash/internal/low_level_hash.cc create mode 100644 absl/hash/internal/low_level_hash.h create mode 100644 absl/hash/internal/low_level_hash_test.cc delete mode 100644 absl/hash/internal/wyhash.cc delete mode 100644 absl/hash/internal/wyhash.h delete mode 100644 absl/hash/internal/wyhash_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 8bdf5a50..ea45c8a2 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -124,8 +124,8 @@ set(ABSL_INTERNAL_DLL_FILES "hash/internal/hash.h" "hash/internal/hash.cc" "hash/internal/spy_hash_state.h" - "hash/internal/wyhash.h" - "hash/internal/wyhash.cc" + "hash/internal/low_level_hash.h" + "hash/internal/low_level_hash.cc" "memory/memory.h" "meta/type_traits.h" "numeric/bits.h" diff --git a/WORKSPACE b/WORKSPACE index f4a5c476..af307872 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -42,3 +42,11 @@ http_archive( strip_prefix = "rules_cc-daf6ace7cfeacd6a83e9ff2ed659f416537b6c74", urls = ["https://github.com/bazelbuild/rules_cc/archive/daf6ace7cfeacd6a83e9ff2ed659f416537b6c74.zip"], ) + +# Bazel platform rules. +http_archive( + name = "platforms", + sha256 = "b601beaf841244de5c5a50d2b2eddd34839788000fa1be4260ce6603ca0d8eb7", + strip_prefix = "platforms-98939346da932eef0b54cf808622f5bb0928f00b", + urls = ["https://github.com/bazelbuild/platforms/archive/98939346da932eef0b54cf808622f5bb0928f00b.zip"], +) diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index c9d4a2da..7239b5bf 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -44,14 +44,14 @@ config_setting( config_setting( name = "osx", constraint_values = [ - "@bazel_tools//platforms:osx", + "@platforms//os:osx", ], ) config_setting( name = "ios", constraint_values = [ - "@bazel_tools//platforms:ios", + "@platforms//os:ios", ], ) diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc index e3dd51c3..51348649 100644 --- a/absl/debugging/internal/stack_consumption.cc +++ b/absl/debugging/internal/stack_consumption.cc @@ -43,7 +43,7 @@ namespace { // unspecified. Therefore, instead we hardcode the direction of the // stack on platforms we know about. #if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \ - defined(__aarch64__) + defined(__aarch64__) || defined(__riscv) constexpr bool kStackGrowsDown = true; #else #error Need to define kStackGrowsDown diff --git a/absl/debugging/internal/stack_consumption.h b/absl/debugging/internal/stack_consumption.h index 2b5e7151..f41b64c3 100644 --- a/absl/debugging/internal/stack_consumption.h +++ b/absl/debugging/internal/stack_consumption.h @@ -26,7 +26,7 @@ #error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly #elif !defined(__APPLE__) && !defined(_WIN32) && \ (defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \ - defined(__aarch64__)) + defined(__aarch64__) || defined(__riscv)) #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 namespace absl { diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h index 8c296f89..27d5e652 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h @@ -28,8 +28,8 @@ #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set -#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \ - !defined(__asmjs__) && !defined(__wasm__) +#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) \ + && !defined(__asmjs__) && !defined(__wasm__) #define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1 #include diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 4b2c220f..21915cc1 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -37,7 +37,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", - ":wyhash", + ":low_level_hash", "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", @@ -143,9 +143,9 @@ cc_test( ) cc_library( - name = "wyhash", - srcs = ["internal/wyhash.cc"], - hdrs = ["internal/wyhash.h"], + name = "low_level_hash", + srcs = ["internal/low_level_hash.cc"], + hdrs = ["internal/low_level_hash.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], @@ -157,13 +157,13 @@ cc_library( ) cc_test( - name = "wyhash_test", - srcs = ["internal/wyhash_test.cc"], + name = "low_level_hash_test", + srcs = ["internal/low_level_hash_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ - ":wyhash", + ":low_level_hash", "//absl/strings", "@com_google_googletest//:gtest_main", ], diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index c82f66f0..6c79c93a 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -36,7 +36,7 @@ absl_cc_library( absl::optional absl::variant absl::utility - absl::wyhash + absl::low_level_hash PUBLIC ) @@ -118,11 +118,11 @@ absl_cc_test( absl_cc_library( NAME - wyhash + low_level_hash HDRS - "internal/wyhash.h" + "internal/low_level_hash.h" SRCS - "internal/wyhash.cc" + "internal/low_level_hash.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -133,13 +133,13 @@ absl_cc_library( absl_cc_test( NAME - wyhash_test + low_level_hash_test SRCS - "internal/wyhash_test.cc" + "internal/low_level_hash_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::wyhash + absl::low_level_hash absl::strings GTest::gmock_main ) diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc index 06f53a59..4b818917 100644 --- a/absl/hash/internal/hash.cc +++ b/absl/hash/internal/hash.cc @@ -46,21 +46,22 @@ uint64_t MixingHashState::CombineLargeContiguousImpl64( ABSL_CONST_INIT const void* const MixingHashState::kSeed = &kSeed; -// The salt array used by Wyhash. This array is NOT the mechanism used to make -// absl::Hash non-deterministic between program invocations. See `Seed()` for -// that mechanism. +// The salt array used by LowLevelHash. This array is NOT the mechanism used to +// make absl::Hash non-deterministic between program invocations. See `Seed()` +// for that mechanism. // // Any random values are fine. These values are just digits from the decimal // part of pi. // https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number -constexpr uint64_t kWyhashSalt[5] = { +constexpr uint64_t kHashSalt[5] = { uint64_t{0x243F6A8885A308D3}, uint64_t{0x13198A2E03707344}, uint64_t{0xA4093822299F31D0}, uint64_t{0x082EFA98EC4E6C89}, uint64_t{0x452821E638D01377}, }; -uint64_t MixingHashState::WyhashImpl(const unsigned char* data, size_t len) { - return Wyhash(data, len, Seed(), kWyhashSalt); +uint64_t MixingHashState::LowLevelHashImpl(const unsigned char* data, + size_t len) { + return LowLevelHash(data, len, Seed(), kHashSalt); } } // namespace hash_internal diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index 90627e00..f5174096 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -42,7 +42,7 @@ #include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" #include "absl/container/fixed_array.h" -#include "absl/hash/internal/wyhash.h" +#include "absl/hash/internal/low_level_hash.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" #include "absl/strings/string_view.h" @@ -874,14 +874,14 @@ class ABSL_DLL MixingHashState : public HashStateBase { return static_cast(m ^ (m >> (sizeof(m) * 8 / 2))); } - // An extern to avoid bloat on a direct call to Wyhash() with fixed values for - // both the seed and salt parameters. - static uint64_t WyhashImpl(const unsigned char* data, size_t len); + // An extern to avoid bloat on a direct call to LowLevelHash() with fixed + // values for both the seed and salt parameters. + static uint64_t LowLevelHashImpl(const unsigned char* data, size_t len); ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Hash64(const unsigned char* data, size_t len) { #ifdef ABSL_HAVE_INTRINSIC_INT128 - return WyhashImpl(data, len); + return LowLevelHashImpl(data, len); #else return absl::hash_internal::CityHash64(reinterpret_cast(data), len); #endif @@ -945,8 +945,8 @@ inline uint64_t MixingHashState::CombineContiguousImpl( inline uint64_t MixingHashState::CombineContiguousImpl( uint64_t state, const unsigned char* first, size_t len, std::integral_constant /* sizeof_size_t */) { - // For large values we use Wyhash or CityHash depending on the platform, for - // small ones we just use a multiplicative hash. + // For large values we use LowLevelHash or CityHash depending on the platform, + // for small ones we just use a multiplicative hash. uint64_t v; if (len > 16) { if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { diff --git a/absl/hash/internal/low_level_hash.cc b/absl/hash/internal/low_level_hash.cc new file mode 100644 index 00000000..856bbd9b --- /dev/null +++ b/absl/hash/internal/low_level_hash.cc @@ -0,0 +1,111 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/hash/internal/low_level_hash.h" + +#include "absl/base/internal/unaligned_access.h" +#include "absl/numeric/int128.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_internal { + +static uint64_t Mix(uint64_t v0, uint64_t v1) { + absl::uint128 p = v0; + p *= v1; + return absl::Uint128Low64(p) ^ absl::Uint128High64(p); +} + +uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, + const uint64_t salt[]) { + const uint8_t* ptr = static_cast(data); + uint64_t starting_length = static_cast(len); + uint64_t current_state = seed ^ salt[0]; + + if (len > 64) { + // If we have more than 64 bytes, we're going to handle chunks of 64 + // bytes at a time. We're going to build up two separate hash states + // which we will then hash together. + uint64_t duplicated_state = current_state; + + do { + uint64_t a = absl::base_internal::UnalignedLoad64(ptr); + uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); + uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16); + uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24); + uint64_t e = absl::base_internal::UnalignedLoad64(ptr + 32); + uint64_t f = absl::base_internal::UnalignedLoad64(ptr + 40); + uint64_t g = absl::base_internal::UnalignedLoad64(ptr + 48); + uint64_t h = absl::base_internal::UnalignedLoad64(ptr + 56); + + uint64_t cs0 = Mix(a ^ salt[1], b ^ current_state); + uint64_t cs1 = Mix(c ^ salt[2], d ^ current_state); + current_state = (cs0 ^ cs1); + + uint64_t ds0 = Mix(e ^ salt[3], f ^ duplicated_state); + uint64_t ds1 = Mix(g ^ salt[4], h ^ duplicated_state); + duplicated_state = (ds0 ^ ds1); + + ptr += 64; + len -= 64; + } while (len > 64); + + current_state = current_state ^ duplicated_state; + } + + // We now have a data `ptr` with at most 64 bytes and the current state + // of the hashing state machine stored in current_state. + while (len > 16) { + uint64_t a = absl::base_internal::UnalignedLoad64(ptr); + uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); + + current_state = Mix(a ^ salt[1], b ^ current_state); + + ptr += 16; + len -= 16; + } + + // We now have a data `ptr` with at most 16 bytes. + uint64_t a = 0; + uint64_t b = 0; + if (len > 8) { + // When we have at least 9 and at most 16 bytes, set A to the first 64 + // bits of the input and B to the last 64 bits of the input. Yes, they will + // overlap in the middle if we are working with less than the full 16 + // bytes. + a = absl::base_internal::UnalignedLoad64(ptr); + b = absl::base_internal::UnalignedLoad64(ptr + len - 8); + } else if (len > 3) { + // If we have at least 4 and at most 8 bytes, set A to the first 32 + // bits and B to the last 32 bits. + a = absl::base_internal::UnalignedLoad32(ptr); + b = absl::base_internal::UnalignedLoad32(ptr + len - 4); + } else if (len > 0) { + // If we have at least 1 and at most 3 bytes, read all of the provided + // bits into A, with some adjustments. + a = ((ptr[0] << 16) | (ptr[len >> 1] << 8) | ptr[len - 1]); + b = 0; + } else { + a = 0; + b = 0; + } + + uint64_t w = Mix(a ^ salt[1], b ^ current_state); + uint64_t z = salt[1] ^ starting_length; + return Mix(w, z); +} + +} // namespace hash_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/hash/internal/low_level_hash.h b/absl/hash/internal/low_level_hash.h new file mode 100644 index 00000000..439968aa --- /dev/null +++ b/absl/hash/internal/low_level_hash.h @@ -0,0 +1,50 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file provides the Google-internal implementation of LowLevelHash. +// +// LowLevelHash is a fast hash function for hash tables, the fastest we've +// currently (late 2020) found that passes the SMHasher tests. The algorithm +// relies on intrinsic 128-bit multiplication for speed. This is not meant to be +// secure - just fast. +// +// It is closely based on a version of wyhash, but does not maintain or +// guarantee future compatibility with it. + +#ifndef ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_ +#define ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_ + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_internal { + +// Hash function for a byte array. A 64-bit seed and a set of five 64-bit +// integers are hashed into the result. +// +// To allow all hashable types (including string_view and Span) to depend on +// this algorithm, we keep the API low-level, with as few dependencies as +// possible. +uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, + const uint64_t salt[5]); + +} // namespace hash_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_ diff --git a/absl/hash/internal/low_level_hash_test.cc b/absl/hash/internal/low_level_hash_test.cc new file mode 100644 index 00000000..0ef50236 --- /dev/null +++ b/absl/hash/internal/low_level_hash_test.cc @@ -0,0 +1,486 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/hash/internal/low_level_hash.h" + +#include "absl/strings/escaping.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace { + +static const uint64_t kCurrentSeed = 0; +static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl, + 0x8ebc6af09c88c6e3, 0x589965cc75374cc3l, + 0x1d8e4e27c47d124f}; + +// Note: We don't account for endianness, so the values here are only correct if +// you're also running on a little endian platform. + +TEST(LowLevelHashTest, EmptyString) { + const std::string s = ""; + EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), + kCurrentSeed, kSalt), + 4808886099364463827); +} + +TEST(LowLevelHashTest, Spaces) { + const std::string s = " "; + EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), + kCurrentSeed, kSalt), + 1686201463024549249); +} + +TEST(LowLevelHashTest, RepeatingString) { + const std::string s = "aaaa"; + EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), + kCurrentSeed, kSalt), + 6646112255271966632); +} + +TEST(LowLevelHashTest, HexString) { + const std::string small = "\x01\x02\x03"; + const std::string med = "\x01\x02\x03\x04"; + + EXPECT_EQ(absl::hash_internal::LowLevelHash(small.c_str(), small.length(), + kCurrentSeed, kSalt), + 11989428023081740911ULL); + EXPECT_EQ(absl::hash_internal::LowLevelHash(med.c_str(), med.length(), + kCurrentSeed, kSalt), + 9765997711188871556ULL); +} + +TEST(LowLevelHashTest, Words) { + const std::string s = "third_party|wyhash|64"; + EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), + kCurrentSeed, kSalt), + 3702018632387611330); +} + +TEST(LowLevelHashTest, LongString) { + const std::string s = + "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz" + "0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp" + "QrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEf" + "GhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz012345" + "6789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789"; + + EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), + kCurrentSeed, kSalt), + 9245411362605796064ULL); +} + +TEST(LowLevelHashTest, BigReference) { + struct ExpectedResult { + absl::string_view base64_data; + uint64_t seed; + uint64_t hash; + } expected_results[] = { + {"", uint64_t{0xec42b7ab404b8acb}, uint64_t{0xe5a40d39ab796423}}, + {"Zw==", uint64_t{0xeeee074043a3ee0f}, uint64_t{0xa6564b468248c683}}, + {"xmk=", uint64_t{0x857902089c393de}, uint64_t{0xef192f401b116e1c}}, + {"c1H/", uint64_t{0x993df040024ca3af}, uint64_t{0xbe8dc0c54617639d}}, + {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}, uint64_t{0x93d7f665b5521c8e}}, + {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}, uint64_t{0x646d70bb42445f28}}, + {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}, uint64_t{0x96a7b1e3cc9bd426}}, + {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}, + uint64_t{0x76020289ab0790c4}}, + {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}, + uint64_t{0x39f842e4133b9b44}}, + {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}, + uint64_t{0x2b8d7047be4bcaab}}, + {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}, + uint64_t{0x99628abef6716a97}}, + {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}, + uint64_t{0x4432e02ba42b2740}}, + {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}, + uint64_t{0x74d810efcad7918a}}, + {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}, + uint64_t{0x88c84e986002507f}}, + {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}, + uint64_t{0x4f99acf193cf39b9}}, + {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}, + uint64_t{0xd90e7a3655891e37}}, + {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}, + uint64_t{0x3bb378b1d4df8fcf}}, + {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}, + uint64_t{0xf78e94045c052d47}}, + {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}, + uint64_t{0x26da0b2130da6b40}}, + {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}, + uint64_t{0x30b4d426af8c6986}}, + {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}, + uint64_t{0x5413b4aaf3baaeae}}, + {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}, + uint64_t{0x756ab265370a1597}}, + {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}, + uint64_t{0xdaf5f4b7d09814fb}}, + {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}, + uint64_t{0x8f874ae37742b75e}}, + {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}, + uint64_t{0x8fecd03956121ce8}}, + {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}, + uint64_t{0x229c292ea7a08285}}, + {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}, + uint64_t{0xbb4bf0692d14bae}}, + {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}, + uint64_t{0x207b24ca3bdac1db}}, + {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", uint64_t{0xd902ee3e44a5705f}, + uint64_t{0x64f6cd6745d3825b}}, + {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}, + uint64_t{0xa2b2e1656b58df1e}}, + {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", uint64_t{0x402d83f9f834f616}, + uint64_t{0xd01d30d9ee7a148}}, + {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==", + uint64_t{0x9c604164c016b72c}, uint64_t{0x1cb4cd00ab804e3b}}, + {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=", + uint64_t{0x3f4507e01f9e73ba}, uint64_t{0x4697f2637fd90999}}, + {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi", + uint64_t{0xc3fe0d5be8d2c7c7}, uint64_t{0x8383a756b5688c07}}, + {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==", + uint64_t{0x531858a40bfa7ea1}, uint64_t{0x695c29cb3696a975}}, + {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=", + uint64_t{0x86689478a7a7e8fa}, uint64_t{0xda2e5a5a5e971521}}, + {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph", + uint64_t{0x4ec948b8e7f27288}, uint64_t{0x7935d4befa056b2b}}, + {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==", + uint64_t{0xce46c7213c10032}, uint64_t{0x38dd541ca95420fe}}, + {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=", + uint64_t{0xf63e96ee6f32a8b6}, uint64_t{0xcc06c7a4963f967f}}, + {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy", + uint64_t{0x1cfe85e65fc5225}, uint64_t{0xbf0f6f66e232fb20}}, + {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==", + uint64_t{0x45c474f1cee1d2e8}, uint64_t{0xf7efb32d373fe71a}}, + {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=", + uint64_t{0x6e024e14015f329c}, uint64_t{0xe2e64634b1c12660}}, + {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt", + uint64_t{0x760c40502103ae1c}, uint64_t{0x285b8fd1638e306d}}, + {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==", + uint64_t{0x17fd05c3c560c320}, uint64_t{0x658e8a4e3b714d6c}}, + {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=", + uint64_t{0x8b34200a6f8e90d9}, uint64_t{0xf391fb968e0eb398}}, + {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn", + uint64_t{0x6be89e50818bdf69}, uint64_t{0x744a9ea0cc144bf2}}, + {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==", + uint64_t{0xfb389773315b47d8}, uint64_t{0x12636f2be11012f1}}, + {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=", + uint64_t{0x4f2512a23f61efee}, uint64_t{0x29c57de825948f80}}, + {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23", + uint64_t{0x59ccd92fc16c6fda}, uint64_t{0x58c6f99ab0d1c021}}, + {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==", + uint64_t{0x25c5a7f5bd330919}, uint64_t{0x13e7b5a7b82fe3bb}}, + {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=", + uint64_t{0x51df4174d34c97d7}, uint64_t{0x10fbc87901e02b63}}, + {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I", + uint64_t{0x80ce6d76f89cb57}, uint64_t{0xa24c9184901b748b}}, + {"64mVTbQ47dHjHlOHGS/hjJwr/" + "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==", + uint64_t{0x20961c911965f684}, uint64_t{0xcac4fd4c5080e581}}, + {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/" + "+a2V5WpA=", + uint64_t{0x4e5b926ec83868e7}, uint64_t{0xc38bdb7483ba68e1}}, + {"PGih0zDEOWCYGxuHGDFu9Ivbff/" + "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS", + uint64_t{0x3927b30b922eecef}, uint64_t{0xdb2a8069b2ceaffa}}, + {"RnpA/" + "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q=" + "=", + uint64_t{0xbd0291284a49b61c}, uint64_t{0xdf9fe91d0d1c7887}}, + {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+" + "BrWPRIbfprszSaPfrI=", + uint64_t{0x73a77c575bcc956}, uint64_t{0xe83f49e96e2e6a08}}, + {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/" + "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT", + uint64_t{0x766a0e2ade6d09a6}, uint64_t{0xc69e61b62ca2b62}}, + {"s/" + "Jf1+" + "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db" + "w==", + uint64_t{0x2599f4f905115869}, uint64_t{0xb4a4f3f85f8298fe}}, + {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+" + "7LgoUT1fJ/axybE=", + uint64_t{0xd8256e5444d21e53}, uint64_t{0x167a1b39e1e95f41}}, + {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+" + "LW4tTuzC6CIWbRGRRD1sQV/4", + uint64_t{0xf664a91333fb8dfd}, uint64_t{0xf8a2a5649855ee41}}, + {"CDK0meI07yrgV2kQlZZ+" + "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==", + uint64_t{0x9625b859be372cd1}, uint64_t{0x27992565b595c498}}, + {"d23/vc5ONh/" + "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+" + "nX7eOvs=", + uint64_t{0x7b99940782e29898}, uint64_t{0x3e08cca5b71f9346}}, + {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/" + "mGoT0pnMTQst7Lv2q6QN6Vm", + uint64_t{0x4fe12fa5383b51a8}, uint64_t{0xad406b10c770a6d2}}, + {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/" + "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==", + uint64_t{0xe2ccb09ac0f5b4b6}, uint64_t{0xd1713ce6e552bcf2}}, + {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/" + "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=", + uint64_t{0x7d0a37adbd7b753b}, uint64_t{0x753b287194c73ad3}}, + {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/" + "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW", + uint64_t{0xd3ae96ef9f7185f2}, uint64_t{0x5ae41a95f600af1c}}, + {"/WiHi9IQcxRImsudkA/KOTqGe8/" + "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==", + uint64_t{0x4fb88ea63f79a0d8}, uint64_t{0x4a61163b86a8bb4c}}, + {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/" + "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=", + uint64_t{0xed564e259bb5ebe9}, uint64_t{0x42eeaa79e760c7e4}}, + {"8FVYHx40lSQPTHheh08Oq0/" + "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi", + uint64_t{0x3e3256b60c428000}, uint64_t{0x698df622ef465b0a}}, + {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/" + "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==", + uint64_t{0xfb05bad59ec8705}, uint64_t{0x157583111e1a6026}}, + {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/" + "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=", + uint64_t{0xafdc251dbf97b5f8}, uint64_t{0xaa1388f078e793e0}}, + {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+" + "juQV4rsqYElMD/gWfDGpsWZKQ", + uint64_t{0x10ec9c92ddb5dcbc}, uint64_t{0xf10d68d0f3309360}}, + {"oswxop+" + "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp" + "L2mRK0rcIUYA4qLt5uOw==", + uint64_t{0x9a767d5822c7dac4}, uint64_t{0x2af056184457a3de}}, + {"0II/" + "697p+" + "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+" + "n5bxNOD1TGrjQtzKU5r7obo=", + uint64_t{0xee46254080d6e2db}, uint64_t{0x6d0058e1590b2489}}, + {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+" + "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc", + uint64_t{0xbbb669588d8bf398}, uint64_t{0x638f287f68817f12}}, + {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D" + "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==", + uint64_t{0xdc2afaa529beef44}, uint64_t{0xc46b71fecefd5467}}, + {"jVDKGYIuWOP/" + "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/" + "zAvSCB+zlf6upAsBlheUKgCfKww=", + uint64_t{0xf1f67391d45013a8}, uint64_t{0x2c8e94679d964e0a}}, + {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/" + "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d", + uint64_t{0x16fce2b8c65a3429}, uint64_t{0x8612b797ce22503a}}, + {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//" + "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==", + uint64_t{0xf4b096699f49fe67}, uint64_t{0x59f929babfba7170}}, + {"DUwXFJzagljo44QeJ7/" + "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1" + "eHuyFirAlkW+zKtwg=", + uint64_t{0xca584c4bc8198682}, uint64_t{0x9527556923fb49a0}}, + {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/" + "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR", + uint64_t{0xed269fc3818b6aad}, uint64_t{0x1039ab644f5e150b}}, + {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+" + "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==", + uint64_t{0x33f253cbb8fe66a8}, uint64_t{0x7816c83f3aa05e6d}}, + {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/" + "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=", + uint64_t{0xd0b76b2c1523d99c}, uint64_t{0xf51d2f564518c619}}, + {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/" + "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H", + uint64_t{0xfd28f0811a2a237f}, uint64_t{0x67d494cff03ac004}}, + {"ueLyMcqJXX+MhO4UApylCN9WlTQ+" + "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib" + "4/J3A5mseA3cS8w==", + uint64_t{0x6261fb136482e84}, uint64_t{0x2802d636ced1cfbb}}, + {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/" + "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=", + uint64_t{0x458efc750bca7c3a}, uint64_t{0xf64e20bad771cb12}}, + {"Q6AbOofGuTJOegPh9Clm/" + "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+" + "y4hHwLqRranl9FjvxfVKm3yvg68", + uint64_t{0xa7e69ff84e5e7c27}, uint64_t{0xb9a6cf84a83e15e}}, + {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/" + "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==", + uint64_t{0x3c59bfd0c29efe9e}, uint64_t{0x8da6630319609301}}, + {"zQUv8hFB3zh2GGl3KTvCmnfzE+" + "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//" + "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=", + uint64_t{0x10befacc6afd298d}, uint64_t{0x40946a86e2a996f3}}, + {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/" + "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd" + "P47L", + uint64_t{0x41d5320b0a38efa7}, uint64_t{0xcab7f5997953fa76}}, + {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP" + "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==", + uint64_t{0x58db1c7450fe17f3}, uint64_t{0x39129ca0e04fc465}}, + {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ" + "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=", + uint64_t{0x6098c055a335b7a6}, uint64_t{0x5238221fd685e1b8}}, + {"gzxyMJIPlU+bJBwhFUCHSofZ/" + "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+" + "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1", + uint64_t{0x1bbacec67845a801}, uint64_t{0x175130c407dbcaab}}, + {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+" + "8yyOw8lQabism19vOQxfmocEOW/" + "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==", + uint64_t{0xc419cfc7442190}, uint64_t{0x2f20e7536c0b0df}}, + {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/" + "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/" + "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=", + uint64_t{0xc95e510d94ba270c}, uint64_t{0x2742cb488a04ad56}}, + {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/" + "u+x+" + "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX" + "8jUfh1il", + uint64_t{0xff1ae05c98089c3f}, uint64_t{0xd6afb593879ff93b}}, + {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs" + "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==", + uint64_t{0x90c02b8dceced493}, uint64_t{0xf50ad64caac0ca7f}}, + {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/" + "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/" + "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=", + uint64_t{0x9f8a76697ab1aa36}, uint64_t{0x2ade95c4261364ae}}, + {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/" + "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND" + "55G2L1W", + uint64_t{0x6ba1bf3d811a531d}, uint64_t{0x5c4f3299faacd07a}}, + {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/" + "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==", + uint64_t{0x6a418974109c67b4}, uint64_t{0xfffe3bff0ae5e9bc}}, + {"6QO5nnDrY2/" + "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+" + "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/" + "ml6fnNXEpxplWJ1vEs4=", + uint64_t{0x8472f1c2b3d230a3}, uint64_t{0x1db785c0005166e4}}, + {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/" + "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww", + uint64_t{0x5e06068f884e73a7}, uint64_t{0xea000d962ad18418}}, + {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/" + "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt" + "gwYHw7yakDUv8WvonctmnoSPKENegQg==", + uint64_t{0x55290b1a8f170f59}, uint64_t{0xe42aef38359362d9}}, + {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+" + "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+" + "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=", + uint64_t{0x5501cfd83dfe706a}, uint64_t{0xc8e95657348a3891}}, + {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY" + "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj", + uint64_t{0xe43ed13d13a66990}, uint64_t{0xc162eca864f238c6}}, + {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G" + "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/" + "ODLcPEFztFnwjvCjmHw==", + uint64_t{0xdf43bc375cf5283f}, uint64_t{0xbe1fb373e20579ad}}, + {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/" + "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+" + "OevnkhUn5jsPlG2r5jYlVn8=", + uint64_t{0x8112b806d288d7b5}, uint64_t{0x628a1d4f40aa6ffd}}, + {"kUw/0z4l3a89jTwN5jpG0SHY5km/" + "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G" + "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB", + uint64_t{0xd52a18abb001cb46}, uint64_t{0xa87bdb7456340f90}}, + {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/" + "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+" + "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==", + uint64_t{0xe12b76a2433a1236}, uint64_t{0x5960ef3ba982c801}}, + {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/" + "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+" + "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=", + uint64_t{0x175bf7319cf1fa00}, uint64_t{0x5026586df9a431ec}}, + {"BrbNpb42+" + "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l" + "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi", + uint64_t{0xd63d57b3f67525ae}, uint64_t{0xfe4b8a20fdf0840b}}, + {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+" + "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/" + "LTmhua+rQ6Wup8ezLwfg==", + uint64_t{0x933faea858832b73}, uint64_t{0xdcb761867da7072f}}, + {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+" + "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+" + "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=", + uint64_t{0x53d061e5f8e7c04f}, uint64_t{0xc10d4653667275b7}}, + {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/" + "9S+" + "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA" + "wCZUOEXIsLU24o2Y", + uint64_t{0xdb4124556dd515e0}, uint64_t{0x727720deec13110b}}, + {"TKo+l+" + "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF" + "T0Gd0a2hI3+" + "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==", + uint64_t{0x4fb31a0dd681ee71}, uint64_t{0x710b009662858dc9}}, + {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+" + "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+" + "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=", + uint64_t{0x27cc72eefa138e4c}, uint64_t{0xfbf8f7a3ecac1eb7}}, + {"/I/" + "eImMwPo1U6wekNFD1Jxjk9XQVi1D+" + "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t" + "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp", + uint64_t{0x44bc2dfba4bd3ced}, uint64_t{0xb6fc4fcd0722e3df}}, + {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC" + "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+" + "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==", + uint64_t{0x242da1e3a439bed8}, uint64_t{0x7cb86dcc55104aac}}, + {"ZlhyQwLhXQyIUEnMH/" + "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/" + "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+" + "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=", + uint64_t{0xdc559c746e35c139}, uint64_t{0x19e71e9b45c3a51e}}, + {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg" + "Rko04h19QMG0mOw/" + "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+" + "xswhVMfL", + uint64_t{0xd0b0350275b9989}, uint64_t{0x51de38573c2bea48}}, + {"QhKlnIS6BuVCTQsnoE67E/" + "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p" + "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/" + "nxtxakyEtrNkKk471Oov9juP8oQ==", + uint64_t{0xb04489e41d17730c}, uint64_t{0xa73ab6996d6df158}}, + {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+" + "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK" + "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=", + uint64_t{0x2217285eb4572156}, uint64_t{0x55ef2b8c930817b2}}, + {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+" + "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+" + "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M", + uint64_t{0x12c2e8e68aede73b}, uint64_t{0xb2850bf5fae87157}}, + {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/" + "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//" + "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+" + "DkYu9ND0O2swg4itGeVSzXA==", + uint64_t{0x4d612125bdc4fd00}, uint64_t{0xecf3de1acd04651f}}, + {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+" + "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/" + "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+" + "hQLfVSh8OGs7fsBb68nNWPLeeSOo=", + uint64_t{0x81826b553954464e}, uint64_t{0xcc0a40552559ff32}}, + {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+" + "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS" + "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi", + uint64_t{0xc2e5d345dc0ddd2d}, uint64_t{0xc385c374f20315b1}}, + {"j+loZ+C87+" + "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++" + "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/" + "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==", + uint64_t{0x3da6830a9e32631e}, uint64_t{0xb90208a4c7234183}}, + {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/" + "sOmqaq8XAQLEn68LKj6/" + "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+" + "gN+7uKpoohgAhIwpAKQXmX5xtd0=", + uint64_t{0xc9ae5c8759b4877a}, uint64_t{0x58aa1ca7a4c075d9}}, + }; + + for (const auto& expected_result : expected_results) { + std::string str; + ASSERT_TRUE(absl::Base64Unescape(expected_result.base64_data, &str)); + EXPECT_EQ(absl::hash_internal::LowLevelHash(str.data(), str.size(), + expected_result.seed, kSalt), + expected_result.hash); + } +} + +} // namespace diff --git a/absl/hash/internal/wyhash.cc b/absl/hash/internal/wyhash.cc deleted file mode 100644 index 642bde43..00000000 --- a/absl/hash/internal/wyhash.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2020 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/hash/internal/wyhash.h" - -#include "absl/base/internal/unaligned_access.h" -#include "absl/numeric/int128.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace hash_internal { - -static uint64_t WyhashMix(uint64_t v0, uint64_t v1) { - absl::uint128 p = v0; - p *= v1; - return absl::Uint128Low64(p) ^ absl::Uint128High64(p); -} - -uint64_t Wyhash(const void* data, size_t len, uint64_t seed, - const uint64_t salt[]) { - const uint8_t* ptr = static_cast(data); - uint64_t starting_length = static_cast(len); - uint64_t current_state = seed ^ salt[0]; - - if (len > 64) { - // If we have more than 64 bytes, we're going to handle chunks of 64 - // bytes at a time. We're going to build up two separate hash states - // which we will then hash together. - uint64_t duplicated_state = current_state; - - do { - uint64_t a = absl::base_internal::UnalignedLoad64(ptr); - uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); - uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16); - uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24); - uint64_t e = absl::base_internal::UnalignedLoad64(ptr + 32); - uint64_t f = absl::base_internal::UnalignedLoad64(ptr + 40); - uint64_t g = absl::base_internal::UnalignedLoad64(ptr + 48); - uint64_t h = absl::base_internal::UnalignedLoad64(ptr + 56); - - uint64_t cs0 = WyhashMix(a ^ salt[1], b ^ current_state); - uint64_t cs1 = WyhashMix(c ^ salt[2], d ^ current_state); - current_state = (cs0 ^ cs1); - - uint64_t ds0 = WyhashMix(e ^ salt[3], f ^ duplicated_state); - uint64_t ds1 = WyhashMix(g ^ salt[4], h ^ duplicated_state); - duplicated_state = (ds0 ^ ds1); - - ptr += 64; - len -= 64; - } while (len > 64); - - current_state = current_state ^ duplicated_state; - } - - // We now have a data `ptr` with at most 64 bytes and the current state - // of the hashing state machine stored in current_state. - while (len > 16) { - uint64_t a = absl::base_internal::UnalignedLoad64(ptr); - uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); - - current_state = WyhashMix(a ^ salt[1], b ^ current_state); - - ptr += 16; - len -= 16; - } - - // We now have a data `ptr` with at most 16 bytes. - uint64_t a = 0; - uint64_t b = 0; - if (len > 8) { - // When we have at least 9 and at most 16 bytes, set A to the first 64 - // bits of the input and B to the last 64 bits of the input. Yes, they will - // overlap in the middle if we are working with less than the full 16 - // bytes. - a = absl::base_internal::UnalignedLoad64(ptr); - b = absl::base_internal::UnalignedLoad64(ptr + len - 8); - } else if (len > 3) { - // If we have at least 4 and at most 8 bytes, set A to the first 32 - // bits and B to the last 32 bits. - a = absl::base_internal::UnalignedLoad32(ptr); - b = absl::base_internal::UnalignedLoad32(ptr + len - 4); - } else if (len > 0) { - // If we have at least 1 and at most 3 bytes, read all of the provided - // bits into A, with some adjustments. - a = ((ptr[0] << 16) | (ptr[len >> 1] << 8) | ptr[len - 1]); - b = 0; - } else { - a = 0; - b = 0; - } - - uint64_t w = WyhashMix(a ^ salt[1], b ^ current_state); - uint64_t z = salt[1] ^ starting_length; - return WyhashMix(w, z); -} - -} // namespace hash_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/hash/internal/wyhash.h b/absl/hash/internal/wyhash.h deleted file mode 100644 index 2b534b47..00000000 --- a/absl/hash/internal/wyhash.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file provides the Google-internal implementation of the Wyhash -// algorithm. -// -// Wyhash is a fast hash function for hash tables, the fastest we've currently -// (late 2020) found that passes the SMHasher tests. The algorithm relies on -// intrinsic 128-bit multiplication for speed. This is not meant to be secure - -// just fast. - -#ifndef ABSL_HASH_INTERNAL_WYHASH_H_ -#define ABSL_HASH_INTERNAL_WYHASH_H_ - -#include -#include - -#include "absl/base/config.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace hash_internal { - -// Hash function for a byte array. A 64-bit seed and a set of five 64-bit -// integers are hashed into the result. -// -// To allow all hashable types (including string_view and Span) to depend on -// this algorithm, we keep the API low-level, with as few dependencies as -// possible. -uint64_t Wyhash(const void* data, size_t len, uint64_t seed, - const uint64_t salt[5]); - -} // namespace hash_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_HASH_INTERNAL_WYHASH_H_ diff --git a/absl/hash/internal/wyhash_test.cc b/absl/hash/internal/wyhash_test.cc deleted file mode 100644 index 9fb06d23..00000000 --- a/absl/hash/internal/wyhash_test.cc +++ /dev/null @@ -1,486 +0,0 @@ -// Copyright 2020 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/hash/internal/wyhash.h" - -#include "absl/strings/escaping.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace { - -static const uint64_t kCurrentSeed = 0; -static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl, - 0x8ebc6af09c88c6e3, 0x589965cc75374cc3l, - 0x1d8e4e27c47d124f}; - -// Note: We don't account for endianness, so the values here are only correct if -// you're also running on a little endian platform. - -TEST(WyhashTest, EmptyString) { - const std::string s = ""; - EXPECT_EQ( - absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), - 4808886099364463827); -} - -TEST(WyhashTest, Spaces) { - const std::string s = " "; - EXPECT_EQ( - absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), - 1686201463024549249); -} - -TEST(WyhashTest, RepeatingString) { - const std::string s = "aaaa"; - EXPECT_EQ( - absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), - 6646112255271966632); -} - -TEST(WyhashTest, HexString) { - const std::string small = "\x01\x02\x03"; - const std::string med = "\x01\x02\x03\x04"; - - EXPECT_EQ(absl::hash_internal::Wyhash(small.c_str(), small.length(), - kCurrentSeed, kSalt), - 11989428023081740911ULL); - EXPECT_EQ(absl::hash_internal::Wyhash(med.c_str(), med.length(), kCurrentSeed, - kSalt), - 9765997711188871556ULL); -} - -TEST(WyhashTest, Words) { - const std::string s = "third_party|wyhash|64"; - EXPECT_EQ( - absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), - 3702018632387611330); -} - -TEST(WyhashTest, LongString) { - const std::string s = - "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz" - "0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp" - "QrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEf" - "GhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz012345" - "6789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789"; - - EXPECT_EQ( - absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), - 9245411362605796064ULL); -} - -TEST(WyhashTest, BigReference) { - struct ExpectedResult { - absl::string_view base64_data; - uint64_t seed; - uint64_t hash; - } expected_results[] = { - {"", uint64_t{0xec42b7ab404b8acb}, uint64_t{0xe5a40d39ab796423}}, - {"Zw==", uint64_t{0xeeee074043a3ee0f}, uint64_t{0xa6564b468248c683}}, - {"xmk=", uint64_t{0x857902089c393de}, uint64_t{0xef192f401b116e1c}}, - {"c1H/", uint64_t{0x993df040024ca3af}, uint64_t{0xbe8dc0c54617639d}}, - {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}, uint64_t{0x93d7f665b5521c8e}}, - {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}, uint64_t{0x646d70bb42445f28}}, - {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}, uint64_t{0x96a7b1e3cc9bd426}}, - {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}, - uint64_t{0x76020289ab0790c4}}, - {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}, - uint64_t{0x39f842e4133b9b44}}, - {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}, - uint64_t{0x2b8d7047be4bcaab}}, - {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}, - uint64_t{0x99628abef6716a97}}, - {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}, - uint64_t{0x4432e02ba42b2740}}, - {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}, - uint64_t{0x74d810efcad7918a}}, - {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}, - uint64_t{0x88c84e986002507f}}, - {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}, - uint64_t{0x4f99acf193cf39b9}}, - {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}, - uint64_t{0xd90e7a3655891e37}}, - {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}, - uint64_t{0x3bb378b1d4df8fcf}}, - {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}, - uint64_t{0xf78e94045c052d47}}, - {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}, - uint64_t{0x26da0b2130da6b40}}, - {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}, - uint64_t{0x30b4d426af8c6986}}, - {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}, - uint64_t{0x5413b4aaf3baaeae}}, - {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}, - uint64_t{0x756ab265370a1597}}, - {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}, - uint64_t{0xdaf5f4b7d09814fb}}, - {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}, - uint64_t{0x8f874ae37742b75e}}, - {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}, - uint64_t{0x8fecd03956121ce8}}, - {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}, - uint64_t{0x229c292ea7a08285}}, - {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}, - uint64_t{0xbb4bf0692d14bae}}, - {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}, - uint64_t{0x207b24ca3bdac1db}}, - {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", uint64_t{0xd902ee3e44a5705f}, - uint64_t{0x64f6cd6745d3825b}}, - {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}, - uint64_t{0xa2b2e1656b58df1e}}, - {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", uint64_t{0x402d83f9f834f616}, - uint64_t{0xd01d30d9ee7a148}}, - {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==", - uint64_t{0x9c604164c016b72c}, uint64_t{0x1cb4cd00ab804e3b}}, - {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=", - uint64_t{0x3f4507e01f9e73ba}, uint64_t{0x4697f2637fd90999}}, - {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi", - uint64_t{0xc3fe0d5be8d2c7c7}, uint64_t{0x8383a756b5688c07}}, - {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==", - uint64_t{0x531858a40bfa7ea1}, uint64_t{0x695c29cb3696a975}}, - {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=", - uint64_t{0x86689478a7a7e8fa}, uint64_t{0xda2e5a5a5e971521}}, - {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph", - uint64_t{0x4ec948b8e7f27288}, uint64_t{0x7935d4befa056b2b}}, - {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==", - uint64_t{0xce46c7213c10032}, uint64_t{0x38dd541ca95420fe}}, - {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=", - uint64_t{0xf63e96ee6f32a8b6}, uint64_t{0xcc06c7a4963f967f}}, - {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy", - uint64_t{0x1cfe85e65fc5225}, uint64_t{0xbf0f6f66e232fb20}}, - {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==", - uint64_t{0x45c474f1cee1d2e8}, uint64_t{0xf7efb32d373fe71a}}, - {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=", - uint64_t{0x6e024e14015f329c}, uint64_t{0xe2e64634b1c12660}}, - {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt", - uint64_t{0x760c40502103ae1c}, uint64_t{0x285b8fd1638e306d}}, - {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==", - uint64_t{0x17fd05c3c560c320}, uint64_t{0x658e8a4e3b714d6c}}, - {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=", - uint64_t{0x8b34200a6f8e90d9}, uint64_t{0xf391fb968e0eb398}}, - {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn", - uint64_t{0x6be89e50818bdf69}, uint64_t{0x744a9ea0cc144bf2}}, - {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==", - uint64_t{0xfb389773315b47d8}, uint64_t{0x12636f2be11012f1}}, - {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=", - uint64_t{0x4f2512a23f61efee}, uint64_t{0x29c57de825948f80}}, - {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23", - uint64_t{0x59ccd92fc16c6fda}, uint64_t{0x58c6f99ab0d1c021}}, - {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==", - uint64_t{0x25c5a7f5bd330919}, uint64_t{0x13e7b5a7b82fe3bb}}, - {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=", - uint64_t{0x51df4174d34c97d7}, uint64_t{0x10fbc87901e02b63}}, - {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I", - uint64_t{0x80ce6d76f89cb57}, uint64_t{0xa24c9184901b748b}}, - {"64mVTbQ47dHjHlOHGS/hjJwr/" - "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==", - uint64_t{0x20961c911965f684}, uint64_t{0xcac4fd4c5080e581}}, - {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/" - "+a2V5WpA=", - uint64_t{0x4e5b926ec83868e7}, uint64_t{0xc38bdb7483ba68e1}}, - {"PGih0zDEOWCYGxuHGDFu9Ivbff/" - "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS", - uint64_t{0x3927b30b922eecef}, uint64_t{0xdb2a8069b2ceaffa}}, - {"RnpA/" - "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q=" - "=", - uint64_t{0xbd0291284a49b61c}, uint64_t{0xdf9fe91d0d1c7887}}, - {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+" - "BrWPRIbfprszSaPfrI=", - uint64_t{0x73a77c575bcc956}, uint64_t{0xe83f49e96e2e6a08}}, - {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/" - "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT", - uint64_t{0x766a0e2ade6d09a6}, uint64_t{0xc69e61b62ca2b62}}, - {"s/" - "Jf1+" - "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db" - "w==", - uint64_t{0x2599f4f905115869}, uint64_t{0xb4a4f3f85f8298fe}}, - {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+" - "7LgoUT1fJ/axybE=", - uint64_t{0xd8256e5444d21e53}, uint64_t{0x167a1b39e1e95f41}}, - {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+" - "LW4tTuzC6CIWbRGRRD1sQV/4", - uint64_t{0xf664a91333fb8dfd}, uint64_t{0xf8a2a5649855ee41}}, - {"CDK0meI07yrgV2kQlZZ+" - "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==", - uint64_t{0x9625b859be372cd1}, uint64_t{0x27992565b595c498}}, - {"d23/vc5ONh/" - "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+" - "nX7eOvs=", - uint64_t{0x7b99940782e29898}, uint64_t{0x3e08cca5b71f9346}}, - {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/" - "mGoT0pnMTQst7Lv2q6QN6Vm", - uint64_t{0x4fe12fa5383b51a8}, uint64_t{0xad406b10c770a6d2}}, - {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/" - "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==", - uint64_t{0xe2ccb09ac0f5b4b6}, uint64_t{0xd1713ce6e552bcf2}}, - {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/" - "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=", - uint64_t{0x7d0a37adbd7b753b}, uint64_t{0x753b287194c73ad3}}, - {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/" - "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW", - uint64_t{0xd3ae96ef9f7185f2}, uint64_t{0x5ae41a95f600af1c}}, - {"/WiHi9IQcxRImsudkA/KOTqGe8/" - "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==", - uint64_t{0x4fb88ea63f79a0d8}, uint64_t{0x4a61163b86a8bb4c}}, - {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/" - "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=", - uint64_t{0xed564e259bb5ebe9}, uint64_t{0x42eeaa79e760c7e4}}, - {"8FVYHx40lSQPTHheh08Oq0/" - "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi", - uint64_t{0x3e3256b60c428000}, uint64_t{0x698df622ef465b0a}}, - {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/" - "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==", - uint64_t{0xfb05bad59ec8705}, uint64_t{0x157583111e1a6026}}, - {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/" - "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=", - uint64_t{0xafdc251dbf97b5f8}, uint64_t{0xaa1388f078e793e0}}, - {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+" - "juQV4rsqYElMD/gWfDGpsWZKQ", - uint64_t{0x10ec9c92ddb5dcbc}, uint64_t{0xf10d68d0f3309360}}, - {"oswxop+" - "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp" - "L2mRK0rcIUYA4qLt5uOw==", - uint64_t{0x9a767d5822c7dac4}, uint64_t{0x2af056184457a3de}}, - {"0II/" - "697p+" - "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+" - "n5bxNOD1TGrjQtzKU5r7obo=", - uint64_t{0xee46254080d6e2db}, uint64_t{0x6d0058e1590b2489}}, - {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+" - "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc", - uint64_t{0xbbb669588d8bf398}, uint64_t{0x638f287f68817f12}}, - {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D" - "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==", - uint64_t{0xdc2afaa529beef44}, uint64_t{0xc46b71fecefd5467}}, - {"jVDKGYIuWOP/" - "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/" - "zAvSCB+zlf6upAsBlheUKgCfKww=", - uint64_t{0xf1f67391d45013a8}, uint64_t{0x2c8e94679d964e0a}}, - {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/" - "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d", - uint64_t{0x16fce2b8c65a3429}, uint64_t{0x8612b797ce22503a}}, - {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//" - "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==", - uint64_t{0xf4b096699f49fe67}, uint64_t{0x59f929babfba7170}}, - {"DUwXFJzagljo44QeJ7/" - "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1" - "eHuyFirAlkW+zKtwg=", - uint64_t{0xca584c4bc8198682}, uint64_t{0x9527556923fb49a0}}, - {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/" - "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR", - uint64_t{0xed269fc3818b6aad}, uint64_t{0x1039ab644f5e150b}}, - {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+" - "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==", - uint64_t{0x33f253cbb8fe66a8}, uint64_t{0x7816c83f3aa05e6d}}, - {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/" - "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=", - uint64_t{0xd0b76b2c1523d99c}, uint64_t{0xf51d2f564518c619}}, - {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/" - "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H", - uint64_t{0xfd28f0811a2a237f}, uint64_t{0x67d494cff03ac004}}, - {"ueLyMcqJXX+MhO4UApylCN9WlTQ+" - "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib" - "4/J3A5mseA3cS8w==", - uint64_t{0x6261fb136482e84}, uint64_t{0x2802d636ced1cfbb}}, - {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/" - "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=", - uint64_t{0x458efc750bca7c3a}, uint64_t{0xf64e20bad771cb12}}, - {"Q6AbOofGuTJOegPh9Clm/" - "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+" - "y4hHwLqRranl9FjvxfVKm3yvg68", - uint64_t{0xa7e69ff84e5e7c27}, uint64_t{0xb9a6cf84a83e15e}}, - {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/" - "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==", - uint64_t{0x3c59bfd0c29efe9e}, uint64_t{0x8da6630319609301}}, - {"zQUv8hFB3zh2GGl3KTvCmnfzE+" - "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//" - "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=", - uint64_t{0x10befacc6afd298d}, uint64_t{0x40946a86e2a996f3}}, - {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/" - "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd" - "P47L", - uint64_t{0x41d5320b0a38efa7}, uint64_t{0xcab7f5997953fa76}}, - {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP" - "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==", - uint64_t{0x58db1c7450fe17f3}, uint64_t{0x39129ca0e04fc465}}, - {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ" - "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=", - uint64_t{0x6098c055a335b7a6}, uint64_t{0x5238221fd685e1b8}}, - {"gzxyMJIPlU+bJBwhFUCHSofZ/" - "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+" - "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1", - uint64_t{0x1bbacec67845a801}, uint64_t{0x175130c407dbcaab}}, - {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+" - "8yyOw8lQabism19vOQxfmocEOW/" - "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==", - uint64_t{0xc419cfc7442190}, uint64_t{0x2f20e7536c0b0df}}, - {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/" - "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/" - "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=", - uint64_t{0xc95e510d94ba270c}, uint64_t{0x2742cb488a04ad56}}, - {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/" - "u+x+" - "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX" - "8jUfh1il", - uint64_t{0xff1ae05c98089c3f}, uint64_t{0xd6afb593879ff93b}}, - {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs" - "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==", - uint64_t{0x90c02b8dceced493}, uint64_t{0xf50ad64caac0ca7f}}, - {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/" - "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/" - "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=", - uint64_t{0x9f8a76697ab1aa36}, uint64_t{0x2ade95c4261364ae}}, - {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/" - "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND" - "55G2L1W", - uint64_t{0x6ba1bf3d811a531d}, uint64_t{0x5c4f3299faacd07a}}, - {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/" - "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==", - uint64_t{0x6a418974109c67b4}, uint64_t{0xfffe3bff0ae5e9bc}}, - {"6QO5nnDrY2/" - "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+" - "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/" - "ml6fnNXEpxplWJ1vEs4=", - uint64_t{0x8472f1c2b3d230a3}, uint64_t{0x1db785c0005166e4}}, - {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/" - "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww", - uint64_t{0x5e06068f884e73a7}, uint64_t{0xea000d962ad18418}}, - {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/" - "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt" - "gwYHw7yakDUv8WvonctmnoSPKENegQg==", - uint64_t{0x55290b1a8f170f59}, uint64_t{0xe42aef38359362d9}}, - {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+" - "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+" - "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=", - uint64_t{0x5501cfd83dfe706a}, uint64_t{0xc8e95657348a3891}}, - {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY" - "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj", - uint64_t{0xe43ed13d13a66990}, uint64_t{0xc162eca864f238c6}}, - {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G" - "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/" - "ODLcPEFztFnwjvCjmHw==", - uint64_t{0xdf43bc375cf5283f}, uint64_t{0xbe1fb373e20579ad}}, - {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/" - "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+" - "OevnkhUn5jsPlG2r5jYlVn8=", - uint64_t{0x8112b806d288d7b5}, uint64_t{0x628a1d4f40aa6ffd}}, - {"kUw/0z4l3a89jTwN5jpG0SHY5km/" - "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G" - "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB", - uint64_t{0xd52a18abb001cb46}, uint64_t{0xa87bdb7456340f90}}, - {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/" - "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+" - "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==", - uint64_t{0xe12b76a2433a1236}, uint64_t{0x5960ef3ba982c801}}, - {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/" - "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+" - "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=", - uint64_t{0x175bf7319cf1fa00}, uint64_t{0x5026586df9a431ec}}, - {"BrbNpb42+" - "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l" - "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi", - uint64_t{0xd63d57b3f67525ae}, uint64_t{0xfe4b8a20fdf0840b}}, - {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+" - "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/" - "LTmhua+rQ6Wup8ezLwfg==", - uint64_t{0x933faea858832b73}, uint64_t{0xdcb761867da7072f}}, - {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+" - "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+" - "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=", - uint64_t{0x53d061e5f8e7c04f}, uint64_t{0xc10d4653667275b7}}, - {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/" - "9S+" - "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA" - "wCZUOEXIsLU24o2Y", - uint64_t{0xdb4124556dd515e0}, uint64_t{0x727720deec13110b}}, - {"TKo+l+" - "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF" - "T0Gd0a2hI3+" - "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==", - uint64_t{0x4fb31a0dd681ee71}, uint64_t{0x710b009662858dc9}}, - {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+" - "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+" - "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=", - uint64_t{0x27cc72eefa138e4c}, uint64_t{0xfbf8f7a3ecac1eb7}}, - {"/I/" - "eImMwPo1U6wekNFD1Jxjk9XQVi1D+" - "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t" - "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp", - uint64_t{0x44bc2dfba4bd3ced}, uint64_t{0xb6fc4fcd0722e3df}}, - {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC" - "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+" - "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==", - uint64_t{0x242da1e3a439bed8}, uint64_t{0x7cb86dcc55104aac}}, - {"ZlhyQwLhXQyIUEnMH/" - "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/" - "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+" - "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=", - uint64_t{0xdc559c746e35c139}, uint64_t{0x19e71e9b45c3a51e}}, - {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg" - "Rko04h19QMG0mOw/" - "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+" - "xswhVMfL", - uint64_t{0xd0b0350275b9989}, uint64_t{0x51de38573c2bea48}}, - {"QhKlnIS6BuVCTQsnoE67E/" - "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p" - "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/" - "nxtxakyEtrNkKk471Oov9juP8oQ==", - uint64_t{0xb04489e41d17730c}, uint64_t{0xa73ab6996d6df158}}, - {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+" - "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK" - "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=", - uint64_t{0x2217285eb4572156}, uint64_t{0x55ef2b8c930817b2}}, - {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+" - "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+" - "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M", - uint64_t{0x12c2e8e68aede73b}, uint64_t{0xb2850bf5fae87157}}, - {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/" - "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//" - "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+" - "DkYu9ND0O2swg4itGeVSzXA==", - uint64_t{0x4d612125bdc4fd00}, uint64_t{0xecf3de1acd04651f}}, - {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+" - "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/" - "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+" - "hQLfVSh8OGs7fsBb68nNWPLeeSOo=", - uint64_t{0x81826b553954464e}, uint64_t{0xcc0a40552559ff32}}, - {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+" - "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS" - "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi", - uint64_t{0xc2e5d345dc0ddd2d}, uint64_t{0xc385c374f20315b1}}, - {"j+loZ+C87+" - "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++" - "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/" - "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==", - uint64_t{0x3da6830a9e32631e}, uint64_t{0xb90208a4c7234183}}, - {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/" - "sOmqaq8XAQLEn68LKj6/" - "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+" - "gN+7uKpoohgAhIwpAKQXmX5xtd0=", - uint64_t{0xc9ae5c8759b4877a}, uint64_t{0x58aa1ca7a4c075d9}}, - }; - - for (const auto& expected_result : expected_results) { - std::string str; - ASSERT_TRUE(absl::Base64Unescape(expected_result.base64_data, &str)); - EXPECT_EQ(absl::hash_internal::Wyhash(str.data(), str.size(), - expected_result.seed, kSalt), - expected_result.hash); - } -} - -} // namespace diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index 45a95292..f549af66 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -26,14 +26,14 @@ filegroup( config_setting( name = "osx", constraint_values = [ - "@bazel_tools//platforms:osx", + "@platforms//os:osx", ], ) config_setting( name = "ios", constraint_values = [ - "@bazel_tools//platforms:ios", + "@platforms//os:ios", ], ) -- cgit v1.2.3 From f9d3bc9e0074c8e01b19bb83303fd8aa02cde5d9 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 17 Aug 2021 23:06:55 +0900 Subject: Add missing ABSL_DLL for a few functions (#1002) --- absl/types/bad_optional_access.h | 2 +- absl/types/bad_variant_access.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h index a500286a..049e72ad 100644 --- a/absl/types/bad_optional_access.h +++ b/absl/types/bad_optional_access.h @@ -67,7 +67,7 @@ class bad_optional_access : public std::exception { namespace optional_internal { // throw delegator -[[noreturn]] void throw_bad_optional_access(); +[[noreturn]] ABSL_DLL void throw_bad_optional_access(); } // namespace optional_internal ABSL_NAMESPACE_END diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h index 095969f9..8ab215e9 100644 --- a/absl/types/bad_variant_access.h +++ b/absl/types/bad_variant_access.h @@ -70,8 +70,8 @@ class bad_variant_access : public std::exception { namespace variant_internal { -[[noreturn]] void ThrowBadVariantAccess(); -[[noreturn]] void Rethrow(); +[[noreturn]] ABSL_DLL void ThrowBadVariantAccess(); +[[noreturn]] ABSL_DLL void Rethrow(); } // namespace variant_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From c1aa431c071af43d7935bc0d44d560ea851a7696 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 17 Aug 2021 13:00:49 -0700 Subject: Export of internal Abseil changes -- aabb5093ff380fc60d9a75ace279b620fdc4bff9 by Abseil Team : Demangle Clang-specific " ::= cp * E" Cf. https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338 PiperOrigin-RevId: 391358031 -- 34dc7baa38f74a8c78b91ac94486707347a85e35 by Derek Mauro : Internal change PiperOrigin-RevId: 391282565 GitOrigin-RevId: aabb5093ff380fc60d9a75ace279b620fdc4bff9 Change-Id: I6d3e235a9c0a1b67a43071de205fed0fefe12912 --- absl/debugging/internal/demangle.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index 5cd56320..93ae3279 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -1617,6 +1617,7 @@ static bool ParseUnresolvedName(State *state) { // ::= <2-ary operator-name> // ::= <3-ary operator-name> // ::= cl + E +// ::= cp * E # Clang-specific. // ::= cv # type (expression) // ::= cv _ * E # type (expr-list) // ::= st @@ -1639,14 +1640,23 @@ static bool ParseExpression(State *state) { return true; } - // Object/function call expression. ParseState copy = state->parse_state; + + // Object/function call expression. if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { return true; } state->parse_state = copy; + // Clang-specific "cp * E" + // https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338 + if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) && + ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + // Function-param expression (level 0). if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { -- cgit v1.2.3 From 189d55a57f57731d335fd84999d5dccf771b8e6b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 18 Aug 2021 10:42:05 -0700 Subject: Export of internal Abseil changes -- 84bcdcd9497d1ec989f50c8dee93f656507c7bd6 by Abseil Team : Reduce length of the `flat_hash_map` type name in order to reduce binary bloat. PiperOrigin-RevId: 391560997 -- 5f49bd435e066989851dc045c7786ef400413f66 by Greg Falcon : Claim a bit from the Cord refcount for future use. Also rename the increasingly-inaccurately named "Refcount" class to "RefcountAndFlags". In optimized builds, this adds an extra mask instruction to decrement and test operations, but no new branches. Future flags can be added at no extra cost. Each additional flag will of course reduce the range of our refcount, but even with the bit added, we still support refcounts of 500 million. PiperOrigin-RevId: 391557567 GitOrigin-RevId: 84bcdcd9497d1ec989f50c8dee93f656507c7bd6 Change-Id: I051823bf5a9a42d4fa9200e39563ab585ecab331 --- absl/container/internal/hash_function_defaults.h | 32 ++++++------ absl/strings/internal/cord_internal.h | 66 ++++++++++++++---------- absl/strings/internal/cord_rep_btree.h | 2 +- 3 files changed, 56 insertions(+), 44 deletions(-) diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h index 0683422a..250e662c 100644 --- a/absl/container/internal/hash_function_defaults.h +++ b/absl/container/internal/hash_function_defaults.h @@ -78,24 +78,26 @@ struct StringHash { } }; +struct StringEq { + using is_transparent = void; + bool operator()(absl::string_view lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(absl::string_view lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } +}; + // Supports heterogeneous lookup for string-like elements. struct StringHashEq { using Hash = StringHash; - struct Eq { - using is_transparent = void; - bool operator()(absl::string_view lhs, absl::string_view rhs) const { - return lhs == rhs; - } - bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const { - return lhs == rhs; - } - bool operator()(const absl::Cord& lhs, absl::string_view rhs) const { - return lhs == rhs; - } - bool operator()(absl::string_view lhs, const absl::Cord& rhs) const { - return lhs == rhs; - } - }; + using Eq = StringEq; }; template <> diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index b7f3f4c0..7172b147 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -80,12 +80,13 @@ enum Constants { kMaxBytesToCopy = 511 }; -// Wraps std::atomic for reference counting. -class Refcount { +// Compact class for tracking the reference count and state flags for CordRep +// instances. Data is stored in an atomic int32_t for compactness and speed. +class RefcountAndFlags { public: - constexpr Refcount() : count_{kRefIncrement} {} + constexpr RefcountAndFlags() : count_{kRefIncrement} {} struct Immortal {}; - explicit constexpr Refcount(Immortal) : count_(kImmortalTag) {} + explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {} // Increments the reference count. Imposes no memory ordering. inline void Increment() { @@ -98,26 +99,27 @@ class Refcount { // Returns false if there are no references outstanding; true otherwise. // Inserts barriers to ensure that state written before this method returns // false will be visible to a thread that just observed this method returning - // false. + // false. Always returns false when the immortal bit is set. inline bool Decrement() { - int32_t refcount = count_.load(std::memory_order_acquire); - assert(refcount > 0 || refcount & kImmortalTag); + int32_t refcount = count_.load(std::memory_order_acquire) & kRefcountMask; + assert(refcount > 0 || refcount & kImmortalFlag); return refcount != kRefIncrement && - count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) != - kRefIncrement; + (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) & + kRefcountMask) != kRefIncrement; } // Same as Decrement but expect that refcount is greater than 1. inline bool DecrementExpectHighRefcount() { int32_t refcount = - count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel); - assert(refcount > 0 || refcount & kImmortalTag); + count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) & + kRefcountMask; + assert(refcount > 0 || refcount & kImmortalFlag); return refcount != kRefIncrement; } // Returns the current reference count using acquire semantics. inline int32_t Get() const { - return count_.load(std::memory_order_acquire) >> kImmortalShift; + return count_.load(std::memory_order_acquire) >> kNumFlags; } // Returns whether the atomic integer is 1. @@ -127,26 +129,34 @@ class Refcount { // This call performs the test for a reference count of one, and // performs the memory barrier needed for the owning thread // to act on the object, knowing that it has exclusive access to the - // object. + // object. Always returns false when the immortal bit is set. inline bool IsOne() { - return count_.load(std::memory_order_acquire) == kRefIncrement; + return (count_.load(std::memory_order_acquire) & kRefcountMask) == + kRefIncrement; } bool IsImmortal() const { - return (count_.load(std::memory_order_relaxed) & kImmortalTag) != 0; + return (count_.load(std::memory_order_relaxed) & kImmortalFlag) != 0; } private: - // We reserve the bottom bit to tag a reference count as immortal. - // By making it `1` we ensure that we never reach `0` when adding/subtracting - // `2`, thus it never looks as if it should be destroyed. - // These are used for the StringConstant constructor where we do not increase - // the refcount at construction time (due to constinit requirements) but we - // will still decrease it at destruction time to avoid branching on Unref. + // We reserve the bottom bits for flags. + // kImmortalBit indicates that this entity should never be collected; it is + // used for the StringConstant constructor to avoid collecting immutable + // constant cords. + // kReservedFlag is reserved for future use. enum { - kImmortalShift = 1, - kRefIncrement = 1 << kImmortalShift, - kImmortalTag = kRefIncrement - 1 + kNumFlags = 2, + + kImmortalFlag = 0x1, + kReservedFlag = 0x2, + kRefIncrement = (1 << kNumFlags), + + // Bitmask to use when checking refcount by equality. This masks out + // all flags except kImmortalFlag, which is part of the refcount for + // purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1 + // if the immortal bit is set.) + kRefcountMask = ~kReservedFlag, }; std::atomic count_; @@ -195,13 +205,13 @@ static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive"); struct CordRep { CordRep() = default; - constexpr CordRep(Refcount::Immortal immortal, size_t l) + constexpr CordRep(RefcountAndFlags::Immortal immortal, size_t l) : length(l), refcount(immortal), tag(EXTERNAL), storage{} {} // The following three fields have to be less than 32 bytes since // that is the smallest supported flat node size. size_t length; - Refcount refcount; + RefcountAndFlags refcount; // If tag < FLAT, it represents CordRepKind and indicates the type of node. // Otherwise, the node type is CordRepFlat and the tag is the encoded size. uint8_t tag; @@ -275,7 +285,7 @@ using ExternalReleaserInvoker = void (*)(CordRepExternal*); struct CordRepExternal : public CordRep { CordRepExternal() = default; explicit constexpr CordRepExternal(absl::string_view str) - : CordRep(Refcount::Immortal{}, str.size()), + : CordRep(RefcountAndFlags::Immortal{}, str.size()), base(str.data()), releaser_invoker(nullptr) {} @@ -529,7 +539,7 @@ class InlineData { // store the size in the last char of `as_chars_` shifted left + 1. // Else we store it in a tree and store a pointer to that tree in // `as_tree_.rep` and store a tag in `tagged_size`. - union { + union { char as_chars_[kMaxInline + 1]; AsTree as_tree_; }; diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 8f000cab..303f4580 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -623,7 +623,7 @@ inline void CordRepBtree::Destroy(CordRepBtree* tree) { inline CordRepBtree* CordRepBtree::CopyRaw() const { auto* tree = static_cast(::operator new(sizeof(CordRepBtree))); memcpy(static_cast(tree), this, sizeof(CordRepBtree)); - new (&tree->refcount) Refcount; + new (&tree->refcount) RefcountAndFlags; return tree; } -- cgit v1.2.3 From a05366d851c5cb88065272f951e03955197e7c11 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 19 Aug 2021 13:34:28 -0700 Subject: Export of internal Abseil changes -- 04cb3b22497190170aa5b774e98080c5de2ba60b by Abseil Team : Alternative bit mixer for LowLevelHash on ARM LowLevelHash's bit-mixer is inefficient on ARM because it calculates a 128-bit product of two 64-bit numbers. On ARM, this requires a sequence of two instructions with a high combined latency and poor throughput. This change provides alternative bit-mixing code for ARM that uses only 64-bit arithmetic (multiplication, xor, and left-shifts) and speeds things up considerably. The bit-mixing code for ARM was inspired by by Woothash[1] and xxh3[1]. Once I landed on a sequence of operations that provided good mixing, I used a test harness to search for the combination of shift / rotate factors that provided the best mixing, as indicated by SMHasher hash quality tests. The new mixing code passes 13 out of 15 of the hash quality test suites in SMHasher, with the two failures being in the noise range: e.g. 1 collision vs. zero expected in a keyset of ~8m keys. [1]: https://github.com/tommyettinger/waterhash/blob/49f5cf0b63b9/woothash.h#L16-L20 [2]: https://github.com/Cyan4973/xxHash/blob/6853ddc36e46/xxhash.h#L3240-L3265 PiperOrigin-RevId: 391833008 -- 17a4de1f9d623155c75b19285d414cd55a487cd6 by Saleem Abdulrasool : debugging: add support for unwinding on RISCV Linux This adds partial support for unwinding the RISCV call stack. It is largely duplicated from the AArch64 support with alterations for the ELF RISCV psABI. This covers RISCV64 and RISCV32, though not the ILP32E calling convention. PiperOrigin-RevId: 391818522 -- 32c93e449327b2cea32b32f6365e84b420fe1ed3 by Gennadiy Rozental : New storage for types smaller than 8 bytes. Also adding new read interface for types smaller than or rqual to 8 bytes to avoid passing the pointer. PiperOrigin-RevId: 391726822 -- e987ac08a7787801cbfc7d7c96649e97fa8cff1a by Abseil Team : Extern template `find_first_non_full` to reduce linkage size for TU with single not inlined function. PiperOrigin-RevId: 391718862 -- 73af9bfcb5bf045089133e18bbd20eb5bb699172 by Gennadiy Rozental : Make most non-mutable most int128 methods and friend free functions constexpr. Some functions are implemented offline (at least in some configurations) and can't be made constexpr. Mutable methods can't be made constexpr until we drop c++11 support. Fixes #978 PiperOrigin-RevId: 391706535 GitOrigin-RevId: 04cb3b22497190170aa5b774e98080c5de2ba60b Change-Id: If051fad5ff004e2e82fa53618fc04a6fe3d2d4be --- absl/base/log_severity_test.cc | 6 +- absl/container/internal/raw_hash_set.cc | 2 + absl/container/internal/raw_hash_set.h | 6 + absl/debugging/BUILD.bazel | 1 + absl/debugging/CMakeLists.txt | 1 + absl/debugging/internal/stacktrace_config.h | 3 + absl/debugging/internal/stacktrace_riscv-inl.inc | 234 +++++++++++ absl/debugging/stacktrace.cc | 1 + absl/flags/flag_test.cc | 37 +- absl/flags/internal/flag.cc | 91 +++-- absl/flags/internal/flag.h | 71 +++- absl/hash/BUILD.bazel | 1 + absl/hash/CMakeLists.txt | 1 + absl/hash/internal/low_level_hash.cc | 12 + absl/hash/internal/low_level_hash_test.cc | 484 +++++++++++++---------- absl/numeric/int128.h | 128 +++--- absl/numeric/int128_have_intrinsic.inc | 44 +-- absl/numeric/int128_no_intrinsic.inc | 133 +++---- 18 files changed, 840 insertions(+), 416 deletions(-) create mode 100644 absl/debugging/internal/stacktrace_riscv-inl.inc diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc index 2c6872b0..55b26d17 100644 --- a/absl/base/log_severity_test.cc +++ b/absl/base/log_severity_test.cc @@ -52,9 +52,9 @@ TEST(StreamTest, Works) { Eq("absl::LogSeverity(4)")); } -static_assert( - absl::flags_internal::FlagUseOneWordStorage::value, - "Flags of type absl::LogSeverity ought to be lock-free."); +static_assert(absl::flags_internal::FlagUseValueAndInitBitStorage< + absl::LogSeverity>::value, + "Flags of type absl::LogSeverity ought to be lock-free."); using ParseFlagFromOutOfRangeIntegerTest = TestWithParam; INSTANTIATE_TEST_SUITE_P( diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index 3c72c41a..687bcb8a 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -59,6 +59,8 @@ void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) { std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes()); ctrl[capacity] = ctrl_t::kSentinel; } +// Extern template instantiotion for inline function. +template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index ade2f584..18b1f5a4 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -570,6 +570,7 @@ inline probe_seq probe(const ctrl_t* ctrl, size_t hash, // - the input is already a set // - there are enough slots // - the element with the hash is not in the table +template inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, size_t capacity) { auto seq = probe(ctrl, hash, capacity); @@ -593,6 +594,11 @@ inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, } } +// Extern template for inline function keep possibility of inlining. +// When compiler decided to not inline, no symbols will be added to the +// corresponding translation unit. +extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); + // Reset all ctrl bytes back to ctrl_t::kEmpty, except the sentinel. inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, size_t slot_size) { diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index a8c51a5c..b536a044 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -37,6 +37,7 @@ cc_library( "internal/stacktrace_emscripten-inl.inc", "internal/stacktrace_generic-inl.inc", "internal/stacktrace_powerpc-inl.inc", + "internal/stacktrace_riscv-inl.inc", "internal/stacktrace_unimplemented-inl.inc", "internal/stacktrace_win32-inl.inc", "internal/stacktrace_x86-inl.inc", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 760772f3..b16fa007 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -25,6 +25,7 @@ absl_cc_library( "internal/stacktrace_emscripten-inl.inc" "internal/stacktrace_generic-inl.inc" "internal/stacktrace_powerpc-inl.inc" + "internal/stacktrace_riscv-inl.inc" "internal/stacktrace_unimplemented-inl.inc" "internal/stacktrace_win32-inl.inc" "internal/stacktrace_x86-inl.inc" diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h index 6cceef26..29b26bdd 100644 --- a/absl/debugging/internal/stacktrace_config.h +++ b/absl/debugging/internal/stacktrace_config.h @@ -65,6 +65,9 @@ #elif defined(__aarch64__) #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_aarch64-inl.inc" +#elif defined(__riscv) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_riscv-inl.inc" #elif defined(__has_include) #if __has_include() // Note: When using glibc this may require -funwind-tables to function properly. diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc new file mode 100644 index 00000000..8cbc7854 --- /dev/null +++ b/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -0,0 +1,234 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ + +// Generate stack trace for riscv + +#include + +#include "absl/base/config.h" +#if defined(__linux__) +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" +#include "absl/debugging/stacktrace.h" + +static const uintptr_t kUnknownFrameSize = 0; + +#if defined(__linux__) +// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. +static const unsigned char *GetKernelRtSigreturnAddress() { + constexpr uintptr_t kImpossibleAddress = 0; + ABSL_CONST_INIT static std::atomic memoized(kImpossibleAddress); + uintptr_t address = memoized.load(std::memory_order_relaxed); + if (address != kImpossibleAddress) { + return reinterpret_cast(address); + } + + address = reinterpret_cast(nullptr); + +#if ABSL_HAVE_VDSO_SUPPORT + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; + // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10. + auto lookup = [&](int type) { + return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_4.15", type, + &symbol_info); + }; + if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || + symbol_info.address == nullptr) { + // Unexpected: VDSO is present, yet the expected symbol is missing or + // null. + assert(false && "VDSO is present, but doesn't have expected symbol"); + } else { + if (reinterpret_cast(symbol_info.address) != + kImpossibleAddress) { + address = reinterpret_cast(symbol_info.address); + } else { + assert(false && "VDSO returned invalid address"); + } + } + } +#endif + + memoized.store(address, std::memory_order_relaxed); + return reinterpret_cast(address); +} +#endif // __linux__ + +// Compute the size of a stack frame in [low..high). We assume that low < high. +// Return size of kUnknownFrameSize. +template +static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { + const char *low_char_ptr = reinterpret_cast(low); + const char *high_char_ptr = reinterpret_cast(high); + return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; +} + +// Given a pointer to a stack frame, locate and return the calling stackframe, +// or return null if no stackframe can be found. Perform sanity checks (the +// strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { + // . + // . + // . + // +-> +----------------+ + // | | return address | + // | | previous fp | + // | | ... | + // | +----------------+ <-+ + // | | return address | | + // +---|- previous fp | | + // | ... | | + // $fp ->|----------------+ | + // | return address | | + // | previous fp -|---+ + // $sp ->| ... | + // +----------------+ + void **new_frame_pointer = reinterpret_cast(old_frame_pointer[-2]); + bool check_frame_size = true; + +#if defined(__linux__) + if (WITH_CONTEXT && uc != nullptr) { + // Check to see if next frame's return address is __kernel_rt_sigreturn. + if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) { + const ucontext_t *ucv = static_cast(uc); + // old_frame_pointer is not suitable for unwinding, look at ucontext to + // discover frame pointer before signal. + // + // RISCV ELF psABI has the frame pointer at x8/fp/s0. + // -- RISCV psABI Table 18.2 + void **const pre_signal_frame_pointer = + reinterpret_cast(ucv->uc_mcontext.__gregs[8]); + + // Check the alleged frame pointer is actually readable. This is to + // prevent "double fault" in case we hit the first fault due to stack + // corruption. + if (!absl::debugging_internal::AddressIsReadable( + pre_signal_frame_pointer)) + return nullptr; + + // Alleged frame pointer is readable, use it for further unwinding. + new_frame_pointer = pre_signal_frame_pointer; + + // Skip frame size check if we return from a signal. We may be using an + // alterate stack for signals. + check_frame_size = false; + } + } +#endif + + // The RISCV ELF psABI mandates that the stack pointer is always 16-byte + // aligned. + // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte + // alignment. + if ((reinterpret_cast(new_frame_pointer) & 15) != 0) + return nullptr; + + // Check frame size. In strict mode, we assume frames to be under 100,000 + // bytes. In non-strict mode, we relax the limit to 1MB. + if (check_frame_size) { + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize || frame_size > max_size) + return nullptr; + } + + return new_frame_pointer; +} + +template +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { +#if defined(__GNUC__) + void **frame_pointer = reinterpret_cast(__builtin_frame_address(0)); +#else +#error reading stack pointer not yet supported on this platform +#endif + + skip_count++; // Skip the frame for this function. + int n = 0; + + // The `frame_pointer` that is computed here points to the top of the frame. + // The two words preceding the address are the return address and the previous + // frame pointer. To find a PC value associated with the current frame, we + // need to go down a level in the call chain. So we remember the return + // address of the last frame seen. This does not work for the first stack + // frame, which belongs to `UnwindImp()` but we skip the frame for + // `UnwindImp()` anyway. + void *prev_return_address = nullptr; + + while (frame_pointer && n < max_depth) { + // The absl::GetStackFrames routine si called when we are in some + // informational context (the failure signal handler for example). Use the + // non-strict unwinding rules to produce a stack trace that is as complete + // as possible (even if it contains a few bogus entries in some rare cases). + void **next_frame_pointer = + NextStackFrame(frame_pointer, ucp); + + if (skip_count > 0) { + skip_count--; + } else { + result[n] = prev_return_address; + if (IS_STACK_FRAMES) { + sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); + } + n++; + } + prev_return_address = frame_pointer[-1]; + frame_pointer = next_frame_pointer; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 200; + int j = 0; + for (; frame_pointer != nullptr && j < kMaxUnwind; j++) { + frame_pointer = + NextStackFrame(frame_pointer, ucp); + } + *min_dropped_frames = j; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { return true; } +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc index 5358b942..ff8069f8 100644 --- a/absl/debugging/stacktrace.cc +++ b/absl/debugging/stacktrace.cc @@ -52,6 +52,7 @@ # include "absl/debugging/internal/stacktrace_emscripten-inl.inc" # include "absl/debugging/internal/stacktrace_generic-inl.inc" # include "absl/debugging/internal/stacktrace_powerpc-inl.inc" +# include "absl/debugging/internal/stacktrace_riscv-inl.inc" # include "absl/debugging/internal/stacktrace_unimplemented-inl.inc" # include "absl/debugging/internal/stacktrace_win32-inl.inc" # include "absl/debugging/internal/stacktrace_x86-inl.inc" diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index ba81317b..6e974a5b 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -103,9 +103,9 @@ struct S2 { TEST_F(FlagTest, Traits) { EXPECT_EQ(flags::StorageKind(), - flags::FlagValueStorageKind::kOneWordAtomic); + flags::FlagValueStorageKind::kValueAndInitBit); EXPECT_EQ(flags::StorageKind(), - flags::FlagValueStorageKind::kOneWordAtomic); + flags::FlagValueStorageKind::kValueAndInitBit); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kOneWordAtomic); EXPECT_EQ(flags::StorageKind(), @@ -724,6 +724,8 @@ ABSL_FLAG(CustomUDT, test_flag_custom_udt, CustomUDT(), "test flag custom UDT"); namespace { TEST_F(FlagTest, TestCustomUDT) { + EXPECT_EQ(flags::StorageKind(), + flags::FlagValueStorageKind::kOneWordAtomic); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1)); absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3)); @@ -944,3 +946,34 @@ TEST_F(FlagTest, TestNonTriviallyCopyableUDT) { } } // namespace + +// -------------------------------------------------------------------- + +namespace { + +enum TestE { A = 1, B = 2, C = 3 }; + +struct EnumWrapper { + EnumWrapper() : e(A) {} + + TestE e; +}; + +bool AbslParseFlag(absl::string_view, EnumWrapper*, std::string*) { + return true; +} +std::string AbslUnparseFlag(const EnumWrapper&) { return ""; } + +} // namespace + +ABSL_FLAG(EnumWrapper, test_enum_wrapper_flag, {}, "help"); + +TEST_F(FlagTest, TesTypeWrappingEnum) { + EnumWrapper value = absl::GetFlag(FLAGS_test_enum_wrapper_flag); + EXPECT_EQ(value.e, A); + + value.e = B; + absl::SetFlag(&FLAGS_test_enum_wrapper_flag, value); + value = absl::GetFlag(FLAGS_test_enum_wrapper_flag); + EXPECT_EQ(value.e, B); +} diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index f83c1fe7..1515022d 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -145,12 +145,7 @@ void FlagImpl::Init() { auto def_kind = static_cast(def_kind_); switch (ValueStorageKind()) { - case FlagValueStorageKind::kAlignedBuffer: - // For this storage kind the default_value_ always points to gen_func - // during initialization. - assert(def_kind == FlagDefaultKind::kGenFunc); - (*default_value_.gen_func)(AlignedBufferValue()); - break; + case FlagValueStorageKind::kValueAndInitBit: case FlagValueStorageKind::kOneWordAtomic: { alignas(int64_t) std::array buf{}; if (def_kind == FlagDefaultKind::kGenFunc) { @@ -159,6 +154,12 @@ void FlagImpl::Init() { assert(def_kind != FlagDefaultKind::kDynamicValue); std::memcpy(buf.data(), &default_value_, Sizeof(op_)); } + if (ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit) { + // We presume here the memory layout of FlagValueAndInitBit struct. + uint8_t initialized = 1; + std::memcpy(buf.data() + Sizeof(op_), &initialized, + sizeof(initialized)); + } OneWordValue().store(absl::bit_cast(buf), std::memory_order_release); break; @@ -170,6 +171,12 @@ void FlagImpl::Init() { (*default_value_.gen_func)(AtomicBufferValue()); break; } + case FlagValueStorageKind::kAlignedBuffer: + // For this storage kind the default_value_ always points to gen_func + // during initialization. + assert(def_kind == FlagDefaultKind::kGenFunc); + (*default_value_.gen_func)(AlignedBufferValue()); + break; } seq_lock_.MarkInitialized(); } @@ -226,12 +233,10 @@ std::unique_ptr FlagImpl::MakeInitValue() const { void FlagImpl::StoreValue(const void* src) { switch (ValueStorageKind()) { - case FlagValueStorageKind::kAlignedBuffer: - Copy(op_, src, AlignedBufferValue()); - seq_lock_.IncrementModificationCount(); - break; + case FlagValueStorageKind::kValueAndInitBit: case FlagValueStorageKind::kOneWordAtomic: { - int64_t one_word_val = 0; + // Load the current value to avoid setting 'init' bit manualy. + int64_t one_word_val = OneWordValue().load(std::memory_order_acquire); std::memcpy(&one_word_val, src, Sizeof(op_)); OneWordValue().store(one_word_val, std::memory_order_release); seq_lock_.IncrementModificationCount(); @@ -241,6 +246,10 @@ void FlagImpl::StoreValue(const void* src) { seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_)); break; } + case FlagValueStorageKind::kAlignedBuffer: + Copy(op_, src, AlignedBufferValue()); + seq_lock_.IncrementModificationCount(); + break; } modified_ = true; InvokeCallback(); @@ -280,10 +289,7 @@ std::string FlagImpl::DefaultValue() const { std::string FlagImpl::CurrentValue() const { auto* guard = DataGuard(); // Make sure flag initialized switch (ValueStorageKind()) { - case FlagValueStorageKind::kAlignedBuffer: { - absl::MutexLock l(guard); - return flags_internal::Unparse(op_, AlignedBufferValue()); - } + case FlagValueStorageKind::kValueAndInitBit: case FlagValueStorageKind::kOneWordAtomic: { const auto one_word_val = absl::bit_cast>( @@ -296,6 +302,10 @@ std::string FlagImpl::CurrentValue() const { ReadSequenceLockedData(cloned.get()); return flags_internal::Unparse(op_, cloned.get()); } + case FlagValueStorageKind::kAlignedBuffer: { + absl::MutexLock l(guard); + return flags_internal::Unparse(op_, AlignedBufferValue()); + } } return ""; @@ -341,11 +351,7 @@ std::unique_ptr FlagImpl::SaveState() { bool modified = modified_; bool on_command_line = on_command_line_; switch (ValueStorageKind()) { - case FlagValueStorageKind::kAlignedBuffer: { - return absl::make_unique( - *this, flags_internal::Clone(op_, AlignedBufferValue()), modified, - on_command_line, ModificationCount()); - } + case FlagValueStorageKind::kValueAndInitBit: case FlagValueStorageKind::kOneWordAtomic: { return absl::make_unique( *this, OneWordValue().load(std::memory_order_acquire), modified, @@ -361,6 +367,11 @@ std::unique_ptr FlagImpl::SaveState() { return absl::make_unique(*this, cloned, modified, on_command_line, ModificationCount()); } + case FlagValueStorageKind::kAlignedBuffer: { + return absl::make_unique( + *this, flags_internal::Clone(op_, AlignedBufferValue()), modified, + on_command_line, ModificationCount()); + } } return nullptr; } @@ -372,13 +383,14 @@ bool FlagImpl::RestoreState(const FlagState& flag_state) { } switch (ValueStorageKind()) { - case FlagValueStorageKind::kAlignedBuffer: - case FlagValueStorageKind::kSequenceLocked: - StoreValue(flag_state.value_.heap_allocated); - break; + case FlagValueStorageKind::kValueAndInitBit: case FlagValueStorageKind::kOneWordAtomic: StoreValue(&flag_state.value_.one_word); break; + case FlagValueStorageKind::kSequenceLocked: + case FlagValueStorageKind::kAlignedBuffer: + StoreValue(flag_state.value_.heap_allocated); + break; } modified_ = flag_state.modified_; @@ -407,7 +419,8 @@ std::atomic* FlagImpl::AtomicBufferValue() const { } std::atomic& FlagImpl::OneWordValue() const { - assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic); + assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic || + ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); return OffsetValue()->value; } @@ -433,11 +446,7 @@ std::unique_ptr FlagImpl::TryParse( void FlagImpl::Read(void* dst) const { auto* guard = DataGuard(); // Make sure flag initialized switch (ValueStorageKind()) { - case FlagValueStorageKind::kAlignedBuffer: { - absl::MutexLock l(guard); - flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst); - break; - } + case FlagValueStorageKind::kValueAndInitBit: case FlagValueStorageKind::kOneWordAtomic: { const int64_t one_word_val = OneWordValue().load(std::memory_order_acquire); @@ -448,9 +457,31 @@ void FlagImpl::Read(void* dst) const { ReadSequenceLockedData(dst); break; } + case FlagValueStorageKind::kAlignedBuffer: { + absl::MutexLock l(guard); + flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst); + break; + } } } +int64_t FlagImpl::ReadOneWord() const { + assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic || + ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); + auto* guard = DataGuard(); // Make sure flag initialized + (void)guard; + return OneWordValue().load(std::memory_order_acquire); +} + +bool FlagImpl::ReadOneBool() const { + assert(ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); + auto* guard = DataGuard(); // Make sure flag initialized + (void)guard; + return absl::bit_cast>( + OneWordValue().load(std::memory_order_acquire)) + .value; +} + void FlagImpl::ReadSequenceLockedData(void* dst) const { int size = Sizeof(op_); // Attempt to read using the sequence lock. diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index e6bade0a..8636fadc 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -29,6 +29,7 @@ #include "absl/base/attributes.h" #include "absl/base/call_once.h" +#include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/optimization.h" #include "absl/base/thread_annotations.h" @@ -304,49 +305,72 @@ constexpr FlagDefaultArg DefaultArg(char) { constexpr int64_t UninitializedFlagValue() { return 0xababababababababll; } +template +using FlagUseValueAndInitBitStorage = std::integral_constant< + bool, absl::type_traits_internal::is_trivially_copyable::value && + std::is_default_constructible::value && (sizeof(T) < 8)>; + template using FlagUseOneWordStorage = std::integral_constant< bool, absl::type_traits_internal::is_trivially_copyable::value && (sizeof(T) <= 8)>; template -using FlagShouldUseSequenceLock = std::integral_constant< +using FlagUseSequenceLockStorage = std::integral_constant< bool, absl::type_traits_internal::is_trivially_copyable::value && (sizeof(T) > 8)>; enum class FlagValueStorageKind : uint8_t { - kAlignedBuffer = 0, + kValueAndInitBit = 0, kOneWordAtomic = 1, kSequenceLocked = 2, + kAlignedBuffer = 3, }; template static constexpr FlagValueStorageKind StorageKind() { - return FlagUseOneWordStorage::value ? FlagValueStorageKind::kOneWordAtomic - : FlagShouldUseSequenceLock::value + return FlagUseValueAndInitBitStorage::value + ? FlagValueStorageKind::kValueAndInitBit + : FlagUseOneWordStorage::value + ? FlagValueStorageKind::kOneWordAtomic + : FlagUseSequenceLockStorage::value ? FlagValueStorageKind::kSequenceLocked : FlagValueStorageKind::kAlignedBuffer; } struct FlagOneWordValue { - constexpr FlagOneWordValue() : value(UninitializedFlagValue()) {} - + constexpr explicit FlagOneWordValue(int64_t v) : value(v) {} std::atomic value; }; +template +struct alignas(8) FlagValueAndInitBit { + T value; + // Use an int instead of a bool to guarantee that a non-zero value has + // a bit set. + uint8_t init; +}; + template ()> struct FlagValue; template -struct FlagValue { - bool Get(const SequenceLock&, T&) const { return false; } - - alignas(T) char value[sizeof(T)]; +struct FlagValue : FlagOneWordValue { + constexpr FlagValue() : FlagOneWordValue(0) {} + bool Get(const SequenceLock&, T& dst) const { + int64_t storage = value.load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(storage == 0)) { + return false; + } + dst = absl::bit_cast>(storage).value; + return true; + } }; template struct FlagValue : FlagOneWordValue { + constexpr FlagValue() : FlagOneWordValue(UninitializedFlagValue()) {} bool Get(const SequenceLock&, T& dst) const { int64_t one_word_val = value.load(std::memory_order_acquire); if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) { @@ -370,6 +394,13 @@ struct FlagValue { std::atomic) std::atomic value_words[kNumWords]; }; +template +struct FlagValue { + bool Get(const SequenceLock&, T&) const { return false; } + + alignas(T) char value[sizeof(T)]; +}; + /////////////////////////////////////////////////////////////////////////////// // Flag callback auxiliary structs. @@ -415,7 +446,27 @@ class FlagImpl final : public CommandLineFlag { data_guard_{} {} // Constant access methods + int64_t ReadOneWord() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool ReadOneBool() const ABSL_LOCKS_EXCLUDED(*DataGuard()); void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard()); + void Read(bool* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { + *value = ReadOneBool(); + } + template () == + FlagValueStorageKind::kOneWordAtomic, + int> = 0> + void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { + int64_t v = ReadOneWord(); + std::memcpy(value, static_cast(&v), sizeof(T)); + } + template () == + FlagValueStorageKind::kValueAndInitBit, + int>::type = 0> + void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { + *value = absl::bit_cast>(ReadOneWord()).value; + } // Mutating access methods void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 21915cc1..392b68f1 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -152,6 +152,7 @@ cc_library( deps = [ "//absl/base:config", "//absl/base:endian", + "//absl/numeric:bits", "//absl/numeric:int128", ], ) diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index 6c79c93a..5916ae3c 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -126,6 +126,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::bits absl::config absl::endian absl::int128 diff --git a/absl/hash/internal/low_level_hash.cc b/absl/hash/internal/low_level_hash.cc index 856bbd9b..6f9cb9c7 100644 --- a/absl/hash/internal/low_level_hash.cc +++ b/absl/hash/internal/low_level_hash.cc @@ -15,6 +15,7 @@ #include "absl/hash/internal/low_level_hash.h" #include "absl/base/internal/unaligned_access.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" namespace absl { @@ -22,9 +23,20 @@ ABSL_NAMESPACE_BEGIN namespace hash_internal { static uint64_t Mix(uint64_t v0, uint64_t v1) { +#if !defined(__aarch64__) + // The default bit-mixer uses 64x64->128-bit multiplication. absl::uint128 p = v0; p *= v1; return absl::Uint128Low64(p) ^ absl::Uint128High64(p); +#else + // The default bit-mixer above would perform poorly on some ARM microarchs, + // where calculating a 128-bit product requires a sequence of two + // instructions with a high combined latency and poor throughput. + // Instead, we mix bits using only 64-bit arithmetic, which is faster. + uint64_t p = v0 ^ absl::rotl(v1, 40); + p *= v1 ^ absl::rotl(v0, 39); + return p ^ (p >> 11); +#endif } uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, diff --git a/absl/hash/internal/low_level_hash_test.cc b/absl/hash/internal/low_level_hash_test.cc index 0ef50236..cf22dc3c 100644 --- a/absl/hash/internal/low_level_hash_test.cc +++ b/absl/hash/internal/low_level_hash_test.cc @@ -14,473 +14,519 @@ #include "absl/hash/internal/low_level_hash.h" -#include "absl/strings/escaping.h" +#include + #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/strings/escaping.h" + +#define UPDATE_GOLDEN 0 namespace { -static const uint64_t kCurrentSeed = 0; static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl, 0x8ebc6af09c88c6e3, 0x589965cc75374cc3l, 0x1d8e4e27c47d124f}; -// Note: We don't account for endianness, so the values here are only correct if -// you're also running on a little endian platform. - -TEST(LowLevelHashTest, EmptyString) { - const std::string s = ""; - EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), - kCurrentSeed, kSalt), - 4808886099364463827); -} - -TEST(LowLevelHashTest, Spaces) { - const std::string s = " "; - EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), - kCurrentSeed, kSalt), - 1686201463024549249); -} - -TEST(LowLevelHashTest, RepeatingString) { - const std::string s = "aaaa"; - EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), - kCurrentSeed, kSalt), - 6646112255271966632); -} - -TEST(LowLevelHashTest, HexString) { - const std::string small = "\x01\x02\x03"; - const std::string med = "\x01\x02\x03\x04"; - - EXPECT_EQ(absl::hash_internal::LowLevelHash(small.c_str(), small.length(), - kCurrentSeed, kSalt), - 11989428023081740911ULL); - EXPECT_EQ(absl::hash_internal::LowLevelHash(med.c_str(), med.length(), - kCurrentSeed, kSalt), - 9765997711188871556ULL); -} - -TEST(LowLevelHashTest, Words) { - const std::string s = "third_party|wyhash|64"; - EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), - kCurrentSeed, kSalt), - 3702018632387611330); -} - -TEST(LowLevelHashTest, LongString) { - const std::string s = - "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz" - "0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp" - "QrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEf" - "GhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz012345" - "6789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789"; - - EXPECT_EQ(absl::hash_internal::LowLevelHash(s.c_str(), s.length(), - kCurrentSeed, kSalt), - 9245411362605796064ULL); -} - -TEST(LowLevelHashTest, BigReference) { - struct ExpectedResult { +TEST(LowLevelHashTest, VerifyGolden) { + constexpr size_t kNumGoldenOutputs = 134; + static struct { absl::string_view base64_data; uint64_t seed; - uint64_t hash; - } expected_results[] = { - {"", uint64_t{0xec42b7ab404b8acb}, uint64_t{0xe5a40d39ab796423}}, - {"Zw==", uint64_t{0xeeee074043a3ee0f}, uint64_t{0xa6564b468248c683}}, - {"xmk=", uint64_t{0x857902089c393de}, uint64_t{0xef192f401b116e1c}}, - {"c1H/", uint64_t{0x993df040024ca3af}, uint64_t{0xbe8dc0c54617639d}}, - {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}, uint64_t{0x93d7f665b5521c8e}}, - {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}, uint64_t{0x646d70bb42445f28}}, - {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}, uint64_t{0x96a7b1e3cc9bd426}}, - {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}, - uint64_t{0x76020289ab0790c4}}, - {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}, - uint64_t{0x39f842e4133b9b44}}, - {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}, - uint64_t{0x2b8d7047be4bcaab}}, - {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}, - uint64_t{0x99628abef6716a97}}, - {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}, - uint64_t{0x4432e02ba42b2740}}, - {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}, - uint64_t{0x74d810efcad7918a}}, - {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}, - uint64_t{0x88c84e986002507f}}, - {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}, - uint64_t{0x4f99acf193cf39b9}}, - {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}, - uint64_t{0xd90e7a3655891e37}}, - {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}, - uint64_t{0x3bb378b1d4df8fcf}}, - {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}, - uint64_t{0xf78e94045c052d47}}, - {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}, - uint64_t{0x26da0b2130da6b40}}, - {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}, - uint64_t{0x30b4d426af8c6986}}, - {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}, - uint64_t{0x5413b4aaf3baaeae}}, - {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}, - uint64_t{0x756ab265370a1597}}, - {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}, - uint64_t{0xdaf5f4b7d09814fb}}, - {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}, - uint64_t{0x8f874ae37742b75e}}, - {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}, - uint64_t{0x8fecd03956121ce8}}, - {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}, - uint64_t{0x229c292ea7a08285}}, - {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}, - uint64_t{0xbb4bf0692d14bae}}, - {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}, - uint64_t{0x207b24ca3bdac1db}}, - {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", uint64_t{0xd902ee3e44a5705f}, - uint64_t{0x64f6cd6745d3825b}}, - {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}, - uint64_t{0xa2b2e1656b58df1e}}, - {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", uint64_t{0x402d83f9f834f616}, - uint64_t{0xd01d30d9ee7a148}}, + } cases[] = { + {"", uint64_t{0xec42b7ab404b8acb}}, + {"ICAg", uint64_t{0}}, + {"YWFhYQ==", uint64_t{0}}, + {"AQID", uint64_t{0}}, + {"AQIDBA==", uint64_t{0}}, + {"dGhpcmRfcGFydHl8d3loYXNofDY0", uint64_t{0}}, + {"Zw==", uint64_t{0xeeee074043a3ee0f}}, + {"xmk=", uint64_t{0x857902089c393de}}, + {"c1H/", uint64_t{0x993df040024ca3af}}, + {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}}, + {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}}, + {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}}, + {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}}, + {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}}, + {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}}, + {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}}, + {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}}, + {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}}, + {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}}, + {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}}, + {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}}, + {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}}, + {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}}, + {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}}, + {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}}, + {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}}, + {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}}, + {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}}, + {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}}, + {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}}, + {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}}, + {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}}, + {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}}, + {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", + uint64_t{0xd902ee3e44a5705f}}, + {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}}, + {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", + uint64_t{0x402d83f9f834f616}}, {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==", - uint64_t{0x9c604164c016b72c}, uint64_t{0x1cb4cd00ab804e3b}}, + uint64_t{0x9c604164c016b72c}}, {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=", - uint64_t{0x3f4507e01f9e73ba}, uint64_t{0x4697f2637fd90999}}, + uint64_t{0x3f4507e01f9e73ba}}, {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi", - uint64_t{0xc3fe0d5be8d2c7c7}, uint64_t{0x8383a756b5688c07}}, + uint64_t{0xc3fe0d5be8d2c7c7}}, {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==", - uint64_t{0x531858a40bfa7ea1}, uint64_t{0x695c29cb3696a975}}, + uint64_t{0x531858a40bfa7ea1}}, {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=", - uint64_t{0x86689478a7a7e8fa}, uint64_t{0xda2e5a5a5e971521}}, + uint64_t{0x86689478a7a7e8fa}}, {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph", - uint64_t{0x4ec948b8e7f27288}, uint64_t{0x7935d4befa056b2b}}, + uint64_t{0x4ec948b8e7f27288}}, {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==", - uint64_t{0xce46c7213c10032}, uint64_t{0x38dd541ca95420fe}}, + uint64_t{0xce46c7213c10032}}, {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=", - uint64_t{0xf63e96ee6f32a8b6}, uint64_t{0xcc06c7a4963f967f}}, + uint64_t{0xf63e96ee6f32a8b6}}, {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy", - uint64_t{0x1cfe85e65fc5225}, uint64_t{0xbf0f6f66e232fb20}}, + uint64_t{0x1cfe85e65fc5225}}, {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==", - uint64_t{0x45c474f1cee1d2e8}, uint64_t{0xf7efb32d373fe71a}}, + uint64_t{0x45c474f1cee1d2e8}}, {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=", - uint64_t{0x6e024e14015f329c}, uint64_t{0xe2e64634b1c12660}}, + uint64_t{0x6e024e14015f329c}}, {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt", - uint64_t{0x760c40502103ae1c}, uint64_t{0x285b8fd1638e306d}}, + uint64_t{0x760c40502103ae1c}}, {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==", - uint64_t{0x17fd05c3c560c320}, uint64_t{0x658e8a4e3b714d6c}}, + uint64_t{0x17fd05c3c560c320}}, {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=", - uint64_t{0x8b34200a6f8e90d9}, uint64_t{0xf391fb968e0eb398}}, + uint64_t{0x8b34200a6f8e90d9}}, {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn", - uint64_t{0x6be89e50818bdf69}, uint64_t{0x744a9ea0cc144bf2}}, + uint64_t{0x6be89e50818bdf69}}, {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==", - uint64_t{0xfb389773315b47d8}, uint64_t{0x12636f2be11012f1}}, + uint64_t{0xfb389773315b47d8}}, {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=", - uint64_t{0x4f2512a23f61efee}, uint64_t{0x29c57de825948f80}}, + uint64_t{0x4f2512a23f61efee}}, {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23", - uint64_t{0x59ccd92fc16c6fda}, uint64_t{0x58c6f99ab0d1c021}}, + uint64_t{0x59ccd92fc16c6fda}}, {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==", - uint64_t{0x25c5a7f5bd330919}, uint64_t{0x13e7b5a7b82fe3bb}}, + uint64_t{0x25c5a7f5bd330919}}, {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=", - uint64_t{0x51df4174d34c97d7}, uint64_t{0x10fbc87901e02b63}}, + uint64_t{0x51df4174d34c97d7}}, {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I", - uint64_t{0x80ce6d76f89cb57}, uint64_t{0xa24c9184901b748b}}, + uint64_t{0x80ce6d76f89cb57}}, {"64mVTbQ47dHjHlOHGS/hjJwr/" "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==", - uint64_t{0x20961c911965f684}, uint64_t{0xcac4fd4c5080e581}}, + uint64_t{0x20961c911965f684}}, {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/" "+a2V5WpA=", - uint64_t{0x4e5b926ec83868e7}, uint64_t{0xc38bdb7483ba68e1}}, + uint64_t{0x4e5b926ec83868e7}}, {"PGih0zDEOWCYGxuHGDFu9Ivbff/" "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS", - uint64_t{0x3927b30b922eecef}, uint64_t{0xdb2a8069b2ceaffa}}, + uint64_t{0x3927b30b922eecef}}, {"RnpA/" "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q=" "=", - uint64_t{0xbd0291284a49b61c}, uint64_t{0xdf9fe91d0d1c7887}}, + uint64_t{0xbd0291284a49b61c}}, {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+" "BrWPRIbfprszSaPfrI=", - uint64_t{0x73a77c575bcc956}, uint64_t{0xe83f49e96e2e6a08}}, + uint64_t{0x73a77c575bcc956}}, {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/" "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT", - uint64_t{0x766a0e2ade6d09a6}, uint64_t{0xc69e61b62ca2b62}}, + uint64_t{0x766a0e2ade6d09a6}}, {"s/" "Jf1+" "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db" "w==", - uint64_t{0x2599f4f905115869}, uint64_t{0xb4a4f3f85f8298fe}}, + uint64_t{0x2599f4f905115869}}, {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+" "7LgoUT1fJ/axybE=", - uint64_t{0xd8256e5444d21e53}, uint64_t{0x167a1b39e1e95f41}}, + uint64_t{0xd8256e5444d21e53}}, {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+" "LW4tTuzC6CIWbRGRRD1sQV/4", - uint64_t{0xf664a91333fb8dfd}, uint64_t{0xf8a2a5649855ee41}}, + uint64_t{0xf664a91333fb8dfd}}, {"CDK0meI07yrgV2kQlZZ+" "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==", - uint64_t{0x9625b859be372cd1}, uint64_t{0x27992565b595c498}}, + uint64_t{0x9625b859be372cd1}}, {"d23/vc5ONh/" "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+" "nX7eOvs=", - uint64_t{0x7b99940782e29898}, uint64_t{0x3e08cca5b71f9346}}, + uint64_t{0x7b99940782e29898}}, {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/" "mGoT0pnMTQst7Lv2q6QN6Vm", - uint64_t{0x4fe12fa5383b51a8}, uint64_t{0xad406b10c770a6d2}}, + uint64_t{0x4fe12fa5383b51a8}}, {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/" "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==", - uint64_t{0xe2ccb09ac0f5b4b6}, uint64_t{0xd1713ce6e552bcf2}}, + uint64_t{0xe2ccb09ac0f5b4b6}}, {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/" "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=", - uint64_t{0x7d0a37adbd7b753b}, uint64_t{0x753b287194c73ad3}}, + uint64_t{0x7d0a37adbd7b753b}}, {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/" "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW", - uint64_t{0xd3ae96ef9f7185f2}, uint64_t{0x5ae41a95f600af1c}}, + uint64_t{0xd3ae96ef9f7185f2}}, {"/WiHi9IQcxRImsudkA/KOTqGe8/" "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==", - uint64_t{0x4fb88ea63f79a0d8}, uint64_t{0x4a61163b86a8bb4c}}, + uint64_t{0x4fb88ea63f79a0d8}}, {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/" "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=", - uint64_t{0xed564e259bb5ebe9}, uint64_t{0x42eeaa79e760c7e4}}, + uint64_t{0xed564e259bb5ebe9}}, {"8FVYHx40lSQPTHheh08Oq0/" "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi", - uint64_t{0x3e3256b60c428000}, uint64_t{0x698df622ef465b0a}}, + uint64_t{0x3e3256b60c428000}}, {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/" "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==", - uint64_t{0xfb05bad59ec8705}, uint64_t{0x157583111e1a6026}}, + uint64_t{0xfb05bad59ec8705}}, {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/" "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=", - uint64_t{0xafdc251dbf97b5f8}, uint64_t{0xaa1388f078e793e0}}, + uint64_t{0xafdc251dbf97b5f8}}, {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+" "juQV4rsqYElMD/gWfDGpsWZKQ", - uint64_t{0x10ec9c92ddb5dcbc}, uint64_t{0xf10d68d0f3309360}}, + uint64_t{0x10ec9c92ddb5dcbc}}, {"oswxop+" "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp" "L2mRK0rcIUYA4qLt5uOw==", - uint64_t{0x9a767d5822c7dac4}, uint64_t{0x2af056184457a3de}}, + uint64_t{0x9a767d5822c7dac4}}, {"0II/" "697p+" "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+" "n5bxNOD1TGrjQtzKU5r7obo=", - uint64_t{0xee46254080d6e2db}, uint64_t{0x6d0058e1590b2489}}, + uint64_t{0xee46254080d6e2db}}, {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+" "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc", - uint64_t{0xbbb669588d8bf398}, uint64_t{0x638f287f68817f12}}, + uint64_t{0xbbb669588d8bf398}}, {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D" "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==", - uint64_t{0xdc2afaa529beef44}, uint64_t{0xc46b71fecefd5467}}, + uint64_t{0xdc2afaa529beef44}}, {"jVDKGYIuWOP/" "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/" "zAvSCB+zlf6upAsBlheUKgCfKww=", - uint64_t{0xf1f67391d45013a8}, uint64_t{0x2c8e94679d964e0a}}, + uint64_t{0xf1f67391d45013a8}}, {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/" "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d", - uint64_t{0x16fce2b8c65a3429}, uint64_t{0x8612b797ce22503a}}, + uint64_t{0x16fce2b8c65a3429}}, {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//" "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==", - uint64_t{0xf4b096699f49fe67}, uint64_t{0x59f929babfba7170}}, + uint64_t{0xf4b096699f49fe67}}, {"DUwXFJzagljo44QeJ7/" "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1" "eHuyFirAlkW+zKtwg=", - uint64_t{0xca584c4bc8198682}, uint64_t{0x9527556923fb49a0}}, + uint64_t{0xca584c4bc8198682}}, {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/" "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR", - uint64_t{0xed269fc3818b6aad}, uint64_t{0x1039ab644f5e150b}}, + uint64_t{0xed269fc3818b6aad}}, {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+" "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==", - uint64_t{0x33f253cbb8fe66a8}, uint64_t{0x7816c83f3aa05e6d}}, + uint64_t{0x33f253cbb8fe66a8}}, {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/" "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=", - uint64_t{0xd0b76b2c1523d99c}, uint64_t{0xf51d2f564518c619}}, + uint64_t{0xd0b76b2c1523d99c}}, {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/" "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H", - uint64_t{0xfd28f0811a2a237f}, uint64_t{0x67d494cff03ac004}}, + uint64_t{0xfd28f0811a2a237f}}, {"ueLyMcqJXX+MhO4UApylCN9WlTQ+" "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib" "4/J3A5mseA3cS8w==", - uint64_t{0x6261fb136482e84}, uint64_t{0x2802d636ced1cfbb}}, + uint64_t{0x6261fb136482e84}}, {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/" "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=", - uint64_t{0x458efc750bca7c3a}, uint64_t{0xf64e20bad771cb12}}, + uint64_t{0x458efc750bca7c3a}}, {"Q6AbOofGuTJOegPh9Clm/" "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+" "y4hHwLqRranl9FjvxfVKm3yvg68", - uint64_t{0xa7e69ff84e5e7c27}, uint64_t{0xb9a6cf84a83e15e}}, + uint64_t{0xa7e69ff84e5e7c27}}, {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/" "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==", - uint64_t{0x3c59bfd0c29efe9e}, uint64_t{0x8da6630319609301}}, + uint64_t{0x3c59bfd0c29efe9e}}, {"zQUv8hFB3zh2GGl3KTvCmnfzE+" "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//" "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=", - uint64_t{0x10befacc6afd298d}, uint64_t{0x40946a86e2a996f3}}, + uint64_t{0x10befacc6afd298d}}, {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/" "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd" "P47L", - uint64_t{0x41d5320b0a38efa7}, uint64_t{0xcab7f5997953fa76}}, + uint64_t{0x41d5320b0a38efa7}}, {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP" "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==", - uint64_t{0x58db1c7450fe17f3}, uint64_t{0x39129ca0e04fc465}}, + uint64_t{0x58db1c7450fe17f3}}, {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ" "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=", - uint64_t{0x6098c055a335b7a6}, uint64_t{0x5238221fd685e1b8}}, + uint64_t{0x6098c055a335b7a6}}, {"gzxyMJIPlU+bJBwhFUCHSofZ/" "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+" "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1", - uint64_t{0x1bbacec67845a801}, uint64_t{0x175130c407dbcaab}}, + uint64_t{0x1bbacec67845a801}}, {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+" "8yyOw8lQabism19vOQxfmocEOW/" "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==", - uint64_t{0xc419cfc7442190}, uint64_t{0x2f20e7536c0b0df}}, + uint64_t{0xc419cfc7442190}}, {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/" "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/" "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=", - uint64_t{0xc95e510d94ba270c}, uint64_t{0x2742cb488a04ad56}}, + uint64_t{0xc95e510d94ba270c}}, {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/" "u+x+" "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX" "8jUfh1il", - uint64_t{0xff1ae05c98089c3f}, uint64_t{0xd6afb593879ff93b}}, + uint64_t{0xff1ae05c98089c3f}}, {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs" "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==", - uint64_t{0x90c02b8dceced493}, uint64_t{0xf50ad64caac0ca7f}}, + uint64_t{0x90c02b8dceced493}}, {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/" "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/" "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=", - uint64_t{0x9f8a76697ab1aa36}, uint64_t{0x2ade95c4261364ae}}, + uint64_t{0x9f8a76697ab1aa36}}, {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/" "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND" "55G2L1W", - uint64_t{0x6ba1bf3d811a531d}, uint64_t{0x5c4f3299faacd07a}}, + uint64_t{0x6ba1bf3d811a531d}}, {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/" "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==", - uint64_t{0x6a418974109c67b4}, uint64_t{0xfffe3bff0ae5e9bc}}, + uint64_t{0x6a418974109c67b4}}, {"6QO5nnDrY2/" "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+" "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/" "ml6fnNXEpxplWJ1vEs4=", - uint64_t{0x8472f1c2b3d230a3}, uint64_t{0x1db785c0005166e4}}, + uint64_t{0x8472f1c2b3d230a3}}, {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/" "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww", - uint64_t{0x5e06068f884e73a7}, uint64_t{0xea000d962ad18418}}, + uint64_t{0x5e06068f884e73a7}}, {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/" "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt" "gwYHw7yakDUv8WvonctmnoSPKENegQg==", - uint64_t{0x55290b1a8f170f59}, uint64_t{0xe42aef38359362d9}}, + uint64_t{0x55290b1a8f170f59}}, {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+" "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+" "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=", - uint64_t{0x5501cfd83dfe706a}, uint64_t{0xc8e95657348a3891}}, + uint64_t{0x5501cfd83dfe706a}}, {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY" "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj", - uint64_t{0xe43ed13d13a66990}, uint64_t{0xc162eca864f238c6}}, + uint64_t{0xe43ed13d13a66990}}, {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G" "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/" "ODLcPEFztFnwjvCjmHw==", - uint64_t{0xdf43bc375cf5283f}, uint64_t{0xbe1fb373e20579ad}}, + uint64_t{0xdf43bc375cf5283f}}, {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/" "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+" "OevnkhUn5jsPlG2r5jYlVn8=", - uint64_t{0x8112b806d288d7b5}, uint64_t{0x628a1d4f40aa6ffd}}, + uint64_t{0x8112b806d288d7b5}}, {"kUw/0z4l3a89jTwN5jpG0SHY5km/" "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G" "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB", - uint64_t{0xd52a18abb001cb46}, uint64_t{0xa87bdb7456340f90}}, + uint64_t{0xd52a18abb001cb46}}, {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/" "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+" "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==", - uint64_t{0xe12b76a2433a1236}, uint64_t{0x5960ef3ba982c801}}, + uint64_t{0xe12b76a2433a1236}}, {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/" "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+" "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=", - uint64_t{0x175bf7319cf1fa00}, uint64_t{0x5026586df9a431ec}}, + uint64_t{0x175bf7319cf1fa00}}, {"BrbNpb42+" "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l" "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi", - uint64_t{0xd63d57b3f67525ae}, uint64_t{0xfe4b8a20fdf0840b}}, + uint64_t{0xd63d57b3f67525ae}}, {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+" "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/" "LTmhua+rQ6Wup8ezLwfg==", - uint64_t{0x933faea858832b73}, uint64_t{0xdcb761867da7072f}}, + uint64_t{0x933faea858832b73}}, {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+" "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+" "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=", - uint64_t{0x53d061e5f8e7c04f}, uint64_t{0xc10d4653667275b7}}, + uint64_t{0x53d061e5f8e7c04f}}, {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/" "9S+" "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA" "wCZUOEXIsLU24o2Y", - uint64_t{0xdb4124556dd515e0}, uint64_t{0x727720deec13110b}}, + uint64_t{0xdb4124556dd515e0}}, {"TKo+l+" "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF" "T0Gd0a2hI3+" "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==", - uint64_t{0x4fb31a0dd681ee71}, uint64_t{0x710b009662858dc9}}, + uint64_t{0x4fb31a0dd681ee71}}, {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+" "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+" "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=", - uint64_t{0x27cc72eefa138e4c}, uint64_t{0xfbf8f7a3ecac1eb7}}, + uint64_t{0x27cc72eefa138e4c}}, {"/I/" "eImMwPo1U6wekNFD1Jxjk9XQVi1D+" "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t" "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp", - uint64_t{0x44bc2dfba4bd3ced}, uint64_t{0xb6fc4fcd0722e3df}}, + uint64_t{0x44bc2dfba4bd3ced}}, {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC" "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+" "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==", - uint64_t{0x242da1e3a439bed8}, uint64_t{0x7cb86dcc55104aac}}, + uint64_t{0x242da1e3a439bed8}}, {"ZlhyQwLhXQyIUEnMH/" "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/" "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+" "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=", - uint64_t{0xdc559c746e35c139}, uint64_t{0x19e71e9b45c3a51e}}, + uint64_t{0xdc559c746e35c139}}, {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg" "Rko04h19QMG0mOw/" "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+" "xswhVMfL", - uint64_t{0xd0b0350275b9989}, uint64_t{0x51de38573c2bea48}}, + uint64_t{0xd0b0350275b9989}}, {"QhKlnIS6BuVCTQsnoE67E/" "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p" "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/" "nxtxakyEtrNkKk471Oov9juP8oQ==", - uint64_t{0xb04489e41d17730c}, uint64_t{0xa73ab6996d6df158}}, + uint64_t{0xb04489e41d17730c}}, {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+" "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK" "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=", - uint64_t{0x2217285eb4572156}, uint64_t{0x55ef2b8c930817b2}}, + uint64_t{0x2217285eb4572156}}, {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+" "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+" "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M", - uint64_t{0x12c2e8e68aede73b}, uint64_t{0xb2850bf5fae87157}}, + uint64_t{0x12c2e8e68aede73b}}, {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/" "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//" "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+" "DkYu9ND0O2swg4itGeVSzXA==", - uint64_t{0x4d612125bdc4fd00}, uint64_t{0xecf3de1acd04651f}}, + uint64_t{0x4d612125bdc4fd00}}, {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+" "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/" "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+" "hQLfVSh8OGs7fsBb68nNWPLeeSOo=", - uint64_t{0x81826b553954464e}, uint64_t{0xcc0a40552559ff32}}, + uint64_t{0x81826b553954464e}}, {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+" "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS" "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi", - uint64_t{0xc2e5d345dc0ddd2d}, uint64_t{0xc385c374f20315b1}}, + uint64_t{0xc2e5d345dc0ddd2d}}, {"j+loZ+C87+" "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++" "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/" "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==", - uint64_t{0x3da6830a9e32631e}, uint64_t{0xb90208a4c7234183}}, + uint64_t{0x3da6830a9e32631e}}, {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/" "sOmqaq8XAQLEn68LKj6/" "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+" "gN+7uKpoohgAhIwpAKQXmX5xtd0=", - uint64_t{0xc9ae5c8759b4877a}, uint64_t{0x58aa1ca7a4c075d9}}, + uint64_t{0xc9ae5c8759b4877a}}, + }; + +#if defined(__aarch64__) + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0x45c0aadee165dcbe, 0x25ed8587f6f20d06, 0x5f23ae668ce7926d, + 0xfef74d1da0846719, 0x54478408e68cb7d4, 0xee27ddaf88c6fe68, + 0xb7ac7031e81867ca, 0xf1168f818ec6c36d, 0x1dd0b734a83b019a, + 0xd6ae30d4142b54fe, 0xcd860c721ccb80fb, 0x068acf8493794756, + 0xd4ada0be58681307, 0x13ffe0f64ca540ed, 0xffc1d7a3401aec02, + 0xd81c4d865cf95fb9, 0x1dd0793acede62e0, 0xa6722abbca8fe4cf, + 0x5453d3e4111a7e40, 0xf29b3e3204c9dcd2, 0x23be2980e43117f7, + 0x74e2ccbc286f08eb, 0x19ef7c0f9496003a, 0xbfbf1c3e49b27987, + 0x6e6c179eb4a82c70, 0x07f4e184216bc4fc, 0xf17fbc4254927554, + 0xe57696b70a45b1b6, 0x6d3b144631b320e8, 0xccf8729792c75a2d, + 0xe832495b41fa980b, 0x5c96cfdc7b227d34, 0xc4dca234ef4e43f4, + 0x5fc801abf9abe307, 0xe41e3c5076d88f4d, 0x522346200ddec3c3, + 0x72bed1946fd7aaa4, 0x0ac1f84dcc335f96, 0x3af78db5e0a47670, + 0x6100ebf1481f1caf, 0xf5fd10037fc651a3, 0xa01227d8944665f3, + 0x7217681c4bbc9420, 0x4adee538e3eb10d1, 0x35e1761ad96de9a7, + 0x8b370aef9613bfba, 0x824506f749eeaf59, 0x85e805fa04423991, + 0xb61e9c33283c3de7, 0xc79721bbcb039ed6, 0x04e1c19a3a1e6639, + 0x6aaf6346b68dd638, 0x601a4b496be6d0c4, 0x3ece355f91c41787, + 0xd2fc8998448d7888, 0xd7529804f843efa9, 0xabdcc38a288536aa, + 0xdd323e48a9718648, 0x2090279c0030a52a, 0xe2f90faca88a3cd1, + 0x3e0c4e92fc50e4aa, 0xa26d308798e801dd, 0x432eefeedee8c02e, + 0xca4ce494614b77df, 0xbba82911e838066d, 0x4b00821016adee4b, + 0x4cf6e526dfb5a20f, 0x5b8466495142cba2, 0xe28ac1406e88a68c, + 0x8511e5f9d3100999, 0x05acbfe02798890b, 0x74c249c7ce4a8425, + 0xdbe7468d09bc34bc, 0x11079ab10e3b9b58, 0xb7788dec9032035a, + 0xb7e8daa786513f80, 0x34c3288831f46b45, 0x014cce5f0c21ecc6, + 0xc6a8f7b024551a28, 0x49784e902e207fd8, 0x4720d32af0b55158, + 0x8df3ec5de0c1da00, 0xf4db677b2c9e6853, 0xaa419abea78d312d, + 0x181e0f91bd757443, 0xa8c45136fada083b, 0x91303b93f5f0582c, + 0x883b95c6ddc62a08, 0x93186a8875fe952b, 0xd94f533928e957e2, + 0x6ba343003e10c172, 0xc8623b620c715d6a, 0x8ca0c512e180e244, + 0xdc9b74c2536b6216, 0x8eb5fdc61b295d96, 0x2ad83966b37c95ba, + 0xb90bf154ac5edec9, 0x902cf847b326cfb3, 0x7b02d0c0ca7808ca, + 0x492f310d003ea15f, 0x3eb6497a47c95990, 0x5d46b0ced31428b7, + 0x081afa67d1986157, 0x043482ec286b20eb, 0xc103c8f18c1a2a53, + 0xe8e9995a81481e83, 0x6bb3295822bc90b5, 0xeec75297a3fa5672, + 0x591c8440c4857412, 0x74947f455aaf24ad, 0xcf0e571586ec77a9, + 0x0c2553ea8c0400ad, 0x380219118066255f, 0x7595adb88b15ebe2, + 0xb33c00696c64ae23, 0xa143516ddd7c9857, 0x39179af229248d26, + 0x65d387a6f2ee2079, 0x89f8a9b21cd2f195, 0xbfef032d25df92e6, + 0x6b7e18a36c69da71, 0x4b3b15f6c28974e6, 0x032a75917f6c544c, + 0xe3b97ecca6d287cd, 0xa4a563110d3cda81, 0x35e09e8134f4e7f1, + 0xc9419dd03a9a390e, 0x7b86fae9000fd329, 0x1e044f8d54fe74c3, + 0x9c4991d7a47e9666, 0xfb485f3a1df4fdb6, 0xb11519969eeb94ff, + 0x3224ea1c44caeb8d, 0x86570bbd7cc6b80d, }; +#else + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8, + 0xa6630143a7e6aa6f, 0x8787cb2d04b0c984, 0x33603654ff574ac2, + 0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d, + 0x93d7f665b5521c8e, 0x646d70bb42445f28, 0x96a7b1e3cc9bd426, + 0x76020289ab0790c4, 0x39f842e4133b9b44, 0x2b8d7047be4bcaab, + 0x99628abef6716a97, 0x4432e02ba42b2740, 0x74d810efcad7918a, + 0x88c84e986002507f, 0x4f99acf193cf39b9, 0xd90e7a3655891e37, + 0x3bb378b1d4df8fcf, 0xf78e94045c052d47, 0x26da0b2130da6b40, + 0x30b4d426af8c6986, 0x5413b4aaf3baaeae, 0x756ab265370a1597, + 0xdaf5f4b7d09814fb, 0x8f874ae37742b75e, 0x8fecd03956121ce8, + 0x229c292ea7a08285, 0x0bb4bf0692d14bae, 0x207b24ca3bdac1db, + 0x64f6cd6745d3825b, 0xa2b2e1656b58df1e, 0x0d01d30d9ee7a148, + 0x1cb4cd00ab804e3b, 0x4697f2637fd90999, 0x8383a756b5688c07, + 0x695c29cb3696a975, 0xda2e5a5a5e971521, 0x7935d4befa056b2b, + 0x38dd541ca95420fe, 0xcc06c7a4963f967f, 0xbf0f6f66e232fb20, + 0xf7efb32d373fe71a, 0xe2e64634b1c12660, 0x285b8fd1638e306d, + 0x658e8a4e3b714d6c, 0xf391fb968e0eb398, 0x744a9ea0cc144bf2, + 0x12636f2be11012f1, 0x29c57de825948f80, 0x58c6f99ab0d1c021, + 0x13e7b5a7b82fe3bb, 0x10fbc87901e02b63, 0xa24c9184901b748b, + 0xcac4fd4c5080e581, 0xc38bdb7483ba68e1, 0xdb2a8069b2ceaffa, + 0xdf9fe91d0d1c7887, 0xe83f49e96e2e6a08, 0x0c69e61b62ca2b62, + 0xb4a4f3f85f8298fe, 0x167a1b39e1e95f41, 0xf8a2a5649855ee41, + 0x27992565b595c498, 0x3e08cca5b71f9346, 0xad406b10c770a6d2, + 0xd1713ce6e552bcf2, 0x753b287194c73ad3, 0x5ae41a95f600af1c, + 0x4a61163b86a8bb4c, 0x42eeaa79e760c7e4, 0x698df622ef465b0a, + 0x157583111e1a6026, 0xaa1388f078e793e0, 0xf10d68d0f3309360, + 0x2af056184457a3de, 0x6d0058e1590b2489, 0x638f287f68817f12, + 0xc46b71fecefd5467, 0x2c8e94679d964e0a, 0x8612b797ce22503a, + 0x59f929babfba7170, 0x9527556923fb49a0, 0x1039ab644f5e150b, + 0x7816c83f3aa05e6d, 0xf51d2f564518c619, 0x67d494cff03ac004, + 0x2802d636ced1cfbb, 0xf64e20bad771cb12, 0x0b9a6cf84a83e15e, + 0x8da6630319609301, 0x40946a86e2a996f3, 0xcab7f5997953fa76, + 0x39129ca0e04fc465, 0x5238221fd685e1b8, 0x175130c407dbcaab, + 0x02f20e7536c0b0df, 0x2742cb488a04ad56, 0xd6afb593879ff93b, + 0xf50ad64caac0ca7f, 0x2ade95c4261364ae, 0x5c4f3299faacd07a, + 0xfffe3bff0ae5e9bc, 0x1db785c0005166e4, 0xea000d962ad18418, + 0xe42aef38359362d9, 0xc8e95657348a3891, 0xc162eca864f238c6, + 0xbe1fb373e20579ad, 0x628a1d4f40aa6ffd, 0xa87bdb7456340f90, + 0x5960ef3ba982c801, 0x5026586df9a431ec, 0xfe4b8a20fdf0840b, + 0xdcb761867da7072f, 0xc10d4653667275b7, 0x727720deec13110b, + 0x710b009662858dc9, 0xfbf8f7a3ecac1eb7, 0xb6fc4fcd0722e3df, + 0x7cb86dcc55104aac, 0x19e71e9b45c3a51e, 0x51de38573c2bea48, + 0xa73ab6996d6df158, 0x55ef2b8c930817b2, 0xb2850bf5fae87157, + 0xecf3de1acd04651f, 0xcc0a40552559ff32, 0xc385c374f20315b1, + 0xb90208a4c7234183, 0x58aa1ca7a4c075d9, + }; +#endif - for (const auto& expected_result : expected_results) { +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + std::string str; + ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str)); + uint64_t h = absl::hash_internal::LowLevelHash(str.data(), str.size(), + cases[i].seed, kSalt); + printf("0x%016" PRIx64 ", ", h); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); + EXPECT_FALSE(true); +#else + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + SCOPED_TRACE(::testing::Message() + << "i = " << i << "; input = " << cases[i].base64_data); std::string str; - ASSERT_TRUE(absl::Base64Unescape(expected_result.base64_data, &str)); + ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str)); EXPECT_EQ(absl::hash_internal::LowLevelHash(str.data(), str.size(), - expected_result.seed, kSalt), - expected_result.hash); + cases[i].seed, kSalt), + kGolden[i]); } +#endif } } // namespace diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index a9cbe489..c7ad96be 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -586,10 +586,10 @@ inline uint128& uint128::operator=(int128 v) { // Arithmetic operators. -uint128 operator<<(uint128 lhs, int amount); -uint128 operator>>(uint128 lhs, int amount); -uint128 operator+(uint128 lhs, uint128 rhs); -uint128 operator-(uint128 lhs, uint128 rhs); +constexpr uint128 operator<<(uint128 lhs, int amount); +constexpr uint128 operator>>(uint128 lhs, int amount); +constexpr uint128 operator+(uint128 lhs, uint128 rhs); +constexpr uint128 operator-(uint128 lhs, uint128 rhs); uint128 operator*(uint128 lhs, uint128 rhs); uint128 operator/(uint128 lhs, uint128 rhs); uint128 operator%(uint128 lhs, uint128 rhs); @@ -786,7 +786,7 @@ inline uint128::operator long double() const { // Comparison operators. -inline bool operator==(uint128 lhs, uint128 rhs) { +constexpr bool operator==(uint128 lhs, uint128 rhs) { #if defined(ABSL_HAVE_INTRINSIC_INT128) return static_cast(lhs) == static_cast(rhs); @@ -796,11 +796,9 @@ inline bool operator==(uint128 lhs, uint128 rhs) { #endif } -inline bool operator!=(uint128 lhs, uint128 rhs) { - return !(lhs == rhs); -} +constexpr bool operator!=(uint128 lhs, uint128 rhs) { return !(lhs == rhs); } -inline bool operator<(uint128 lhs, uint128 rhs) { +constexpr bool operator<(uint128 lhs, uint128 rhs) { #ifdef ABSL_HAVE_INTRINSIC_INT128 return static_cast(lhs) < static_cast(rhs); @@ -811,11 +809,11 @@ inline bool operator<(uint128 lhs, uint128 rhs) { #endif } -inline bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; } +constexpr bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; } -inline bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); } +constexpr bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); } -inline bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } +constexpr bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } // Unary operators. @@ -827,14 +825,13 @@ constexpr inline int128 operator+(int128 val) { return val; } -inline uint128 operator-(uint128 val) { +constexpr uint128 operator-(uint128 val) { #if defined(ABSL_HAVE_INTRINSIC_INT128) return -static_cast(val); #else - uint64_t hi = ~Uint128High64(val); - uint64_t lo = ~Uint128Low64(val) + 1; - if (lo == 0) ++hi; // carry - return MakeUint128(hi, lo); + return MakeUint128( + ~Uint128High64(val) + static_cast(Uint128Low64(val) == 0), + ~Uint128Low64(val) + 1); #endif } @@ -903,67 +900,77 @@ inline uint128& uint128::operator^=(uint128 other) { // Arithmetic operators. -inline uint128 operator<<(uint128 lhs, int amount) { +constexpr uint128 operator<<(uint128 lhs, int amount) { #ifdef ABSL_HAVE_INTRINSIC_INT128 return static_cast(lhs) << amount; #else // uint64_t shifts of >= 64 are undefined, so we will need some // special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeUint128( - (Uint128High64(lhs) << amount) | (Uint128Low64(lhs) >> (64 - amount)), - Uint128Low64(lhs) << amount); - } - return lhs; - } - return MakeUint128(Uint128Low64(lhs) << (amount - 64), 0); + return amount >= 64 ? MakeUint128(Uint128Low64(lhs) << (amount - 64), 0) + : amount == 0 ? lhs + : MakeUint128((Uint128High64(lhs) << amount) | + (Uint128Low64(lhs) >> (64 - amount)), + Uint128Low64(lhs) << amount); #endif } -inline uint128 operator>>(uint128 lhs, int amount) { +constexpr uint128 operator>>(uint128 lhs, int amount) { #ifdef ABSL_HAVE_INTRINSIC_INT128 return static_cast(lhs) >> amount; #else // uint64_t shifts of >= 64 are undefined, so we will need some // special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeUint128(Uint128High64(lhs) >> amount, - (Uint128Low64(lhs) >> amount) | - (Uint128High64(lhs) << (64 - amount))); - } - return lhs; - } - return MakeUint128(0, Uint128High64(lhs) >> (amount - 64)); + return amount >= 64 ? MakeUint128(0, Uint128High64(lhs) >> (amount - 64)) + : amount == 0 ? lhs + : MakeUint128(Uint128High64(lhs) >> amount, + (Uint128Low64(lhs) >> amount) | + (Uint128High64(lhs) << (64 - amount))); #endif } -inline uint128 operator+(uint128 lhs, uint128 rhs) { +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 AddResult(uint128 result, uint128 lhs) { + // check for carry + return (Uint128Low64(result) < Uint128Low64(lhs)) + ? MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)) + : result; +} +} // namespace int128_internal +#endif + +constexpr uint128 operator+(uint128 lhs, uint128 rhs) { #if defined(ABSL_HAVE_INTRINSIC_INT128) return static_cast(lhs) + static_cast(rhs); #else - uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), - Uint128Low64(lhs) + Uint128Low64(rhs)); - if (Uint128Low64(result) < Uint128Low64(lhs)) { // check for carry - return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)); - } - return result; + return int128_internal::AddResult( + MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), + Uint128Low64(lhs) + Uint128Low64(rhs)), + lhs); #endif } -inline uint128 operator-(uint128 lhs, uint128 rhs) { +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 SubstructResult(uint128 result, uint128 lhs, uint128 rhs) { + // check for carry + return (Uint128Low64(lhs) < Uint128Low64(rhs)) + ? MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)) + : result; +} +} // namespace int128_internal +#endif + +constexpr uint128 operator-(uint128 lhs, uint128 rhs) { #if defined(ABSL_HAVE_INTRINSIC_INT128) return static_cast(lhs) - static_cast(rhs); #else - uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), - Uint128Low64(lhs) - Uint128Low64(rhs)); - if (Uint128Low64(lhs) < Uint128Low64(rhs)) { // check for carry - return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)); - } - return result; + return int128_internal::SubstructResult( + MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), + Uint128Low64(lhs) - Uint128Low64(rhs)), + lhs, rhs); #endif } @@ -1063,17 +1070,17 @@ inline int128& int128::operator=(unsigned long long v) { } // Arithmetic operators. - -int128 operator+(int128 lhs, int128 rhs); -int128 operator-(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 v); +constexpr int128 operator+(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 lhs, int128 rhs); int128 operator*(int128 lhs, int128 rhs); int128 operator/(int128 lhs, int128 rhs); int128 operator%(int128 lhs, int128 rhs); -int128 operator|(int128 lhs, int128 rhs); -int128 operator&(int128 lhs, int128 rhs); -int128 operator^(int128 lhs, int128 rhs); -int128 operator<<(int128 lhs, int amount); -int128 operator>>(int128 lhs, int amount); +constexpr int128 operator|(int128 lhs, int128 rhs); +constexpr int128 operator&(int128 lhs, int128 rhs); +constexpr int128 operator^(int128 lhs, int128 rhs); +constexpr int128 operator<<(int128 lhs, int amount); +constexpr int128 operator>>(int128 lhs, int amount); inline int128& int128::operator+=(int128 other) { *this = *this + other; @@ -1125,6 +1132,9 @@ inline int128& int128::operator>>=(int amount) { return *this; } +// Forward declaration for comparison operators. +constexpr bool operator!=(int128 lhs, int128 rhs); + namespace int128_internal { // Casts from unsigned to signed while preserving the underlying binary diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc index d6c76dd3..3945fa29 100644 --- a/absl/numeric/int128_have_intrinsic.inc +++ b/absl/numeric/int128_have_intrinsic.inc @@ -155,7 +155,7 @@ constexpr int128::operator unsigned __int128() const { #if defined(__clang__) && !defined(__ppc64__) inline int128::operator float() const { return static_cast(v_); } -inline int128::operator double () const { return static_cast(v_); } +inline int128::operator double() const { return static_cast(v_); } inline int128::operator long double() const { return static_cast(v_); @@ -163,8 +163,8 @@ inline int128::operator long double() const { #else // Clang on PowerPC // Forward declaration for conversion operators to floating point types. -int128 operator-(int128 v); -bool operator!=(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 v); +constexpr bool operator!=(int128 lhs, int128 rhs); inline int128::operator float() const { // We must convert the absolute value and then negate as needed, because @@ -199,51 +199,45 @@ inline int128::operator long double() const { // Comparison operators. -inline bool operator==(int128 lhs, int128 rhs) { +constexpr bool operator==(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) == static_cast<__int128>(rhs); } -inline bool operator!=(int128 lhs, int128 rhs) { +constexpr bool operator!=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) != static_cast<__int128>(rhs); } -inline bool operator<(int128 lhs, int128 rhs) { +constexpr bool operator<(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) < static_cast<__int128>(rhs); } -inline bool operator>(int128 lhs, int128 rhs) { +constexpr bool operator>(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) > static_cast<__int128>(rhs); } -inline bool operator<=(int128 lhs, int128 rhs) { +constexpr bool operator<=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs); } -inline bool operator>=(int128 lhs, int128 rhs) { +constexpr bool operator>=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs); } // Unary operators. -inline int128 operator-(int128 v) { - return -static_cast<__int128>(v); -} +constexpr int128 operator-(int128 v) { return -static_cast<__int128>(v); } -inline bool operator!(int128 v) { - return !static_cast<__int128>(v); -} +constexpr bool operator!(int128 v) { return !static_cast<__int128>(v); } -inline int128 operator~(int128 val) { - return ~static_cast<__int128>(val); -} +constexpr int128 operator~(int128 val) { return ~static_cast<__int128>(val); } // Arithmetic operators. -inline int128 operator+(int128 lhs, int128 rhs) { +constexpr int128 operator+(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) + static_cast<__int128>(rhs); } -inline int128 operator-(int128 lhs, int128 rhs) { +constexpr int128 operator-(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) - static_cast<__int128>(rhs); } @@ -281,22 +275,22 @@ inline int128& int128::operator--() { return *this; } -inline int128 operator|(int128 lhs, int128 rhs) { +constexpr int128 operator|(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) | static_cast<__int128>(rhs); } -inline int128 operator&(int128 lhs, int128 rhs) { +constexpr int128 operator&(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) & static_cast<__int128>(rhs); } -inline int128 operator^(int128 lhs, int128 rhs) { +constexpr int128 operator^(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs); } -inline int128 operator<<(int128 lhs, int amount) { +constexpr int128 operator<<(int128 lhs, int amount) { return static_cast<__int128>(lhs) << amount; } -inline int128 operator>>(int128 lhs, int amount) { +constexpr int128 operator>>(int128 lhs, int amount) { return static_cast<__int128>(lhs) >> amount; } diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc index c753771a..66f6809f 100644 --- a/absl/numeric/int128_no_intrinsic.inc +++ b/absl/numeric/int128_no_intrinsic.inc @@ -134,10 +134,6 @@ constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int) return static_cast(lo_); // NOLINT(runtime/int) } -// Forward declaration for conversion operators to floating point types. -int128 operator-(int128 v); -bool operator!=(int128 lhs, int128 rhs); - inline int128::operator float() const { // We must convert the absolute value and then negate as needed, because // floating point types are typically sign-magnitude. Otherwise, the @@ -169,76 +165,80 @@ inline int128::operator long double() const { // Comparison operators. -inline bool operator==(int128 lhs, int128 rhs) { +constexpr bool operator==(int128 lhs, int128 rhs) { return (Int128Low64(lhs) == Int128Low64(rhs) && Int128High64(lhs) == Int128High64(rhs)); } -inline bool operator!=(int128 lhs, int128 rhs) { - return !(lhs == rhs); -} +constexpr bool operator!=(int128 lhs, int128 rhs) { return !(lhs == rhs); } -inline bool operator<(int128 lhs, int128 rhs) { +constexpr bool operator<(int128 lhs, int128 rhs) { return (Int128High64(lhs) == Int128High64(rhs)) ? (Int128Low64(lhs) < Int128Low64(rhs)) : (Int128High64(lhs) < Int128High64(rhs)); } -inline bool operator>(int128 lhs, int128 rhs) { +constexpr bool operator>(int128 lhs, int128 rhs) { return (Int128High64(lhs) == Int128High64(rhs)) ? (Int128Low64(lhs) > Int128Low64(rhs)) : (Int128High64(lhs) > Int128High64(rhs)); } -inline bool operator<=(int128 lhs, int128 rhs) { - return !(lhs > rhs); -} +constexpr bool operator<=(int128 lhs, int128 rhs) { return !(lhs > rhs); } -inline bool operator>=(int128 lhs, int128 rhs) { - return !(lhs < rhs); -} +constexpr bool operator>=(int128 lhs, int128 rhs) { return !(lhs < rhs); } // Unary operators. -inline int128 operator-(int128 v) { - int64_t hi = ~Int128High64(v); - uint64_t lo = ~Int128Low64(v) + 1; - if (lo == 0) ++hi; // carry - return MakeInt128(hi, lo); +constexpr int128 operator-(int128 v) { + return MakeInt128(~Int128High64(v) + (Int128Low64(v) == 0), + ~Int128Low64(v) + 1); } -inline bool operator!(int128 v) { +constexpr bool operator!(int128 v) { return !Int128Low64(v) && !Int128High64(v); } -inline int128 operator~(int128 val) { +constexpr int128 operator~(int128 val) { return MakeInt128(~Int128High64(val), ~Int128Low64(val)); } // Arithmetic operators. -inline int128 operator+(int128 lhs, int128 rhs) { - int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs), - Int128Low64(lhs) + Int128Low64(rhs)); - if (Int128Low64(result) < Int128Low64(lhs)) { // check for carry - return MakeInt128(Int128High64(result) + 1, Int128Low64(result)); - } - return result; +namespace int128_internal { +constexpr int128 SignedAddResult(int128 result, int128 lhs) { + // check for carry + return (Int128Low64(result) < Int128Low64(lhs)) + ? MakeInt128(Int128High64(result) + 1, Int128Low64(result)) + : result; +} +} // namespace int128_internal +constexpr int128 operator+(int128 lhs, int128 rhs) { + return int128_internal::SignedAddResult( + MakeInt128(Int128High64(lhs) + Int128High64(rhs), + Int128Low64(lhs) + Int128Low64(rhs)), + lhs); } -inline int128 operator-(int128 lhs, int128 rhs) { - int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs), - Int128Low64(lhs) - Int128Low64(rhs)); - if (Int128Low64(lhs) < Int128Low64(rhs)) { // check for carry - return MakeInt128(Int128High64(result) - 1, Int128Low64(result)); - } - return result; +namespace int128_internal { +constexpr int128 SignedSubstructResult(int128 result, int128 lhs, int128 rhs) { + // check for carry + return (Int128Low64(lhs) < Int128Low64(rhs)) + ? MakeInt128(Int128High64(result) - 1, Int128Low64(result)) + : result; +} +} // namespace int128_internal +constexpr int128 operator-(int128 lhs, int128 rhs) { + return int128_internal::SignedSubstructResult( + MakeInt128(Int128High64(lhs) - Int128High64(rhs), + Int128Low64(lhs) - Int128Low64(rhs)), + lhs, rhs); } inline int128 operator*(int128 lhs, int128 rhs) { - uint128 result = uint128(lhs) * rhs; - return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)), - Uint128Low64(result)); + return MakeInt128( + int128_internal::BitCastToSigned(Uint128High64(uint128(lhs) * rhs)), + Uint128Low64(uint128(lhs) * rhs)); } inline int128 int128::operator++(int) { @@ -263,46 +263,43 @@ inline int128& int128::operator--() { return *this; } -inline int128 operator|(int128 lhs, int128 rhs) { +constexpr int128 operator|(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) | Int128High64(rhs), Int128Low64(lhs) | Int128Low64(rhs)); } -inline int128 operator&(int128 lhs, int128 rhs) { +constexpr int128 operator&(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) & Int128High64(rhs), Int128Low64(lhs) & Int128Low64(rhs)); } -inline int128 operator^(int128 lhs, int128 rhs) { +constexpr int128 operator^(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs), Int128Low64(lhs) ^ Int128Low64(rhs)); } -inline int128 operator<<(int128 lhs, int amount) { +constexpr int128 operator<<(int128 lhs, int amount) { // uint64_t shifts of >= 64 are undefined, so we need some special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeInt128( - (Int128High64(lhs) << amount) | - static_cast(Int128Low64(lhs) >> (64 - amount)), - Int128Low64(lhs) << amount); - } - return lhs; - } - return MakeInt128(static_cast(Int128Low64(lhs) << (amount - 64)), 0); -} - -inline int128 operator>>(int128 lhs, int amount) { + return amount >= 64 + ? MakeInt128( + static_cast(Int128Low64(lhs) << (amount - 64)), 0) + : amount == 0 + ? lhs + : MakeInt128( + (Int128High64(lhs) << amount) | + static_cast(Int128Low64(lhs) >> (64 - amount)), + Int128Low64(lhs) << amount); +} + +constexpr int128 operator>>(int128 lhs, int amount) { // uint64_t shifts of >= 64 are undefined, so we need some special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeInt128( - Int128High64(lhs) >> amount, - (Int128Low64(lhs) >> amount) | - (static_cast(Int128High64(lhs)) << (64 - amount))); - } - return lhs; - } - return MakeInt128(0, - static_cast(Int128High64(lhs) >> (amount - 64))); + return amount >= 64 + ? MakeInt128( + 0, static_cast(Int128High64(lhs) >> (amount - 64))) + : amount == 0 + ? lhs + : MakeInt128(Int128High64(lhs) >> amount, + (Int128Low64(lhs) >> amount) | + (static_cast(Int128High64(lhs)) + << (64 - amount))); } -- cgit v1.2.3 From f39e6ad4753e06d4a0d6a9bf6310478757479984 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 19 Aug 2021 20:52:14 -0700 Subject: Export of internal Abseil changes -- 82011e44e917fb70fa00078f0bad7f787580bd8e by Abseil Team : Add lifetime annotations to status methods that return references. PiperOrigin-RevId: 391907063 GitOrigin-RevId: 82011e44e917fb70fa00078f0bad7f787580bd8e Change-Id: Icb2cafbf5eb1201b3bdb56a17263073e22b642e7 --- absl/status/statusor.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/absl/status/statusor.h b/absl/status/statusor.h index 235a3433..7fa623fd 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -518,10 +518,10 @@ class StatusOr : private internal_statusor::StatusOrData, // // The `std::move` on statusor instead of on the whole expression enables // warnings about possible uses of the statusor object after the move. - const T& value() const&; - T& value() &; - const T&& value() const&&; - T&& value() &&; + const T& value() const& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T& value() & ABSL_ATTRIBUTE_LIFETIME_BOUND; + const T&& value() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T&& value() && ABSL_ATTRIBUTE_LIFETIME_BOUND; // StatusOr:: operator*() // @@ -533,10 +533,10 @@ class StatusOr : private internal_statusor::StatusOrData, // `absl::StatusOr`. Alternatively, see the `value()` member function for a // similar API that guarantees crashing or throwing an exception if there is // no current value. - const T& operator*() const&; - T& operator*() &; - const T&& operator*() const&&; - T&& operator*() &&; + const T& operator*() const& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T& operator*() & ABSL_ATTRIBUTE_LIFETIME_BOUND; + const T&& operator*() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T&& operator*() && ABSL_ATTRIBUTE_LIFETIME_BOUND; // StatusOr::operator->() // @@ -545,8 +545,8 @@ class StatusOr : private internal_statusor::StatusOrData, // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined. // // Use `this->ok()` to verify that there is a current value. - const T* operator->() const; - T* operator->(); + const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND; + T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND; // StatusOr::value_or() // -- cgit v1.2.3 From 9134967d017fcd09f71539c8957b73eb05c26f45 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 20 Aug 2021 22:47:15 -0700 Subject: Export of internal Abseil changes -- 2aa219620f39aa490fa989f5d92e1bd3e52a46c5 by Abseil Team : Add lifetime annotations to FunctionRef. PiperOrigin-RevId: 392131866 GitOrigin-RevId: 2aa219620f39aa490fa989f5d92e1bd3e52a46c5 Change-Id: I295b808986857e46a565e047db25a951dd7ce0e3 --- absl/functional/BUILD.bazel | 1 + absl/functional/CMakeLists.txt | 1 + absl/functional/function_ref.h | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel index ebd9b99b..4555cd59 100644 --- a/absl/functional/BUILD.bazel +++ b/absl/functional/BUILD.bazel @@ -60,6 +60,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:base_internal", + "//absl/base:core_headers", "//absl/meta:type_traits", ], ) diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt index 3919e9a1..338ddc6c 100644 --- a/absl/functional/CMakeLists.txt +++ b/absl/functional/CMakeLists.txt @@ -53,6 +53,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base_internal + absl::core_headers absl::meta PUBLIC ) diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h index 5790a652..824e3cea 100644 --- a/absl/functional/function_ref.h +++ b/absl/functional/function_ref.h @@ -50,6 +50,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/functional/internal/function_ref.h" #include "absl/meta/type_traits.h" @@ -98,7 +99,8 @@ class FunctionRef { public: // Constructs a FunctionRef from any invokable type. template > - FunctionRef(const F& f) // NOLINT(runtime/explicit) + // NOLINTNEXTLINE(runtime/explicit) + FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND) : invoker_(&absl::functional_internal::InvokeObject) { absl::functional_internal::AssertNonNull(f); ptr_.obj = &f; -- cgit v1.2.3 From 095dfc24ff35e5c90f341eac832bdccb891dc35a Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 24 Aug 2021 11:18:06 -0700 Subject: Export of internal Abseil changes -- 5d05c54a619a969da5b4b7f66a2af2d969dc7920 by Abseil Team : Save not needed copies of the predicate in raw_hash_set's EraseIf. PiperOrigin-RevId: 392706073 -- 61ee9b808cd3c81dd10a600c8de5428d6a43cfeb by Abseil Team : Save unnecessary copies of the iterator in raw_hash_set's EraseIf. PiperOrigin-RevId: 392668288 GitOrigin-RevId: 5d05c54a619a969da5b4b7f66a2af2d969dc7920 Change-Id: I180dab2706841ce56f27cf6eabdad1106ebdcf73 --- absl/container/internal/raw_hash_set.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 18b1f5a4..f4919ce3 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1921,11 +1921,12 @@ class raw_hash_set { // Erases all elements that satisfy the predicate `pred` from the container `c`. template -void EraseIf(Predicate pred, raw_hash_set* c) { +void EraseIf(Predicate& pred, raw_hash_set* c) { for (auto it = c->begin(), last = c->end(); it != last;) { - auto copy_it = it++; - if (pred(*copy_it)) { - c->erase(copy_it); + if (pred(*it)) { + c->erase(it++); + } else { + ++it; } } } -- cgit v1.2.3 From 637722af3a60c17915d3325604a0435ee92a41b4 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 25 Aug 2021 12:02:10 -0700 Subject: Export of internal Abseil changes -- e1c30aa6d6bb25987916d3ec39245c6d4a2a93ea by Derek Mauro : Only build the non-stub implementation of RandenHwAes when accelerated AES can be detected by compiler-set flags. This removes the case where the full RandenHwAes is built when only ABSL_RANDOM_INTERNAL_AES_DISPATCH is true. This also removes the case where ARM crypto is enabled through the crypto directive. This directive doesn't appear to reliably work when used with arm_neon.h. As far as I can tell, the crypto directive is only meant to work with crypto instructions in handwritten asm. For this to work with arm_neon.h, it appears several hacks are needed, including overriding some compiler-set defines. PiperOrigin-RevId: 392948948 GitOrigin-RevId: e1c30aa6d6bb25987916d3ec39245c6d4a2a93ea Change-Id: Ie97e26f0204c8a86f72d2f38a59181f1ef578418 --- absl/random/internal/BUILD.bazel | 2 +- absl/random/internal/randen_hwaes.cc | 54 +++--------------------------------- 2 files changed, 5 insertions(+), 51 deletions(-) diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 8420b5c5..df6e42fc 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -626,7 +626,7 @@ cc_test( name = "randen_hwaes_test", size = "small", srcs = ["randen_hwaes_test.cc"], - copts = ABSL_TEST_COPTS, + copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, tags = ABSL_RANDOM_NONPORTABLE_TAGS, deps = [ diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc index ab51e4a3..776c2e18 100644 --- a/absl/random/internal/randen_hwaes.cc +++ b/absl/random/internal/randen_hwaes.cc @@ -30,43 +30,13 @@ // ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain // a hardware accelerated implementation of randen, or whether it // will contain stubs that exit the process. -#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) -// The platform.h directives are sufficient to indicate whether -// we should build accelerated implementations for x86. -#if (ABSL_HAVE_ACCELERATED_AES || ABSL_RANDOM_INTERNAL_AES_DISPATCH) -#define ABSL_RANDEN_HWAES_IMPL 1 -#endif -#elif defined(ABSL_ARCH_PPC) -// The platform.h directives are sufficient to indicate whether -// we should build accelerated implementations for PPC. -// -// NOTE: This has mostly been tested on 64-bit Power variants, -// and not embedded cpus such as powerpc32-8540 #if ABSL_HAVE_ACCELERATED_AES +// The following plaforms have implemented RandenHwAws. +#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \ + defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \ + defined(ABSL_ARCH_AARCH64) #define ABSL_RANDEN_HWAES_IMPL 1 #endif -#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) -// ARM is somewhat more complicated. We might support crypto natively... -#if ABSL_HAVE_ACCELERATED_AES || \ - (defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO)) -#define ABSL_RANDEN_HWAES_IMPL 1 - -#elif ABSL_RANDOM_INTERNAL_AES_DISPATCH && !defined(__APPLE__) && \ - (defined(__GNUC__) && __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 9) -// ...or, on GCC, we can use an ASM directive to -// instruct the assember to allow crypto instructions. -#define ABSL_RANDEN_HWAES_IMPL 1 -#define ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE 1 -#endif -#else -// HWAES is unsupported by these architectures / platforms: -// __myriad2__ -// __mips__ -// -// Other architectures / platforms are unknown. -// -// See the Abseil documentation on supported macros at: -// https://abseil.io/docs/cpp/platforms/macros #endif #if !defined(ABSL_RANDEN_HWAES_IMPL) @@ -192,22 +162,6 @@ inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) { #elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) -// This asm directive will cause the file to be compiled with crypto extensions -// whether or not the cpu-architecture supports it. -#if ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE -asm(".arch_extension crypto\n"); - -// Override missing defines. -#if !defined(__ARM_NEON) -#define __ARM_NEON 1 -#endif - -#if !defined(__ARM_FEATURE_CRYPTO) -#define __ARM_FEATURE_CRYPTO 1 -#endif - -#endif - // Rely on the ARM NEON+Crypto advanced simd types, defined in . // uint8x16_t is the user alias for underlying __simd128_uint8_t type. // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf -- cgit v1.2.3 From 665ac5b425cea2462bf85dcbb56dd68ec19ad00f Mon Sep 17 00:00:00 2001 From: Oleg Fatkhiev <41271510+ofats@users.noreply.github.com> Date: Wed, 1 Sep 2021 21:48:37 +0100 Subject: Add -Wno-unknown-warning-option to ABSL_LLVM_FLAGS to disable warnings on unknown warning flags. (#1008) --- absl/copts/GENERATED_AbseilCopts.cmake | 1 + absl/copts/GENERATED_copts.bzl | 1 + absl/copts/copts.py | 3 +++ 3 files changed, 5 insertions(+) diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake index 22a25eba..a4ab1aa2 100644 --- a/absl/copts/GENERATED_AbseilCopts.cmake +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -94,6 +94,7 @@ list(APPEND ABSL_LLVM_FLAGS "-Wno-implicit-int-conversion" "-Wno-shorten-64-to-32" "-Wno-sign-conversion" + "-Wno-unknown-warning-option" "-DNOMINMAX" ) diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl index 0c7a91bd..a6efc98e 100644 --- a/absl/copts/GENERATED_copts.bzl +++ b/absl/copts/GENERATED_copts.bzl @@ -95,6 +95,7 @@ ABSL_LLVM_FLAGS = [ "-Wno-implicit-int-conversion", "-Wno-shorten-64-to-32", "-Wno-sign-conversion", + "-Wno-unknown-warning-option", "-DNOMINMAX", ] diff --git a/absl/copts/copts.py b/absl/copts/copts.py index 7268c680..0d6c1ec3 100644 --- a/absl/copts/copts.py +++ b/absl/copts/copts.py @@ -112,6 +112,9 @@ COPT_VARS = { "-Wno-implicit-int-conversion", "-Wno-shorten-64-to-32", "-Wno-sign-conversion", + # Disable warnings on unknown warning flags (when warning flags are + # unknown on older compiler versions) + "-Wno-unknown-warning-option", # Don't define min and max macros (Build on Windows using clang) "-DNOMINMAX", ], -- cgit v1.2.3 From 4bb9e39c88854dbf466688177257d11810719853 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 1 Sep 2021 13:48:23 -0700 Subject: Export of internal Abseil changes -- f73e17cb24f7878933fc100bd9bfc39fce190b64 by Derek Mauro : Internal change PiperOrigin-RevId: 394306402 -- 3d3eeffa4e37f63aa50fec1b90858043b40fe377 by Abseil Team : Release a few more absl::Cord unit tests that were accidentally omitted from the OSS release before. PiperOrigin-RevId: 394016464 -- 8a77a8eb93d021aadd8fdf43e219bf35328001ad by CJ Johnson : Fix typo in identifier PiperOrigin-RevId: 394000560 -- d87206c7c8e045b03d74b91e47ef3db0eb47a17b by Derek Mauro : Fix typo: RandenHwAes PiperOrigin-RevId: 393879427 -- 980a3402eea77b0c77fb20dd124203002ff791ba by Derek Mauro : Adds macros `ABSL_LTS_RELEASE_VERSION` and `ABSL_LTS_RELEASE_PATCH_LEVEL` to allow projects to detect if an LTS version is being used. Fixes #1006 PiperOrigin-RevId: 393807178 -- aecc7ed34de718c64733dab76621eacb5af9af5f by CJ Johnson : Change `alloc` to `allocator` to match the fact that other identifiers are full words PiperOrigin-RevId: 393794869 -- ad754bbcf7b78f5d51ed5f39193ac3159429b2b4 by Derek Mauro : Remove self-include of cord_rep_btree.h PiperOrigin-RevId: 393792085 -- f8e937a0d8fe26400560754f3596e3c21bb6d0d7 by Abseil Team : Fix trivial typo in comment. PiperOrigin-RevId: 393770527 -- 7a58ca5d708038d222c6a2b6ff5076b4ceffd370 by Tomas Dzetkulic : Update Cord::AppendArray resize policy. PiperOrigin-RevId: 393362184 -- 316050d171190d9d6312cadf88b1cc2db2d1caa7 by Abseil Team : Add a new top level profiling/ directory to the Abseil library PiperOrigin-RevId: 393358109 -- 0dbb8e10f7fa4a7ac74e12b178e936a67b266c51 by CJ Johnson : Switch to the more common enable_if pattern of ` = 0` in InlinedVector PiperOrigin-RevId: 393301549 -- 136d3068ce33b50ac820e8bd01395a7164d5181f by Abseil Team : Clean up typedefs in internal/inlined_vector.h PiperOrigin-RevId: 393181754 GitOrigin-RevId: f73e17cb24f7878933fc100bd9bfc39fce190b64 Change-Id: I0c4cd4d71d97bd1bf651701b6302ea3d9ac59b66 --- CMake/install_test_project/CMakeLists.txt | 2 +- CMake/install_test_project/simple.cc | 9 + README.md | 3 + absl/CMakeLists.txt | 1 + absl/base/config.h | 29 ++ absl/container/inlined_vector.h | 181 ++++---- absl/container/internal/inlined_vector.h | 707 ++++++++++++++---------------- absl/profiling/BUILD.bazel | 17 + absl/profiling/CMakeLists.txt | 14 + absl/random/internal/randen_hwaes.cc | 2 +- absl/strings/cord.cc | 11 +- absl/strings/cord_test.cc | 53 +++ absl/strings/internal/cord_rep_btree.h | 1 - absl/strings/internal/str_format/bind.h | 2 +- create_lts.py | 7 + 15 files changed, 560 insertions(+), 479 deletions(-) create mode 100644 absl/profiling/BUILD.bazel create mode 100644 absl/profiling/CMakeLists.txt diff --git a/CMake/install_test_project/CMakeLists.txt b/CMake/install_test_project/CMakeLists.txt index eebfe617..b865b2ec 100644 --- a/CMake/install_test_project/CMakeLists.txt +++ b/CMake/install_test_project/CMakeLists.txt @@ -22,4 +22,4 @@ add_executable(simple simple.cc) find_package(absl REQUIRED) -target_link_libraries(simple absl::strings) +target_link_libraries(simple absl::strings absl::config) diff --git a/CMake/install_test_project/simple.cc b/CMake/install_test_project/simple.cc index e9e35291..7daa7f09 100644 --- a/CMake/install_test_project/simple.cc +++ b/CMake/install_test_project/simple.cc @@ -14,8 +14,17 @@ // limitations under the License. #include +#include "absl/base/config.h" #include "absl/strings/substitute.h" +#if !defined(ABSL_LTS_RELEASE_VERSION) || ABSL_LTS_RELEASE_VERSION != 99998877 +#error ABSL_LTS_RELEASE_VERSION is not set correctly. +#endif + +#if !defined(ABSL_LTS_RELEASE_PATCH_LEVEL) || ABSL_LTS_RELEASE_PATCH_LEVEL != 0 +#error ABSL_LTS_RELEASE_PATCH_LEVEL is not set correctly. +#endif + int main(int argc, char** argv) { for (int i = 0; i < argc; ++i) { std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]); diff --git a/README.md b/README.md index 264c4b3f..db3a7b44 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,9 @@ Abseil contains the following C++ library components: available within C++14 and C++17 versions of the C++ `` library. * [`numeric`](absl/numeric/)
The `numeric` library contains C++11-compatible 128-bit integers. +* [`profiling`](absl/profiling/) +
The `profiling` library contains utility code for profiling C++ + entities. It is currently a private dependency of other Abseil libraries. * [`status`](absl/status/)
The `status` contains abstractions for error handling, specifically `absl::Status` and `absl::StatusOr`. diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt index a41e1eeb..b1715846 100644 --- a/absl/CMakeLists.txt +++ b/absl/CMakeLists.txt @@ -25,6 +25,7 @@ add_subdirectory(hash) add_subdirectory(memory) add_subdirectory(meta) add_subdirectory(numeric) +add_subdirectory(profiling) add_subdirectory(random) add_subdirectory(status) add_subdirectory(strings) diff --git a/absl/base/config.h b/absl/base/config.h index 0524196d..c7b2e64d 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -66,6 +66,35 @@ #include "absl/base/options.h" #include "absl/base/policy_checks.h" +// Abseil long-term support (LTS) releases will define +// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the +// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the +// integer representing the patch-level for that release. +// +// For example, for LTS release version "20300401.2", this would give us +// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2 +// +// These symbols will not be defined in non-LTS code. +// +// Abseil recommends that clients live-at-head. Therefore, if you are using +// these symbols to assert a minimum version requirement, we recommend you do it +// as +// +// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401 +// #error Project foo requires Abseil LTS version >= 20300401 +// #endif +// +// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes +// live-at-head clients from the minimum version assertion. +// +// See https://abseil.io/about/releases for more information on Abseil release +// management. +// +// LTS releases can be obtained from +// https://github.com/abseil/abseil-cpp/releases. +#undef ABSL_LTS_RELEASE_VERSION +#undef ABSL_LTS_RELEASE_PATCH_LEVEL + // Helper macro to convert a CPP variable to a string literal. #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x #define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 7c182342..37e5fef8 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -72,37 +72,43 @@ class InlinedVector { using Storage = inlined_vector_internal::Storage; - using AllocatorTraits = typename Storage::AllocatorTraits; - using RValueReference = typename Storage::RValueReference; - using MoveIterator = typename Storage::MoveIterator; - using IsMemcpyOk = typename Storage::IsMemcpyOk; - - template + template + using AllocatorTraits = inlined_vector_internal::AllocatorTraits; + template + using MoveIterator = inlined_vector_internal::MoveIterator; + template + using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk; + + template using IteratorValueAdapter = - typename Storage::template IteratorValueAdapter; - using CopyValueAdapter = typename Storage::CopyValueAdapter; - using DefaultValueAdapter = typename Storage::DefaultValueAdapter; + inlined_vector_internal::IteratorValueAdapter; + template + using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter; + template + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter; template using EnableIfAtLeastForwardIterator = absl::enable_if_t< - inlined_vector_internal::IsAtLeastForwardIterator::value>; + inlined_vector_internal::IsAtLeastForwardIterator::value, int>; template using DisableIfAtLeastForwardIterator = absl::enable_if_t< - !inlined_vector_internal::IsAtLeastForwardIterator::value>; + !inlined_vector_internal::IsAtLeastForwardIterator::value, int>; public: - using allocator_type = typename Storage::allocator_type; - using value_type = typename Storage::value_type; - using pointer = typename Storage::pointer; - using const_pointer = typename Storage::const_pointer; - using size_type = typename Storage::size_type; - using difference_type = typename Storage::difference_type; - using reference = typename Storage::reference; - using const_reference = typename Storage::const_reference; - using iterator = typename Storage::iterator; - using const_iterator = typename Storage::const_iterator; - using reverse_iterator = typename Storage::reverse_iterator; - using const_reverse_iterator = typename Storage::const_reverse_iterator; + using allocator_type = A; + using value_type = inlined_vector_internal::ValueType; + using pointer = inlined_vector_internal::Pointer; + using const_pointer = inlined_vector_internal::ConstPointer; + using size_type = inlined_vector_internal::SizeType; + using difference_type = inlined_vector_internal::DifferenceType; + using reference = inlined_vector_internal::Reference; + using const_reference = inlined_vector_internal::ConstReference; + using iterator = inlined_vector_internal::Iterator; + using const_iterator = inlined_vector_internal::ConstIterator; + using reverse_iterator = inlined_vector_internal::ReverseIterator; + using const_reverse_iterator = + inlined_vector_internal::ConstReverseIterator; // --------------------------------------------------------------------------- // InlinedVector Constructors and Destructor @@ -111,28 +117,28 @@ class InlinedVector { // Creates an empty inlined vector with a value-initialized allocator. InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} - // Creates an empty inlined vector with a copy of `alloc`. - explicit InlinedVector(const allocator_type& alloc) noexcept - : storage_(alloc) {} + // Creates an empty inlined vector with a copy of `allocator`. + explicit InlinedVector(const allocator_type& allocator) noexcept + : storage_(allocator) {} // Creates an inlined vector with `n` copies of `value_type()`. explicit InlinedVector(size_type n, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { - storage_.Initialize(DefaultValueAdapter(), n); + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(DefaultValueAdapter(), n); } // Creates an inlined vector with `n` copies of `v`. InlinedVector(size_type n, const_reference v, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { - storage_.Initialize(CopyValueAdapter(v), n); + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(CopyValueAdapter(std::addressof(v)), n); } // Creates an inlined vector with copies of the elements of `list`. InlinedVector(std::initializer_list list, - const allocator_type& alloc = allocator_type()) - : InlinedVector(list.begin(), list.end(), alloc) {} + const allocator_type& allocator = allocator_type()) + : InlinedVector(list.begin(), list.end(), allocator) {} // Creates an inlined vector with elements constructed from the provided // forward iterator range [`first`, `last`). @@ -141,35 +147,36 @@ class InlinedVector { // this constructor with two integral arguments and a call to the above // `InlinedVector(size_type, const_reference)` constructor. template * = nullptr> + EnableIfAtLeastForwardIterator = 0> InlinedVector(ForwardIterator first, ForwardIterator last, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { - storage_.Initialize(IteratorValueAdapter(first), + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(IteratorValueAdapter(first), std::distance(first, last)); } // Creates an inlined vector with elements constructed from the provided input // iterator range [`first`, `last`). template * = nullptr> + DisableIfAtLeastForwardIterator = 0> InlinedVector(InputIterator first, InputIterator last, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { std::copy(first, last, std::back_inserter(*this)); } // Creates an inlined vector by copying the contents of `other` using // `other`'s allocator. InlinedVector(const InlinedVector& other) - : InlinedVector(other, *other.storage_.GetAllocPtr()) {} + : InlinedVector(other, other.storage_.GetAllocator()) {} - // Creates an inlined vector by copying the contents of `other` using `alloc`. - InlinedVector(const InlinedVector& other, const allocator_type& alloc) - : storage_(alloc) { + // Creates an inlined vector by copying the contents of `other` using the + // provided `allocator`. + InlinedVector(const InlinedVector& other, const allocator_type& allocator) + : storage_(allocator) { if (other.empty()) { // Empty; nothing to do. - } else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { + } else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { // Memcpy-able and do not need allocation. storage_.MemcpyFrom(other.storage_); } else { @@ -194,8 +201,8 @@ class InlinedVector { InlinedVector(InlinedVector&& other) noexcept( absl::allocator_is_nothrow::value || std::is_nothrow_move_constructible::value) - : storage_(*other.storage_.GetAllocPtr()) { - if (IsMemcpyOk::value) { + : storage_(other.storage_.GetAllocator()) { + if (IsMemcpyOk::value) { storage_.MemcpyFrom(other.storage_); other.storage_.SetInlinedSize(0); @@ -206,11 +213,11 @@ class InlinedVector { other.storage_.SetInlinedSize(0); } else { - IteratorValueAdapter other_values( - MoveIterator(other.storage_.GetInlinedData())); + IteratorValueAdapter> other_values( + MoveIterator(other.storage_.GetInlinedData())); - inlined_vector_internal::ConstructElements( - storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values, + inlined_vector_internal::ConstructElements( + storage_.GetAllocator(), storage_.GetInlinedData(), other_values, other.storage_.GetSize()); storage_.SetInlinedSize(other.storage_.GetSize()); @@ -218,20 +225,22 @@ class InlinedVector { } // Creates an inlined vector by moving in the contents of `other` with a copy - // of `alloc`. + // of `allocator`. // - // NOTE: if `other`'s allocator is not equal to `alloc`, even if `other` + // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other` // contains allocated memory, this move constructor will still allocate. Since // allocation is performed, this constructor can only be `noexcept` if the // specified allocator is also `noexcept`. - InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept( - absl::allocator_is_nothrow::value) - : storage_(alloc) { - if (IsMemcpyOk::value) { + InlinedVector( + InlinedVector&& other, + const allocator_type& allocator) + noexcept(absl::allocator_is_nothrow::value) + : storage_(allocator) { + if (IsMemcpyOk::value) { storage_.MemcpyFrom(other.storage_); other.storage_.SetInlinedSize(0); - } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) && + } else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) && other.storage_.GetIsAllocated()) { storage_.SetAllocatedData(other.storage_.GetAllocatedData(), other.storage_.GetAllocatedCapacity()); @@ -239,9 +248,9 @@ class InlinedVector { other.storage_.SetInlinedSize(0); } else { - storage_.Initialize( - IteratorValueAdapter(MoveIterator(other.data())), - other.size()); + storage_.Initialize(IteratorValueAdapter>( + MoveIterator(other.data())), + other.size()); } } @@ -442,7 +451,7 @@ class InlinedVector { // `InlinedVector::get_allocator()` // // Returns a copy of the inlined vector's allocator. - allocator_type get_allocator() const { return *storage_.GetAllocPtr(); } + allocator_type get_allocator() const { return storage_.GetAllocator(); } // --------------------------------------------------------------------------- // InlinedVector Member Mutators @@ -476,16 +485,16 @@ class InlinedVector { // unspecified state. InlinedVector& operator=(InlinedVector&& other) { if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { - if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { - inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), - size()); + if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { + inlined_vector_internal::DestroyElements(storage_.GetAllocator(), + data(), size()); storage_.DeallocateIfAllocated(); storage_.MemcpyFrom(other.storage_); other.storage_.SetInlinedSize(0); } else { - storage_.Assign(IteratorValueAdapter( - MoveIterator(other.storage_.GetInlinedData())), + storage_.Assign(IteratorValueAdapter>( + MoveIterator(other.storage_.GetInlinedData())), other.size()); } } @@ -497,7 +506,7 @@ class InlinedVector { // // Replaces the contents of the inlined vector with `n` copies of `v`. void assign(size_type n, const_reference v) { - storage_.Assign(CopyValueAdapter(v), n); + storage_.Assign(CopyValueAdapter(std::addressof(v)), n); } // Overload of `InlinedVector::assign(...)` that replaces the contents of the @@ -511,9 +520,9 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "forward" category or better. template * = nullptr> + EnableIfAtLeastForwardIterator = 0> void assign(ForwardIterator first, ForwardIterator last) { - storage_.Assign(IteratorValueAdapter(first), + storage_.Assign(IteratorValueAdapter(first), std::distance(first, last)); } @@ -522,7 +531,7 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "input" category. template * = nullptr> + DisableIfAtLeastForwardIterator = 0> void assign(InputIterator first, InputIterator last) { size_type i = 0; for (; i < size() && first != last; ++i, static_cast(++first)) { @@ -541,7 +550,7 @@ class InlinedVector { // is larger than `size()`, new elements are value-initialized. void resize(size_type n) { ABSL_HARDENING_ASSERT(n <= max_size()); - storage_.Resize(DefaultValueAdapter(), n); + storage_.Resize(DefaultValueAdapter(), n); } // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to @@ -551,7 +560,7 @@ class InlinedVector { // is larger than `size()`, new elements are copied-constructed from `v`. void resize(size_type n, const_reference v) { ABSL_HARDENING_ASSERT(n <= max_size()); - storage_.Resize(CopyValueAdapter(v), n); + storage_.Resize(CopyValueAdapter(std::addressof(v)), n); } // `InlinedVector::insert(...)` @@ -564,7 +573,7 @@ class InlinedVector { // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using // move semantics, returning an `iterator` to the newly inserted element. - iterator insert(const_iterator pos, RValueReference v) { + iterator insert(const_iterator pos, value_type&& v) { return emplace(pos, std::move(v)); } @@ -577,7 +586,8 @@ class InlinedVector { if (ABSL_PREDICT_TRUE(n != 0)) { value_type dealias = v; - return storage_.Insert(pos, CopyValueAdapter(dealias), n); + return storage_.Insert(pos, CopyValueAdapter(std::addressof(dealias)), + n); } else { return const_cast(pos); } @@ -596,14 +606,15 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "forward" category or better. template * = nullptr> + EnableIfAtLeastForwardIterator = 0> iterator insert(const_iterator pos, ForwardIterator first, ForwardIterator last) { ABSL_HARDENING_ASSERT(pos >= begin()); ABSL_HARDENING_ASSERT(pos <= end()); if (ABSL_PREDICT_TRUE(first != last)) { - return storage_.Insert(pos, IteratorValueAdapter(first), + return storage_.Insert(pos, + IteratorValueAdapter(first), std::distance(first, last)); } else { return const_cast(pos); @@ -616,7 +627,7 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "input" category. template * = nullptr> + DisableIfAtLeastForwardIterator = 0> iterator insert(const_iterator pos, InputIterator first, InputIterator last) { ABSL_HARDENING_ASSERT(pos >= begin()); ABSL_HARDENING_ASSERT(pos <= end()); @@ -640,8 +651,8 @@ class InlinedVector { value_type dealias(std::forward(args)...); return storage_.Insert(pos, - IteratorValueAdapter( - MoveIterator(std::addressof(dealias))), + IteratorValueAdapter>( + MoveIterator(std::addressof(dealias))), 1); } @@ -661,7 +672,7 @@ class InlinedVector { // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` // using move semantics. - void push_back(RValueReference v) { + void push_back(value_type&& v) { static_cast(emplace_back(std::move(v))); } @@ -671,7 +682,7 @@ class InlinedVector { void pop_back() noexcept { ABSL_HARDENING_ASSERT(!empty()); - AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); + AllocatorTraits::destroy(storage_.GetAllocator(), data() + (size() - 1)); storage_.SubtractSize(1); } @@ -710,8 +721,8 @@ class InlinedVector { // Destroys all elements in the inlined vector, setting the size to `0` and // deallocating any held memory. void clear() noexcept { - inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), - size()); + inlined_vector_internal::DestroyElements(storage_.GetAllocator(), data(), + size()); storage_.DeallocateIfAllocated(); storage_.SetInlinedSize(0); diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 49822af0..1cfba9b2 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -40,45 +40,65 @@ namespace inlined_vector_internal { #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif +template +using AllocatorTraits = std::allocator_traits; +template +using ValueType = typename AllocatorTraits::value_type; +template +using SizeType = typename AllocatorTraits::size_type; +template +using Pointer = typename AllocatorTraits::pointer; +template +using ConstPointer = typename AllocatorTraits::const_pointer; +template +using SizeType = typename AllocatorTraits::size_type; +template +using DifferenceType = typename AllocatorTraits::difference_type; +template +using Reference = ValueType&; +template +using ConstReference = const ValueType&; +template +using Iterator = Pointer; +template +using ConstIterator = ConstPointer; +template +using ReverseIterator = typename std::reverse_iterator>; +template +using ConstReverseIterator = typename std::reverse_iterator>; +template +using MoveIterator = typename std::move_iterator>; + template using IsAtLeastForwardIterator = std::is_convertible< typename std::iterator_traits::iterator_category, std::forward_iterator_tag>; -template ::value_type> +template using IsMemcpyOk = - absl::conjunction>, - absl::is_trivially_copy_constructible, - absl::is_trivially_copy_assignable, - absl::is_trivially_destructible>; + absl::conjunction>>, + absl::is_trivially_copy_constructible>, + absl::is_trivially_copy_assignable>, + absl::is_trivially_destructible>>; + +template +struct TypeIdentity { + using type = T; +}; -template -void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first, - SizeType destroy_size) { - using AllocatorTraits = absl::allocator_traits; +// Used for function arguments in template functions to prevent ADL by forcing +// callers to explicitly specify the template parameter. +template +using NoTypeDeduction = typename TypeIdentity::type; +template +void DestroyElements(NoTypeDeduction& allocator, Pointer destroy_first, + SizeType destroy_size) { if (destroy_first != nullptr) { for (auto i = destroy_size; i != 0;) { --i; - AllocatorTraits::destroy(*alloc_ptr, destroy_first + i); + AllocatorTraits::destroy(allocator, destroy_first + i); } - -#if !defined(NDEBUG) - { - using ValueType = typename AllocatorTraits::value_type; - - // Overwrite unused memory with `0xab` so we can catch uninitialized - // usage. - // - // Cast to `void*` to tell the compiler that we don't care that we might - // be scribbling on a vtable pointer. - void* memory_ptr = destroy_first; - auto memory_size = destroy_size * sizeof(ValueType); - std::memset(memory_ptr, 0xab, memory_size); - } -#endif // !defined(NDEBUG) } } @@ -99,54 +119,45 @@ inline void MemcpyIfAllowed(void* dst, const void* src, size_t n) { template <> inline void MemcpyIfAllowed(void*, const void*, size_t) {} -template -void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first, - ValueAdapter* values_ptr, SizeType construct_size) { - for (SizeType i = 0; i < construct_size; ++i) { - ABSL_INTERNAL_TRY { - values_ptr->ConstructNext(alloc_ptr, construct_first + i); - } +template +void ConstructElements(NoTypeDeduction& allocator, + Pointer construct_first, ValueAdapter& values, + SizeType construct_size) { + for (SizeType i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); } ABSL_INTERNAL_CATCH_ANY { - inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i); + DestroyElements(allocator, construct_first, i); ABSL_INTERNAL_RETHROW; } } } -template -void AssignElements(Pointer assign_first, ValueAdapter* values_ptr, - SizeType assign_size) { - for (SizeType i = 0; i < assign_size; ++i) { - values_ptr->AssignNext(assign_first + i); +template +void AssignElements(Pointer assign_first, ValueAdapter& values, + SizeType assign_size) { + for (SizeType i = 0; i < assign_size; ++i) { + values.AssignNext(assign_first + i); } } -template +template struct StorageView { - using AllocatorTraits = absl::allocator_traits; - using Pointer = typename AllocatorTraits::pointer; - using SizeType = typename AllocatorTraits::size_type; - - Pointer data; - SizeType size; - SizeType capacity; + Pointer data; + SizeType size; + SizeType capacity; }; -template +template class IteratorValueAdapter { - using AllocatorTraits = absl::allocator_traits; - using Pointer = typename AllocatorTraits::pointer; - public: explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} - void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { - AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at, *it_); ++it_; } - void AssignNext(Pointer assign_at) { + void AssignNext(Pointer assign_at) { *assign_at = *it_; ++it_; } @@ -155,68 +166,55 @@ class IteratorValueAdapter { Iterator it_; }; -template +template class CopyValueAdapter { - using AllocatorTraits = absl::allocator_traits; - using ValueType = typename AllocatorTraits::value_type; - using Pointer = typename AllocatorTraits::pointer; - using ConstPointer = typename AllocatorTraits::const_pointer; - public: - explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {} + explicit CopyValueAdapter(ConstPointer p) : ptr_(p) {} - void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { - AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at, *ptr_); } - void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } + void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } private: - ConstPointer ptr_; + ConstPointer ptr_; }; -template +template class DefaultValueAdapter { - using AllocatorTraits = absl::allocator_traits; - using ValueType = typename AllocatorTraits::value_type; - using Pointer = typename AllocatorTraits::pointer; - public: explicit DefaultValueAdapter() {} - void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { - AllocatorTraits::construct(*alloc_ptr, construct_at); + void ConstructNext(A& allocator, Pointer construct_at) { + AllocatorTraits::construct(allocator, construct_at); } - void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } + void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } }; -template +template class AllocationTransaction { - using AllocatorTraits = absl::allocator_traits; - using Pointer = typename AllocatorTraits::pointer; - using SizeType = typename AllocatorTraits::size_type; - public: - explicit AllocationTransaction(AllocatorType* alloc_ptr) - : alloc_data_(*alloc_ptr, nullptr) {} + explicit AllocationTransaction(A& allocator) + : allocator_data_(allocator, nullptr), capacity_(0) {} ~AllocationTransaction() { if (DidAllocate()) { - AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); + AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); } } AllocationTransaction(const AllocationTransaction&) = delete; void operator=(const AllocationTransaction&) = delete; - AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - Pointer& GetData() { return alloc_data_.template get<1>(); } - SizeType& GetCapacity() { return capacity_; } + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer& GetData() { return allocator_data_.template get<1>(); } + SizeType& GetCapacity() { return capacity_; } bool DidAllocate() { return GetData() != nullptr; } - Pointer Allocate(SizeType capacity) { - GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); + Pointer Allocate(SizeType capacity) { + GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); GetCapacity() = capacity; return GetData(); } @@ -227,39 +225,33 @@ class AllocationTransaction { } private: - container_internal::CompressedTuple alloc_data_; - SizeType capacity_ = 0; + container_internal::CompressedTuple> allocator_data_; + SizeType capacity_; }; -template +template class ConstructionTransaction { - using AllocatorTraits = absl::allocator_traits; - using Pointer = typename AllocatorTraits::pointer; - using SizeType = typename AllocatorTraits::size_type; - public: - explicit ConstructionTransaction(AllocatorType* alloc_ptr) - : alloc_data_(*alloc_ptr, nullptr) {} + explicit ConstructionTransaction(A& allocator) + : allocator_data_(allocator, nullptr), size_(0) {} ~ConstructionTransaction() { if (DidConstruct()) { - inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()), - GetData(), GetSize()); + DestroyElements(GetAllocator(), GetData(), GetSize()); } } ConstructionTransaction(const ConstructionTransaction&) = delete; void operator=(const ConstructionTransaction&) = delete; - AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - Pointer& GetData() { return alloc_data_.template get<1>(); } - SizeType& GetSize() { return size_; } + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer& GetData() { return allocator_data_.template get<1>(); } + SizeType& GetSize() { return size_; } bool DidConstruct() { return GetData() != nullptr; } template - void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) { - inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), - data, values_ptr, size); + void Construct(Pointer data, ValueAdapter& values, SizeType size) { + ConstructElements(GetAllocator(), data, values, size); GetData() = data; GetSize() = size; } @@ -269,52 +261,19 @@ class ConstructionTransaction { } private: - container_internal::CompressedTuple alloc_data_; - SizeType size_ = 0; + container_internal::CompressedTuple> allocator_data_; + SizeType size_; }; template class Storage { public: - using AllocatorTraits = absl::allocator_traits; - using allocator_type = typename AllocatorTraits::allocator_type; - using value_type = typename AllocatorTraits::value_type; - using pointer = typename AllocatorTraits::pointer; - using const_pointer = typename AllocatorTraits::const_pointer; - using size_type = typename AllocatorTraits::size_type; - using difference_type = typename AllocatorTraits::difference_type; - - using reference = value_type&; - using const_reference = const value_type&; - using RValueReference = value_type&&; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using MoveIterator = std::move_iterator; - using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk; - - using StorageView = inlined_vector_internal::StorageView; - - template - using IteratorValueAdapter = - inlined_vector_internal::IteratorValueAdapter; - using CopyValueAdapter = - inlined_vector_internal::CopyValueAdapter; - using DefaultValueAdapter = - inlined_vector_internal::DefaultValueAdapter; - - using AllocationTransaction = - inlined_vector_internal::AllocationTransaction; - using ConstructionTransaction = - inlined_vector_internal::ConstructionTransaction; - - static size_type NextCapacity(size_type current_capacity) { + static SizeType NextCapacity(SizeType current_capacity) { return current_capacity * 2; } - static size_type ComputeCapacity(size_type current_capacity, - size_type requested_capacity) { + static SizeType ComputeCapacity(SizeType current_capacity, + SizeType requested_capacity) { return (std::max)(NextCapacity(current_capacity), requested_capacity); } @@ -322,15 +281,15 @@ class Storage { // Storage Constructors and Destructor // --------------------------------------------------------------------------- - Storage() : metadata_(allocator_type(), /* size and is_allocated */ 0) {} + Storage() : metadata_(A(), /* size and is_allocated */ 0) {} - explicit Storage(const allocator_type& alloc) - : metadata_(alloc, /* size and is_allocated */ 0) {} + explicit Storage(const A& allocator) + : metadata_(allocator, /* size and is_allocated */ 0) {} ~Storage() { if (GetSizeAndIsAllocated() == 0) { // Empty and not allocated; nothing to do. - } else if (IsMemcpyOk::value) { + } else if (IsMemcpyOk::value) { // No destructors need to be run; just deallocate if necessary. DeallocateIfAllocated(); } else { @@ -342,52 +301,48 @@ class Storage { // Storage Member Accessors // --------------------------------------------------------------------------- - size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + SizeType& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } - const size_type& GetSizeAndIsAllocated() const { + const SizeType& GetSizeAndIsAllocated() const { return metadata_.template get<1>(); } - size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; } + SizeType GetSize() const { return GetSizeAndIsAllocated() >> 1; } bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } - pointer GetAllocatedData() { return data_.allocated.allocated_data; } + Pointer GetAllocatedData() { return data_.allocated.allocated_data; } - const_pointer GetAllocatedData() const { + ConstPointer GetAllocatedData() const { return data_.allocated.allocated_data; } - pointer GetInlinedData() { - return reinterpret_cast( + Pointer GetInlinedData() { + return reinterpret_cast>( std::addressof(data_.inlined.inlined_data[0])); } - const_pointer GetInlinedData() const { - return reinterpret_cast( + ConstPointer GetInlinedData() const { + return reinterpret_cast>( std::addressof(data_.inlined.inlined_data[0])); } - size_type GetAllocatedCapacity() const { + SizeType GetAllocatedCapacity() const { return data_.allocated.allocated_capacity; } - size_type GetInlinedCapacity() const { return static_cast(N); } + SizeType GetInlinedCapacity() const { return static_cast>(N); } - StorageView MakeStorageView() { - return GetIsAllocated() - ? StorageView{GetAllocatedData(), GetSize(), - GetAllocatedCapacity()} - : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()}; + StorageView MakeStorageView() { + return GetIsAllocated() ? StorageView{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView{GetInlinedData(), GetSize(), + GetInlinedCapacity()}; } - allocator_type* GetAllocPtr() { - return std::addressof(metadata_.template get<0>()); - } + A& GetAllocator() { return metadata_.template get<0>(); } - const allocator_type* GetAllocPtr() const { - return std::addressof(metadata_.template get<0>()); - } + const A& GetAllocator() const { return metadata_.template get<0>(); } // --------------------------------------------------------------------------- // Storage Member Mutators @@ -396,74 +351,73 @@ class Storage { ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); template - void Initialize(ValueAdapter values, size_type new_size); + void Initialize(ValueAdapter values, SizeType new_size); template - void Assign(ValueAdapter values, size_type new_size); + void Assign(ValueAdapter values, SizeType new_size); template - void Resize(ValueAdapter values, size_type new_size); + void Resize(ValueAdapter values, SizeType new_size); template - iterator Insert(const_iterator pos, ValueAdapter values, - size_type insert_count); + Iterator Insert(ConstIterator pos, ValueAdapter values, + SizeType insert_count); template - reference EmplaceBack(Args&&... args); + Reference EmplaceBack(Args&&... args); - iterator Erase(const_iterator from, const_iterator to); + Iterator Erase(ConstIterator from, ConstIterator to); - void Reserve(size_type requested_capacity); + void Reserve(SizeType requested_capacity); void ShrinkToFit(); void Swap(Storage* other_storage_ptr); void SetIsAllocated() { - GetSizeAndIsAllocated() |= static_cast(1); + GetSizeAndIsAllocated() |= static_cast>(1); } void UnsetIsAllocated() { - GetSizeAndIsAllocated() &= ((std::numeric_limits::max)() - 1); + GetSizeAndIsAllocated() &= ((std::numeric_limits>::max)() - 1); } - void SetSize(size_type size) { + void SetSize(SizeType size) { GetSizeAndIsAllocated() = - (size << 1) | static_cast(GetIsAllocated()); + (size << 1) | static_cast>(GetIsAllocated()); } - void SetAllocatedSize(size_type size) { - GetSizeAndIsAllocated() = (size << 1) | static_cast(1); + void SetAllocatedSize(SizeType size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast>(1); } - void SetInlinedSize(size_type size) { - GetSizeAndIsAllocated() = size << static_cast(1); + void SetInlinedSize(SizeType size) { + GetSizeAndIsAllocated() = size << static_cast>(1); } - void AddSize(size_type count) { - GetSizeAndIsAllocated() += count << static_cast(1); + void AddSize(SizeType count) { + GetSizeAndIsAllocated() += count << static_cast>(1); } - void SubtractSize(size_type count) { + void SubtractSize(SizeType count) { assert(count <= GetSize()); - GetSizeAndIsAllocated() -= count << static_cast(1); + GetSizeAndIsAllocated() -= count << static_cast>(1); } - void SetAllocatedData(pointer data, size_type capacity) { + void SetAllocatedData(Pointer data, SizeType capacity) { data_.allocated.allocated_data = data; data_.allocated.allocated_capacity = capacity; } - void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) { - SetAllocatedData(allocation_tx_ptr->GetData(), - allocation_tx_ptr->GetCapacity()); + void AcquireAllocatedData(AllocationTransaction& allocation_tx) { + SetAllocatedData(allocation_tx.GetData(), allocation_tx.GetCapacity()); - allocation_tx_ptr->Reset(); + allocation_tx.Reset(); } void MemcpyFrom(const Storage& other_storage) { - assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); + assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); data_ = other_storage.data_; @@ -471,24 +425,23 @@ class Storage { void DeallocateIfAllocated() { if (GetIsAllocated()) { - AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(), - GetAllocatedCapacity()); + AllocatorTraits::deallocate(GetAllocator(), GetAllocatedData(), + GetAllocatedCapacity()); } } private: ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); - using Metadata = - container_internal::CompressedTuple; + using Metadata = container_internal::CompressedTuple>; struct Allocated { - pointer allocated_data; - size_type allocated_capacity; + Pointer allocated_data; + SizeType allocated_capacity; }; struct Inlined { - alignas(value_type) char inlined_data[sizeof(value_type[N])]; + alignas(ValueType) char inlined_data[sizeof(ValueType[N])]; }; union Data { @@ -497,7 +450,7 @@ class Storage { }; template - ABSL_ATTRIBUTE_NOINLINE reference EmplaceBackSlow(Args&&... args); + ABSL_ATTRIBUTE_NOINLINE Reference EmplaceBackSlow(Args&&... args); Metadata metadata_; Data data_; @@ -505,8 +458,8 @@ class Storage { template void Storage::DestroyContents() { - pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); - inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize()); + Pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); + DestroyElements(GetAllocator(), data, GetSize()); DeallocateIfAllocated(); } @@ -514,8 +467,8 @@ template void Storage::InitFrom(const Storage& other) { const auto n = other.GetSize(); assert(n > 0); // Empty sources handled handled in caller. - const_pointer src; - pointer dst; + ConstPointer src; + Pointer dst; if (!other.GetIsAllocated()) { dst = GetInlinedData(); src = other.GetInlinedData(); @@ -523,43 +476,42 @@ void Storage::InitFrom(const Storage& other) { // Because this is only called from the `InlinedVector` constructors, it's // safe to take on the allocation with size `0`. If `ConstructElements(...)` // throws, deallocation will be automatically handled by `~Storage()`. - size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), n); - dst = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + SizeType new_capacity = ComputeCapacity(GetInlinedCapacity(), n); + dst = AllocatorTraits::allocate(GetAllocator(), new_capacity); SetAllocatedData(dst, new_capacity); src = other.GetAllocatedData(); } - if (IsMemcpyOk::value) { - MemcpyIfAllowed(dst, src, sizeof(dst[0]) * n); + if (IsMemcpyOk::value) { + MemcpyIfAllowed::value>(dst, src, sizeof(dst[0]) * n); } else { - auto values = IteratorValueAdapter(src); - inlined_vector_internal::ConstructElements(GetAllocPtr(), dst, &values, n); + auto values = IteratorValueAdapter>(src); + ConstructElements(GetAllocator(), dst, values, n); } GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); } template template -auto Storage::Initialize(ValueAdapter values, size_type new_size) +auto Storage::Initialize(ValueAdapter values, SizeType new_size) -> void { // Only callable from constructors! assert(!GetIsAllocated()); assert(GetSize() == 0); - pointer construct_data; + Pointer construct_data; if (new_size > GetInlinedCapacity()) { // Because this is only called from the `InlinedVector` constructors, it's // safe to take on the allocation with size `0`. If `ConstructElements(...)` // throws, deallocation will be automatically handled by `~Storage()`. - size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); - construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + SizeType new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); + construct_data = AllocatorTraits::allocate(GetAllocator(), new_capacity); SetAllocatedData(construct_data, new_capacity); SetIsAllocated(); } else { construct_data = GetInlinedData(); } - inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, - &values, new_size); + ConstructElements(GetAllocator(), construct_data, values, new_size); // Since the initial size was guaranteed to be `0` and the allocated bit is // already correct for either case, *adding* `new_size` gives us the correct @@ -569,17 +521,18 @@ auto Storage::Initialize(ValueAdapter values, size_type new_size) template template -auto Storage::Assign(ValueAdapter values, size_type new_size) -> void { - StorageView storage_view = MakeStorageView(); +auto Storage::Assign(ValueAdapter values, SizeType new_size) + -> void { + StorageView storage_view = MakeStorageView(); - AllocationTransaction allocation_tx(GetAllocPtr()); + AllocationTransaction allocation_tx(GetAllocator()); - absl::Span assign_loop; - absl::Span construct_loop; - absl::Span destroy_loop; + absl::Span> assign_loop; + absl::Span> construct_loop; + absl::Span> destroy_loop; if (new_size > storage_view.capacity) { - size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + SizeType new_capacity = ComputeCapacity(storage_view.capacity, new_size); construct_loop = {allocation_tx.Allocate(new_capacity), new_size}; destroy_loop = {storage_view.data, storage_view.size}; } else if (new_size > storage_view.size) { @@ -591,18 +544,16 @@ auto Storage::Assign(ValueAdapter values, size_type new_size) -> void { destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; } - inlined_vector_internal::AssignElements(assign_loop.data(), &values, - assign_loop.size()); + AssignElements(assign_loop.data(), values, assign_loop.size()); - inlined_vector_internal::ConstructElements( - GetAllocPtr(), construct_loop.data(), &values, construct_loop.size()); + ConstructElements(GetAllocator(), construct_loop.data(), values, + construct_loop.size()); - inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), - destroy_loop.size()); + DestroyElements(GetAllocator(), destroy_loop.data(), destroy_loop.size()); if (allocation_tx.DidAllocate()) { DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + AcquireAllocatedData(allocation_tx); SetIsAllocated(); } @@ -611,19 +562,18 @@ auto Storage::Assign(ValueAdapter values, size_type new_size) -> void { template template -auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { - StorageView storage_view = MakeStorageView(); +auto Storage::Resize(ValueAdapter values, SizeType new_size) + -> void { + StorageView storage_view = MakeStorageView(); auto* const base = storage_view.data; - const size_type size = storage_view.size; - auto* alloc = GetAllocPtr(); + const SizeType size = storage_view.size; + auto& alloc = GetAllocator(); if (new_size <= size) { // Destroy extra old elements. - inlined_vector_internal::DestroyElements(alloc, base + new_size, - size - new_size); + DestroyElements(alloc, base + new_size, size - new_size); } else if (new_size <= storage_view.capacity) { // Construct new elements in place. - inlined_vector_internal::ConstructElements(alloc, base + size, &values, - new_size - size); + ConstructElements(alloc, base + size, values, new_size - size); } else { // Steps: // a. Allocate new backing store. @@ -632,21 +582,21 @@ auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { // d. Destroy all elements in old backing store. // Use transactional wrappers for the first two steps so we can roll // back if necessary due to exceptions. - AllocationTransaction allocation_tx(alloc); - size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); - pointer new_data = allocation_tx.Allocate(new_capacity); + AllocationTransaction allocation_tx(alloc); + SizeType new_capacity = ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(new_capacity); - ConstructionTransaction construction_tx(alloc); - construction_tx.Construct(new_data + size, &values, new_size - size); + ConstructionTransaction construction_tx(alloc); + construction_tx.Construct(new_data + size, values, new_size - size); - IteratorValueAdapter move_values((MoveIterator(base))); - inlined_vector_internal::ConstructElements(alloc, new_data, &move_values, - size); + IteratorValueAdapter> move_values( + (MoveIterator(base))); + ConstructElements(alloc, new_data, move_values, size); - inlined_vector_internal::DestroyElements(alloc, base, size); + DestroyElements(alloc, base, size); construction_tx.Commit(); DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + AcquireAllocatedData(allocation_tx); SetIsAllocated(); } SetSize(new_size); @@ -654,76 +604,75 @@ auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { template template -auto Storage::Insert(const_iterator pos, ValueAdapter values, - size_type insert_count) -> iterator { - StorageView storage_view = MakeStorageView(); +auto Storage::Insert(ConstIterator pos, ValueAdapter values, + SizeType insert_count) -> Iterator { + StorageView storage_view = MakeStorageView(); - size_type insert_index = - std::distance(const_iterator(storage_view.data), pos); - size_type insert_end_index = insert_index + insert_count; - size_type new_size = storage_view.size + insert_count; + SizeType insert_index = + std::distance(ConstIterator(storage_view.data), pos); + SizeType insert_end_index = insert_index + insert_count; + SizeType new_size = storage_view.size + insert_count; if (new_size > storage_view.capacity) { - AllocationTransaction allocation_tx(GetAllocPtr()); - ConstructionTransaction construction_tx(GetAllocPtr()); - ConstructionTransaction move_construciton_tx(GetAllocPtr()); + AllocationTransaction allocation_tx(GetAllocator()); + ConstructionTransaction construction_tx(GetAllocator()); + ConstructionTransaction move_construction_tx(GetAllocator()); - IteratorValueAdapter move_values( - MoveIterator(storage_view.data)); + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); - size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); - pointer new_data = allocation_tx.Allocate(new_capacity); + SizeType new_capacity = ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(new_capacity); - construction_tx.Construct(new_data + insert_index, &values, insert_count); + construction_tx.Construct(new_data + insert_index, values, insert_count); - move_construciton_tx.Construct(new_data, &move_values, insert_index); + move_construction_tx.Construct(new_data, move_values, insert_index); - inlined_vector_internal::ConstructElements( - GetAllocPtr(), new_data + insert_end_index, &move_values, - storage_view.size - insert_index); + ConstructElements(GetAllocator(), new_data + insert_end_index, + move_values, storage_view.size - insert_index); - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); + DestroyElements(GetAllocator(), storage_view.data, storage_view.size); construction_tx.Commit(); - move_construciton_tx.Commit(); + move_construction_tx.Commit(); DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + AcquireAllocatedData(allocation_tx); SetAllocatedSize(new_size); - return iterator(new_data + insert_index); + return Iterator(new_data + insert_index); } else { - size_type move_construction_destination_index = + SizeType move_construction_destination_index = (std::max)(insert_end_index, storage_view.size); - ConstructionTransaction move_construction_tx(GetAllocPtr()); + ConstructionTransaction move_construction_tx(GetAllocator()); - IteratorValueAdapter move_construction_values( - MoveIterator(storage_view.data + - (move_construction_destination_index - insert_count))); - absl::Span move_construction = { + IteratorValueAdapter> move_construction_values( + MoveIterator(storage_view.data + + (move_construction_destination_index - insert_count))); + absl::Span> move_construction = { storage_view.data + move_construction_destination_index, new_size - move_construction_destination_index}; - pointer move_assignment_values = storage_view.data + insert_index; - absl::Span move_assignment = { + Pointer move_assignment_values = storage_view.data + insert_index; + absl::Span> move_assignment = { storage_view.data + insert_end_index, move_construction_destination_index - insert_end_index}; - absl::Span insert_assignment = {move_assignment_values, - move_construction.size()}; + absl::Span> insert_assignment = {move_assignment_values, + move_construction.size()}; - absl::Span insert_construction = { + absl::Span> insert_construction = { insert_assignment.data() + insert_assignment.size(), insert_count - insert_assignment.size()}; move_construction_tx.Construct(move_construction.data(), - &move_construction_values, + move_construction_values, move_construction.size()); - for (pointer destination = move_assignment.data() + move_assignment.size(), - last_destination = move_assignment.data(), - source = move_assignment_values + move_assignment.size(); + for (Pointer + destination = move_assignment.data() + move_assignment.size(), + last_destination = move_assignment.data(), + source = move_assignment_values + move_assignment.size(); ;) { --destination; --source; @@ -731,30 +680,29 @@ auto Storage::Insert(const_iterator pos, ValueAdapter values, *destination = std::move(*source); } - inlined_vector_internal::AssignElements(insert_assignment.data(), &values, - insert_assignment.size()); + AssignElements(insert_assignment.data(), values, + insert_assignment.size()); - inlined_vector_internal::ConstructElements( - GetAllocPtr(), insert_construction.data(), &values, - insert_construction.size()); + ConstructElements(GetAllocator(), insert_construction.data(), values, + insert_construction.size()); move_construction_tx.Commit(); AddSize(insert_count); - return iterator(storage_view.data + insert_index); + return Iterator(storage_view.data + insert_index); } } template template -auto Storage::EmplaceBack(Args&&... args) -> reference { - StorageView storage_view = MakeStorageView(); +auto Storage::EmplaceBack(Args&&... args) -> Reference { + StorageView storage_view = MakeStorageView(); const auto n = storage_view.size; if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { // Fast path; new element fits. - pointer last_ptr = storage_view.data + n; - AllocatorTraits::construct(*GetAllocPtr(), last_ptr, - std::forward(args)...); + Pointer last_ptr = storage_view.data + n; + AllocatorTraits::construct(GetAllocator(), last_ptr, + std::forward(args)...); AddSize(1); return *last_ptr; } @@ -764,87 +712,83 @@ auto Storage::EmplaceBack(Args&&... args) -> reference { template template -auto Storage::EmplaceBackSlow(Args&&... args) -> reference { - StorageView storage_view = MakeStorageView(); - AllocationTransaction allocation_tx(GetAllocPtr()); - IteratorValueAdapter move_values( - MoveIterator(storage_view.data)); - size_type new_capacity = NextCapacity(storage_view.capacity); - pointer construct_data = allocation_tx.Allocate(new_capacity); - pointer last_ptr = construct_data + storage_view.size; +auto Storage::EmplaceBackSlow(Args&&... args) -> Reference { + StorageView storage_view = MakeStorageView(); + AllocationTransaction allocation_tx(GetAllocator()); + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); + SizeType new_capacity = NextCapacity(storage_view.capacity); + Pointer construct_data = allocation_tx.Allocate(new_capacity); + Pointer last_ptr = construct_data + storage_view.size; // Construct new element. - AllocatorTraits::construct(*GetAllocPtr(), last_ptr, - std::forward(args)...); + AllocatorTraits::construct(GetAllocator(), last_ptr, + std::forward(args)...); // Move elements from old backing store to new backing store. ABSL_INTERNAL_TRY { - inlined_vector_internal::ConstructElements( - GetAllocPtr(), allocation_tx.GetData(), &move_values, - storage_view.size); + ConstructElements(GetAllocator(), allocation_tx.GetData(), move_values, + storage_view.size); } ABSL_INTERNAL_CATCH_ANY { - AllocatorTraits::destroy(*GetAllocPtr(), last_ptr); + AllocatorTraits::destroy(GetAllocator(), last_ptr); ABSL_INTERNAL_RETHROW; } // Destroy elements in old backing store. - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); + DestroyElements(GetAllocator(), storage_view.data, storage_view.size); DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + AcquireAllocatedData(allocation_tx); SetIsAllocated(); AddSize(1); return *last_ptr; } template -auto Storage::Erase(const_iterator from, const_iterator to) - -> iterator { - StorageView storage_view = MakeStorageView(); +auto Storage::Erase(ConstIterator from, ConstIterator to) + -> Iterator { + StorageView storage_view = MakeStorageView(); - size_type erase_size = std::distance(from, to); - size_type erase_index = - std::distance(const_iterator(storage_view.data), from); - size_type erase_end_index = erase_index + erase_size; + SizeType erase_size = std::distance(from, to); + SizeType erase_index = + std::distance(ConstIterator(storage_view.data), from); + SizeType erase_end_index = erase_index + erase_size; - IteratorValueAdapter move_values( - MoveIterator(storage_view.data + erase_end_index)); + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data + erase_end_index)); - inlined_vector_internal::AssignElements(storage_view.data + erase_index, - &move_values, - storage_view.size - erase_end_index); + AssignElements(storage_view.data + erase_index, move_values, + storage_view.size - erase_end_index); - inlined_vector_internal::DestroyElements( - GetAllocPtr(), storage_view.data + (storage_view.size - erase_size), - erase_size); + DestroyElements(GetAllocator(), + storage_view.data + (storage_view.size - erase_size), + erase_size); SubtractSize(erase_size); - return iterator(storage_view.data + erase_index); + return Iterator(storage_view.data + erase_index); } template -auto Storage::Reserve(size_type requested_capacity) -> void { - StorageView storage_view = MakeStorageView(); +auto Storage::Reserve(SizeType requested_capacity) -> void { + StorageView storage_view = MakeStorageView(); if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; - AllocationTransaction allocation_tx(GetAllocPtr()); + AllocationTransaction allocation_tx(GetAllocator()); - IteratorValueAdapter move_values( - MoveIterator(storage_view.data)); + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); - size_type new_capacity = + SizeType new_capacity = ComputeCapacity(storage_view.capacity, requested_capacity); - pointer new_data = allocation_tx.Allocate(new_capacity); + Pointer new_data = allocation_tx.Allocate(new_capacity); - inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data, - &move_values, storage_view.size); + ConstructElements(GetAllocator(), new_data, move_values, + storage_view.size); - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); + DestroyElements(GetAllocator(), storage_view.data, storage_view.size); DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + AcquireAllocatedData(allocation_tx); SetIsAllocated(); } @@ -853,41 +797,40 @@ auto Storage::ShrinkToFit() -> void { // May only be called on allocated instances! assert(GetIsAllocated()); - StorageView storage_view{GetAllocatedData(), GetSize(), - GetAllocatedCapacity()}; + StorageView storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; - AllocationTransaction allocation_tx(GetAllocPtr()); + AllocationTransaction allocation_tx(GetAllocator()); - IteratorValueAdapter move_values( - MoveIterator(storage_view.data)); + IteratorValueAdapter> move_values( + MoveIterator(storage_view.data)); - pointer construct_data; + Pointer construct_data; if (storage_view.size > GetInlinedCapacity()) { - size_type new_capacity = storage_view.size; + SizeType new_capacity = storage_view.size; construct_data = allocation_tx.Allocate(new_capacity); } else { construct_data = GetInlinedData(); } ABSL_INTERNAL_TRY { - inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, - &move_values, storage_view.size); + ConstructElements(GetAllocator(), construct_data, move_values, + storage_view.size); } ABSL_INTERNAL_CATCH_ANY { SetAllocatedData(storage_view.data, storage_view.capacity); ABSL_INTERNAL_RETHROW; } - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); + DestroyElements(GetAllocator(), storage_view.data, storage_view.size); - AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data, - storage_view.capacity); + AllocatorTraits::deallocate(GetAllocator(), storage_view.data, + storage_view.capacity); if (allocation_tx.DidAllocate()) { - AcquireAllocatedData(&allocation_tx); + AcquireAllocatedData(allocation_tx); } else { UnsetIsAllocated(); } @@ -905,38 +848,37 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { Storage* large_ptr = other_storage_ptr; if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); - for (size_type i = 0; i < small_ptr->GetSize(); ++i) { + for (SizeType i = 0; i < small_ptr->GetSize(); ++i) { swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); } - IteratorValueAdapter move_values( - MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); + IteratorValueAdapter> move_values( + MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); - inlined_vector_internal::ConstructElements( - large_ptr->GetAllocPtr(), - small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values, - large_ptr->GetSize() - small_ptr->GetSize()); + ConstructElements(large_ptr->GetAllocator(), + small_ptr->GetInlinedData() + small_ptr->GetSize(), + move_values, + large_ptr->GetSize() - small_ptr->GetSize()); - inlined_vector_internal::DestroyElements( - large_ptr->GetAllocPtr(), - large_ptr->GetInlinedData() + small_ptr->GetSize(), - large_ptr->GetSize() - small_ptr->GetSize()); + DestroyElements(large_ptr->GetAllocator(), + large_ptr->GetInlinedData() + small_ptr->GetSize(), + large_ptr->GetSize() - small_ptr->GetSize()); } else { Storage* allocated_ptr = this; Storage* inlined_ptr = other_storage_ptr; if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); - StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(), - allocated_ptr->GetSize(), - allocated_ptr->GetAllocatedCapacity()}; + StorageView allocated_storage_view{ + allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(), + allocated_ptr->GetAllocatedCapacity()}; - IteratorValueAdapter move_values( - MoveIterator(inlined_ptr->GetInlinedData())); + IteratorValueAdapter> move_values( + MoveIterator(inlined_ptr->GetInlinedData())); ABSL_INTERNAL_TRY { - inlined_vector_internal::ConstructElements( - inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(), - &move_values, inlined_ptr->GetSize()); + ConstructElements(inlined_ptr->GetAllocator(), + allocated_ptr->GetInlinedData(), move_values, + inlined_ptr->GetSize()); } ABSL_INTERNAL_CATCH_ANY { allocated_ptr->SetAllocatedData(allocated_storage_view.data, @@ -944,16 +886,15 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { ABSL_INTERNAL_RETHROW; } - inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(), - inlined_ptr->GetInlinedData(), - inlined_ptr->GetSize()); + DestroyElements(inlined_ptr->GetAllocator(), + inlined_ptr->GetInlinedData(), inlined_ptr->GetSize()); inlined_ptr->SetAllocatedData(allocated_storage_view.data, allocated_storage_view.capacity); } swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); - swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); + swap(GetAllocator(), other_storage_ptr->GetAllocator()); } // End ignore "array-bounds" and "maybe-uninitialized" diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel new file mode 100644 index 00000000..10b256d6 --- /dev/null +++ b/absl/profiling/BUILD.bazel @@ -0,0 +1,17 @@ +# Copyright 2021 The Abseil Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package(default_visibility = ["//visibility:private"]) + +licenses(["notice"]) diff --git a/absl/profiling/CMakeLists.txt b/absl/profiling/CMakeLists.txt new file mode 100644 index 00000000..3c37491e --- /dev/null +++ b/absl/profiling/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright 2021 The Abseil Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc index 776c2e18..3040b3a7 100644 --- a/absl/random/internal/randen_hwaes.cc +++ b/absl/random/internal/randen_hwaes.cc @@ -31,7 +31,7 @@ // a hardware accelerated implementation of randen, or whether it // will contain stubs that exit the process. #if ABSL_HAVE_ACCELERATED_AES -// The following plaforms have implemented RandenHwAws. +// The following plaforms have implemented RandenHwAes. #if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \ defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \ defined(ABSL_ARCH_AARCH64) diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index e9d72fa8..115705a2 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -689,13 +689,10 @@ void Cord::InlineRep::AppendArray(absl::string_view src, return; } - // Note: we don't concern ourselves if src aliases data stored in the - // inlined data of 'this', as we update the InlineData only at the end. - // We are going from an inline size to beyond inline size. Make the new size - // either double the inlined size, or the added size + 10%. - const size_t size1 = inline_length * 2 + src.size(); - const size_t size2 = inline_length + src.size() / 10; - rep = CordRepFlat::New(std::max(size1, size2)); + // Allocate flat to be a perfect fit on first append exceeding inlined size. + // Subsequent growth will use amortized growth until we reach maximum flat + // size. + rep = CordRepFlat::New(inline_length + src.size()); appended = std::min(src.size(), rep->flat()->Capacity() - inline_length); memcpy(rep->flat()->Data(), data_.as_chars(), inline_length); memcpy(rep->flat()->Data() + inline_length, src.data(), appended); diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index d0296338..06a7bd6c 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -1385,6 +1385,59 @@ TEST_P(CordTest, DiabolicalGrowth) { cord.EstimatedMemoryUsage()); } +// The following tests check support for >4GB cords in 64-bit binaries, and +// 2GB-4GB cords in 32-bit binaries. This function returns the large cord size +// that's appropriate for the binary. + +// Construct a huge cord with the specified valid prefix. +static absl::Cord MakeHuge(absl::string_view prefix) { + absl::Cord cord; + if (sizeof(size_t) > 4) { + // In 64-bit binaries, test 64-bit Cord support. + const size_t size = + static_cast(std::numeric_limits::max()) + 314; + cord.Append(absl::MakeCordFromExternal( + absl::string_view(prefix.data(), size), + [](absl::string_view s) { DoNothing(s, nullptr); })); + } else { + // Cords are limited to 32-bit lengths in 32-bit binaries. The following + // tests check for use of "signed int" to represent Cord length/offset. + // However absl::string_view does not allow lengths >= (1u<<31), so we need + // to append in two parts; + const size_t s1 = (1u << 31) - 1; + // For shorter cord, `Append` copies the data rather than allocating a new + // node. The threshold is currently set to 511, so `s2` needs to be bigger + // to not trigger the copy. + const size_t s2 = 600; + cord.Append(absl::MakeCordFromExternal( + absl::string_view(prefix.data(), s1), + [](absl::string_view s) { DoNothing(s, nullptr); })); + cord.Append(absl::MakeCordFromExternal( + absl::string_view("", s2), + [](absl::string_view s) { DoNothing(s, nullptr); })); + } + return cord; +} + +TEST_P(CordTest, HugeCord) { + absl::Cord cord = MakeHuge("huge cord"); + EXPECT_LE(cord.size(), cord.EstimatedMemoryUsage()); + EXPECT_GE(cord.size() + 100, cord.EstimatedMemoryUsage()); +} + +// Tests that Append() works ok when handed a self reference +TEST_P(CordTest, AppendSelf) { + // We run the test until data is ~16K + // This guarantees it covers small, medium and large data. + std::string control_data = "Abc"; + absl::Cord data(control_data); + while (control_data.length() < 0x4000) { + data.Append(data); + control_data.append(control_data); + ASSERT_EQ(control_data, data); + } +} + TEST_P(CordTest, MakeFragmentedCordFromInitializerList) { absl::Cord fragmented = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 303f4580..bbaa7934 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -23,7 +23,6 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/optimization.h" #include "absl/strings/internal/cord_internal.h" -#include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index 267cc0ef..b26cff66 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h @@ -100,7 +100,7 @@ class FormatSpecTemplate // We use the 'unavailable' attribute to give a better compiler error than // just 'method is deleted'. // To avoid checking the format twice, we just check that the format is - // constexpr. If is it valid, then the overload below will kick in. + // constexpr. If it is valid, then the overload below will kick in. // We add the template here to make this overload have lower priority. template FormatSpecTemplate(const char* s) // NOLINT diff --git a/create_lts.py b/create_lts.py index d5d7b28c..56170806 100755 --- a/create_lts.py +++ b/create_lts.py @@ -95,6 +95,13 @@ def main(argv): 'datestamp={} is not in the YYYYMMDD format'.format(datestamp)) # Replacement directives go here. + ReplaceStringsInFile( + 'absl/base/config.h', { + '#undef ABSL_LTS_RELEASE_VERSION': + '#define ABSL_LTS_RELEASE_VERSION {}'.format(datestamp), + '#undef ABSL_LTS_RELEASE_PATCH_LEVEL': + '#define ABSL_LTS_RELEASE_PATCH_LEVEL 0' + }) ReplaceStringsInFile( 'absl/base/options.h', { '#define ABSL_OPTION_USE_INLINE_NAMESPACE 0': -- cgit v1.2.3 From 6039dd95bb62e6072731929279e917c7be39369f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 2 Sep 2021 11:09:51 -0700 Subject: Export of internal Abseil changes -- 7507caf944f8eb98e55730e60e77f9060c572788 by Derek Mauro : Remove RunningOnValgrind() and ValgrindSlowdown() from dynamic_annotations.h These are defined by some sanitizer implementations and possibly Valgrind. Abseil decided to define them when the implementation was unavailable so that they could be unconditionally called, but we now consider this a mistake. Other libraries, including Python, have copied this method, leading to multiple definitions when these libraries are used together. In the unlikely case that code is using Abseil's definition, it is recommended that the call be guarded instead: ``` #if __has_feature(thread_sanitizer) extern "C" int RunningOnValgrind(); if (RunningOnValgrind()) ... #endif ``` Fixes #1011 PiperOrigin-RevId: 394501460 GitOrigin-RevId: 7507caf944f8eb98e55730e60e77f9060c572788 Change-Id: I5ddaf9a590c857d645461352a0f6fbccbb3d5584 --- absl/base/dynamic_annotations.h | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h index 03d7096b..3ea7c156 100644 --- a/absl/base/dynamic_annotations.h +++ b/absl/base/dynamic_annotations.h @@ -433,31 +433,6 @@ ABSL_NAMESPACE_END #endif -#ifdef __cplusplus -#ifdef ABSL_HAVE_THREAD_SANITIZER -ABSL_INTERNAL_BEGIN_EXTERN_C -int RunningOnValgrind(); -double ValgrindSlowdown(); -ABSL_INTERNAL_END_EXTERN_C -#else -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { -ABSL_DEPRECATED( - "Don't use this interface. It is misleading and is being deleted.") -ABSL_ATTRIBUTE_ALWAYS_INLINE inline int RunningOnValgrind() { return 0; } -ABSL_DEPRECATED( - "Don't use this interface. It is misleading and is being deleted.") -ABSL_ATTRIBUTE_ALWAYS_INLINE inline double ValgrindSlowdown() { return 1.0; } -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -using absl::base_internal::RunningOnValgrind; -using absl::base_internal::ValgrindSlowdown; -#endif -#endif - // ------------------------------------------------------------------------- // Address sanitizer annotations -- cgit v1.2.3 From 8c800bb08d353c73d7dd68538388cf3bd2bcc6d0 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 7 Sep 2021 06:10:15 -0700 Subject: Export of internal Abseil changes -- 6eac0cc7bca997ee95afd4ec485077ae8fd99333 by Abseil Team : Add comment not to use `ABSL_ATTRIBUTE_PACKED` with `std::atomic`. PiperOrigin-RevId: 395231460 GitOrigin-RevId: 6eac0cc7bca997ee95afd4ec485077ae8fd99333 Change-Id: Ib4e83bed0f313724b309b6278e9e24a6e5fe9b2c --- absl/base/attributes.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 52139556..2665d8f3 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -548,13 +548,19 @@ // ABSL_ATTRIBUTE_PACKED // // Instructs the compiler not to use natural alignment for a tagged data -// structure, but instead to reduce its alignment to 1. This attribute can -// either be applied to members of a structure or to a structure in its -// entirety. Applying this attribute (judiciously) to a structure in its -// entirety to optimize the memory footprint of very commonly-used structs is -// fine. Do not apply this attribute to a structure in its entirety if the -// purpose is to control the offsets of the members in the structure. Instead, -// apply this attribute only to structure members that need it. +// structure, but instead to reduce its alignment to 1. +// +// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// so can cause atomic variables to be mis-aligned and silently violate +// atomicity on x86. +// +// This attribute can either be applied to members of a structure or to a +// structure in its entirety. Applying this attribute (judiciously) to a +// structure in its entirety to optimize the memory footprint of very +// commonly-used structs is fine. Do not apply this attribute to a structure in +// its entirety if the purpose is to control the offsets of the members in the +// structure. Instead, apply this attribute only to structure members that need +// it. // // When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the // natural alignment of structure members not annotated is preserved. Aligned -- cgit v1.2.3 From c22c032a353b5dc16d86ddc879e628344e591e77 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 9 Sep 2021 13:41:40 -0700 Subject: Export of internal Abseil changes -- 4fd27d1c0fda72a8772f2779714c327b18ae657b by Derek Mauro : Adds missing documentation for btree's lower_bound and upper_bound methods PiperOrigin-RevId: 395777262 GitOrigin-RevId: 4fd27d1c0fda72a8772f2779714c327b18ae657b Change-Id: Ife2f9149bcf30523be93ee9d8919c7085a9d3a29 --- absl/container/btree_map.h | 72 ++++++++++++++++++++++++++++++++++++++-------- absl/container/btree_set.h | 70 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index ea49d446..6bbf414e 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -366,8 +366,8 @@ class btree_map // Determines whether an element comparing equal to the given `key` exists // within the `btree_map`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::contains; // btree_map::count() @@ -378,8 +378,8 @@ class btree_map // the `btree_map`. Note that this function will return either `1` or `0` // since duplicate elements are not allowed within a `btree_map`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::count; // btree_map::equal_range() @@ -395,10 +395,34 @@ class btree_map // // Finds an element with the passed `key` within the `btree_map`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::find; + // btree_map::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element with a key that is not less than `key` within the + // `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_map::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element with a key that is greater than `key` within the + // `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_map::operator[]() // // Returns a reference to the value mapped to the passed key within the @@ -691,8 +715,8 @@ class btree_multimap // Determines whether an element comparing equal to the given `key` exists // within the `btree_multimap`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::contains; // btree_multimap::count() @@ -702,8 +726,8 @@ class btree_multimap // Returns the number of elements comparing equal to the given `key` within // the `btree_multimap`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::count; // btree_multimap::equal_range() @@ -720,10 +744,34 @@ class btree_multimap // // Finds an element with the passed `key` within the `btree_multimap`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::find; + // btree_multimap::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element with a key that is not less than `key` within the + // `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_multimap::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element with a key that is greater than `key` within the + // `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_multimap::get_allocator() // // Returns the allocator function associated with this `btree_multimap`. diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 21ef0a03..c07ccd91 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -300,8 +300,8 @@ class btree_set // Determines whether an element comparing equal to the given `key` exists // within the `btree_set`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::contains; // btree_set::count() @@ -312,8 +312,8 @@ class btree_set // the `btree_set`. Note that this function will return either `1` or `0` // since duplicate elements are not allowed within a `btree_set`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::count; // btree_set::equal_range() @@ -330,10 +330,32 @@ class btree_set // // Finds an element with the passed `key` within the `btree_set`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::find; + // btree_set::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element that is not less than `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_set::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element that is greater than `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_set::get_allocator() // // Returns the allocator function associated with this `btree_set`. @@ -604,8 +626,8 @@ class btree_multiset // Determines whether an element comparing equal to the given `key` exists // within the `btree_multiset`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::contains; // btree_multiset::count() @@ -615,8 +637,8 @@ class btree_multiset // Returns the number of elements comparing equal to the given `key` within // the `btree_multiset`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::count; // btree_multiset::equal_range() @@ -633,10 +655,34 @@ class btree_multiset // // Finds an element with the passed `key` within the `btree_multiset`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::find; + // btree_multiset::lower_bound() + // + // template iterator lower_bound(const K& key): + // template const_iterator lower_bound(const K& key) const: + // + // Finds the first element that is not less than `key` within the + // `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_multiset::upper_bound() + // + // template iterator upper_bound(const K& key): + // template const_iterator upper_bound(const K& key) const: + // + // Finds the first element that is greater than `key` within the + // `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_multiset::get_allocator() // // Returns the allocator function associated with this `btree_multiset`. -- cgit v1.2.3 From cfbf5bf948a2656bda7ddab59d3bcb29595c144c Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 10 Sep 2021 12:22:01 -0700 Subject: Export of internal Abseil changes -- 77e710b0ced5792a328e88bcb938a41484bf4cdc by Saleem Abdulrasool : absl: add an implementation for UnscaledCycleClock on RISCV Add an implementation for UnscaledCycleClock on RISC-V targets. PiperOrigin-RevId: 395982312 -- 84430fce6760c488ca36401cd530f44268ac710d by Martijn Vels : Harden CordRepBtreeReader against reading up to or beyond EOF This change hardens the reader to Next() calls on EOF situations. It changes the 'consumed()' property inside CordRepBtreeReader into a 'remaining()' property which is easier to understand and use than the 'consumed()' property QED the function documentation and use in cord.cc This change also adds the CharIterator test to the CordTest fixture enabling them to be run with btree cords. PiperOrigin-RevId: 395971732 -- 6557e628f2613169da8f693189223acb30e07833 by Martijn Vels : Add AdvanceAndRead() test addressing the edge case surfaced in b/197776822 This adds a test explicitly exercising all possible AdvanceAndRead() calls on CharIterator. As per the linked bug, a partial or full small read ending exactly at the end of the last edge of a btree cord results in an attempt to read beyond that last edge and subsequent failure. We will fix the bug and enable these tests for btree in a subsequent change. PiperOrigin-RevId: 395958317 GitOrigin-RevId: 77e710b0ced5792a328e88bcb938a41484bf4cdc Change-Id: Ie6e21ce36980515165af7cf046cf199ecbe0ddb0 --- absl/base/internal/unscaledcycleclock.cc | 12 +++++ absl/base/internal/unscaledcycleclock.h | 8 +-- absl/strings/cord_test.cc | 41 ++++++++++++-- absl/strings/internal/cord_rep_btree_reader.cc | 6 +-- absl/strings/internal/cord_rep_btree_reader.h | 58 +++++++++----------- .../strings/internal/cord_rep_btree_reader_test.cc | 62 ++++++++++++---------- 6 files changed, 117 insertions(+), 70 deletions(-) diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index 1545288c..fc07e300 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -119,6 +119,18 @@ double UnscaledCycleClock::Frequency() { return aarch64_timer_frequency; } +#elif defined(__riscv) + +int64_t UnscaledCycleClock::Now() { + int64_t virtual_timer_value; + asm volatile("rdcycle %0" : "=r"(virtual_timer_value)); + return virtual_timer_value; +} + +double UnscaledCycleClock::Frequency() { + return base_internal::NominalCPUFrequency(); +} + #elif defined(_M_IX86) || defined(_M_X64) #pragma intrinsic(__rdtsc) diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index 82f2c87a..681ff8f9 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -46,8 +46,8 @@ // The following platforms have an implementation of a hardware counter. #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ - defined(__powerpc__) || defined(__ppc__) || \ - defined(_M_IX86) || defined(_M_X64) + defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ + defined(_M_IX86) || defined(_M_X64) #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 #else #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 @@ -80,8 +80,8 @@ // This macro can be used to test if UnscaledCycleClock::Frequency() // is NominalCPUFrequency() on a particular platform. -#if (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) +#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \ + defined(_M_IX86) || defined(_M_X64)) #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY #endif diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 06a7bd6c..50079b7c 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -1591,7 +1591,7 @@ TEST_P(CordTest, CordChunkIteratorOperations) { VerifyChunkIterator(subcords, 128); } -TEST(CordCharIterator, Traits) { +TEST_P(CordTest, CharIteratorTraits) { static_assert(std::is_copy_constructible::value, ""); static_assert(std::is_copy_assignable::value, ""); @@ -1700,7 +1700,7 @@ static void VerifyCharIterator(const absl::Cord& cord) { } } -TEST(CordCharIterator, Operations) { +TEST_P(CordTest, CharIteratorOperations) { absl::Cord empty_cord; VerifyCharIterator(empty_cord); @@ -1729,6 +1729,41 @@ TEST(CordCharIterator, Operations) { VerifyCharIterator(subcords); } +TEST_P(CordTest, CharIteratorAdvanceAndRead) { + // Create a Cord holding 6 flats of 2500 bytes each, and then iterate over it + // reading 150, 1500, 2500 and 3000 bytes. This will result in all possible + // partial, full and straddled read combinations including reads below + // kMaxBytesToCopy. b/197776822 surfaced a bug for a specific partial, small + // read 'at end' on Cord which caused a failure on attempting to read past the + // end in CordRepBtreeReader which was not covered by any existing test. + constexpr int kBlocks = 6; + constexpr size_t kBlockSize = 2500; + constexpr size_t kChunkSize1 = 1500; + constexpr size_t kChunkSize2 = 2500; + constexpr size_t kChunkSize3 = 3000; + constexpr size_t kChunkSize4 = 150; + RandomEngine rng; + std::string data = RandomLowercaseString(&rng, kBlocks * kBlockSize); + absl::Cord cord; + for (int i = 0; i < kBlocks; ++i) { + const std::string block = data.substr(i * kBlockSize, kBlockSize); + cord.Append(absl::Cord(block)); + } + + for (size_t chunk_size : + {kChunkSize1, kChunkSize2, kChunkSize3, kChunkSize4}) { + absl::Cord::CharIterator it = cord.char_begin(); + size_t offset = 0; + while (offset < data.length()) { + const size_t n = std::min(data.length() - offset, chunk_size); + absl::Cord chunk = cord.AdvanceAndRead(&it, n); + ASSERT_EQ(chunk.size(), n); + ASSERT_EQ(chunk.Compare(data.substr(offset, n)), 0); + offset += n; + } + } +} + TEST_P(CordTest, StreamingOutput) { absl::Cord c = absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."}); @@ -1778,7 +1813,7 @@ TEST_P(CordTest, Format) { EXPECT_EQ(c, "There were 0003 little pigs.And 1 bad wolf!"); } -TEST(CordDeathTest, Hardening) { +TEST_P(CordTest, Hardening) { absl::Cord cord("hello"); // These statement should abort the program in all builds modes. EXPECT_DEATH_IF_SUPPORTED(cord.RemovePrefix(6), ""); diff --git a/absl/strings/internal/cord_rep_btree_reader.cc b/absl/strings/internal/cord_rep_btree_reader.cc index 3ba43144..5dc76966 100644 --- a/absl/strings/internal/cord_rep_btree_reader.cc +++ b/absl/strings/internal/cord_rep_btree_reader.cc @@ -52,14 +52,14 @@ absl::string_view CordRepBtreeReader::Read(size_t n, size_t chunk_size, // data, calling `navigator_.Current()` is not safe before checking if we // already consumed all remaining data. const size_t consumed_by_read = n - chunk_size - result.n; - if (consumed_ + consumed_by_read >= length()) { - consumed_ = length(); + if (consumed_by_read >= remaining_) { + remaining_ = 0; return {}; } // We did not read all data, return remaining data from current edge. edge = navigator_.Current(); - consumed_ += consumed_by_read + edge->length; + remaining_ -= consumed_by_read + edge->length; return CordRepBtree::EdgeData(edge).substr(result.n); } diff --git a/absl/strings/internal/cord_rep_btree_reader.h b/absl/strings/internal/cord_rep_btree_reader.h index c19fa43d..66e97f5d 100644 --- a/absl/strings/internal/cord_rep_btree_reader.h +++ b/absl/strings/internal/cord_rep_btree_reader.h @@ -31,9 +31,7 @@ namespace cord_internal { // References to the underlying data are returned as absl::string_view values. // The most typical use case is a forward only iteration over tree data. // The class also provides `Skip()`, `Seek()` and `Read()` methods similar to -// CordRepBtreeNavigator that allow more advanced navigation. The class provides -// a `consumed` property which contains the end offset of the chunk last -// returned to the user which is useful in cord iteration logic. +// CordRepBtreeNavigator that allow more advanced navigation. // // Example: iterate over all data inside a cord btree: // @@ -61,9 +59,9 @@ namespace cord_internal { // absl::string_view sv = reader.Next(); // } // -// It is important to notice that `consumed` represents the end position of the -// last data edge returned to the caller, not the cumulative data returned to -// the caller which can be less in cases of skipping or seeking over data. +// It is important to notice that `remaining` is based on the end position of +// the last data edge returned to the caller, not the cumulative data returned +// to the caller which can be less in cases of skipping or seeking over data. // // For example, consider a cord btree with five data edges: "abc", "def", "ghi", // "jkl" and "mno": @@ -71,14 +69,12 @@ namespace cord_internal { // absl::string_view sv; // CordRepBtreeReader reader; // -// sv = reader.Init(tree); // sv = "abc", reader.consumed() = 3 -// sv = reader.Skip(4); // sv = "hi", reader.consumed() = 9 -// sv = reader.Skip(2); // sv = "l", reader.consumed() = 12 -// sv = reader.Next(); // sv = "mno", reader.consumed() = 15 +// sv = reader.Init(tree); // sv = "abc", remaining = 12 +// sv = reader.Skip(4); // sv = "hi", remaining = 6 +// sv = reader.Skip(2); // sv = "l", remaining = 3 +// sv = reader.Next(); // sv = "mno", remaining = 0 +// sv = reader.Seek(1); // sv = "bc", remaining = 12 // -// In the above example, `reader.consumed()` reflects the data edges iterated -// over or skipped by the reader, not the amount of data 'consumed' by the -// caller. class CordRepBtreeReader { public: using ReadResult = CordRepBtreeNavigator::ReadResult; @@ -98,13 +94,14 @@ class CordRepBtreeReader { // Requires that the current instance is not empty. size_t length() const; - // Returns the end offset of the last navigated to chunk, which represents the - // total bytes 'consumed' relative to the start of the tree. The returned - // value is never zero. For example, initializing a reader with a tree with a - // first data edge of 19 bytes will return `consumed() = 19`. See also the - // class comments on the meaning of `consumed`. - // Requires that the current instance is not empty. - size_t consumed() const; + // Returns the number of remaining bytes available for iteration, which is the + // number of bytes directly following the end of the last chunk returned. + // This value will be zero if we iterated over the last edge in the bound + // tree, in which case any call to Next() or Skip() will return an empty + // string_view reflecting the EOF state. + // Note that a call to `Seek()` resets `remaining` to a value based on the + // end position of the chunk returned by that call. + size_t remaining() const { return remaining_; } // Resets this instance to an empty value. void Reset() { navigator_.Reset(); } @@ -157,7 +154,7 @@ class CordRepBtreeReader { absl::string_view Seek(size_t offset); private: - size_t consumed_; + size_t remaining_ = 0; CordRepBtreeNavigator navigator_; }; @@ -166,23 +163,18 @@ inline size_t CordRepBtreeReader::length() const { return btree()->length; } -inline size_t CordRepBtreeReader::consumed() const { - assert(btree() != nullptr); - return consumed_; -} - inline absl::string_view CordRepBtreeReader::Init(CordRepBtree* tree) { assert(tree != nullptr); const CordRep* edge = navigator_.InitFirst(tree); - consumed_ = edge->length; + remaining_ = tree->length - edge->length;; return CordRepBtree::EdgeData(edge); } inline absl::string_view CordRepBtreeReader::Next() { - assert(consumed() < length()); + if (remaining_ == 0) return {}; const CordRep* edge = navigator_.Next(); assert(edge != nullptr); - consumed_ += edge->length; + remaining_ -= edge->length; return CordRepBtree::EdgeData(edge); } @@ -192,23 +184,23 @@ inline absl::string_view CordRepBtreeReader::Skip(size_t skip) { const size_t edge_length = navigator_.Current()->length; CordRepBtreeNavigator::Position pos = navigator_.Skip(skip + edge_length); if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { - consumed_ = length(); + remaining_ = 0; return {}; } // The combined length of all edges skipped before `pos.edge` is `skip - // pos.offset`, all of which are 'consumed', as well as the current edge. - consumed_ += skip - pos.offset + pos.edge->length; + remaining_ -= skip - pos.offset + pos.edge->length; return CordRepBtree::EdgeData(pos.edge).substr(pos.offset); } inline absl::string_view CordRepBtreeReader::Seek(size_t offset) { const CordRepBtreeNavigator::Position pos = navigator_.Seek(offset); if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { - consumed_ = length(); + remaining_ = 0; return {}; } absl::string_view chunk = CordRepBtree::EdgeData(pos.edge).substr(pos.offset); - consumed_ = offset + chunk.length(); + remaining_ = length() - offset - chunk.length(); return chunk; } diff --git a/absl/strings/internal/cord_rep_btree_reader_test.cc b/absl/strings/internal/cord_rep_btree_reader_test.cc index 44d3365f..9b27a81f 100644 --- a/absl/strings/internal/cord_rep_btree_reader_test.cc +++ b/absl/strings/internal/cord_rep_btree_reader_test.cc @@ -58,22 +58,26 @@ TEST(CordRepBtreeReaderTest, Next) { CordRepBtree* node = CordRepBtreeFromFlats(flats); CordRepBtreeReader reader; + size_t remaining = data.length(); absl::string_view chunk = reader.Init(node); EXPECT_THAT(chunk, Eq(data.substr(0, chunk.length()))); - size_t consumed = chunk.length(); - EXPECT_THAT(reader.consumed(), Eq(consumed)); + remaining -= chunk.length(); + EXPECT_THAT(reader.remaining(), Eq(remaining)); - while (consumed < data.length()) { + while (remaining > 0) { + const size_t offset = data.length() - remaining; chunk = reader.Next(); - EXPECT_THAT(chunk, Eq(data.substr(consumed, chunk.length()))); + EXPECT_THAT(chunk, Eq(data.substr(offset, chunk.length()))); - consumed += chunk.length(); - EXPECT_THAT(reader.consumed(), Eq(consumed)); + remaining -= chunk.length(); + EXPECT_THAT(reader.remaining(), Eq(remaining)); } - EXPECT_THAT(consumed, Eq(data.length())); - EXPECT_THAT(reader.consumed(), Eq(data.length())); + EXPECT_THAT(reader.remaining(), Eq(0)); + + // Verify trying to read beyond EOF returns empty string_view + EXPECT_THAT(reader.Next(), testing::IsEmpty()); CordRep::Unref(node); } @@ -92,19 +96,22 @@ TEST(CordRepBtreeReaderTest, Skip) { for (size_t skip1 = 0; skip1 < data.length() - kChars; ++skip1) { for (size_t skip2 = 0; skip2 < data.length() - kChars; ++skip2) { CordRepBtreeReader reader; + size_t remaining = data.length(); absl::string_view chunk = reader.Init(node); - size_t consumed = chunk.length(); + remaining -= chunk.length(); chunk = reader.Skip(skip1); - ASSERT_THAT(chunk, Eq(data.substr(consumed + skip1, chunk.length()))); - consumed += chunk.length() + skip1; - ASSERT_THAT(reader.consumed(), Eq(consumed)); + size_t offset = data.length() - remaining; + ASSERT_THAT(chunk, Eq(data.substr(offset + skip1, chunk.length()))); + remaining -= chunk.length() + skip1; + ASSERT_THAT(reader.remaining(), Eq(remaining)); - if (consumed >= data.length()) continue; + if (remaining == 0) continue; - size_t skip = std::min(data.length() - consumed - 1, skip2); + size_t skip = std::min(remaining - 1, skip2); chunk = reader.Skip(skip); - ASSERT_THAT(chunk, Eq(data.substr(consumed + skip, chunk.length()))); + offset = data.length() - remaining; + ASSERT_THAT(chunk, Eq(data.substr(offset + skip, chunk.length()))); } } @@ -118,7 +125,7 @@ TEST(CordRepBtreeReaderTest, SkipBeyondLength) { CordRepBtreeReader reader; reader.Init(tree); EXPECT_THAT(reader.Skip(100), IsEmpty()); - EXPECT_THAT(reader.consumed(), Eq(6)); + EXPECT_THAT(reader.remaining(), Eq(0)); CordRep::Unref(tree); } @@ -138,7 +145,8 @@ TEST(CordRepBtreeReaderTest, Seek) { absl::string_view chunk = reader.Seek(seek); ASSERT_THAT(chunk, Not(IsEmpty())); ASSERT_THAT(chunk, Eq(data.substr(seek, chunk.length()))); - ASSERT_THAT(reader.consumed(), Eq(seek + chunk.length())); + ASSERT_THAT(reader.remaining(), + Eq(data.length() - seek - chunk.length())); } CordRep::Unref(node); @@ -151,9 +159,9 @@ TEST(CordRepBtreeReaderTest, SeekBeyondLength) { CordRepBtreeReader reader; reader.Init(tree); EXPECT_THAT(reader.Seek(6), IsEmpty()); - EXPECT_THAT(reader.consumed(), Eq(6)); + EXPECT_THAT(reader.remaining(), Eq(0)); EXPECT_THAT(reader.Seek(100), IsEmpty()); - EXPECT_THAT(reader.consumed(), Eq(6)); + EXPECT_THAT(reader.remaining(), Eq(0)); CordRep::Unref(tree); } @@ -171,7 +179,7 @@ TEST(CordRepBtreeReaderTest, Read) { chunk = reader.Read(0, chunk.length(), tree); EXPECT_THAT(tree, Eq(nullptr)); EXPECT_THAT(chunk, Eq("abcde")); - EXPECT_THAT(reader.consumed(), Eq(5)); + EXPECT_THAT(reader.remaining(), Eq(10)); EXPECT_THAT(reader.Next(), Eq("fghij")); // Read in full @@ -180,7 +188,7 @@ TEST(CordRepBtreeReaderTest, Read) { EXPECT_THAT(tree, Ne(nullptr)); EXPECT_THAT(CordToString(tree), Eq("abcdefghijklmno")); EXPECT_THAT(chunk, Eq("")); - EXPECT_THAT(reader.consumed(), Eq(15)); + EXPECT_THAT(reader.remaining(), Eq(0)); CordRep::Unref(tree); // Read < chunk bytes @@ -189,7 +197,7 @@ TEST(CordRepBtreeReaderTest, Read) { ASSERT_THAT(tree, Ne(nullptr)); EXPECT_THAT(CordToString(tree), Eq("abc")); EXPECT_THAT(chunk, Eq("de")); - EXPECT_THAT(reader.consumed(), Eq(5)); + EXPECT_THAT(reader.remaining(), Eq(10)); EXPECT_THAT(reader.Next(), Eq("fghij")); CordRep::Unref(tree); @@ -199,7 +207,7 @@ TEST(CordRepBtreeReaderTest, Read) { ASSERT_THAT(tree, Ne(nullptr)); EXPECT_THAT(CordToString(tree), Eq("cd")); EXPECT_THAT(chunk, Eq("e")); - EXPECT_THAT(reader.consumed(), Eq(5)); + EXPECT_THAT(reader.remaining(), Eq(10)); EXPECT_THAT(reader.Next(), Eq("fghij")); CordRep::Unref(tree); @@ -209,7 +217,7 @@ TEST(CordRepBtreeReaderTest, Read) { ASSERT_THAT(tree, Ne(nullptr)); EXPECT_THAT(CordToString(tree), Eq("fgh")); EXPECT_THAT(chunk, Eq("ij")); - EXPECT_THAT(reader.consumed(), Eq(10)); + EXPECT_THAT(reader.remaining(), Eq(5)); EXPECT_THAT(reader.Next(), Eq("klmno")); CordRep::Unref(tree); @@ -219,7 +227,7 @@ TEST(CordRepBtreeReaderTest, Read) { ASSERT_THAT(tree, Ne(nullptr)); EXPECT_THAT(CordToString(tree), Eq("cdefghijklmn")); EXPECT_THAT(chunk, Eq("o")); - EXPECT_THAT(reader.consumed(), Eq(15)); + EXPECT_THAT(reader.remaining(), Eq(0)); CordRep::Unref(tree); // Read across chunks landing on exact edge boundary @@ -228,7 +236,7 @@ TEST(CordRepBtreeReaderTest, Read) { ASSERT_THAT(tree, Ne(nullptr)); EXPECT_THAT(CordToString(tree), Eq("cdefghij")); EXPECT_THAT(chunk, Eq("klmno")); - EXPECT_THAT(reader.consumed(), Eq(15)); + EXPECT_THAT(reader.remaining(), Eq(0)); CordRep::Unref(tree); CordRep::Unref(node); @@ -264,7 +272,7 @@ TEST(CordRepBtreeReaderTest, ReadExhaustive) { consumed += n; remaining -= n; - EXPECT_THAT(reader.consumed(), Eq(consumed + chunk.length())); + EXPECT_THAT(reader.remaining(), Eq(remaining - chunk.length())); if (remaining > 0) { ASSERT_FALSE(chunk.empty()); -- cgit v1.2.3 From 669184b4f33421037dae51f87a15794198566295 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 13 Sep 2021 17:05:29 -0700 Subject: Include immintrin.h instead of wmmintrin.h (#1015) immintrin.h is the de-factor standard header for clang and GCC to include Intel intrinsics. Using this header avoids requiring the compiler to use the `-maes` and `-msse4.1` compiler options on systems that may not have AES or SSE instruction support. clang: As seen in https://github.com/llvm-mirror/clang/blob/master/lib/Headers/immintrin.h, specific intrinsic header files are conditionally included depending on whether the feature is available. gcc: As seen in https://github.com/gcc-mirror/gcc/blob/master/gcc/config/i386/immintrin.h, gcc includes all intrinsic header files, but each individual file guards against the feature not being available. This came out of an investigation in https://github.com/grpc/grpc/pull/27121. --- absl/random/internal/randen_hwaes.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc index 3040b3a7..fee6677c 100644 --- a/absl/random/internal/randen_hwaes.cc +++ b/absl/random/internal/randen_hwaes.cc @@ -211,7 +211,7 @@ inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {} #elif defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) // On x86 we rely on the aesni instructions -#include +#include namespace { -- cgit v1.2.3 From b2dc72c17ac663885b62334d334da9f8970543b5 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 13 Sep 2021 17:04:32 -0700 Subject: Export of internal Abseil changes -- ca3a0009e675b699b5d6dd41f00ebac0e7d1935c by Derek Mauro : Internal change PiperOrigin-RevId: 396475923 -- 04d9fff79085bb18612af3da49007907394ae0b6 by Abseil Team : Move HastablezSampler from container/internal to profiling/internal. PiperOrigin-RevId: 396362093 GitOrigin-RevId: ca3a0009e675b699b5d6dd41f00ebac0e7d1935c Change-Id: I42d6d2944786afa24259fde002fed5e611f4e1f9 --- CMake/AbseilDll.cmake | 2 + absl/container/BUILD.bazel | 2 + absl/container/CMakeLists.txt | 1 + absl/container/internal/hashtablez_sampler.cc | 104 +--------- absl/container/internal/hashtablez_sampler.h | 81 +------- absl/container/internal/hashtablez_sampler_test.cc | 3 +- absl/container/internal/raw_hash_set_test.cc | 4 +- absl/profiling/BUILD.bazel | 34 +++ absl/profiling/CMakeLists.txt | 25 +++ absl/profiling/internal/sample_recorder.h | 231 +++++++++++++++++++++ absl/profiling/internal/sample_recorder_test.cc | 171 +++++++++++++++ 11 files changed, 482 insertions(+), 176 deletions(-) create mode 100644 absl/profiling/internal/sample_recorder.h create mode 100644 absl/profiling/internal/sample_recorder_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index ea45c8a2..056343e5 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -133,6 +133,7 @@ set(ABSL_INTERNAL_DLL_FILES "numeric/int128.h" "numeric/internal/bits.h" "numeric/internal/representation.h" + "profiling/internal/sample_recorder.h" "random/bernoulli_distribution.h" "random/beta_distribution.h" "random/bit_gen_ref.h" @@ -457,6 +458,7 @@ set(ABSL_INTERNAL_DLL_TARGETS "raw_hash_set" "layout" "tracked" + "sample_recorder" ) function(absl_internal_dll_contains) diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index cf55f4bb..c9d387d6 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -513,6 +513,7 @@ cc_library( "//absl/base:exponential_biased", "//absl/debugging:stacktrace", "//absl/memory", + "//absl/profiling:sample_recorder", "//absl/synchronization", "//absl/utility", ], @@ -526,6 +527,7 @@ cc_test( ":hashtablez_sampler", ":have_sse", "//absl/base:core_headers", + "//absl/profiling:sample_recorder", "//absl/synchronization", "//absl/synchronization:thread_pool", "//absl/time", diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 691a05c0..9b8a7509 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -548,6 +548,7 @@ absl_cc_library( absl::base absl::exponential_biased absl::have_sse + absl::sample_recorder absl::synchronization ) diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 5a29bed7..ca03d9b6 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -25,6 +25,7 @@ #include "absl/container/internal/have_sse.h" #include "absl/debugging/stacktrace.h" #include "absl/memory/memory.h" +#include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/mutex.h" namespace absl { @@ -37,7 +38,6 @@ ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ false }; ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; -ABSL_CONST_INIT std::atomic g_hashtablez_max_samples{1 << 20}; #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased @@ -50,16 +50,11 @@ ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -HashtablezSampler& HashtablezSampler::Global() { +HashtablezSampler& GlobalHashtablezSampler() { static auto* sampler = new HashtablezSampler(); return *sampler; } -HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback( - DisposeCallback f) { - return dispose_.exchange(f, std::memory_order_relaxed); -} - HashtablezInfo::HashtablezInfo() { PrepareForSampling(); } HashtablezInfo::~HashtablezInfo() = default; @@ -80,93 +75,6 @@ void HashtablezInfo::PrepareForSampling() { // instead. depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, /* skip_count= */ 0); - dead = nullptr; -} - -HashtablezSampler::HashtablezSampler() - : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) { - absl::MutexLock l(&graveyard_.init_mu); - graveyard_.dead = &graveyard_; -} - -HashtablezSampler::~HashtablezSampler() { - HashtablezInfo* s = all_.load(std::memory_order_acquire); - while (s != nullptr) { - HashtablezInfo* next = s->next; - delete s; - s = next; - } -} - -void HashtablezSampler::PushNew(HashtablezInfo* sample) { - sample->next = all_.load(std::memory_order_relaxed); - while (!all_.compare_exchange_weak(sample->next, sample, - std::memory_order_release, - std::memory_order_relaxed)) { - } -} - -void HashtablezSampler::PushDead(HashtablezInfo* sample) { - if (auto* dispose = dispose_.load(std::memory_order_relaxed)) { - dispose(*sample); - } - - absl::MutexLock graveyard_lock(&graveyard_.init_mu); - absl::MutexLock sample_lock(&sample->init_mu); - sample->dead = graveyard_.dead; - graveyard_.dead = sample; -} - -HashtablezInfo* HashtablezSampler::PopDead() { - absl::MutexLock graveyard_lock(&graveyard_.init_mu); - - // The list is circular, so eventually it collapses down to - // graveyard_.dead == &graveyard_ - // when it is empty. - HashtablezInfo* sample = graveyard_.dead; - if (sample == &graveyard_) return nullptr; - - absl::MutexLock sample_lock(&sample->init_mu); - graveyard_.dead = sample->dead; - sample->PrepareForSampling(); - return sample; -} - -HashtablezInfo* HashtablezSampler::Register() { - int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); - if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) { - size_estimate_.fetch_sub(1, std::memory_order_relaxed); - dropped_samples_.fetch_add(1, std::memory_order_relaxed); - return nullptr; - } - - HashtablezInfo* sample = PopDead(); - if (sample == nullptr) { - // Resurrection failed. Hire a new warlock. - sample = new HashtablezInfo(); - PushNew(sample); - } - - return sample; -} - -void HashtablezSampler::Unregister(HashtablezInfo* sample) { - PushDead(sample); - size_estimate_.fetch_sub(1, std::memory_order_relaxed); -} - -int64_t HashtablezSampler::Iterate( - const std::function& f) { - HashtablezInfo* s = all_.load(std::memory_order_acquire); - while (s != nullptr) { - absl::MutexLock l(&s->init_mu); - if (s->dead == nullptr) { - f(*s); - } - s = s->next; - } - - return dropped_samples_.load(std::memory_order_relaxed); } static bool ShouldForceSampling() { @@ -192,7 +100,7 @@ static bool ShouldForceSampling() { HashtablezInfo* SampleSlow(int64_t* next_sample) { if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; - return HashtablezSampler::Global().Register(); + return GlobalHashtablezSampler().Register(); } #if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) @@ -217,12 +125,12 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { return SampleSlow(next_sample); } - return HashtablezSampler::Global().Register(); + return GlobalHashtablezSampler().Register(); #endif } void UnsampleSlow(HashtablezInfo* info) { - HashtablezSampler::Global().Unregister(info); + GlobalHashtablezSampler().Unregister(info); } void RecordInsertSlow(HashtablezInfo* info, size_t hash, @@ -262,7 +170,7 @@ void SetHashtablezSampleParameter(int32_t rate) { void SetHashtablezMaxSamples(int32_t max) { if (max > 0) { - g_hashtablez_max_samples.store(max, std::memory_order_release); + GlobalHashtablezSampler().SetMaxSamples(max); } else { ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld", static_cast(max)); // NOLINT(runtime/int) diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 85685f72..d86207f5 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -47,6 +47,7 @@ #include "absl/base/internal/per_thread_tls.h" #include "absl/base/optimization.h" #include "absl/container/internal/have_sse.h" +#include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/mutex.h" #include "absl/utility/utility.h" @@ -57,7 +58,7 @@ namespace container_internal { // Stores information about a sampled hashtable. All mutations to this *must* // be made through `Record*` functions below. All reads from this *must* only // occur in the callback to `HashtablezSampler::Iterate`. -struct HashtablezInfo { +struct HashtablezInfo : public profiling_internal::Sample { // Constructs the object but does not fill in any fields. HashtablezInfo(); ~HashtablezInfo(); @@ -80,14 +81,6 @@ struct HashtablezInfo { std::atomic hashes_bitwise_and; std::atomic hashes_bitwise_xor; - // `HashtablezSampler` maintains intrusive linked lists for all samples. See - // comments on `HashtablezSampler::all_` for details on these. `init_mu` - // guards the ability to restore the sample to a pristine state. This - // prevents races with sampling and resurrecting an object. - absl::Mutex init_mu; - HashtablezInfo* next; - HashtablezInfo* dead ABSL_GUARDED_BY(init_mu); - // All of the fields below are set by `PrepareForSampling`, they must not be // mutated in `Record*` functions. They are logically `const` in that sense. // These are guarded by init_mu, but that is not externalized to clients, who @@ -231,73 +224,11 @@ inline HashtablezInfoHandle Sample() { #endif // !ABSL_PER_THREAD_TLS } -// Holds samples and their associated stack traces with a soft limit of -// `SetHashtablezMaxSamples()`. -// -// Thread safe. -class HashtablezSampler { - public: - // Returns a global Sampler. - static HashtablezSampler& Global(); - - HashtablezSampler(); - ~HashtablezSampler(); - - // Registers for sampling. Returns an opaque registration info. - HashtablezInfo* Register(); +using HashtablezSampler = + ::absl::profiling_internal::SampleRecorder; - // Unregisters the sample. - void Unregister(HashtablezInfo* sample); - - // The dispose callback will be called on all samples the moment they are - // being unregistered. Only affects samples that are unregistered after the - // callback has been set. - // Returns the previous callback. - using DisposeCallback = void (*)(const HashtablezInfo&); - DisposeCallback SetDisposeCallback(DisposeCallback f); - - // Iterates over all the registered `StackInfo`s. Returning the number of - // samples that have been dropped. - int64_t Iterate(const std::function& f); - - private: - void PushNew(HashtablezInfo* sample); - void PushDead(HashtablezInfo* sample); - HashtablezInfo* PopDead(); - - std::atomic dropped_samples_; - std::atomic size_estimate_; - - // Intrusive lock free linked lists for tracking samples. - // - // `all_` records all samples (they are never removed from this list) and is - // terminated with a `nullptr`. - // - // `graveyard_.dead` is a circular linked list. When it is empty, - // `graveyard_.dead == &graveyard`. The list is circular so that - // every item on it (even the last) has a non-null dead pointer. This allows - // `Iterate` to determine if a given sample is live or dead using only - // information on the sample itself. - // - // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead - // looks like this (G is the Graveyard): - // - // +---+ +---+ +---+ +---+ +---+ - // all -->| A |--->| B |--->| C |--->| D |--->| E | - // | | | | | | | | | | - // +---+ | | +->| |-+ | | +->| |-+ | | - // | G | +---+ | +---+ | +---+ | +---+ | +---+ - // | | | | | | - // | | --------+ +--------+ | - // +---+ | - // ^ | - // +--------------------------------------+ - // - std::atomic all_; - HashtablezInfo graveyard_; - - std::atomic dispose_; -}; +// Returns a global Sampler. +HashtablezSampler& GlobalHashtablezSampler(); // Enables or disables sampling for Swiss tables. void SetHashtablezEnabled(bool enabled); diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index 5f4c83b7..53fcfe6f 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -22,6 +22,7 @@ #include "gtest/gtest.h" #include "absl/base/attributes.h" #include "absl/container/internal/have_sse.h" +#include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/blocking_counter.h" #include "absl/synchronization/internal/thread_pool.h" #include "absl/synchronization/mutex.h" @@ -232,7 +233,7 @@ TEST(HashtablezSamplerTest, Sample) { } TEST(HashtablezSamplerTest, Handle) { - auto& sampler = HashtablezSampler::Global(); + auto& sampler = GlobalHashtablezSampler(); HashtablezInfoHandle h(sampler.Register()); auto* info = HashtablezInfoHandlePeer::GetInfo(&h); info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed); diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 4fb31fad..4012a3aa 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -2038,7 +2038,7 @@ TEST(RawHashSamplerTest, Sample) { SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); - auto& sampler = HashtablezSampler::Global(); + auto& sampler = GlobalHashtablezSampler(); size_t start_size = 0; std::unordered_set preexisting_info; start_size += sampler.Iterate([&](const HashtablezInfo& info) { @@ -2076,7 +2076,7 @@ TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) { SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); - auto& sampler = HashtablezSampler::Global(); + auto& sampler = GlobalHashtablezSampler(); size_t start_size = 0; start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; }); diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel index 10b256d6..5f3a1030 100644 --- a/absl/profiling/BUILD.bazel +++ b/absl/profiling/BUILD.bazel @@ -12,6 +12,40 @@ # See the License for the specific language governing permissions and # limitations under the License. +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", +) + package(default_visibility = ["//visibility:private"]) licenses(["notice"]) + +cc_library( + name = "sample_recorder", + hdrs = ["internal/sample_recorder.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl:__subpackages__"], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/synchronization", + "//absl/time", + ], +) + +cc_test( + name = "sample_recorder_test", + srcs = ["internal/sample_recorder_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":sample_recorder", + "//absl/base:core_headers", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/profiling/CMakeLists.txt b/absl/profiling/CMakeLists.txt index 3c37491e..7b6a7780 100644 --- a/absl/profiling/CMakeLists.txt +++ b/absl/profiling/CMakeLists.txt @@ -12,3 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +absl_cc_library( + NAME + sample_recorder + HDRS + "internal/sample_recorder.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::synchronization +) + +absl_cc_test( + NAME + sample_recorder_test + SRCS + "internal/sample_recorder_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::sample_recorder + absl::time + GTest::gmock_main +) + diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h new file mode 100644 index 00000000..a257ea5d --- /dev/null +++ b/absl/profiling/internal/sample_recorder.h @@ -0,0 +1,231 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: sample_recorder.h +// ----------------------------------------------------------------------------- +// +// This header file defines a lock-free linked list for recording samples +// collected from a random/stochastic process. +// +// This utility is internal-only. Use at your own risk. + +#ifndef ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_ +#define ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_ + +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +// Sample that has members required for linking samples in the linked list of +// samples maintained by the SampleRecorder. Type T defines the sampled data. +template +struct Sample { + public: + // Guards the ability to restore the sample to a pristine state. This + // prevents races with sampling and resurrecting an object. + absl::Mutex init_mu; + T* next = nullptr; + T* dead ABSL_GUARDED_BY(init_mu) = nullptr; +}; + +// Holds samples and their associated stack traces with a soft limit of +// `SetHashtablezMaxSamples()`. +// +// Thread safe. +template +class SampleRecorder { + public: + SampleRecorder(); + ~SampleRecorder(); + + // Registers for sampling. Returns an opaque registration info. + T* Register(); + + // Unregisters the sample. + void Unregister(T* sample); + + // The dispose callback will be called on all samples the moment they are + // being unregistered. Only affects samples that are unregistered after the + // callback has been set. + // Returns the previous callback. + using DisposeCallback = void (*)(const T&); + DisposeCallback SetDisposeCallback(DisposeCallback f); + + // Iterates over all the registered `StackInfo`s. Returning the number of + // samples that have been dropped. + int64_t Iterate(const std::function& f); + + void SetMaxSamples(int32_t max); + + private: + void PushNew(T* sample); + void PushDead(T* sample); + T* PopDead(); + + std::atomic dropped_samples_; + std::atomic size_estimate_; + std::atomic max_samples_{1 << 20}; + + // Intrusive lock free linked lists for tracking samples. + // + // `all_` records all samples (they are never removed from this list) and is + // terminated with a `nullptr`. + // + // `graveyard_.dead` is a circular linked list. When it is empty, + // `graveyard_.dead == &graveyard`. The list is circular so that + // every item on it (even the last) has a non-null dead pointer. This allows + // `Iterate` to determine if a given sample is live or dead using only + // information on the sample itself. + // + // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead + // looks like this (G is the Graveyard): + // + // +---+ +---+ +---+ +---+ +---+ + // all -->| A |--->| B |--->| C |--->| D |--->| E | + // | | | | | | | | | | + // +---+ | | +->| |-+ | | +->| |-+ | | + // | G | +---+ | +---+ | +---+ | +---+ | +---+ + // | | | | | | + // | | --------+ +--------+ | + // +---+ | + // ^ | + // +--------------------------------------+ + // + std::atomic all_; + T graveyard_; + + std::atomic dispose_; +}; + +template +typename SampleRecorder::DisposeCallback +SampleRecorder::SetDisposeCallback(DisposeCallback f) { + return dispose_.exchange(f, std::memory_order_relaxed); +} + +template +SampleRecorder::SampleRecorder() + : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) { + absl::MutexLock l(&graveyard_.init_mu); + graveyard_.dead = &graveyard_; +} + +template +SampleRecorder::~SampleRecorder() { + T* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + T* next = s->next; + delete s; + s = next; + } +} + +template +void SampleRecorder::PushNew(T* sample) { + sample->next = all_.load(std::memory_order_relaxed); + while (!all_.compare_exchange_weak(sample->next, sample, + std::memory_order_release, + std::memory_order_relaxed)) { + } +} + +template +void SampleRecorder::PushDead(T* sample) { + if (auto* dispose = dispose_.load(std::memory_order_relaxed)) { + dispose(*sample); + } + + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + absl::MutexLock sample_lock(&sample->init_mu); + sample->dead = graveyard_.dead; + graveyard_.dead = sample; +} + +template +T* SampleRecorder::PopDead() { + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + + // The list is circular, so eventually it collapses down to + // graveyard_.dead == &graveyard_ + // when it is empty. + T* sample = graveyard_.dead; + if (sample == &graveyard_) return nullptr; + + absl::MutexLock sample_lock(&sample->init_mu); + graveyard_.dead = sample->dead; + sample->dead = nullptr; + sample->PrepareForSampling(); + return sample; +} + +template +T* SampleRecorder::Register() { + int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); + if (size > max_samples_.load(std::memory_order_relaxed)) { + size_estimate_.fetch_sub(1, std::memory_order_relaxed); + dropped_samples_.fetch_add(1, std::memory_order_relaxed); + return nullptr; + } + + T* sample = PopDead(); + if (sample == nullptr) { + // Resurrection failed. Hire a new warlock. + sample = new T(); + PushNew(sample); + } + + return sample; +} + +template +void SampleRecorder::Unregister(T* sample) { + PushDead(sample); + size_estimate_.fetch_sub(1, std::memory_order_relaxed); +} + +template +int64_t SampleRecorder::Iterate( + const std::function& f) { + T* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + absl::MutexLock l(&s->init_mu); + if (s->dead == nullptr) { + f(*s); + } + s = s->next; + } + + return dropped_samples_.load(std::memory_order_relaxed); +} + +template +void SampleRecorder::SetMaxSamples(int32_t max) { + max_samples_.store(max, std::memory_order_release); +} + +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_ diff --git a/absl/profiling/internal/sample_recorder_test.cc b/absl/profiling/internal/sample_recorder_test.cc new file mode 100644 index 00000000..ec6e0fa2 --- /dev/null +++ b/absl/profiling/internal/sample_recorder_test.cc @@ -0,0 +1,171 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/profiling/internal/sample_recorder.h" + +#include +#include +#include + +#include "gmock/gmock.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +namespace { +using ::absl::synchronization_internal::ThreadPool; +using ::testing::IsEmpty; +using ::testing::UnorderedElementsAre; + +struct Info : public Sample { + public: + void PrepareForSampling() {} + std::atomic size; + absl::Time create_time; +}; + +std::vector GetSizes(SampleRecorder* s) { + std::vector res; + s->Iterate([&](const Info& info) { + res.push_back(info.size.load(std::memory_order_acquire)); + }); + return res; +} + +Info* Register(SampleRecorder* s, size_t size) { + auto* info = s->Register(); + assert(info != nullptr); + info->size.store(size); + return info; +} + +TEST(SampleRecorderTest, Registration) { + SampleRecorder sampler; + auto* info1 = Register(&sampler, 1); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1)); + + auto* info2 = Register(&sampler, 2); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2)); + info1->size.store(3); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2)); + + sampler.Unregister(info1); + sampler.Unregister(info2); +} + +TEST(SampleRecorderTest, Unregistration) { + SampleRecorder sampler; + std::vector infos; + for (size_t i = 0; i < 3; ++i) { + infos.push_back(Register(&sampler, i)); + } + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2)); + + sampler.Unregister(infos[1]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2)); + + infos.push_back(Register(&sampler, 3)); + infos.push_back(Register(&sampler, 4)); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4)); + sampler.Unregister(infos[3]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4)); + + sampler.Unregister(infos[0]); + sampler.Unregister(infos[2]); + sampler.Unregister(infos[4]); + EXPECT_THAT(GetSizes(&sampler), IsEmpty()); +} + +TEST(SampleRecorderTest, MultiThreaded) { + SampleRecorder sampler; + Notification stop; + ThreadPool pool(10); + + for (int i = 0; i < 10; ++i) { + pool.Schedule([&sampler, &stop]() { + std::random_device rd; + std::mt19937 gen(rd()); + + std::vector infoz; + while (!stop.HasBeenNotified()) { + if (infoz.empty()) { + infoz.push_back(sampler.Register()); + } + switch (std::uniform_int_distribution<>(0, 2)(gen)) { + case 0: { + infoz.push_back(sampler.Register()); + break; + } + case 1: { + size_t p = + std::uniform_int_distribution<>(0, infoz.size() - 1)(gen); + Info* info = infoz[p]; + infoz[p] = infoz.back(); + infoz.pop_back(); + sampler.Unregister(info); + break; + } + case 2: { + absl::Duration oldest = absl::ZeroDuration(); + sampler.Iterate([&](const Info& info) { + oldest = std::max(oldest, absl::Now() - info.create_time); + }); + ASSERT_GE(oldest, absl::ZeroDuration()); + break; + } + } + } + }); + } + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); +} + +TEST(SampleRecorderTest, Callback) { + SampleRecorder sampler; + + auto* info1 = Register(&sampler, 1); + auto* info2 = Register(&sampler, 2); + + static const Info* expected; + + auto callback = [](const Info& info) { + // We can't use `info` outside of this callback because the object will be + // disposed as soon as we return from here. + EXPECT_EQ(&info, expected); + }; + + // Set the callback. + EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr); + expected = info1; + sampler.Unregister(info1); + + // Unset the callback. + EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr)); + expected = nullptr; // no more calls. + sampler.Unregister(info2); +} + +} // namespace +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl -- cgit v1.2.3 From 2f25e6ea5f91d1646aca2337719c9b4deb64100a Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 14 Sep 2021 19:15:10 -0700 Subject: Export of internal Abseil changes -- a7924266cefd1c175142545996c967fb18b6144f by Abseil Team : Document the performance advantages of the `ToInt64*()` family of functions. PiperOrigin-RevId: 396736253 -- 77aa845d3aa4c56a48b899ce5a5ffcf9b81991b3 by Matt Kulukundis : tiny cleanup: remove redundant `public:` PiperOrigin-RevId: 396615677 -- b837e3de0ebd99e4c4f692a80a5d7ece3e829d18 by Martijn Vels : Make btree growth factor identical to concat code The btree code is using a more aggressive growth rate on Cord::Append than the Concat version. To minimize impact on migration and existing tests using empirical assumptions on growth, this change makes the btree use the same rate as Concat logic. PiperOrigin-RevId: 396595109 -- e958c06b6ab52e389caa7b3e43d83f924367e79c by Martijn Vels : Change CordRepBtree::Dump to include capacity of flats PiperOrigin-RevId: 396591195 GitOrigin-RevId: a7924266cefd1c175142545996c967fb18b6144f Change-Id: Ia38c09ba9891364b0727509dec4776eb6aadf984 --- absl/profiling/internal/sample_recorder.h | 1 - absl/strings/cord.cc | 4 ++-- absl/strings/internal/cord_rep_btree.cc | 3 ++- absl/time/time.h | 5 +++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h index a257ea5d..5e04a9cd 100644 --- a/absl/profiling/internal/sample_recorder.h +++ b/absl/profiling/internal/sample_recorder.h @@ -41,7 +41,6 @@ namespace profiling_internal { // samples maintained by the SampleRecorder. Type T defines the sampled data. template struct Sample { - public: // Guards the ability to restore the sample to a pristine state. This // prevents races with sampling and resurrecting an object. absl::Mutex init_mu; diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 115705a2..29af9782 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -708,8 +708,8 @@ void Cord::InlineRep::AppendArray(absl::string_view src, if (btree_enabled()) { // TODO(b/192061034): keep legacy 10% growth rate: consider other rates. rep = ForceBtree(rep); - const size_t alloc_hint = (std::min)(kMaxFlatLength, rep->length / 10); - rep = CordRepBtree::Append(rep->btree(), src, alloc_hint); + const size_t min_growth = std::max(rep->length / 10, src.size()); + rep = CordRepBtree::Append(rep->btree(), src, min_growth - src.size()); } else { // Use new block(s) for any remaining bytes that were not handled above. // Alloc extra memory only if the right child of the root of the new tree diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 8fe589fa..6d53ab61 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -96,7 +96,8 @@ void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, maybe_dump_data(rep); DumpAll(substring->child, include_contents, stream, depth + 1); } else if (rep->tag >= FLAT) { - stream << "Flat, len = " << rep->length; + stream << "Flat, len = " << rep->length + << ", cap = " << rep->flat()->Capacity(); maybe_dump_data(rep); } else if (rep->tag == EXTERNAL) { stream << "Extn, len = " << rep->length; diff --git a/absl/time/time.h b/absl/time/time.h index e9cbce84..5abd815a 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -480,8 +480,9 @@ Duration Hours(T n) { // ToInt64Hours() // // Helper functions that convert a Duration to an integral count of the -// indicated unit. These functions are shorthand for the `IDivDuration()` -// function above; see its documentation for details about overflow, etc. +// indicated unit. These return the same results as the `IDivDuration()` +// function, though they usually do so more efficiently; see the +// documentation of `IDivDuration()` for details about overflow, etc. // // Example: // -- cgit v1.2.3 From de71511109d967000e68baedb75de104adb2b778 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 15 Sep 2021 09:17:09 -0700 Subject: Export of internal Abseil changes -- 3794fe8db7a8e5a17945a8d6e198cde1db1fed7d by Chris Kennelly : Record maximum reserve or rehash argument in hashtable statistics. This makes it possible to identify containers that are large because of reserve calls or ones that could have an opportunity for more precise capacity sizing. PiperOrigin-RevId: 396851064 -- c3d247c08acfd45d8e19cfd8e6e841e16e38e23d by Abseil Team : Remove extra semi-colon PiperOrigin-RevId: 396823800 GitOrigin-RevId: 3794fe8db7a8e5a17945a8d6e198cde1db1fed7d Change-Id: I9f26407c2dc6b3dff04f6f3e249571dd8ab288c3 --- absl/container/internal/hashtablez_sampler.cc | 1 + absl/container/internal/hashtablez_sampler.h | 25 ++++++++++++++++++++++ absl/container/internal/hashtablez_sampler_test.cc | 19 ++++++++++++++++ absl/container/internal/raw_hash_set.h | 13 +++++++++++ absl/container/internal/raw_hash_set_test.cc | 25 ++++++++++++++++++++++ absl/strings/internal/cord_rep_btree_reader.h | 2 +- 6 files changed, 84 insertions(+), 1 deletion(-) diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index ca03d9b6..4b133705 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -68,6 +68,7 @@ void HashtablezInfo::PrepareForSampling() { hashes_bitwise_or.store(0, std::memory_order_relaxed); hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed); hashes_bitwise_xor.store(0, std::memory_order_relaxed); + max_reserve.store(0, std::memory_order_relaxed); create_time = absl::Now(); // The inliner makes hardcoded skip_count difficult (especially when combined diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index d86207f5..812118e3 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -80,6 +80,7 @@ struct HashtablezInfo : public profiling_internal::Sample { std::atomic hashes_bitwise_or; std::atomic hashes_bitwise_and; std::atomic hashes_bitwise_xor; + std::atomic max_reserve; // All of the fields below are set by `PrepareForSampling`, they must not be // mutated in `Record*` functions. They are logically `const` in that sense. @@ -107,6 +108,18 @@ inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { std::memory_order_relaxed); } +inline void RecordReservationSlow(HashtablezInfo* info, + size_t target_capacity) { + info->max_reserve.store( + (std::max)(info->max_reserve.load(std::memory_order_relaxed), + target_capacity), + std::memory_order_relaxed); +} + +inline void RecordClearedReservationSlow(HashtablezInfo* info) { + info->max_reserve.store(0, std::memory_order_relaxed); +} + inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, size_t capacity) { info->size.store(size, std::memory_order_relaxed); @@ -170,6 +183,16 @@ class HashtablezInfoHandle { RecordRehashSlow(info_, total_probe_length); } + inline void RecordReservation(size_t target_capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordReservationSlow(info_, target_capacity); + } + + inline void RecordClearedReservation() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordClearedReservationSlow(info_); + } + inline void RecordInsert(size_t hash, size_t distance_from_desired) { if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; RecordInsertSlow(info_, hash, distance_from_desired); @@ -199,6 +222,8 @@ class HashtablezInfoHandle { inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {} inline void RecordRehash(size_t /*total_probe_length*/) {} + inline void RecordReservation(size_t /*target_capacity*/) {} + inline void RecordClearedReservation() {} inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {} inline void RecordErase() {} diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index 53fcfe6f..f053c19b 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -91,6 +91,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_or.load(), 0); EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); + EXPECT_EQ(info.max_reserve.load(), 0); EXPECT_GE(info.create_time, test_start); info.capacity.store(1, std::memory_order_relaxed); @@ -101,6 +102,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { info.hashes_bitwise_or.store(1, std::memory_order_relaxed); info.hashes_bitwise_and.store(1, std::memory_order_relaxed); info.hashes_bitwise_xor.store(1, std::memory_order_relaxed); + info.max_reserve.store(1, std::memory_order_relaxed); info.create_time = test_start - absl::Hours(20); info.PrepareForSampling(); @@ -113,6 +115,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_or.load(), 0); EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); + EXPECT_EQ(info.max_reserve.load(), 0); EXPECT_GE(info.create_time, test_start); } @@ -187,6 +190,22 @@ TEST(HashtablezInfoTest, RecordRehash) { EXPECT_EQ(info.num_rehashes.load(), 1); } +TEST(HashtablezInfoTest, RecordReservation) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + RecordReservationSlow(&info, 3); + EXPECT_EQ(info.max_reserve.load(), 3); + + RecordReservationSlow(&info, 2); + // High watermark does not change + EXPECT_EQ(info.max_reserve.load(), 3); + + RecordReservationSlow(&info, 10); + // High watermark does change + EXPECT_EQ(info.max_reserve.load(), 10); +} + #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) TEST(HashtablezSamplerTest, SmallSampleParameter) { SetHashtablezEnabled(true); diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index f4919ce3..212052ea 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1069,6 +1069,8 @@ class raw_hash_set { // past that we simply deallocate the array. if (capacity_ > 127) { destroy_slots(); + + infoz().RecordClearedReservation(); } else if (capacity_) { for (size_t i = 0; i != capacity_; ++i) { if (IsFull(ctrl_[i])) { @@ -1382,14 +1384,20 @@ class raw_hash_set { if (n == 0 && size_ == 0) { destroy_slots(); infoz().RecordStorageChanged(0, 0); + infoz().RecordClearedReservation(); return; } + // bitor is a faster way of doing `max` here. We will round up to the next // power-of-2-minus-1, so bitor is good enough. auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); // n == 0 unconditionally rehashes as per the standard. if (n == 0 || m > capacity_) { resize(m); + + // This is after resize, to ensure that we have completed the allocation + // and have potentially sampled the hashtable. + infoz().RecordReservation(n); } } @@ -1397,6 +1405,10 @@ class raw_hash_set { if (n > size() + growth_left()) { size_t m = GrowthToLowerboundCapacity(n); resize(NormalizeCapacity(m)); + + // This is after resize, to ensure that we have completed the allocation + // and have potentially sampled the hashtable. + infoz().RecordReservation(n); } } @@ -1638,6 +1650,7 @@ class raw_hash_set { PolicyTraits::destroy(&alloc_ref(), slots_ + i); } } + // Unpoison before returning the memory to the allocator. SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); Deallocate( diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 4012a3aa..b46c4920 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -2049,15 +2049,31 @@ TEST(RawHashSamplerTest, Sample) { std::vector tables; for (int i = 0; i < 1000000; ++i) { tables.emplace_back(); + + const bool do_reserve = (i % 10 > 5); + const bool do_rehash = !do_reserve && (i % 10 > 0); + + if (do_reserve) { + // Don't reserve on all tables. + tables.back().reserve(10 * (i % 10)); + } + tables.back().insert(1); tables.back().insert(i % 5); + + if (do_rehash) { + // Rehash some other tables. + tables.back().rehash(10 * (i % 10)); + } } size_t end_size = 0; std::unordered_map observed_checksums; + std::unordered_map reservations; end_size += sampler.Iterate([&](const HashtablezInfo& info) { if (preexisting_info.count(&info) == 0) { observed_checksums[info.hashes_bitwise_xor.load( std::memory_order_relaxed)]++; + reservations[info.max_reserve.load(std::memory_order_relaxed)]++; } ++end_size; }); @@ -2068,6 +2084,15 @@ TEST(RawHashSamplerTest, Sample) { for (const auto& [_, count] : observed_checksums) { EXPECT_NEAR((100 * count) / static_cast(tables.size()), 0.2, 0.05); } + + EXPECT_EQ(reservations.size(), 10); + for (const auto& [reservation, count] : reservations) { + EXPECT_GE(reservation, 0); + EXPECT_LT(reservation, 100); + + EXPECT_NEAR((100 * count) / static_cast(tables.size()), 0.1, 0.05) + << reservation; + } } #endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE diff --git a/absl/strings/internal/cord_rep_btree_reader.h b/absl/strings/internal/cord_rep_btree_reader.h index 66e97f5d..7aa79dbf 100644 --- a/absl/strings/internal/cord_rep_btree_reader.h +++ b/absl/strings/internal/cord_rep_btree_reader.h @@ -166,7 +166,7 @@ inline size_t CordRepBtreeReader::length() const { inline absl::string_view CordRepBtreeReader::Init(CordRepBtree* tree) { assert(tree != nullptr); const CordRep* edge = navigator_.InitFirst(tree); - remaining_ = tree->length - edge->length;; + remaining_ = tree->length - edge->length; return CordRepBtree::EdgeData(edge); } -- cgit v1.2.3 From 29f8307d8e629e989bf26a84ab1a49a0738a5470 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 20 Sep 2021 13:35:36 -0700 Subject: Export of internal Abseil changes -- d56207f5535c3aad1624e33d20777ea6e66f51a7 by Abseil Team : Internal change PiperOrigin-RevId: 397830482 -- 7f7ff3e88e0d3cd61d63da477b2a08e61a1aeea2 by Evan Brown : Update implementation details comment in raw_hash_set to include information about the heap allocation's layout. PiperOrigin-RevId: 397786239 -- fde783b12a79ae8d587d1027bc8736dff6844897 by Abseil Team : Add comments on #endif to make nesting clearer PiperOrigin-RevId: 397684219 GitOrigin-RevId: d56207f5535c3aad1624e33d20777ea6e66f51a7 Change-Id: I43dc2b5c982f1ef2b21f82b6133c49c428baf223 --- absl/container/internal/raw_hash_set.h | 11 +++++++++++ absl/debugging/internal/stacktrace_config.h | 11 ++++++----- absl/profiling/BUILD.bazel | 4 +++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 212052ea..5c5db12e 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -87,6 +87,17 @@ // // This probing function guarantees that after N probes, all the groups of the // table will be probed exactly once. +// +// The control state and slot array are stored contiguously in a shared heap +// allocation. The layout of this allocation is: `capacity()` control bytes, +// one sentinel control byte, `Group::kWidth - 1` cloned control bytes, +// , `capacity()` slots. The sentinel control byte is used in +// iteration so we know when we reach the end of the table. The cloned control +// bytes at the end of the table are cloned from the beginning of the table so +// groups that begin near the end of the table can see a full group. In cases in +// which there are more than `capacity()` cloned control bytes, the extra bytes +// are `kEmpty`, and these ensure that we always see at least one empty slot and +// can stop an unsuccessful search. #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h index 29b26bdd..ff21b719 100644 --- a/absl/debugging/internal/stacktrace_config.h +++ b/absl/debugging/internal/stacktrace_config.h @@ -35,7 +35,7 @@ // Thread local support required for UnwindImpl. #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_generic-inl.inc" -#endif +#endif // defined(ABSL_HAVE_THREAD_LOCAL) #elif defined(__EMSCRIPTEN__) #define ABSL_STACKTRACE_INL_HEADER \ @@ -55,7 +55,7 @@ // Note: When using glibc this may require -funwind-tables to function properly. #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_generic-inl.inc" -#endif +#endif // __has_include() #elif defined(__i386__) || defined(__x86_64__) #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_x86-inl.inc" @@ -73,9 +73,10 @@ // Note: When using glibc this may require -funwind-tables to function properly. #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_generic-inl.inc" -#endif -#endif -#endif +#endif // __has_include() +#endif // defined(__has_include) + +#endif // defined(__linux__) && !defined(__ANDROID__) // Fallback to the empty implementation. #if !defined(ABSL_STACKTRACE_INL_HEADER) diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel index 5f3a1030..ba4811b3 100644 --- a/absl/profiling/BUILD.bazel +++ b/absl/profiling/BUILD.bazel @@ -27,7 +27,9 @@ cc_library( hdrs = ["internal/sample_recorder.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//absl:__subpackages__"], + visibility = [ + "//absl:__subpackages__", + ], deps = [ "//absl/base:config", "//absl/base:core_headers", -- cgit v1.2.3 From d4af654d971cc46cde6213269a364cdf170fe0ce Mon Sep 17 00:00:00 2001 From: Markus Dreseler Date: Wed, 22 Sep 2021 21:58:42 +0200 Subject: Update from_chars documentation with regard to whitespace (#1020) --- absl/strings/charconv.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h index e04be32f..7c509812 100644 --- a/absl/strings/charconv.h +++ b/absl/strings/charconv.h @@ -64,8 +64,9 @@ struct from_chars_result { // the result in `value`. // // The matching pattern format is almost the same as that of strtod(), except -// that C locale is not respected, and an initial '+' character in the input -// range will never be matched. +// that (1) C locale is not respected, (2) an initial '+' character in the +// input range will never be matched, and (3) leading whitespaces are not +// ignored. // // If `fmt` is set, it must be one of the enumerator values of the chars_format. // (This is despite the fact that chars_format is a bitmask type.) If set to -- cgit v1.2.3 From 020619c4aa68d13dfbdd6107a373912bb5ea85af Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 22 Sep 2021 12:57:35 -0700 Subject: Export of internal Abseil changes -- 336f161ad8cb2cc3e1a6bbcbbb8c5b692ee59789 by Derek Mauro : Internal change PiperOrigin-RevId: 398308807 -- 80d512823d17561a45feca81f37713a91a175349 by Abseil Team : Internal change. PiperOrigin-RevId: 398257218 -- f1f9792000355eb1d0c11b17800048491662a218 by Abseil Team : Fix documentation for btree_multi{map,set}::merge to match behavior for elements with equivalent keys. PiperOrigin-RevId: 398071060 -- 8a9a302aebf2419e83f0c7dc5a63c33d26b807a3 by James Y Knight : Silence -Wunused-value warning newly emitted by ToT Clang. The value is being intentionally ignored, as the purpose of the call is only to eliminate this overload via SFINAE when `GenT{}` is not constant evaluable. PiperOrigin-RevId: 397861294 GitOrigin-RevId: 336f161ad8cb2cc3e1a6bbcbbb8c5b692ee59789 Change-Id: I946e1d22619f92ce6a424c8c13a20a50b39ed463 --- absl/container/btree_map.h | 5 +- absl/container/btree_set.h | 5 +- absl/container/inlined_vector.h | 21 +++-- absl/container/internal/inlined_vector.h | 135 ++++++++++++++++++++----------- absl/flags/internal/flag.h | 2 +- absl/meta/type_traits.h | 10 ++- 6 files changed, 110 insertions(+), 68 deletions(-) diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index 6bbf414e..f0a8d4a6 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -693,9 +693,8 @@ class btree_multimap // btree_multimap::merge() // - // Extracts elements from a given `source` btree_multimap into this - // `btree_multimap`. If the destination `btree_multimap` already contains an - // element with an equivalent key, that element is not extracted. + // Extracts all elements from a given `source` btree_multimap into this + // `btree_multimap`. using Base::merge; // btree_multimap::swap(btree_multimap& other) diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index c07ccd91..89739006 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -604,9 +604,8 @@ class btree_multiset // btree_multiset::merge() // - // Extracts elements from a given `source` btree_multiset into this - // `btree_multiset`. If the destination `btree_multiset` already contains an - // element with an equivalent key, that element is not extracted. + // Extracts all elements from a given `source` btree_multiset into this + // `btree_multiset`. using Base::merge; // btree_multiset::swap(btree_multiset& other) diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 37e5fef8..df9e0991 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -207,8 +207,8 @@ class InlinedVector { other.storage_.SetInlinedSize(0); } else if (other.storage_.GetIsAllocated()) { - storage_.SetAllocatedData(other.storage_.GetAllocatedData(), - other.storage_.GetAllocatedCapacity()); + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); storage_.SetAllocatedSize(other.storage_.GetSize()); other.storage_.SetInlinedSize(0); @@ -242,8 +242,8 @@ class InlinedVector { other.storage_.SetInlinedSize(0); } else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) && other.storage_.GetIsAllocated()) { - storage_.SetAllocatedData(other.storage_.GetAllocatedData(), - other.storage_.GetAllocatedCapacity()); + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); storage_.SetAllocatedSize(other.storage_.GetSize()); other.storage_.SetInlinedSize(0); @@ -735,15 +735,12 @@ class InlinedVector { // `InlinedVector::shrink_to_fit()` // - // Reduces memory usage by freeing unused memory. After being called, calls to - // `capacity()` will be equal to `max(N, size())`. + // Attempts to reduce memory usage by moving elements to (or keeping elements + // in) the smallest available buffer sufficient for containing `size()` + // elements. // - // If `size() <= N` and the inlined vector contains allocated memory, the - // elements will all be moved to the inlined space and the allocated memory - // will be deallocated. - // - // If `size() > N` and `size() < capacity()`, the elements will be moved to a - // smaller allocation. + // If `size()` is sufficiently small, the elements will be moved into (or kept + // in) the inlined space. void shrink_to_fit() { if (storage_.GetIsAllocated()) { storage_.ShrinkToFit(); diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 1cfba9b2..bb365b80 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -21,8 +21,11 @@ #include #include #include +#include +#include #include +#include "absl/base/attributes.h" #include "absl/base/macros.h" #include "absl/container/internal/compressed_tuple.h" #include "absl/memory/memory.h" @@ -102,6 +105,27 @@ void DestroyElements(NoTypeDeduction& allocator, Pointer destroy_first, } } +template +struct Allocation { + Pointer data; + SizeType capacity; +}; + +template ) > ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT)> +struct MallocAdapter { + static Allocation Allocate(A& allocator, SizeType requested_capacity) { + return {AllocatorTraits::allocate(allocator, requested_capacity), + requested_capacity}; + } + + static void Deallocate(A& allocator, Pointer pointer, + SizeType capacity) { + AllocatorTraits::deallocate(allocator, pointer, capacity); + } +}; + // If kUseMemcpy is true, memcpy(dst, src, n); else do nothing. // Useful to avoid compiler warnings when memcpy() is used for T values // that are not trivially copyable in non-reachable code. @@ -201,7 +225,7 @@ class AllocationTransaction { ~AllocationTransaction() { if (DidAllocate()) { - AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); + MallocAdapter::Deallocate(GetAllocator(), GetData(), GetCapacity()); } } @@ -213,18 +237,27 @@ class AllocationTransaction { SizeType& GetCapacity() { return capacity_; } bool DidAllocate() { return GetData() != nullptr; } - Pointer Allocate(SizeType capacity) { - GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); - GetCapacity() = capacity; - return GetData(); + + Pointer Allocate(SizeType requested_capacity) { + Allocation result = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + GetData() = result.data; + GetCapacity() = result.capacity; + return result.data; + } + + ABSL_MUST_USE_RESULT Allocation Release() && { + Allocation result = {GetData(), GetCapacity()}; + Reset(); + return result; } + private: void Reset() { GetData() = nullptr; GetCapacity() = 0; } - private: container_internal::CompressedTuple> allocator_data_; SizeType capacity_; }; @@ -405,15 +438,9 @@ class Storage { GetSizeAndIsAllocated() -= count << static_cast>(1); } - void SetAllocatedData(Pointer data, SizeType capacity) { - data_.allocated.allocated_data = data; - data_.allocated.allocated_capacity = capacity; - } - - void AcquireAllocatedData(AllocationTransaction& allocation_tx) { - SetAllocatedData(allocation_tx.GetData(), allocation_tx.GetCapacity()); - - allocation_tx.Reset(); + void SetAllocation(Allocation allocation) { + data_.allocated.allocated_data = allocation.data; + data_.allocated.allocated_capacity = allocation.capacity; } void MemcpyFrom(const Storage& other_storage) { @@ -425,8 +452,8 @@ class Storage { void DeallocateIfAllocated() { if (GetIsAllocated()) { - AllocatorTraits::deallocate(GetAllocator(), GetAllocatedData(), - GetAllocatedCapacity()); + MallocAdapter::Deallocate(GetAllocator(), GetAllocatedData(), + GetAllocatedCapacity()); } } @@ -476,9 +503,11 @@ void Storage::InitFrom(const Storage& other) { // Because this is only called from the `InlinedVector` constructors, it's // safe to take on the allocation with size `0`. If `ConstructElements(...)` // throws, deallocation will be automatically handled by `~Storage()`. - SizeType new_capacity = ComputeCapacity(GetInlinedCapacity(), n); - dst = AllocatorTraits::allocate(GetAllocator(), new_capacity); - SetAllocatedData(dst, new_capacity); + SizeType requested_capacity = ComputeCapacity(GetInlinedCapacity(), n); + Allocation allocation = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + SetAllocation(allocation); + dst = allocation.data; src = other.GetAllocatedData(); } if (IsMemcpyOk::value) { @@ -503,9 +532,12 @@ auto Storage::Initialize(ValueAdapter values, SizeType new_size) // Because this is only called from the `InlinedVector` constructors, it's // safe to take on the allocation with size `0`. If `ConstructElements(...)` // throws, deallocation will be automatically handled by `~Storage()`. - SizeType new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); - construct_data = AllocatorTraits::allocate(GetAllocator(), new_capacity); - SetAllocatedData(construct_data, new_capacity); + SizeType requested_capacity = + ComputeCapacity(GetInlinedCapacity(), new_size); + Allocation allocation = + MallocAdapter::Allocate(GetAllocator(), requested_capacity); + construct_data = allocation.data; + SetAllocation(allocation); SetIsAllocated(); } else { construct_data = GetInlinedData(); @@ -532,8 +564,9 @@ auto Storage::Assign(ValueAdapter values, SizeType new_size) absl::Span> destroy_loop; if (new_size > storage_view.capacity) { - SizeType new_capacity = ComputeCapacity(storage_view.capacity, new_size); - construct_loop = {allocation_tx.Allocate(new_capacity), new_size}; + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + construct_loop = {allocation_tx.Allocate(requested_capacity), new_size}; destroy_loop = {storage_view.data, storage_view.size}; } else if (new_size > storage_view.size) { assign_loop = {storage_view.data, storage_view.size}; @@ -553,7 +586,7 @@ auto Storage::Assign(ValueAdapter values, SizeType new_size) if (allocation_tx.DidAllocate()) { DeallocateIfAllocated(); - AcquireAllocatedData(allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); } @@ -583,8 +616,9 @@ auto Storage::Resize(ValueAdapter values, SizeType new_size) // Use transactional wrappers for the first two steps so we can roll // back if necessary due to exceptions. AllocationTransaction allocation_tx(alloc); - SizeType new_capacity = ComputeCapacity(storage_view.capacity, new_size); - Pointer new_data = allocation_tx.Allocate(new_capacity); + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(requested_capacity); ConstructionTransaction construction_tx(alloc); construction_tx.Construct(new_data + size, values, new_size - size); @@ -596,7 +630,7 @@ auto Storage::Resize(ValueAdapter values, SizeType new_size) DestroyElements(alloc, base, size); construction_tx.Commit(); DeallocateIfAllocated(); - AcquireAllocatedData(allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); } SetSize(new_size); @@ -621,8 +655,9 @@ auto Storage::Insert(ConstIterator pos, ValueAdapter values, IteratorValueAdapter> move_values( MoveIterator(storage_view.data)); - SizeType new_capacity = ComputeCapacity(storage_view.capacity, new_size); - Pointer new_data = allocation_tx.Allocate(new_capacity); + SizeType requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer new_data = allocation_tx.Allocate(requested_capacity); construction_tx.Construct(new_data + insert_index, values, insert_count); @@ -636,7 +671,7 @@ auto Storage::Insert(ConstIterator pos, ValueAdapter values, construction_tx.Commit(); move_construction_tx.Commit(); DeallocateIfAllocated(); - AcquireAllocatedData(allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetAllocatedSize(new_size); return Iterator(new_data + insert_index); @@ -717,8 +752,8 @@ auto Storage::EmplaceBackSlow(Args&&... args) -> Reference { AllocationTransaction allocation_tx(GetAllocator()); IteratorValueAdapter> move_values( MoveIterator(storage_view.data)); - SizeType new_capacity = NextCapacity(storage_view.capacity); - Pointer construct_data = allocation_tx.Allocate(new_capacity); + SizeType requested_capacity = NextCapacity(storage_view.capacity); + Pointer construct_data = allocation_tx.Allocate(requested_capacity); Pointer last_ptr = construct_data + storage_view.size; // Construct new element. @@ -737,7 +772,7 @@ auto Storage::EmplaceBackSlow(Args&&... args) -> Reference { DestroyElements(GetAllocator(), storage_view.data, storage_view.size); DeallocateIfAllocated(); - AcquireAllocatedData(allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); AddSize(1); return *last_ptr; @@ -778,9 +813,9 @@ auto Storage::Reserve(SizeType requested_capacity) -> void { IteratorValueAdapter> move_values( MoveIterator(storage_view.data)); - SizeType new_capacity = + SizeType new_requested_capacity = ComputeCapacity(storage_view.capacity, requested_capacity); - Pointer new_data = allocation_tx.Allocate(new_capacity); + Pointer new_data = allocation_tx.Allocate(new_requested_capacity); ConstructElements(GetAllocator(), new_data, move_values, storage_view.size); @@ -788,7 +823,7 @@ auto Storage::Reserve(SizeType requested_capacity) -> void { DestroyElements(GetAllocator(), storage_view.data, storage_view.size); DeallocateIfAllocated(); - AcquireAllocatedData(allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); } @@ -809,8 +844,12 @@ auto Storage::ShrinkToFit() -> void { Pointer construct_data; if (storage_view.size > GetInlinedCapacity()) { - SizeType new_capacity = storage_view.size; - construct_data = allocation_tx.Allocate(new_capacity); + SizeType requested_capacity = storage_view.size; + construct_data = allocation_tx.Allocate(requested_capacity); + if (allocation_tx.GetCapacity() >= storage_view.capacity) { + // Already using the smallest available heap allocation. + return; + } } else { construct_data = GetInlinedData(); } @@ -820,17 +859,17 @@ auto Storage::ShrinkToFit() -> void { storage_view.size); } ABSL_INTERNAL_CATCH_ANY { - SetAllocatedData(storage_view.data, storage_view.capacity); + SetAllocation({storage_view.data, storage_view.capacity}); ABSL_INTERNAL_RETHROW; } DestroyElements(GetAllocator(), storage_view.data, storage_view.size); - AllocatorTraits::deallocate(GetAllocator(), storage_view.data, - storage_view.capacity); + MallocAdapter::Deallocate(GetAllocator(), storage_view.data, + storage_view.capacity); if (allocation_tx.DidAllocate()) { - AcquireAllocatedData(allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); } else { UnsetIsAllocated(); } @@ -881,16 +920,16 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { inlined_ptr->GetSize()); } ABSL_INTERNAL_CATCH_ANY { - allocated_ptr->SetAllocatedData(allocated_storage_view.data, - allocated_storage_view.capacity); + allocated_ptr->SetAllocation( + {allocated_storage_view.data, allocated_storage_view.capacity}); ABSL_INTERNAL_RETHROW; } DestroyElements(inlined_ptr->GetAllocator(), inlined_ptr->GetInlinedData(), inlined_ptr->GetSize()); - inlined_ptr->SetAllocatedData(allocated_storage_view.data, - allocated_storage_view.capacity); + inlined_ptr->SetAllocation( + {allocated_storage_view.data, allocated_storage_view.capacity}); } swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 8636fadc..124a2f1c 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -290,7 +290,7 @@ constexpr T InitDefaultValue(EmptyBraces) { template ::value, int>::type = - (GenT{}, 0)> + ((void)GenT{}, 0)> constexpr FlagDefaultArg DefaultArg(int) { return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kOneWord}; } diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index e7c12393..8358e799 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -35,7 +35,7 @@ #ifndef ABSL_META_TYPE_TRAITS_H_ #define ABSL_META_TYPE_TRAITS_H_ -#include +#include #include #include @@ -47,6 +47,14 @@ #define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 #endif +// Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17 +// feature. +#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) +#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__ +#else // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) +#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT alignof(std::max_align_t) +#endif // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) + namespace absl { ABSL_NAMESPACE_BEGIN -- cgit v1.2.3 From f3a42743db4df4c98e1df690045577c775daf20b Mon Sep 17 00:00:00 2001 From: Milad Fa <46688537+miladfarca@users.noreply.github.com> Date: Thu, 23 Sep 2021 16:23:02 -0400 Subject: Initial support for AIX (#1021) * Init support of AIX * make sysinfo change AIX specific * Relocate TBF * Add comments for .csect psudo op. --- absl/base/attributes.h | 8 ++++++++ absl/base/config.h | 10 +++++----- absl/base/internal/sysinfo.cc | 2 ++ absl/base/internal/unscaledcycleclock.cc | 4 ++++ absl/time/clock_test.cc | 2 ++ 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 2665d8f3..e3907827 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -318,8 +318,16 @@ // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. // This functionality is supported by GNU linker. #ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#ifdef _AIX +// __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo +// op which includes an additional integer as part of its syntax indcating +// alignment. If data fall under different alignments then you might get a +// compilation error indicating a `Section type conflict`. +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#else #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) #endif +#endif // ABSL_DECLARE_ATTRIBUTE_SECTION_VARS // diff --git a/absl/base/config.h b/absl/base/config.h index c7b2e64d..5d3edcd2 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -408,10 +408,10 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // POSIX.1-2001. #ifdef ABSL_HAVE_MMAP #error ABSL_HAVE_MMAP cannot be directly set -#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ - defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \ - defined(__ASYLO__) || defined(__myriad2__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ + defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ + defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) #define ABSL_HAVE_MMAP 1 #endif @@ -422,7 +422,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) + defined(_AIX) || defined(__ros__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 08a1e288..a7cfb461 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -131,6 +131,8 @@ static int GetNumCPUs() { #elif defined(_WIN32) const unsigned hardware_concurrency = Win32NumCPUs(); return hardware_concurrency ? hardware_concurrency : 1; +#elif defined(_AIX) + return sysconf(_SC_NPROCESSORS_ONLN); #else // Other possibilities: // - Read /sys/devices/system/cpu/online and use cpumask_parse() diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index fc07e300..4d352bd1 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -87,6 +87,10 @@ int64_t UnscaledCycleClock::Now() { double UnscaledCycleClock::Frequency() { #ifdef __GLIBC__ return __ppc_get_timebase_freq(); +#elif defined(_AIX) + // This is the same constant value as returned by + // __ppc_get_timebase_freq(). + return static_cast(512000000); #elif defined(__FreeBSD__) static once_flag init_timebase_frequency_once; static double timebase_frequency = 0.0; diff --git a/absl/time/clock_test.cc b/absl/time/clock_test.cc index 4bcfc6bc..e6f627b3 100644 --- a/absl/time/clock_test.cc +++ b/absl/time/clock_test.cc @@ -18,6 +18,8 @@ #if defined(ABSL_HAVE_ALARM) #include #include +#elif defined(_AIX) +typedef void (*sig_t)(int); #elif defined(__linux__) || defined(__APPLE__) #error all known Linux and Apple targets have alarm #endif -- cgit v1.2.3 From 1ce4ceca2b2931bc4d7e470228c2dbb2f3dfea0f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 23 Sep 2021 13:21:54 -0700 Subject: Export of internal Abseil changes -- 1801102e11205861bc063e067e9fd4754b625c5a by Derek Mauro : Internal change PiperOrigin-RevId: 398562681 -- 485008445725d4013f60f4b2876f84b6b47932ec by Jorg Brown : Replace calls to std::isinf with comparison against max(). PiperOrigin-RevId: 398534255 -- 9b99d074d39ad677cf92f99549d22bb73f504f8f by Saleem Abdulrasool : debugging: add support for non-glibc targets for debugging This relaxes the ELF mem_image handling and subsequently enables the VDSO support for non-glibc targets. The primary need for the restriction was the use of the `__GLIBC_PREREQ` macro. If it is undefined, assume that the glibc pre-requisite is unavailable. This allows building the debugging_internal target on musl targets. PiperOrigin-RevId: 398499050 -- 3cc3630ef2226ae1981a944573f0f9c27a527ebf by Abseil Team : Replace usages of `auto` with proper typedefs. PiperOrigin-RevId: 398479551 GitOrigin-RevId: 1801102e11205861bc063e067e9fd4754b625c5a Change-Id: Ib13e8612d1b263b9c1ae7f56a9f394b24c3add2e --- absl/container/internal/inlined_vector.h | 10 +++++----- absl/debugging/internal/elf_mem_image.cc | 21 +++++++++++---------- absl/debugging/internal/elf_mem_image.h | 4 ++-- absl/debugging/internal/vdso_support.cc | 19 ++++++++++++++++--- absl/strings/numbers.cc | 2 +- 5 files changed, 35 insertions(+), 21 deletions(-) diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index bb365b80..e2ecf46c 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -98,7 +98,7 @@ template void DestroyElements(NoTypeDeduction& allocator, Pointer destroy_first, SizeType destroy_size) { if (destroy_first != nullptr) { - for (auto i = destroy_size; i != 0;) { + for (SizeType i = destroy_size; i != 0;) { --i; AllocatorTraits::destroy(allocator, destroy_first + i); } @@ -492,7 +492,7 @@ void Storage::DestroyContents() { template void Storage::InitFrom(const Storage& other) { - const auto n = other.GetSize(); + const SizeType n = other.GetSize(); assert(n > 0); // Empty sources handled handled in caller. ConstPointer src; Pointer dst; @@ -598,9 +598,9 @@ template auto Storage::Resize(ValueAdapter values, SizeType new_size) -> void { StorageView storage_view = MakeStorageView(); - auto* const base = storage_view.data; + Pointer const base = storage_view.data; const SizeType size = storage_view.size; - auto& alloc = GetAllocator(); + A& alloc = GetAllocator(); if (new_size <= size) { // Destroy extra old elements. DestroyElements(alloc, base + new_size, size - new_size); @@ -732,7 +732,7 @@ template template auto Storage::EmplaceBack(Args&&... args) -> Reference { StorageView storage_view = MakeStorageView(); - const auto n = storage_view.size; + const SizeType n = storage_view.size; if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { // Fast path; new element fits. Pointer last_ptr = storage_view.data + n; diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index 24cc0130..d6832eaf 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -22,6 +22,7 @@ #include #include #include +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" // From binutils/include/elf/common.h (this doesn't appear to be documented @@ -43,11 +44,11 @@ namespace debugging_internal { namespace { -#if __WORDSIZE == 32 +#if __SIZEOF_POINTER__ == 4 const int kElfClass = ELFCLASS32; int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); } int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); } -#elif __WORDSIZE == 64 +#elif __SIZEOF_POINTER__ == 8 const int kElfClass = ELFCLASS64; int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); } int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); } @@ -175,17 +176,17 @@ void ElfMemImage::Init(const void *base) { } switch (base_as_char[EI_DATA]) { case ELFDATA2LSB: { - if (__LITTLE_ENDIAN != __BYTE_ORDER) { - assert(false); - return; - } +#ifndef ABSL_IS_LITTLE_ENDIAN + assert(false); + return; +#endif break; } case ELFDATA2MSB: { - if (__BIG_ENDIAN != __BYTE_ORDER) { - assert(false); - return; - } +#ifndef ABSL_IS_BIG_ENDIAN + assert(false); + return; +#endif break; } default: { diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index 46bfade3..86474819 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -31,8 +31,8 @@ #error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set #endif -#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \ - !defined(__asmjs__) && !defined(__wasm__) +#if defined(__ELF__) && !defined(__native_client__) && !defined(__asmjs__) && \ + !defined(__wasm__) #define ABSL_HAVE_ELF_MEM_IMAGE 1 #endif diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index 6be16d90..0dfe9ca6 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -20,12 +20,25 @@ #ifdef ABSL_HAVE_VDSO_SUPPORT // defined in vdso_support.h +#if !defined(__has_include) +#define __has_include(header) 0 +#endif + #include #include +#if __has_include() +#include +#elif __has_include() #include +#endif #include -#if __GLIBC_PREREQ(2, 16) // GLIBC-2.16 implements getauxval. +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)) +#define ABSL_HAVE_GETAUXVAL +#endif + +#ifdef ABSL_HAVE_GETAUXVAL #include #endif @@ -65,7 +78,7 @@ VDSOSupport::VDSOSupport() // the operation should be idempotent. const void *VDSOSupport::Init() { const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase; -#if __GLIBC_PREREQ(2, 16) +#ifdef ABSL_HAVE_GETAUXVAL if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) { errno = 0; const void *const sysinfo_ehdr = @@ -74,7 +87,7 @@ const void *VDSOSupport::Init() { vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed); } } -#endif // __GLIBC_PREREQ(2, 16) +#endif // ABSL_HAVE_GETAUXVAL if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) { int fd = open("/proc/self/auxv", O_RDONLY); if (fd == -1) { diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index 966d94bd..cbd84c91 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc @@ -505,7 +505,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { *out++ = '-'; d = -d; } - if (std::isinf(d)) { + if (d > std::numeric_limits::max()) { strcpy(out, "inf"); // NOLINT(runtime/printf) return out + 3 - buffer; } -- cgit v1.2.3 From b1b63f7aa8467ff5c2fc81231f6ec69fe93ca3b0 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 24 Sep 2021 10:32:31 -0700 Subject: Export of internal Abseil changes -- d6f0dab708b123a5e24b98da1de0b11e36a7a86e by Evan Brown : In STLStringResizeUninitializedAmortized, use basic_string::__append_default_init for amortized growth rather than conditionally adding reserve in STLStringReserveAmortized. This way, we can avoid extra branches, e.g. in basic_string::__shrink_or_extend. PiperOrigin-RevId: 398761382 GitOrigin-RevId: d6f0dab708b123a5e24b98da1de0b11e36a7a86e Change-Id: Ib2d99411c95d61300519c32b885ce586b410c3bf --- absl/strings/internal/resize_uninitialized.h | 31 ++++++++++++++--- absl/strings/internal/resize_uninitialized_test.cc | 40 ++++++++++++++++++---- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h index 749c66e7..49859dcc 100644 --- a/absl/strings/internal/resize_uninitialized.h +++ b/absl/strings/internal/resize_uninitialized.h @@ -29,8 +29,9 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace strings_internal { -// Is a subclass of true_type or false_type, depending on whether or not -// T has a __resize_default_init member. +// In this type trait, we look for a __resize_default_init member function, and +// we use it if available, otherwise, we use resize. We provide HasMember to +// indicate whether __resize_default_init is present. template struct ResizeUninitializedTraits { using HasMember = std::false_type; @@ -79,14 +80,36 @@ void STLStringReserveAmortized(string_type* s, size_t new_size) { } } +// In this type trait, we look for an __append_default_init member function, and +// we use it if available, otherwise, we use append. +template +struct AppendUninitializedTraits { + static void Append(string_type* s, size_t n) { + s->append(n, typename string_type::value_type()); + } +}; + +template +struct AppendUninitializedTraits< + string_type, absl::void_t() + .__append_default_init(237))> > { + static void Append(string_type* s, size_t n) { + s->__append_default_init(n); + } +}; + // Like STLStringResizeUninitialized(str, new_size), except guaranteed to use // exponential growth so that the amortized complexity of increasing the string // size by a small amount is O(1), in contrast to O(str->size()) in the case of // precise growth. template void STLStringResizeUninitializedAmortized(string_type* s, size_t new_size) { - STLStringReserveAmortized(s, new_size); - STLStringResizeUninitialized(s, new_size); + const size_t size = s->size(); + if (new_size > size) { + AppendUninitializedTraits::Append(s, new_size - size); + } else { + s->erase(new_size); + } } } // namespace strings_internal diff --git a/absl/strings/internal/resize_uninitialized_test.cc b/absl/strings/internal/resize_uninitialized_test.cc index 01ee476b..ad1b9c58 100644 --- a/absl/strings/internal/resize_uninitialized_test.cc +++ b/absl/strings/internal/resize_uninitialized_test.cc @@ -19,10 +19,12 @@ namespace { int resize_call_count = 0; +int append_call_count = 0; // A mock string class whose only purpose is to track how many times its -// resize() method has been called. +// resize()/append() methods have been called. struct resizable_string { + using value_type = char; size_t size() const { return 0; } size_t capacity() const { return 0; } char& operator[](size_t) { @@ -30,14 +32,18 @@ struct resizable_string { return c; } void resize(size_t) { resize_call_count += 1; } + void append(size_t, value_type) { append_call_count += 1; } void reserve(size_t) {} + resizable_string& erase(size_t = 0, size_t = 0) { return *this; } }; int resize_default_init_call_count = 0; +int append_default_init_call_count = 0; // A mock string class whose only purpose is to track how many times its -// resize() and __resize_default_init() methods have been called. -struct resize_default_init_string { +// resize()/__resize_default_init()/append()/__append_default_init() methods +// have been called. +struct default_init_string { size_t size() const { return 0; } size_t capacity() const { return 0; } char& operator[](size_t) { @@ -46,46 +52,68 @@ struct resize_default_init_string { } void resize(size_t) { resize_call_count += 1; } void __resize_default_init(size_t) { resize_default_init_call_count += 1; } + void __append_default_init(size_t) { append_default_init_call_count += 1; } void reserve(size_t) {} + default_init_string& erase(size_t = 0, size_t = 0) { return *this; } }; TEST(ResizeUninit, WithAndWithout) { resize_call_count = 0; + append_call_count = 0; resize_default_init_call_count = 0; + append_default_init_call_count = 0; { resizable_string rs; EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); EXPECT_FALSE( absl::strings_internal::STLStringSupportsNontrashingResize(&rs)); EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitialized(&rs, 237); EXPECT_EQ(resize_call_count, 1); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitializedAmortized(&rs, 1000); - EXPECT_EQ(resize_call_count, 2); + EXPECT_EQ(resize_call_count, 1); + EXPECT_EQ(append_call_count, 1); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); } resize_call_count = 0; + append_call_count = 0; resize_default_init_call_count = 0; + append_default_init_call_count = 0; { - resize_default_init_string rus; + default_init_string rus; EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); EXPECT_TRUE( absl::strings_internal::STLStringSupportsNontrashingResize(&rus)); EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitialized(&rus, 237); EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 1); + EXPECT_EQ(append_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitializedAmortized(&rus, 1000); EXPECT_EQ(resize_call_count, 0); - EXPECT_EQ(resize_default_init_call_count, 2); + EXPECT_EQ(append_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 1); + EXPECT_EQ(append_default_init_call_count, 1); } } -- cgit v1.2.3 From 7143e49e74857a009e16c51f6076eb197b6ccb49 Mon Sep 17 00:00:00 2001 From: Martin Blais Date: Mon, 27 Sep 2021 14:06:52 -0400 Subject: Fixed typo `constuct` to `construct` in 3 places. (#1022) --- absl/status/status.h | 2 +- absl/status/statusor.h | 4 ++-- absl/strings/string_view.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/absl/status/status.h b/absl/status/status.h index c5fe0a70..638b9ca4 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -346,7 +346,7 @@ inline StatusToStringMode& operator^=(StatusToStringMode& lhs, // API developers should construct their functions to return `absl::OkStatus()` // upon success, or an `absl::StatusCode` upon another type of error (e.g // an `absl::StatusCode::kInvalidArgument` error). The API provides convenience -// functions to constuct each status code. +// functions to construct each status code. // // Example: // diff --git a/absl/status/statusor.h b/absl/status/statusor.h index 7fa623fd..c051fbb3 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -429,8 +429,8 @@ class StatusOr : private internal_statusor::StatusOrData, // if `T` can be constructed from a `U`. Can accept move or copy constructors. // // This constructor is explicit if `U` is not convertible to `T`. To avoid - // ambiguity, this constuctor is disabled if `U` is a `StatusOr`, where `J` - // is convertible to `T`. + // ambiguity, this constructor is disabled if `U` is a `StatusOr`, where + // `J` is convertible to `T`. template < typename U = T, absl::enable_if_t< diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index ea760526..a4c9a652 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -596,7 +596,7 @@ class string_view { } private: - // The constructor from std::string delegates to this constuctor. + // The constructor from std::string delegates to this constructor. // See the comment on that constructor for the rationale. struct SkipCheckLengthTag {}; string_view(const char* data, size_type len, SkipCheckLengthTag) noexcept -- cgit v1.2.3 From 4167eab063636a1fadcd571e0a762ff67d742c25 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 29 Sep 2021 09:40:33 -0700 Subject: Export of internal Abseil changes -- 506fa3e10b3d8399ad937c32ecea26d1ad4e62bb by Abseil Team : Disable ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE when GCC < 8.2.0 is used with libc++ PiperOrigin-RevId: 399707056 -- 656b7c7cee87f46a4bc7953618796f82da08e62c by Derek Mauro : Remove the MSVC flag implementation from flag.h to help clarify that methods on absl::Flag are not part of the public API PiperOrigin-RevId: 399584678 -- a92a9bc156303bc663b84c4b704891ec8f67e333 by Abseil Team : Get rid of MemcpyIfAllowed while continuing to suppress erroneous warnings PiperOrigin-RevId: 399468864 -- 5f9a66895f707ba001fb51b88c0c6025f8c872a3 by Abseil Team : Use feature testing to check for availability of invoke_result. Feature testing should be available by C++20, which removes invoke_result. https://en.cppreference.com/w/cpp/feature_test PiperOrigin-RevId: 399447373 -- 946c0a502b4499dbfcabf1ab93ddde0048288fb4 by CJ Johnson : Add rvalue-reference qualifier to the Commit method on ConstructionTransaction PiperOrigin-RevId: 399442206 -- 726a4d036eff49aeb6fd0ca2b1775699b6844395 by Greg Falcon : Internal change PiperOrigin-RevId: 399441870 -- 1df6d3f659b88dbac13c3d8e13db23bb3844ece2 by Abseil Team : Clang-format whitespace changes PiperOrigin-RevId: 399281271 -- 4a828cde95a07421d699ebac775b37810624214f by Derek Mauro : Internal change PiperOrigin-RevId: 399234071 -- e520c72b34ba2f98668c889139001f8276243d31 by Greg Falcon : Import of CCTZ from GitHub. PiperOrigin-RevId: 399233662 GitOrigin-RevId: 506fa3e10b3d8399ad937c32ecea26d1ad4e62bb Change-Id: I92b9176d2387c08eb167f9268efa78b55b8e09c2 --- absl/base/config.h | 12 ++- absl/container/internal/inlined_vector.h | 30 ++---- absl/flags/BUILD.bazel | 1 + absl/flags/CMakeLists.txt | 1 + absl/flags/flag.h | 96 +---------------- absl/flags/internal/flag_msvc.inc | 116 +++++++++++++++++++++ absl/hash/internal/hash.cc | 6 +- absl/hash/internal/hash.h | 6 +- absl/meta/type_traits.h | 3 +- absl/strings/cord.h | 16 +-- absl/time/internal/cctz/src/tzfile.h | 2 +- absl/time/internal/cctz/testdata/version | 2 +- .../internal/cctz/testdata/zoneinfo/Africa/Accra | Bin 700 -> 130 bytes .../cctz/testdata/zoneinfo/America/Anguilla | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/Antigua | Bin 130 -> 177 bytes .../internal/cctz/testdata/zoneinfo/America/Aruba | Bin 151 -> 177 bytes .../cctz/testdata/zoneinfo/America/Atikokan | Bin 224 -> 149 bytes .../cctz/testdata/zoneinfo/America/Barbados | Bin 231 -> 278 bytes .../cctz/testdata/zoneinfo/America/Blanc-Sablon | Bin 205 -> 177 bytes .../cctz/testdata/zoneinfo/America/Coral_Harbour | Bin 224 -> 149 bytes .../cctz/testdata/zoneinfo/America/Creston | Bin 158 -> 240 bytes .../cctz/testdata/zoneinfo/America/Curacao | Bin 151 -> 177 bytes .../cctz/testdata/zoneinfo/America/Dominica | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/Grenada | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/Guadeloupe | Bin 130 -> 177 bytes .../internal/cctz/testdata/zoneinfo/America/Guyana | Bin 172 -> 181 bytes .../cctz/testdata/zoneinfo/America/Kralendijk | Bin 151 -> 177 bytes .../cctz/testdata/zoneinfo/America/Lower_Princes | Bin 151 -> 177 bytes .../cctz/testdata/zoneinfo/America/Marigot | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/Montserrat | Bin 130 -> 177 bytes .../internal/cctz/testdata/zoneinfo/America/Nassau | Bin 1006 -> 1717 bytes .../cctz/testdata/zoneinfo/America/Port_of_Spain | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/St_Barthelemy | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/St_Kitts | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/St_Lucia | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/St_Thomas | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/St_Vincent | Bin 130 -> 177 bytes .../cctz/testdata/zoneinfo/America/Tortola | Bin 130 -> 177 bytes .../internal/cctz/testdata/zoneinfo/America/Virgin | Bin 130 -> 177 bytes .../testdata/zoneinfo/Antarctica/DumontDUrville | Bin 152 -> 154 bytes .../cctz/testdata/zoneinfo/Antarctica/Syowa | Bin 133 -> 133 bytes .../internal/cctz/testdata/zoneinfo/Asia/Amman | Bin 787 -> 922 bytes .../cctz/testdata/zoneinfo/Atlantic/Azores | Bin 1435 -> 1453 bytes .../cctz/testdata/zoneinfo/Atlantic/Jan_Mayen | Bin 676 -> 705 bytes .../cctz/testdata/zoneinfo/Atlantic/Madeira | Bin 1435 -> 1453 bytes .../internal/cctz/testdata/zoneinfo/Europe/Lisbon | Bin 1436 -> 1454 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Apia | Bin 268 -> 407 bytes .../cctz/testdata/zoneinfo/Pacific/Enderbury | Bin 172 -> 172 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Kanton | Bin 0 -> 172 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Niue | Bin 175 -> 154 bytes .../cctz/testdata/zoneinfo/Pacific/Rarotonga | Bin 391 -> 406 bytes .../cctz/testdata/zoneinfo/Pacific/Tongatapu | Bin 237 -> 237 bytes absl/time/internal/cctz/testdata/zoneinfo/Portugal | Bin 1436 -> 1454 bytes .../internal/cctz/testdata/zoneinfo/zone1970.tab | 29 ++---- 54 files changed, 158 insertions(+), 162 deletions(-) create mode 100644 absl/flags/internal/flag_msvc.inc create mode 100644 absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton diff --git a/absl/base/config.h b/absl/base/config.h index 5d3edcd2..0562cddb 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -243,15 +243,17 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // // Checks whether `std::is_trivially_copy_assignable` is supported. -// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with -// either libc++ or libstdc++, and Visual Studio (but not NVCC). +// Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with +// libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC). #if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set -#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ - (!defined(__clang__) && ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ +#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ + (!defined(__clang__) && \ + ((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \ + (ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) && \ + defined(_LIBCPP_VERSION)))) || \ (defined(_MSC_VER) && !defined(__NVCC__)) #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index e2ecf46c..1d7d6cda 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -126,23 +126,6 @@ struct MallocAdapter { } }; -// If kUseMemcpy is true, memcpy(dst, src, n); else do nothing. -// Useful to avoid compiler warnings when memcpy() is used for T values -// that are not trivially copyable in non-reachable code. -template -inline void MemcpyIfAllowed(void* dst, const void* src, size_t n); - -// memcpy when allowed. -template <> -inline void MemcpyIfAllowed(void* dst, const void* src, size_t n) { - memcpy(dst, src, n); -} - -// Do nothing for types that are not memcpy-able. This function is only -// called from non-reachable branches. -template <> -inline void MemcpyIfAllowed(void*, const void*, size_t) {} - template void ConstructElements(NoTypeDeduction& allocator, Pointer construct_first, ValueAdapter& values, @@ -288,7 +271,7 @@ class ConstructionTransaction { GetData() = data; GetSize() = size; } - void Commit() { + void Commit() && { GetData() = nullptr; GetSize() = 0; } @@ -511,7 +494,8 @@ void Storage::InitFrom(const Storage& other) { src = other.GetAllocatedData(); } if (IsMemcpyOk::value) { - MemcpyIfAllowed::value>(dst, src, sizeof(dst[0]) * n); + std::memcpy(reinterpret_cast(dst), + reinterpret_cast(src), n * sizeof(ValueType)); } else { auto values = IteratorValueAdapter>(src); ConstructElements(GetAllocator(), dst, values, n); @@ -628,7 +612,7 @@ auto Storage::Resize(ValueAdapter values, SizeType new_size) ConstructElements(alloc, new_data, move_values, size); DestroyElements(alloc, base, size); - construction_tx.Commit(); + std::move(construction_tx).Commit(); DeallocateIfAllocated(); SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); @@ -668,8 +652,8 @@ auto Storage::Insert(ConstIterator pos, ValueAdapter values, DestroyElements(GetAllocator(), storage_view.data, storage_view.size); - construction_tx.Commit(); - move_construction_tx.Commit(); + std::move(construction_tx).Commit(); + std::move(move_construction_tx).Commit(); DeallocateIfAllocated(); SetAllocation(std::move(allocation_tx).Release()); @@ -721,7 +705,7 @@ auto Storage::Insert(ConstIterator pos, ValueAdapter values, ConstructElements(GetAllocator(), insert_construction.data(), values, insert_construction.size()); - move_construction_tx.Commit(); + std::move(move_construction_tx).Commit(); AddSize(insert_count); return Iterator(storage_view.data + insert_index); diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index c178b86b..7957c6dd 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -217,6 +217,7 @@ cc_library( name = "flag", srcs = [ "flag.cc", + "internal/flag_msvc.inc", ], hdrs = [ "declare.h", diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 956f70f8..7f3298e9 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -202,6 +202,7 @@ absl_cc_library( HDRS "declare.h" "flag.h" + "internal/flag_msvc.inc" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 14209e7b..1a7e4d4d 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -71,101 +71,7 @@ ABSL_NAMESPACE_BEGIN template using Flag = flags_internal::Flag; #else -// MSVC debug builds do not implement initialization with constexpr constructors -// correctly. To work around this we add a level of indirection, so that the -// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias -// to that class) and dynamically allocates an instance when necessary. We also -// forward all calls to internal::Flag methods via trampoline methods. In this -// setup the `absl::Flag` class does not have constructor and virtual methods, -// all the data members are public and thus MSVC is able to initialize it at -// link time. To deal with multiple threads accessing the flag for the first -// time concurrently we use an atomic boolean indicating if flag object is -// initialized. We also employ the double-checked locking pattern where the -// second level of protection is a global Mutex, so if two threads attempt to -// construct the flag concurrently only one wins. -// This solution is based on a recomendation here: -// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 - -namespace flags_internal { -absl::Mutex* GetGlobalConstructionGuard(); -} // namespace flags_internal - -template -class Flag { - public: - // No constructor and destructor to ensure this is an aggregate type. - // Visual Studio 2015 still requires the constructor for class to be - // constexpr initializable. -#if _MSC_VER <= 1900 - constexpr Flag(const char* name, const char* filename, - const flags_internal::HelpGenFunc help_gen, - const flags_internal::FlagDfltGenFunc default_value_gen) - : name_(name), - filename_(filename), - help_gen_(help_gen), - default_value_gen_(default_value_gen), - inited_(false), - impl_(nullptr) {} -#endif - - flags_internal::Flag& GetImpl() const { - if (!inited_.load(std::memory_order_acquire)) { - absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); - - if (inited_.load(std::memory_order_acquire)) { - return *impl_; - } - - impl_ = new flags_internal::Flag( - name_, filename_, - {flags_internal::FlagHelpMsg(help_gen_), - flags_internal::FlagHelpKind::kGenFunc}, - {flags_internal::FlagDefaultSrc(default_value_gen_), - flags_internal::FlagDefaultKind::kGenFunc}); - inited_.store(true, std::memory_order_release); - } - - return *impl_; - } - - // Public methods of `absl::Flag` are NOT part of the Abseil Flags API. - // See https://abseil.io/docs/cpp/guides/flags - bool IsRetired() const { return GetImpl().IsRetired(); } - absl::string_view Name() const { return GetImpl().Name(); } - std::string Help() const { return GetImpl().Help(); } - bool IsModified() const { return GetImpl().IsModified(); } - bool IsSpecifiedOnCommandLine() const { - return GetImpl().IsSpecifiedOnCommandLine(); - } - std::string Filename() const { return GetImpl().Filename(); } - std::string DefaultValue() const { return GetImpl().DefaultValue(); } - std::string CurrentValue() const { return GetImpl().CurrentValue(); } - template - inline bool IsOfType() const { - return GetImpl().template IsOfType(); - } - T Get() const { - return flags_internal::FlagImplPeer::InvokeGet(GetImpl()); - } - void Set(const T& v) { - flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v); - } - void InvokeCallback() { GetImpl().InvokeCallback(); } - - const CommandLineFlag& Reflect() const { - return flags_internal::FlagImplPeer::InvokeReflect(GetImpl()); - } - - // The data members are logically private, but they need to be public for - // this to be an aggregate type. - const char* name_; - const char* filename_; - const flags_internal::HelpGenFunc help_gen_; - const flags_internal::FlagDfltGenFunc default_value_gen_; - - mutable std::atomic inited_; - mutable flags_internal::Flag* impl_; -}; +#include "absl/flags/internal/flag_msvc.inc" #endif // GetFlag() diff --git a/absl/flags/internal/flag_msvc.inc b/absl/flags/internal/flag_msvc.inc new file mode 100644 index 00000000..c31bd27f --- /dev/null +++ b/absl/flags/internal/flag_msvc.inc @@ -0,0 +1,116 @@ +// +// Copyright 2021 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Do not include this file directly. +// Include absl/flags/flag.h instead. + +// MSVC debug builds do not implement initialization with constexpr constructors +// correctly. To work around this we add a level of indirection, so that the +// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias +// to that class) and dynamically allocates an instance when necessary. We also +// forward all calls to internal::Flag methods via trampoline methods. In this +// setup the `absl::Flag` class does not have constructor and virtual methods, +// all the data members are public and thus MSVC is able to initialize it at +// link time. To deal with multiple threads accessing the flag for the first +// time concurrently we use an atomic boolean indicating if flag object is +// initialized. We also employ the double-checked locking pattern where the +// second level of protection is a global Mutex, so if two threads attempt to +// construct the flag concurrently only one wins. +// +// This solution is based on a recomendation here: +// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 + +namespace flags_internal { +absl::Mutex* GetGlobalConstructionGuard(); +} // namespace flags_internal + +// Public methods of `absl::Flag` are NOT part of the Abseil Flags API. +// See https://abseil.io/docs/cpp/guides/flags +template +class Flag { + public: + // No constructor and destructor to ensure this is an aggregate type. + // Visual Studio 2015 still requires the constructor for class to be + // constexpr initializable. +#if _MSC_VER <= 1900 + constexpr Flag(const char* name, const char* filename, + const flags_internal::HelpGenFunc help_gen, + const flags_internal::FlagDfltGenFunc default_value_gen) + : name_(name), + filename_(filename), + help_gen_(help_gen), + default_value_gen_(default_value_gen), + inited_(false), + impl_(nullptr) {} +#endif + + flags_internal::Flag& GetImpl() const { + if (!inited_.load(std::memory_order_acquire)) { + absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); + + if (inited_.load(std::memory_order_acquire)) { + return *impl_; + } + + impl_ = new flags_internal::Flag( + name_, filename_, + {flags_internal::FlagHelpMsg(help_gen_), + flags_internal::FlagHelpKind::kGenFunc}, + {flags_internal::FlagDefaultSrc(default_value_gen_), + flags_internal::FlagDefaultKind::kGenFunc}); + inited_.store(true, std::memory_order_release); + } + + return *impl_; + } + + // Public methods of `absl::Flag` are NOT part of the Abseil Flags API. + // See https://abseil.io/docs/cpp/guides/flags + bool IsRetired() const { return GetImpl().IsRetired(); } + absl::string_view Name() const { return GetImpl().Name(); } + std::string Help() const { return GetImpl().Help(); } + bool IsModified() const { return GetImpl().IsModified(); } + bool IsSpecifiedOnCommandLine() const { + return GetImpl().IsSpecifiedOnCommandLine(); + } + std::string Filename() const { return GetImpl().Filename(); } + std::string DefaultValue() const { return GetImpl().DefaultValue(); } + std::string CurrentValue() const { return GetImpl().CurrentValue(); } + template + inline bool IsOfType() const { + return GetImpl().template IsOfType(); + } + T Get() const { + return flags_internal::FlagImplPeer::InvokeGet(GetImpl()); + } + void Set(const T& v) { + flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v); + } + void InvokeCallback() { GetImpl().InvokeCallback(); } + + const CommandLineFlag& Reflect() const { + return flags_internal::FlagImplPeer::InvokeReflect(GetImpl()); + } + + // The data members are logically private, but they need to be public for + // this to be an aggregate type. + const char* name_; + const char* filename_; + const flags_internal::HelpGenFunc help_gen_; + const flags_internal::FlagDfltGenFunc default_value_gen_; + + mutable std::atomic inited_; + mutable flags_internal::Flag* impl_; +}; diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc index 4b818917..11451e57 100644 --- a/absl/hash/internal/hash.cc +++ b/absl/hash/internal/hash.cc @@ -21,9 +21,9 @@ namespace hash_internal { uint64_t MixingHashState::CombineLargeContiguousImpl32( uint64_t state, const unsigned char* first, size_t len) { while (len >= PiecewiseChunkSize()) { - state = - Mix(state, absl::hash_internal::CityHash32(reinterpret_cast(first), - PiecewiseChunkSize())); + state = Mix(state, + hash_internal::CityHash32(reinterpret_cast(first), + PiecewiseChunkSize())); len -= PiecewiseChunkSize(); first += PiecewiseChunkSize(); } diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index f5174096..e5af0ac0 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -42,6 +42,7 @@ #include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" #include "absl/container/fixed_array.h" +#include "absl/hash/internal/city.h" #include "absl/hash/internal/low_level_hash.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" @@ -49,7 +50,6 @@ #include "absl/types/optional.h" #include "absl/types/variant.h" #include "absl/utility/utility.h" -#include "absl/hash/internal/city.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -883,7 +883,7 @@ class ABSL_DLL MixingHashState : public HashStateBase { #ifdef ABSL_HAVE_INTRINSIC_INT128 return LowLevelHashImpl(data, len); #else - return absl::hash_internal::CityHash64(reinterpret_cast(data), len); + return hash_internal::CityHash64(reinterpret_cast(data), len); #endif } @@ -929,7 +929,7 @@ inline uint64_t MixingHashState::CombineContiguousImpl( if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { return CombineLargeContiguousImpl32(state, first, len); } - v = absl::hash_internal::CityHash32(reinterpret_cast(first), len); + v = hash_internal::CityHash32(reinterpret_cast(first), len); } else if (len >= 4) { v = Read4To8(first, len); } else if (len > 0) { diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 8358e799..d886cb30 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -642,7 +642,8 @@ using underlying_type_t = typename std::underlying_type::type; namespace type_traits_internal { -#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) // std::result_of is deprecated (C++17) or removed (C++20) template struct result_of; template diff --git a/absl/strings/cord.h b/absl/strings/cord.h index ac1832f0..175d7b90 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -256,9 +256,7 @@ class Cord { // swap() // // Swaps the contents of two Cords. - friend void swap(Cord& x, Cord& y) noexcept { - x.swap(y); - } + friend void swap(Cord& x, Cord& y) noexcept { x.swap(y); } // Cord::size() // @@ -722,14 +720,14 @@ class Cord { const char* data() const; // Returns nullptr if holding pointer void set_data(const char* data, size_t n, bool nullify_tail); // Discards pointer, if any - char* set_data(size_t n); // Write data to the result + char* set_data(size_t n); // Write data to the result // Returns nullptr if holding bytes absl::cord_internal::CordRep* tree() const; absl::cord_internal::CordRep* as_tree() const; // Returns non-null iff was holding a pointer absl::cord_internal::CordRep* clear(); // Converts to pointer if necessary. - void reduce_size(size_t n); // REQUIRES: holding data + void reduce_size(size_t n); // REQUIRES: holding data void remove_prefix(size_t n); // REQUIRES: holding data void AppendArray(absl::string_view src, MethodIdentifier method); absl::string_view FindFlatStartPiece() const; @@ -1440,12 +1438,8 @@ inline bool operator==(const Cord& lhs, const Cord& rhs) { } inline bool operator!=(const Cord& x, const Cord& y) { return !(x == y); } -inline bool operator<(const Cord& x, const Cord& y) { - return x.Compare(y) < 0; -} -inline bool operator>(const Cord& x, const Cord& y) { - return x.Compare(y) > 0; -} +inline bool operator<(const Cord& x, const Cord& y) { return x.Compare(y) < 0; } +inline bool operator>(const Cord& x, const Cord& y) { return x.Compare(y) > 0; } inline bool operator<=(const Cord& x, const Cord& y) { return x.Compare(y) <= 0; } diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h index 269fa36c..31e85982 100644 --- a/absl/time/internal/cctz/src/tzfile.h +++ b/absl/time/internal/cctz/src/tzfile.h @@ -43,7 +43,7 @@ struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ + char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */ char tzh_reserved[15]; /* reserved; must be zero */ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index 1d590958..ba5601ef 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2021a +2021b diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra index c39ae382..8906e88c 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra and b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla and b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua and b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba index d6ddf7d8..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba and b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan b/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan index c8287152..9154643f 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan and b/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados index 9d3afa6a..720c9863 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados and b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon b/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon index 7096b69a..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon and b/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour b/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour index c8287152..9154643f 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour and b/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston index 9d69a0ab..c2bd2f94 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston and b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao index d6ddf7d8..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao and b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica and b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada and b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe and b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana index ebd85d0f..bcc66881 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana and b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk index d6ddf7d8..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk and b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes index d6ddf7d8..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes and b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot and b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat and b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau index 2ef2aa89..fe6be8ea 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau and b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain and b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy and b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts and b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia and b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas and b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent and b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola and b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin index f4fe5903..47b4dc34 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin and b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville index c0cfc85a..5d8fc3a1 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville and b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa index 97d80d75..01c47ccb 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa and b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman index 1bd09fef..d97d308d 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman and b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores index b7f75a9c..e6e2616e 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores and b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen index dfc50957..465546bd 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen and b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira index 7c3a49c0..cf965c3f 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira and b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon index 64841661..f0c70b69 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon and b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia index 244af26f..a6b835aa 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia and b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury index b22ab147..2b6a0608 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury and b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton new file mode 100644 index 00000000..2b6a0608 Binary files /dev/null and b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue index 7b357935..be874e24 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue and b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga index 143a1883..7220bda0 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga and b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu index 54aeb0ff..f28c8401 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu and b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Portugal b/absl/time/internal/cctz/testdata/zoneinfo/Portugal index 64841661..f0c70b69 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Portugal and b/absl/time/internal/cctz/testdata/zoneinfo/Portugal differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab index 396e4d38..c614be81 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab +++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab @@ -40,11 +40,9 @@ AL +4120+01950 Europe/Tirane AM +4011+04430 Asia/Yerevan AQ -6617+11031 Antarctica/Casey Casey AQ -6835+07758 Antarctica/Davis Davis -AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville AQ -6736+06253 Antarctica/Mawson Mawson AQ -6448-06406 Antarctica/Palmer Palmer AQ -6734-06808 Antarctica/Rothera Rothera -AQ -690022+0393524 Antarctica/Syowa Syowa AQ -720041+0023206 Antarctica/Troll Troll AQ -7824+10654 Antarctica/Vostok Vostok AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) @@ -97,7 +95,6 @@ BR +0249-06040 America/Boa_Vista Roraima BR -0308-06001 America/Manaus Amazonas (east) BR -0640-06952 America/Eirunepe Amazonas (west) BR -0958-06748 America/Rio_Branco Acre -BS +2505-07721 America/Nassau BT +2728+08939 Asia/Thimphu BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize @@ -106,13 +103,11 @@ CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) -CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) -CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas), Bahamas CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) -CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) CA +744144-0944945 America/Resolute Central - NU (Resolute) @@ -123,7 +118,6 @@ CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +6227-11421 America/Yellowknife Mountain - NT (central) CA +682059-1334300 America/Inuvik Mountain - NT (west) -CA +4906-11631 America/Creston MST - BC (Creston) CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) CA +6043-13503 America/Whitehorse MST - Yukon (east) @@ -131,7 +125,7 @@ CA +6404-13925 America/Dawson MST - Yukon (west) CA +4916-12307 America/Vancouver Pacific - BC (most areas) CC -1210+09655 Indian/Cocos CH,DE,LI +4723+00832 Europe/Zurich Swiss time -CI,BF,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan +CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga CL -3327-07040 America/Santiago Chile (most areas) CL -5309-07055 America/Punta_Arenas Region of Magallanes @@ -142,7 +136,6 @@ CO +0436-07405 America/Bogota CR +0956-08405 America/Costa_Rica CU +2308-08222 America/Havana CV +1455-02331 Atlantic/Cape_Verde -CW,AW,BQ,SX +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas CY +3510+03322 Asia/Nicosia Cyprus (most areas) CY +3507+03357 Asia/Famagusta Northern Cyprus @@ -170,7 +163,6 @@ FR +4852+00220 Europe/Paris GB,GG,IM,JE +513030-0000731 Europe/London GE +4143+04449 Asia/Tbilisi GF +0456-05220 America/Cayenne -GH +0533-00013 Africa/Accra GI +3608-00521 Europe/Gibraltar GL +6411-05144 America/Nuuk Greenland (most areas) GL +7646-01840 America/Danmarkshavn National Park (east coast) @@ -204,7 +196,7 @@ JP +353916+1394441 Asia/Tokyo KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi KG +4254+07436 Asia/Bishkek KI +0125+17300 Pacific/Tarawa Gilbert Islands -KI -0308-17105 Pacific/Enderbury Phoenix Islands +KI -0247-17143 Pacific/Kanton Phoenix Islands KI +0152-15720 Pacific/Kiritimati Line Islands KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul @@ -262,19 +254,19 @@ NR -0031+16655 Pacific/Nauru NU -1901-16955 Pacific/Niue NZ,AQ -3652+17446 Pacific/Auckland New Zealand time NZ -4357-17633 Pacific/Chatham Chatham Islands -PA,KY +0858-07932 America/Panama +PA,CA,KY +0858-07932 America/Panama EST - Panama, Cayman, ON (Atikokan), NU (Coral H) PE -1203-07703 America/Lima PF -1732-14934 Pacific/Tahiti Society Islands PF -0900-13930 Pacific/Marquesas Marquesas Islands PF -2308-13457 Pacific/Gambier Gambier Islands -PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) +PG,AQ -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Dumont d'Urville PG -0613+15534 Pacific/Bougainville Bougainville PH +1435+12100 Asia/Manila PK +2452+06703 Asia/Karachi PL +5215+02100 Europe/Warsaw PM +4703-05620 America/Miquelon PN -2504-13005 Pacific/Pitcairn -PR +182806-0660622 America/Puerto_Rico +PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST PS +3130+03428 Asia/Gaza Gaza Strip PS +313200+0350542 Asia/Hebron West Bank PT +3843-00908 Europe/Lisbon Portugal (mainland) @@ -314,12 +306,12 @@ RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea -SA,KW,YE +2438+04643 Asia/Riyadh +SA,AQ,KW,YE +2438+04643 Asia/Riyadh Arabia, Syowa SB -0932+16012 Pacific/Guadalcanal SC -0440+05528 Indian/Mahe SD +1536+03232 Africa/Khartoum SE +5920+01803 Europe/Stockholm -SG +0117+10351 Asia/Singapore +SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia SR +0550-05510 America/Paramaribo SS +0451+03137 Africa/Juba ST +0020+00644 Africa/Sao_Tome @@ -334,9 +326,8 @@ TK -0922-17114 Pacific/Fakaofo TL -0833+12535 Asia/Dili TM +3757+05823 Asia/Ashgabat TN +3648+01011 Africa/Tunis -TO -2110-17510 Pacific/Tongatapu +TO -210800-1751200 Pacific/Tongatapu TR +4101+02858 Europe/Istanbul -TT,AG,AI,BL,DM,GD,GP,KN,LC,MF,MS,VC,VG,VI +1039-06131 America/Port_of_Spain TV -0831+17913 Pacific/Funafuti TW +2503+12130 Asia/Taipei UA +5026+03031 Europe/Kiev Ukraine (most areas) @@ -362,7 +353,7 @@ US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) US +433649-1161209 America/Boise Mountain - ID (south); OR (east) -US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) +US,CA +332654-1120424 America/Phoenix MST - Arizona (except Navajo), Creston BC US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) US +581807-1342511 America/Juneau Alaska - Juneau area -- cgit v1.2.3 From b9b925341f9e90f5e7aa0cf23f036c29c7e454eb Mon Sep 17 00:00:00 2001 From: Milad Fa <46688537+miladfarca@users.noreply.github.com> Date: Tue, 5 Oct 2021 15:21:55 -0400 Subject: Fix typedef of sig_t on AIX (#1030) --- absl/time/clock_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/absl/time/clock_test.cc b/absl/time/clock_test.cc index e6f627b3..bc77dbc2 100644 --- a/absl/time/clock_test.cc +++ b/absl/time/clock_test.cc @@ -18,8 +18,10 @@ #if defined(ABSL_HAVE_ALARM) #include #include -#elif defined(_AIX) +#ifdef _AIX +// sig_t is not defined in AIX. typedef void (*sig_t)(int); +#endif #elif defined(__linux__) || defined(__APPLE__) #error all known Linux and Apple targets have alarm #endif -- cgit v1.2.3 From ae0f4c266095c9003786cd571bc1fb72544104a1 Mon Sep 17 00:00:00 2001 From: Milad Fa <46688537+miladfarca@users.noreply.github.com> Date: Wed, 6 Oct 2021 17:20:14 -0400 Subject: Fix hashing on big endian platforms (#1028) Avoid using libstdc++'s implementation of std::hash and std::hash on big endian platforms in the implementation of absl::Hash. This is a workaround for a buggy implementation that results in many collisions. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98731 --- absl/hash/internal/hash.h | 52 ++++++++++++++++++++++++++++--- absl/hash/internal/low_level_hash_test.cc | 50 ++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index e5af0ac0..b1e33caf 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -489,8 +490,9 @@ typename std::enable_if::value, H>::type AbslHashValue( // AbslHashValue for hashing std::vector // -// Do not use this for vector. It does not have a .data(), and a fallback -// for std::hash<> is most likely faster. +// Do not use this for vector on platforms that have a working +// implementation of std::hash. It does not have a .data(), and a fallback for +// std::hash<> is most likely faster. template typename std::enable_if::value && !std::is_same::value, H>::type @@ -500,6 +502,27 @@ AbslHashValue(H hash_state, const std::vector& vector) { vector.size()); } +#if defined(ABSL_IS_BIG_ENDIAN) && \ + (defined(__GLIBCXX__) || defined(__GLIBCPP__)) +// AbslHashValue for hashing std::vector +// +// std::hash in libstdc++ does not work correctly with vector on Big +// Endian platforms therefore we need to implement a custom AbslHashValue for +// it. More details on the bug: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 +template +typename std::enable_if::value && std::is_same::value, + H>::type +AbslHashValue(H hash_state, const std::vector& vector) { + typename H::AbslInternalPiecewiseCombiner combiner; + for (const auto& i : vector) { + unsigned char c = static_cast(i); + hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c)); + } + return H::combine(combiner.finalize(std::move(hash_state)), vector.size()); +} +#endif + // ----------------------------------------------------------------------------- // AbslHashValue for Ordered Associative Containers // ----------------------------------------------------------------------------- @@ -592,9 +615,28 @@ AbslHashValue(H hash_state, const absl::variant& v) { // AbslHashValue for Other Types // ----------------------------------------------------------------------------- -// AbslHashValue for hashing std::bitset is not defined, for the same reason as -// for vector (see std::vector above): It does not expose the raw bytes, -// and a fallback to std::hash<> is most likely faster. +// AbslHashValue for hashing std::bitset is not defined on Little Endian +// platforms, for the same reason as for vector (see std::vector above): +// It does not expose the raw bytes, and a fallback to std::hash<> is most +// likely faster. + +#if defined(ABSL_IS_BIG_ENDIAN) && \ + (defined(__GLIBCXX__) || defined(__GLIBCPP__)) +// AbslHashValue for hashing std::bitset +// +// std::hash in libstdc++ does not work correctly with std::bitset on Big Endian +// platforms therefore we need to implement a custom AbslHashValue for it. More +// details on the bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 +template +H AbslHashValue(H hash_state, const std::bitset& set) { + typename H::AbslInternalPiecewiseCombiner combiner; + for (int i = 0; i < N; i++) { + unsigned char c = static_cast(set[i]); + hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c)); + } + return H::combine(combiner.finalize(std::move(hash_state)), N); +} +#endif // ----------------------------------------------------------------------------- diff --git a/absl/hash/internal/low_level_hash_test.cc b/absl/hash/internal/low_level_hash_test.cc index cf22dc3c..ae930b34 100644 --- a/absl/hash/internal/low_level_hash_test.cc +++ b/absl/hash/internal/low_level_hash_test.cc @@ -404,7 +404,55 @@ TEST(LowLevelHashTest, VerifyGolden) { uint64_t{0xc9ae5c8759b4877a}}, }; -#if defined(__aarch64__) +#if defined(ABSL_IS_BIG_ENDIAN) + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8, + 0xa6630143a7e6aa6f, 0x17645cb7318b86b, 0x218b175f30ba61f8, + 0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d, + 0xe7b01610fc22dbb8, 0x99d9f694404af913, 0xf4eecd37464b45c5, + 0x7d2c653d63596d9b, 0x3f15c8544ec5393a, 0x6b9dc0c1704f796c, + 0xf1ded7a7eae5ed5a, 0x2db2fd7c6dd4641b, 0x151ca2d3d4cd33ab, + 0xa5af5994ac2ccd64, 0x2b2a4ca3191d2fce, 0xf89e68c9364e7c05, + 0x71724c70b799c21, 0x70536fabfd157369, 0xdee92794c3c3082b, + 0xac033a6743d3b3eb, 0xed2956b506cd5151, 0xbd669644755264b6, + 0x6ab1ff5d5f549a63, 0xf6bd551a2e3e04e, 0x7b5a8cef6875ea73, + 0x22bccf4d4db0a91c, 0x4f2bc07754c7c7eb, 0xfb6b8342a86725db, + 0x13a1a0d4c5854da, 0x5f6e44655f7dedac, 0x54a9198dff2bdf85, + 0xdb17e6915d4e4042, 0xa69926cf5c3b89f, 0xf77f031bfd74c096, + 0x1d6f916fdd50ec3c, 0x334ac76013ade393, 0x99370f899111de15, + 0x352457a03ada6de, 0x341974d4f42d854d, 0xda89ab02872aeb5, + 0x6ec2b74e143b10d9, 0x6f284c0b5cd60522, 0xf9670de353438f88, + 0xde920913adf0a2b4, 0xb7a07d7c0c17a8ec, 0x879a69f558ba3a98, + 0x360cf6d802df20f9, 0x53530f8046673738, 0xbd8f5f2bcf35e483, + 0x3f171f047144b983, 0x644d04e820823465, 0x50e44773a20b2702, + 0xe584ed4c05c745dd, 0x9a825c85b95ab6c0, 0xbce2931deb74e775, + 0x10468e9e705c7cfe, 0x12e01de3104141e2, 0x5c11ae2ee3713abd, + 0x6ac5ffb0860319e6, 0xc1e6da1849d30fc9, 0xa0e4d247a458b447, + 0x4530d4615c32b89b, 0x116aa09107a76505, 0xf941339d00d9bb73, + 0x573a0fc1615afb33, 0xa975c81dc868b258, 0x3ab2c5250ab54bda, + 0x37f99f208a3e3b11, 0x4b49b0ff706689d, 0x30bafa0b8f0a87fe, + 0xea6787a65cc20cdd, 0x55861729f1fc3ab8, 0xea38e009c5be9b72, + 0xcb8522cba33c3c66, 0x352e77653fe306f3, 0xe0bb760793bac064, + 0xf66ec59322662956, 0x637aa320455d56f8, 0x46ee546be5824a89, + 0x9e6842421e83d8a4, 0xf98ac2bc96b9fb8c, 0xf2c1002fd9a70b99, + 0x4c2b62b1e39e9405, 0x3248555fa3ade9c4, 0xd4d04c37f6417c21, + 0xf40cd506b1bf5653, 0x6c45d6005c760d2f, 0x61d88a7e61ff0d7e, + 0x131591e8a53cc967, 0xdae85cb9bc29bab6, 0xe98835334905e626, + 0x7cce50a2b66b8754, 0x5b0b3d0c5ac498ae, 0xd35a218c974d1756, + 0xfce436ddc1d003c, 0xd183901de90bb741, 0x9378f8f34974a66, + 0x21f11ae0a0402368, 0xf2fbd7c94ef89cb6, 0xc329c69d0f0d080b, + 0xf2841cba16216a61, 0x47aba97b44916df1, 0x724d4e00a8019fcf, + 0x2df9005c2a728d63, 0xc788892a1a5d7515, 0x9e993a65f9df0480, + 0x76876721ff49f969, 0xbe7a796cfba15bf5, 0xa4c8bd54586f5488, + 0xb390a325275501ab, 0x893f11317427ccf1, 0x92f2bb57da5695b9, + 0x30985b90da88269f, 0x2c690e268e086de8, 0x1c02df6097997196, + 0x1f9778f8bbdf6455, 0x7d57378c7bf8416d, 0xba8582a5f8d84d38, + 0xe8ca43b85050be4e, 0x5048cf6bed8a5d9f, 0xfbc5ba80917d0ea4, + 0x8011026525bf1691, 0x26b8dc6aed9fb50d, 0x191f5bfee77c1fe3, + 0xdd497891465a2cc1, 0x6f1fe8c57a33072e, 0x2c9f4ec078c460c0, + 0x9a725bde8f6a1437, 0x6ce545fa3ef61e4d, + }; +#elif defined(__aarch64__) constexpr uint64_t kGolden[kNumGoldenOutputs] = { 0x45c0aadee165dcbe, 0x25ed8587f6f20d06, 0x5f23ae668ce7926d, 0xfef74d1da0846719, 0x54478408e68cb7d4, 0xee27ddaf88c6fe68, -- cgit v1.2.3 From a59b4daa07a14326d2ceb28cc6d0e079feea3338 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 6 Oct 2021 17:55:30 -0700 Subject: Export of internal Abseil changes -- 17141711ee419daa597a9f31e73721f80143e55a by Gennadiy Rozental : Import of CCTZ from GitHub. PiperOrigin-RevId: 401384949 -- ac48584a7b16e8a12e26d49deb6cddec584a20b5 by Derek Mauro : Internal change PiperOrigin-RevId: 401337785 -- 8a51bb7c962845e0707240c5ba12c1b80f6fbbe9 by Derek Mauro : Internal change PiperOrigin-RevId: 401047691 -- 8e18024510869247f3c04c7807c93709eca2322a by Chris Kennelly : Note that SpinLock does not guarantee priorities for wakeups. PiperOrigin-RevId: 400999238 -- 75bc09b5f95fbb74b74d14c370bfb80011e8fb7f by Derek Mauro : Add visibility restrictions to some internal targets PiperOrigin-RevId: 400718253 -- 1de5061016bc42cd7be009c9725ed2343ce12e3d by Abseil Team : Make it clear that operator<< can also be used in place of ToString when logging absl::Status. PiperOrigin-RevId: 400248269 -- cda15d9dc6e5cd569de7e5e73f409b72a3caed51 by Abseil Team : Minor cleanup PiperOrigin-RevId: 400087535 -- b001375ec47da3a0434be9ca9a45c0df510e7dda by Abseil Team : Move periodic_sampler from base/internal to profiling/internal PiperOrigin-RevId: 400038533 -- e7e02e686abc3900e723080849a3607d190ef57f by Abseil Team : Move exponential_biased from base/internal to profiling/internal PiperOrigin-RevId: 400020329 GitOrigin-RevId: 17141711ee419daa597a9f31e73721f80143e55a Change-Id: I10924df7e1cc198447813dbe97a374a5cef66b49 --- CMake/AbseilDll.cmake | 8 +- absl/base/BUILD.bazel | 69 ------- absl/base/CMakeLists.txt | 54 ------ absl/base/internal/exponential_biased.cc | 93 --------- absl/base/internal/exponential_biased.h | 130 ------------- absl/base/internal/exponential_biased_test.cc | 199 ------------------- absl/base/internal/periodic_sampler.cc | 53 ------ absl/base/internal/periodic_sampler.h | 211 --------------------- absl/base/internal/periodic_sampler_benchmark.cc | 79 -------- absl/base/internal/periodic_sampler_test.cc | 177 ----------------- absl/base/internal/spinlock.h | 4 +- absl/base/internal/spinlock_wait.h | 2 + absl/container/BUILD.bazel | 2 +- absl/container/internal/hashtablez_sampler.cc | 4 +- absl/debugging/BUILD.bazel | 2 + absl/profiling/BUILD.bazel | 73 +++++++ absl/profiling/CMakeLists.txt | 54 ++++++ absl/profiling/internal/exponential_biased.cc | 93 +++++++++ absl/profiling/internal/exponential_biased.h | 130 +++++++++++++ absl/profiling/internal/exponential_biased_test.cc | 199 +++++++++++++++++++ absl/profiling/internal/periodic_sampler.cc | 53 ++++++ absl/profiling/internal/periodic_sampler.h | 211 +++++++++++++++++++++ .../internal/periodic_sampler_benchmark.cc | 79 ++++++++ absl/profiling/internal/periodic_sampler_test.cc | 177 +++++++++++++++++ absl/status/status.h | 2 +- absl/strings/BUILD.bazel | 2 +- absl/strings/internal/cordz_functions.cc | 4 +- absl/synchronization/mutex_test.cc | 55 +++--- absl/time/internal/cctz/src/cctz_benchmark.cc | 1 + .../internal/cctz/src/time_zone_lookup_test.cc | 1 + absl/time/internal/cctz/testdata/version | 2 +- .../cctz/testdata/zoneinfo/Atlantic/Jan_Mayen | Bin 705 -> 676 bytes 32 files changed, 1118 insertions(+), 1105 deletions(-) delete mode 100644 absl/base/internal/exponential_biased.cc delete mode 100644 absl/base/internal/exponential_biased.h delete mode 100644 absl/base/internal/exponential_biased_test.cc delete mode 100644 absl/base/internal/periodic_sampler.cc delete mode 100644 absl/base/internal/periodic_sampler.h delete mode 100644 absl/base/internal/periodic_sampler_benchmark.cc delete mode 100644 absl/base/internal/periodic_sampler_test.cc create mode 100644 absl/profiling/internal/exponential_biased.cc create mode 100644 absl/profiling/internal/exponential_biased.h create mode 100644 absl/profiling/internal/exponential_biased_test.cc create mode 100644 absl/profiling/internal/periodic_sampler.cc create mode 100644 absl/profiling/internal/periodic_sampler.h create mode 100644 absl/profiling/internal/periodic_sampler_benchmark.cc create mode 100644 absl/profiling/internal/periodic_sampler_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 056343e5..fa323ff8 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -17,8 +17,6 @@ set(ABSL_INTERNAL_DLL_FILES "base/internal/dynamic_annotations.h" "base/internal/endian.h" "base/internal/errno_saver.h" - "base/internal/exponential_biased.cc" - "base/internal/exponential_biased.h" "base/internal/fast_type_id.h" "base/internal/hide_ptr.h" "base/internal/identity.h" @@ -28,8 +26,6 @@ set(ABSL_INTERNAL_DLL_FILES "base/internal/low_level_alloc.h" "base/internal/low_level_scheduling.h" "base/internal/per_thread_tls.h" - "base/internal/periodic_sampler.cc" - "base/internal/periodic_sampler.h" "base/internal/pretty_function.h" "base/internal/raw_logging.cc" "base/internal/raw_logging.h" @@ -133,6 +129,10 @@ set(ABSL_INTERNAL_DLL_FILES "numeric/int128.h" "numeric/internal/bits.h" "numeric/internal/representation.h" + "profiling/internal/exponential_biased.cc" + "profiling/internal/exponential_biased.h" + "profiling/internal/periodic_sampler.cc" + "profiling/internal/periodic_sampler.h" "profiling/internal/sample_recorder.h" "random/bernoulli_distribution.h" "random/beta_distribution.h" diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 65ff0dde..8b09b6e3 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -593,75 +593,6 @@ cc_test( ], ) -cc_library( - name = "exponential_biased", - srcs = ["internal/exponential_biased.cc"], - hdrs = ["internal/exponential_biased.h"], - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl:__subpackages__", - ], - deps = [ - ":config", - ":core_headers", - ], -) - -cc_test( - name = "exponential_biased_test", - size = "small", - srcs = ["internal/exponential_biased_test.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], - deps = [ - ":exponential_biased", - "//absl/strings", - "@com_google_googletest//:gtest_main", - ], -) - -cc_library( - name = "periodic_sampler", - srcs = ["internal/periodic_sampler.cc"], - hdrs = ["internal/periodic_sampler.h"], - copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - deps = [ - ":core_headers", - ":exponential_biased", - ], -) - -cc_test( - name = "periodic_sampler_test", - size = "small", - srcs = ["internal/periodic_sampler_test.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], - deps = [ - ":core_headers", - ":periodic_sampler", - "@com_google_googletest//:gtest_main", - ], -) - -cc_binary( - name = "periodic_sampler_benchmark", - testonly = 1, - srcs = ["internal/periodic_sampler_benchmark.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["benchmark"], - visibility = ["//visibility:private"], - deps = [ - ":core_headers", - ":periodic_sampler", - "@com_github_google_benchmark//:benchmark_main", - ], -) - cc_library( name = "scoped_set_env", testonly = 1, diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 7d56aa13..c7233cb3 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -519,60 +519,6 @@ absl_cc_test( GTest::gtest_main ) -absl_cc_library( - NAME - exponential_biased - SRCS - "internal/exponential_biased.cc" - HDRS - "internal/exponential_biased.h" - COPTS - ${ABSL_DEFAULT_COPTS} - DEPS - absl::config - absl::core_headers -) - -absl_cc_test( - NAME - exponential_biased_test - SRCS - "internal/exponential_biased_test.cc" - COPTS - ${ABSL_TEST_COPTS} - DEPS - absl::exponential_biased - absl::strings - GTest::gmock_main -) - -absl_cc_library( - NAME - periodic_sampler - SRCS - "internal/periodic_sampler.cc" - HDRS - "internal/periodic_sampler.h" - COPTS - ${ABSL_DEFAULT_COPTS} - DEPS - absl::core_headers - absl::exponential_biased -) - -absl_cc_test( - NAME - periodic_sampler_test - SRCS - "internal/periodic_sampler_test.cc" - COPTS - ${ABSL_TEST_COPTS} - DEPS - absl::core_headers - absl::periodic_sampler - GTest::gmock_main -) - absl_cc_library( NAME scoped_set_env diff --git a/absl/base/internal/exponential_biased.cc b/absl/base/internal/exponential_biased.cc deleted file mode 100644 index 05aeea56..00000000 --- a/absl/base/internal/exponential_biased.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/base/internal/exponential_biased.h" - -#include - -#include -#include -#include -#include - -#include "absl/base/attributes.h" -#include "absl/base/optimization.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -// The algorithm generates a random number between 0 and 1 and applies the -// inverse cumulative distribution function for an exponential. Specifically: -// Let m be the inverse of the sample period, then the probability -// distribution function is m*exp(-mx) so the CDF is -// p = 1 - exp(-mx), so -// q = 1 - p = exp(-mx) -// log_e(q) = -mx -// -log_e(q)/m = x -// log_2(q) * (-log_e(2) * 1/m) = x -// In the code, q is actually in the range 1 to 2**26, hence the -26 below -int64_t ExponentialBiased::GetSkipCount(int64_t mean) { - if (ABSL_PREDICT_FALSE(!initialized_)) { - Initialize(); - } - - uint64_t rng = NextRandom(rng_); - rng_ = rng; - - // Take the top 26 bits as the random number - // (This plus the 1<<58 sampling bound give a max possible step of - // 5194297183973780480 bytes.) - // The uint32_t cast is to prevent a (hard-to-reproduce) NAN - // under piii debug for some binaries. - double q = static_cast(rng >> (kPrngNumBits - 26)) + 1.0; - // Put the computed p-value through the CDF of a geometric. - double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean); - // Very large values of interval overflow int64_t. To avoid that, we will - // cheat and clamp any huge values to (int64_t max)/2. This is a potential - // source of bias, but the mean would need to be such a large value that it's - // not likely to come up. For example, with a mean of 1e18, the probability of - // hitting this condition is about 1/1000. For a mean of 1e17, standard - // calculators claim that this event won't happen. - if (interval > static_cast(std::numeric_limits::max() / 2)) { - // Assume huge values are bias neutral, retain bias for next call. - return std::numeric_limits::max() / 2; - } - double value = std::rint(interval); - bias_ = interval - value; - return value; -} - -int64_t ExponentialBiased::GetStride(int64_t mean) { - return GetSkipCount(mean - 1) + 1; -} - -void ExponentialBiased::Initialize() { - // We don't get well distributed numbers from `this` so we call NextRandom() a - // bunch to mush the bits around. We use a global_rand to handle the case - // where the same thread (by memory address) gets created and destroyed - // repeatedly. - ABSL_CONST_INIT static std::atomic global_rand(0); - uint64_t r = reinterpret_cast(this) + - global_rand.fetch_add(1, std::memory_order_relaxed); - for (int i = 0; i < 20; ++i) { - r = NextRandom(r); - } - rng_ = r; - initialized_ = true; -} - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/base/internal/exponential_biased.h b/absl/base/internal/exponential_biased.h deleted file mode 100644 index a81f10e2..00000000 --- a/absl/base/internal/exponential_biased.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ -#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ - -#include - -#include "absl/base/config.h" -#include "absl/base/macros.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -// ExponentialBiased provides a small and fast random number generator for a -// rounded exponential distribution. This generator manages very little state, -// and imposes no synchronization overhead. This makes it useful in specialized -// scenarios requiring minimum overhead, such as stride based periodic sampling. -// -// ExponentialBiased provides two closely related functions, GetSkipCount() and -// GetStride(), both returning a rounded integer defining a number of events -// required before some event with a given mean probability occurs. -// -// The distribution is useful to generate a random wait time or some periodic -// event with a given mean probability. For example, if an action is supposed to -// happen on average once every 'N' events, then we can get a random 'stride' -// counting down how long before the event to happen. For example, if we'd want -// to sample one in every 1000 'Frobber' calls, our code could look like this: -// -// Frobber::Frobber() { -// stride_ = exponential_biased_.GetStride(1000); -// } -// -// void Frobber::Frob(int arg) { -// if (--stride == 0) { -// SampleFrob(arg); -// stride_ = exponential_biased_.GetStride(1000); -// } -// ... -// } -// -// The rounding of the return value creates a bias, especially for smaller means -// where the distribution of the fraction is not evenly distributed. We correct -// this bias by tracking the fraction we rounded up or down on each iteration, -// effectively tracking the distance between the cumulative value, and the -// rounded cumulative value. For example, given a mean of 2: -// -// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923 -// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624 -// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805 -// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206 -// etc... -// -// Adjusting with rounding bias is relatively trivial: -// -// double value = bias_ + exponential_distribution(mean)(); -// double rounded_value = std::rint(value); -// bias_ = value - rounded_value; -// return rounded_value; -// -// This class is thread-compatible. -class ExponentialBiased { - public: - // The number of bits set by NextRandom. - static constexpr int kPrngNumBits = 48; - - // `GetSkipCount()` returns the number of events to skip before some chosen - // event happens. For example, randomly tossing a coin, we will on average - // throw heads once before we get tails. We can simulate random coin tosses - // using GetSkipCount() as: - // - // ExponentialBiased eb; - // for (...) { - // int number_of_heads_before_tail = eb.GetSkipCount(1); - // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) { - // printf("head..."); - // } - // printf("tail\n"); - // } - // - int64_t GetSkipCount(int64_t mean); - - // GetStride() returns the number of events required for a specific event to - // happen. See the class comments for a usage example. `GetStride()` is - // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or - // `GetSkipCount()` depends mostly on what best fits the use case. - int64_t GetStride(int64_t mean); - - // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] - // - // This is public to enable testing. - static uint64_t NextRandom(uint64_t rnd); - - private: - void Initialize(); - - uint64_t rng_{0}; - double bias_{0}; - bool initialized_{false}; -}; - -// Returns the next prng value. -// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 -// This is the lrand64 generator. -inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { - const uint64_t prng_mult = uint64_t{0x5DEECE66D}; - const uint64_t prng_add = 0xB; - const uint64_t prng_mod_power = 48; - const uint64_t prng_mod_mask = - ~((~static_cast(0)) << prng_mod_power); - return (prng_mult * rnd + prng_add) & prng_mod_mask; -} - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ diff --git a/absl/base/internal/exponential_biased_test.cc b/absl/base/internal/exponential_biased_test.cc deleted file mode 100644 index 075583ca..00000000 --- a/absl/base/internal/exponential_biased_test.cc +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/base/internal/exponential_biased.h" - -#include - -#include -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" - -using ::testing::Ge; - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -MATCHER_P2(IsBetween, a, b, - absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a, - " and ", b)) { - return a <= arg && arg <= b; -} - -// Tests of the quality of the random numbers generated -// This uses the Anderson Darling test for uniformity. -// See "Evaluating the Anderson-Darling Distribution" by Marsaglia -// for details. - -// Short cut version of ADinf(z), z>0 (from Marsaglia) -// This returns the p-value for Anderson Darling statistic in -// the limit as n-> infinity. For finite n, apply the error fix below. -double AndersonDarlingInf(double z) { - if (z < 2) { - return exp(-1.2337141 / z) / sqrt(z) * - (2.00012 + - (0.247105 - - (0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) * - z) * - z); - } - return exp( - -exp(1.0776 - - (2.30695 - - (0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) * - z)); -} - -// Corrects the approximation error in AndersonDarlingInf for small values of n -// Add this to AndersonDarlingInf to get a better approximation -// (from Marsaglia) -double AndersonDarlingErrFix(int n, double x) { - if (x > 0.8) { - return (-130.2137 + - (745.2337 - - (1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) * - x) / - n; - } - double cutoff = 0.01265 + 0.1757 / n; - if (x < cutoff) { - double t = x / cutoff; - t = sqrt(t) * (1 - t) * (49 * t - 102); - return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n; - } else { - double t = (x - cutoff) / (0.8 - cutoff); - t = -0.00022633 + - (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) * - t; - return t * (0.04213 + 0.01365 / n) / n; - } -} - -// Returns the AndersonDarling p-value given n and the value of the statistic -double AndersonDarlingPValue(int n, double z) { - double ad = AndersonDarlingInf(z); - double errfix = AndersonDarlingErrFix(n, ad); - return ad + errfix; -} - -double AndersonDarlingStatistic(const std::vector& random_sample) { - int n = random_sample.size(); - double ad_sum = 0; - for (int i = 0; i < n; i++) { - ad_sum += (2 * i + 1) * - std::log(random_sample[i] * (1 - random_sample[n - 1 - i])); - } - double ad_statistic = -n - 1 / static_cast(n) * ad_sum; - return ad_statistic; -} - -// Tests if the array of doubles is uniformly distributed. -// Returns the p-value of the Anderson Darling Statistic -// for the given set of sorted random doubles -// See "Evaluating the Anderson-Darling Distribution" by -// Marsaglia and Marsaglia for details. -double AndersonDarlingTest(const std::vector& random_sample) { - double ad_statistic = AndersonDarlingStatistic(random_sample); - double p = AndersonDarlingPValue(random_sample.size(), ad_statistic); - return p; -} - -TEST(ExponentialBiasedTest, CoinTossDemoWithGetSkipCount) { - ExponentialBiased eb; - for (int runs = 0; runs < 10; ++runs) { - for (int flips = eb.GetSkipCount(1); flips > 0; --flips) { - printf("head..."); - } - printf("tail\n"); - } - int heads = 0; - for (int i = 0; i < 10000000; i += 1 + eb.GetSkipCount(1)) { - ++heads; - } - printf("Heads = %d (%f%%)\n", heads, 100.0 * heads / 10000000); -} - -TEST(ExponentialBiasedTest, SampleDemoWithStride) { - ExponentialBiased eb; - int stride = eb.GetStride(10); - int samples = 0; - for (int i = 0; i < 10000000; ++i) { - if (--stride == 0) { - ++samples; - stride = eb.GetStride(10); - } - } - printf("Samples = %d (%f%%)\n", samples, 100.0 * samples / 10000000); -} - - -// Testing that NextRandom generates uniform random numbers. Applies the -// Anderson-Darling test for uniformity -TEST(ExponentialBiasedTest, TestNextRandom) { - for (auto n : std::vector({ - 10, // Check short-range correlation - 100, 1000, - 10000 // Make sure there's no systemic error - })) { - uint64_t x = 1; - // This assumes that the prng returns 48 bit numbers - uint64_t max_prng_value = static_cast(1) << 48; - // Initialize. - for (int i = 1; i <= 20; i++) { - x = ExponentialBiased::NextRandom(x); - } - std::vector int_random_sample(n); - // Collect samples - for (int i = 0; i < n; i++) { - int_random_sample[i] = x; - x = ExponentialBiased::NextRandom(x); - } - // First sort them... - std::sort(int_random_sample.begin(), int_random_sample.end()); - std::vector random_sample(n); - // Convert them to uniform randoms (in the range [0,1]) - for (int i = 0; i < n; i++) { - random_sample[i] = - static_cast(int_random_sample[i]) / max_prng_value; - } - // Now compute the Anderson-Darling statistic - double ad_pvalue = AndersonDarlingTest(random_sample); - EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001) - << "prng is not uniform: n = " << n << " p = " << ad_pvalue; - } -} - -// The generator needs to be available as a thread_local and as a static -// variable. -TEST(ExponentialBiasedTest, InitializationModes) { - ABSL_CONST_INIT static ExponentialBiased eb_static; - EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0)); - -#ifdef ABSL_HAVE_THREAD_LOCAL - thread_local ExponentialBiased eb_thread; - EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0)); -#endif - - ExponentialBiased eb_stack; - EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0)); -} - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/base/internal/periodic_sampler.cc b/absl/base/internal/periodic_sampler.cc deleted file mode 100644 index 520dabba..00000000 --- a/absl/base/internal/periodic_sampler.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/base/internal/periodic_sampler.h" - -#include - -#include "absl/base/internal/exponential_biased.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept { - return rng_.GetStride(period); -} - -bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { - int current_period = period(); - - // Deal with period case 0 (always off) and 1 (always on) - if (ABSL_PREDICT_FALSE(current_period < 2)) { - stride_ = 0; - return current_period == 1; - } - - // Check if this is the first call to Sample() - if (ABSL_PREDICT_FALSE(stride_ == 1)) { - stride_ = static_cast(-GetExponentialBiased(current_period)); - if (static_cast(stride_) < -1) { - ++stride_; - return false; - } - } - - stride_ = static_cast(-GetExponentialBiased(current_period)); - return true; -} - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/base/internal/periodic_sampler.h b/absl/base/internal/periodic_sampler.h deleted file mode 100644 index f8a86796..00000000 --- a/absl/base/internal/periodic_sampler.h +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ -#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ - -#include - -#include - -#include "absl/base/internal/exponential_biased.h" -#include "absl/base/optimization.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -// PeriodicSamplerBase provides the basic period sampler implementation. -// -// This is the base class for the templated PeriodicSampler class, which holds -// a global std::atomic value identified by a user defined tag, such that -// each specific PeriodSampler implementation holds its own global period. -// -// PeriodicSamplerBase is thread-compatible except where stated otherwise. -class PeriodicSamplerBase { - public: - // PeriodicSamplerBase is trivial / copyable / movable / destructible. - PeriodicSamplerBase() = default; - PeriodicSamplerBase(PeriodicSamplerBase&&) = default; - PeriodicSamplerBase(const PeriodicSamplerBase&) = default; - - // Returns true roughly once every `period` calls. This is established by a - // randomly picked `stride` that is counted down on each call to `Sample`. - // This stride is picked such that the probability of `Sample()` returning - // true is 1 in `period`. - inline bool Sample() noexcept; - - // The below methods are intended for optimized use cases where the - // size of the inlined fast path code is highly important. Applications - // should use the `Sample()` method unless they have proof that their - // specific use case requires the optimizations offered by these methods. - // - // An example of such a use case is SwissTable sampling. All sampling checks - // are in inlined SwissTable methods, and the number of call sites is huge. - // In this case, the inlined code size added to each translation unit calling - // SwissTable methods is non-trivial. - // - // The `SubtleMaybeSample()` function spuriously returns true even if the - // function should not be sampled, applications MUST match each call to - // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call, - // and use the result of the latter as the sampling decision. - // In other words: the code should logically be equivalent to: - // - // if (SubtleMaybeSample() && SubtleConfirmSample()) { - // // Sample this call - // } - // - // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can - // be placed out of line, for example, the typical use case looks as follows: - // - // // --- frobber.h ----------- - // void FrobberSampled(); - // - // inline void FrobberImpl() { - // // ... - // } - // - // inline void Frobber() { - // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) { - // FrobberSampled(); - // } else { - // FrobberImpl(); - // } - // } - // - // // --- frobber.cc ----------- - // void FrobberSampled() { - // if (!sampler.SubtleConfirmSample())) { - // // Spurious false positive - // FrobberImpl(); - // return; - // } - // - // // Sampled execution - // // ... - // } - inline bool SubtleMaybeSample() noexcept; - bool SubtleConfirmSample() noexcept; - - protected: - // We explicitly don't use a virtual destructor as this class is never - // virtually destroyed, and it keeps the class trivial, which avoids TLS - // prologue and epilogue code for our TLS instances. - ~PeriodicSamplerBase() = default; - - // Returns the next stride for our sampler. - // This function is virtual for testing purposes only. - virtual int64_t GetExponentialBiased(int period) noexcept; - - private: - // Returns the current period of this sampler. Thread-safe. - virtual int period() const noexcept = 0; - - // Keep and decrement stride_ as an unsigned integer, but compare the value - // to zero casted as a signed int. clang and msvc do not create optimum code - // if we use signed for the combined decrement and sign comparison. - // - // Below 3 alternative options, all compiles generate the best code - // using the unsigned increment <---> signed int comparison option. - // - // Option 1: - // int64_t stride_; - // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... } - // - // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA - // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt - // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd - // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W - // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS - // - // Option 2: - // int64_t stride_ = 0; - // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... } - // - // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK - // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA - // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX - // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd - // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE - // - // Option 3: - // uint64_t stride_; - // if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { ... } - // - // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy - // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE - // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4 - // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD - // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 - uint64_t stride_ = 0; - ExponentialBiased rng_; -}; - -inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { - // See comments on `stride_` for the unsigned increment / signed compare. - if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { - return false; - } - return true; -} - -inline bool PeriodicSamplerBase::Sample() noexcept { - return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() - : false; -} - -// PeriodicSampler is a concreted periodic sampler implementation. -// The user provided Tag identifies the implementation, and is required to -// isolate the global state of this instance from other instances. -// -// Typical use case: -// -// struct HashTablezTag {}; -// thread_local PeriodicSampler sampler; -// -// void HashTableSamplingLogic(...) { -// if (sampler.Sample()) { -// HashTableSlowSamplePath(...); -// } -// } -// -template -class PeriodicSampler final : public PeriodicSamplerBase { - public: - ~PeriodicSampler() = default; - - int period() const noexcept final { - return period_.load(std::memory_order_relaxed); - } - - // Sets the global period for this sampler. Thread-safe. - // Setting a period of 0 disables the sampler, i.e., every call to Sample() - // will return false. Setting a period of 1 puts the sampler in 'always on' - // mode, i.e., every call to Sample() returns true. - static void SetGlobalPeriod(int period) { - period_.store(period, std::memory_order_relaxed); - } - - private: - static std::atomic period_; -}; - -template -std::atomic PeriodicSampler::period_(default_period); - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ diff --git a/absl/base/internal/periodic_sampler_benchmark.cc b/absl/base/internal/periodic_sampler_benchmark.cc deleted file mode 100644 index 5ad469ce..00000000 --- a/absl/base/internal/periodic_sampler_benchmark.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "benchmark/benchmark.h" -#include "absl/base/internal/periodic_sampler.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { -namespace { - -template -void BM_Sample(Sampler* sampler, benchmark::State& state) { - for (auto _ : state) { - benchmark::DoNotOptimize(sampler); - benchmark::DoNotOptimize(sampler->Sample()); - } -} - -template -void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) { - for (auto _ : state) { - benchmark::DoNotOptimize(sampler); - if (ABSL_PREDICT_FALSE(sampler->SubtleMaybeSample())) { - benchmark::DoNotOptimize(sampler->SubtleConfirmSample()); - } - } -} - -void BM_PeriodicSampler_TinySample(benchmark::State& state) { - struct Tag {}; - PeriodicSampler sampler; - BM_Sample(&sampler, state); -} -BENCHMARK(BM_PeriodicSampler_TinySample); - -void BM_PeriodicSampler_ShortSample(benchmark::State& state) { - struct Tag {}; - PeriodicSampler sampler; - BM_Sample(&sampler, state); -} -BENCHMARK(BM_PeriodicSampler_ShortSample); - -void BM_PeriodicSampler_LongSample(benchmark::State& state) { - struct Tag {}; - PeriodicSampler sampler; - BM_Sample(&sampler, state); -} -BENCHMARK(BM_PeriodicSampler_LongSample); - -void BM_PeriodicSampler_LongSampleMinunumInlined(benchmark::State& state) { - struct Tag {}; - PeriodicSampler sampler; - BM_SampleMinunumInlined(&sampler, state); -} -BENCHMARK(BM_PeriodicSampler_LongSampleMinunumInlined); - -void BM_PeriodicSampler_Disabled(benchmark::State& state) { - struct Tag {}; - PeriodicSampler sampler; - BM_Sample(&sampler, state); -} -BENCHMARK(BM_PeriodicSampler_Disabled); - -} // namespace -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/base/internal/periodic_sampler_test.cc b/absl/base/internal/periodic_sampler_test.cc deleted file mode 100644 index 3b301e37..00000000 --- a/absl/base/internal/periodic_sampler_test.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/base/internal/periodic_sampler.h" - -#include // NOLINT(build/c++11) - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/attributes.h" -#include "absl/base/macros.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { -namespace { - -using testing::Eq; -using testing::Return; -using testing::StrictMock; - -class MockPeriodicSampler : public PeriodicSamplerBase { - public: - virtual ~MockPeriodicSampler() = default; - - MOCK_METHOD(int, period, (), (const, noexcept)); - MOCK_METHOD(int64_t, GetExponentialBiased, (int), (noexcept)); -}; - -TEST(PeriodicSamplerBaseTest, Sample) { - StrictMock sampler; - - EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16)); - EXPECT_CALL(sampler, GetExponentialBiased(16)) - .WillOnce(Return(2)) - .WillOnce(Return(3)) - .WillOnce(Return(4)); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_TRUE(sampler.Sample()); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); - EXPECT_TRUE(sampler.Sample()); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); -} - -TEST(PeriodicSamplerBaseTest, ImmediatelySample) { - StrictMock sampler; - - EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); - EXPECT_CALL(sampler, GetExponentialBiased(16)) - .WillOnce(Return(1)) - .WillOnce(Return(2)) - .WillOnce(Return(3)); - - EXPECT_TRUE(sampler.Sample()); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_TRUE(sampler.Sample()); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); -} - -TEST(PeriodicSamplerBaseTest, Disabled) { - StrictMock sampler; - - EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(0)); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); -} - -TEST(PeriodicSamplerBaseTest, AlwaysOn) { - StrictMock sampler; - - EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(1)); - - EXPECT_TRUE(sampler.Sample()); - EXPECT_TRUE(sampler.Sample()); - EXPECT_TRUE(sampler.Sample()); -} - -TEST(PeriodicSamplerBaseTest, Disable) { - StrictMock sampler; - - EXPECT_CALL(sampler, period()).WillOnce(Return(16)); - EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(3)); - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); - - EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(0)); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); -} - -TEST(PeriodicSamplerBaseTest, Enable) { - StrictMock sampler; - - EXPECT_CALL(sampler, period()).WillOnce(Return(0)); - EXPECT_FALSE(sampler.Sample()); - - EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); - EXPECT_CALL(sampler, GetExponentialBiased(16)) - .Times(2) - .WillRepeatedly(Return(3)); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); - EXPECT_TRUE(sampler.Sample()); - - EXPECT_FALSE(sampler.Sample()); - EXPECT_FALSE(sampler.Sample()); -} - -TEST(PeriodicSamplerTest, ConstructConstInit) { - struct Tag {}; - ABSL_CONST_INIT static PeriodicSampler sampler; - (void)sampler; -} - -TEST(PeriodicSamplerTest, DefaultPeriod0) { - struct Tag {}; - PeriodicSampler sampler; - EXPECT_THAT(sampler.period(), Eq(0)); -} - -TEST(PeriodicSamplerTest, DefaultPeriod) { - struct Tag {}; - PeriodicSampler sampler; - EXPECT_THAT(sampler.period(), Eq(100)); -} - -TEST(PeriodicSamplerTest, SetGlobalPeriod) { - struct Tag1 {}; - struct Tag2 {}; - PeriodicSampler sampler1; - PeriodicSampler sampler2; - - EXPECT_THAT(sampler1.period(), Eq(25)); - EXPECT_THAT(sampler2.period(), Eq(50)); - - std::thread thread([] { - PeriodicSampler sampler1; - PeriodicSampler sampler2; - EXPECT_THAT(sampler1.period(), Eq(25)); - EXPECT_THAT(sampler2.period(), Eq(50)); - sampler1.SetGlobalPeriod(10); - sampler2.SetGlobalPeriod(20); - }); - thread.join(); - - EXPECT_THAT(sampler1.period(), Eq(10)); - EXPECT_THAT(sampler2.period(), Eq(20)); -} - -} // namespace -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index c73b5e09..ac40daff 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h @@ -16,13 +16,15 @@ // Most users requiring mutual exclusion should use Mutex. // SpinLock is provided for use in two situations: -// - for use in code that Mutex itself depends on +// - for use by Abseil internal code that Mutex itself depends on // - for async signal safety (see below) // SpinLock is async signal safe. If a spinlock is used within a signal // handler, all code that acquires the lock must ensure that the signal cannot // arrive while they are holding the lock. Typically, this is done by blocking // the signal. +// +// Threads waiting on a SpinLock may be woken in an arbitrary order. #ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_ #define ABSL_BASE_INTERNAL_SPINLOCK_H_ diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h index 579bd09f..9a1adcda 100644 --- a/absl/base/internal/spinlock_wait.h +++ b/absl/base/internal/spinlock_wait.h @@ -39,6 +39,8 @@ struct SpinLockWaitTransition { // satisfying 0<=i *w, int n, const SpinLockWaitTransition trans[], SchedulingMode scheduling_mode); diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index c9d387d6..9e3bc06a 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -510,9 +510,9 @@ cc_library( ":have_sse", "//absl/base", "//absl/base:core_headers", - "//absl/base:exponential_biased", "//absl/debugging:stacktrace", "//absl/memory", + "//absl/profiling:exponential_biased", "//absl/profiling:sample_recorder", "//absl/synchronization", "//absl/utility", diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 4b133705..7070912e 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -21,10 +21,10 @@ #include #include "absl/base/attributes.h" -#include "absl/base/internal/exponential_biased.h" #include "absl/container/internal/have_sse.h" #include "absl/debugging/stacktrace.h" #include "absl/memory/memory.h" +#include "absl/profiling/internal/exponential_biased.h" #include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/mutex.h" @@ -40,7 +40,7 @@ ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased +ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased g_exponential_biased_generator; #endif diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index b536a044..b503da51 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -183,6 +183,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -197,6 +198,7 @@ cc_library( srcs = ["internal/demangle.cc"], hdrs = ["internal/demangle.h"], copts = ABSL_DEFAULT_COPTS, + visibility = ["//visibility:private"], deps = [ "//absl/base", "//absl/base:config", diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel index ba4811b3..496a06b2 100644 --- a/absl/profiling/BUILD.bazel +++ b/absl/profiling/BUILD.bazel @@ -16,6 +16,7 @@ load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", "ABSL_DEFAULT_LINKOPTS", + "ABSL_TEST_COPTS", ) package(default_visibility = ["//visibility:private"]) @@ -51,3 +52,75 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "exponential_biased", + srcs = ["internal/exponential_biased.cc"], + hdrs = ["internal/exponential_biased.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "exponential_biased_test", + size = "small", + srcs = ["internal/exponential_biased_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":exponential_biased", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "periodic_sampler", + srcs = ["internal/periodic_sampler.cc"], + hdrs = ["internal/periodic_sampler.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":exponential_biased", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "periodic_sampler_test", + size = "small", + srcs = ["internal/periodic_sampler_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":periodic_sampler", + "//absl/base:core_headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_binary( + name = "periodic_sampler_benchmark", + testonly = 1, + srcs = ["internal/periodic_sampler_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":periodic_sampler", + "//absl/base:core_headers", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/profiling/CMakeLists.txt b/absl/profiling/CMakeLists.txt index 7b6a7780..9b3a7102 100644 --- a/absl/profiling/CMakeLists.txt +++ b/absl/profiling/CMakeLists.txt @@ -37,3 +37,57 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_library( + NAME + exponential_biased + SRCS + "internal/exponential_biased.cc" + HDRS + "internal/exponential_biased.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers +) + +absl_cc_test( + NAME + exponential_biased_test + SRCS + "internal/exponential_biased_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::exponential_biased + absl::strings + GTest::gmock_main +) + +absl_cc_library( + NAME + periodic_sampler + SRCS + "internal/periodic_sampler.cc" + HDRS + "internal/periodic_sampler.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::exponential_biased +) + +absl_cc_test( + NAME + periodic_sampler_test + SRCS + "internal/periodic_sampler_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::core_headers + absl::periodic_sampler + GTest::gmock_main +) + diff --git a/absl/profiling/internal/exponential_biased.cc b/absl/profiling/internal/exponential_biased.cc new file mode 100644 index 00000000..81d9a757 --- /dev/null +++ b/absl/profiling/internal/exponential_biased.cc @@ -0,0 +1,93 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/profiling/internal/exponential_biased.h" + +#include + +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +// The algorithm generates a random number between 0 and 1 and applies the +// inverse cumulative distribution function for an exponential. Specifically: +// Let m be the inverse of the sample period, then the probability +// distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below +int64_t ExponentialBiased::GetSkipCount(int64_t mean) { + if (ABSL_PREDICT_FALSE(!initialized_)) { + Initialize(); + } + + uint64_t rng = NextRandom(rng_); + rng_ = rng; + + // Take the top 26 bits as the random number + // (This plus the 1<<58 sampling bound give a max possible step of + // 5194297183973780480 bytes.) + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast(rng >> (kPrngNumBits - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean); + // Very large values of interval overflow int64_t. To avoid that, we will + // cheat and clamp any huge values to (int64_t max)/2. This is a potential + // source of bias, but the mean would need to be such a large value that it's + // not likely to come up. For example, with a mean of 1e18, the probability of + // hitting this condition is about 1/1000. For a mean of 1e17, standard + // calculators claim that this event won't happen. + if (interval > static_cast(std::numeric_limits::max() / 2)) { + // Assume huge values are bias neutral, retain bias for next call. + return std::numeric_limits::max() / 2; + } + double value = std::rint(interval); + bias_ = interval - value; + return value; +} + +int64_t ExponentialBiased::GetStride(int64_t mean) { + return GetSkipCount(mean - 1) + 1; +} + +void ExponentialBiased::Initialize() { + // We don't get well distributed numbers from `this` so we call NextRandom() a + // bunch to mush the bits around. We use a global_rand to handle the case + // where the same thread (by memory address) gets created and destroyed + // repeatedly. + ABSL_CONST_INIT static std::atomic global_rand(0); + uint64_t r = reinterpret_cast(this) + + global_rand.fetch_add(1, std::memory_order_relaxed); + for (int i = 0; i < 20; ++i) { + r = NextRandom(r); + } + rng_ = r; + initialized_ = true; +} + +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/profiling/internal/exponential_biased.h b/absl/profiling/internal/exponential_biased.h new file mode 100644 index 00000000..d31f7782 --- /dev/null +++ b/absl/profiling/internal/exponential_biased.h @@ -0,0 +1,130 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_ +#define ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_ + +#include + +#include "absl/base/config.h" +#include "absl/base/macros.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +// ExponentialBiased provides a small and fast random number generator for a +// rounded exponential distribution. This generator manages very little state, +// and imposes no synchronization overhead. This makes it useful in specialized +// scenarios requiring minimum overhead, such as stride based periodic sampling. +// +// ExponentialBiased provides two closely related functions, GetSkipCount() and +// GetStride(), both returning a rounded integer defining a number of events +// required before some event with a given mean probability occurs. +// +// The distribution is useful to generate a random wait time or some periodic +// event with a given mean probability. For example, if an action is supposed to +// happen on average once every 'N' events, then we can get a random 'stride' +// counting down how long before the event to happen. For example, if we'd want +// to sample one in every 1000 'Frobber' calls, our code could look like this: +// +// Frobber::Frobber() { +// stride_ = exponential_biased_.GetStride(1000); +// } +// +// void Frobber::Frob(int arg) { +// if (--stride == 0) { +// SampleFrob(arg); +// stride_ = exponential_biased_.GetStride(1000); +// } +// ... +// } +// +// The rounding of the return value creates a bias, especially for smaller means +// where the distribution of the fraction is not evenly distributed. We correct +// this bias by tracking the fraction we rounded up or down on each iteration, +// effectively tracking the distance between the cumulative value, and the +// rounded cumulative value. For example, given a mean of 2: +// +// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923 +// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624 +// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805 +// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206 +// etc... +// +// Adjusting with rounding bias is relatively trivial: +// +// double value = bias_ + exponential_distribution(mean)(); +// double rounded_value = std::rint(value); +// bias_ = value - rounded_value; +// return rounded_value; +// +// This class is thread-compatible. +class ExponentialBiased { + public: + // The number of bits set by NextRandom. + static constexpr int kPrngNumBits = 48; + + // `GetSkipCount()` returns the number of events to skip before some chosen + // event happens. For example, randomly tossing a coin, we will on average + // throw heads once before we get tails. We can simulate random coin tosses + // using GetSkipCount() as: + // + // ExponentialBiased eb; + // for (...) { + // int number_of_heads_before_tail = eb.GetSkipCount(1); + // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) { + // printf("head..."); + // } + // printf("tail\n"); + // } + // + int64_t GetSkipCount(int64_t mean); + + // GetStride() returns the number of events required for a specific event to + // happen. See the class comments for a usage example. `GetStride()` is + // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or + // `GetSkipCount()` depends mostly on what best fits the use case. + int64_t GetStride(int64_t mean); + + // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] + // + // This is public to enable testing. + static uint64_t NextRandom(uint64_t rnd); + + private: + void Initialize(); + + uint64_t rng_{0}; + double bias_{0}; + bool initialized_{false}; +}; + +// Returns the next prng value. +// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 +// This is the lrand64 generator. +inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { + const uint64_t prng_mult = uint64_t{0x5DEECE66D}; + const uint64_t prng_add = 0xB; + const uint64_t prng_mod_power = 48; + const uint64_t prng_mod_mask = + ~((~static_cast(0)) << prng_mod_power); + return (prng_mult * rnd + prng_add) & prng_mod_mask; +} + +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_ diff --git a/absl/profiling/internal/exponential_biased_test.cc b/absl/profiling/internal/exponential_biased_test.cc new file mode 100644 index 00000000..5675001d --- /dev/null +++ b/absl/profiling/internal/exponential_biased_test.cc @@ -0,0 +1,199 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/profiling/internal/exponential_biased.h" + +#include + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" + +using ::testing::Ge; + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +MATCHER_P2(IsBetween, a, b, + absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a, + " and ", b)) { + return a <= arg && arg <= b; +} + +// Tests of the quality of the random numbers generated +// This uses the Anderson Darling test for uniformity. +// See "Evaluating the Anderson-Darling Distribution" by Marsaglia +// for details. + +// Short cut version of ADinf(z), z>0 (from Marsaglia) +// This returns the p-value for Anderson Darling statistic in +// the limit as n-> infinity. For finite n, apply the error fix below. +double AndersonDarlingInf(double z) { + if (z < 2) { + return exp(-1.2337141 / z) / sqrt(z) * + (2.00012 + + (0.247105 - + (0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) * + z) * + z); + } + return exp( + -exp(1.0776 - + (2.30695 - + (0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) * + z)); +} + +// Corrects the approximation error in AndersonDarlingInf for small values of n +// Add this to AndersonDarlingInf to get a better approximation +// (from Marsaglia) +double AndersonDarlingErrFix(int n, double x) { + if (x > 0.8) { + return (-130.2137 + + (745.2337 - + (1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) * + x) / + n; + } + double cutoff = 0.01265 + 0.1757 / n; + if (x < cutoff) { + double t = x / cutoff; + t = sqrt(t) * (1 - t) * (49 * t - 102); + return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n; + } else { + double t = (x - cutoff) / (0.8 - cutoff); + t = -0.00022633 + + (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) * + t; + return t * (0.04213 + 0.01365 / n) / n; + } +} + +// Returns the AndersonDarling p-value given n and the value of the statistic +double AndersonDarlingPValue(int n, double z) { + double ad = AndersonDarlingInf(z); + double errfix = AndersonDarlingErrFix(n, ad); + return ad + errfix; +} + +double AndersonDarlingStatistic(const std::vector& random_sample) { + int n = random_sample.size(); + double ad_sum = 0; + for (int i = 0; i < n; i++) { + ad_sum += (2 * i + 1) * + std::log(random_sample[i] * (1 - random_sample[n - 1 - i])); + } + double ad_statistic = -n - 1 / static_cast(n) * ad_sum; + return ad_statistic; +} + +// Tests if the array of doubles is uniformly distributed. +// Returns the p-value of the Anderson Darling Statistic +// for the given set of sorted random doubles +// See "Evaluating the Anderson-Darling Distribution" by +// Marsaglia and Marsaglia for details. +double AndersonDarlingTest(const std::vector& random_sample) { + double ad_statistic = AndersonDarlingStatistic(random_sample); + double p = AndersonDarlingPValue(random_sample.size(), ad_statistic); + return p; +} + +TEST(ExponentialBiasedTest, CoinTossDemoWithGetSkipCount) { + ExponentialBiased eb; + for (int runs = 0; runs < 10; ++runs) { + for (int flips = eb.GetSkipCount(1); flips > 0; --flips) { + printf("head..."); + } + printf("tail\n"); + } + int heads = 0; + for (int i = 0; i < 10000000; i += 1 + eb.GetSkipCount(1)) { + ++heads; + } + printf("Heads = %d (%f%%)\n", heads, 100.0 * heads / 10000000); +} + +TEST(ExponentialBiasedTest, SampleDemoWithStride) { + ExponentialBiased eb; + int stride = eb.GetStride(10); + int samples = 0; + for (int i = 0; i < 10000000; ++i) { + if (--stride == 0) { + ++samples; + stride = eb.GetStride(10); + } + } + printf("Samples = %d (%f%%)\n", samples, 100.0 * samples / 10000000); +} + + +// Testing that NextRandom generates uniform random numbers. Applies the +// Anderson-Darling test for uniformity +TEST(ExponentialBiasedTest, TestNextRandom) { + for (auto n : std::vector({ + 10, // Check short-range correlation + 100, 1000, + 10000 // Make sure there's no systemic error + })) { + uint64_t x = 1; + // This assumes that the prng returns 48 bit numbers + uint64_t max_prng_value = static_cast(1) << 48; + // Initialize. + for (int i = 1; i <= 20; i++) { + x = ExponentialBiased::NextRandom(x); + } + std::vector int_random_sample(n); + // Collect samples + for (int i = 0; i < n; i++) { + int_random_sample[i] = x; + x = ExponentialBiased::NextRandom(x); + } + // First sort them... + std::sort(int_random_sample.begin(), int_random_sample.end()); + std::vector random_sample(n); + // Convert them to uniform randoms (in the range [0,1]) + for (int i = 0; i < n; i++) { + random_sample[i] = + static_cast(int_random_sample[i]) / max_prng_value; + } + // Now compute the Anderson-Darling statistic + double ad_pvalue = AndersonDarlingTest(random_sample); + EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001) + << "prng is not uniform: n = " << n << " p = " << ad_pvalue; + } +} + +// The generator needs to be available as a thread_local and as a static +// variable. +TEST(ExponentialBiasedTest, InitializationModes) { + ABSL_CONST_INIT static ExponentialBiased eb_static; + EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0)); + +#ifdef ABSL_HAVE_THREAD_LOCAL + thread_local ExponentialBiased eb_thread; + EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0)); +#endif + + ExponentialBiased eb_stack; + EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0)); +} + +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/profiling/internal/periodic_sampler.cc b/absl/profiling/internal/periodic_sampler.cc new file mode 100644 index 00000000..a738a82c --- /dev/null +++ b/absl/profiling/internal/periodic_sampler.cc @@ -0,0 +1,53 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/profiling/internal/periodic_sampler.h" + +#include + +#include "absl/profiling/internal/exponential_biased.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept { + return rng_.GetStride(period); +} + +bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { + int current_period = period(); + + // Deal with period case 0 (always off) and 1 (always on) + if (ABSL_PREDICT_FALSE(current_period < 2)) { + stride_ = 0; + return current_period == 1; + } + + // Check if this is the first call to Sample() + if (ABSL_PREDICT_FALSE(stride_ == 1)) { + stride_ = static_cast(-GetExponentialBiased(current_period)); + if (static_cast(stride_) < -1) { + ++stride_; + return false; + } + } + + stride_ = static_cast(-GetExponentialBiased(current_period)); + return true; +} + +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/profiling/internal/periodic_sampler.h b/absl/profiling/internal/periodic_sampler.h new file mode 100644 index 00000000..54f0af45 --- /dev/null +++ b/absl/profiling/internal/periodic_sampler.h @@ -0,0 +1,211 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ +#define ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ + +#include + +#include + +#include "absl/base/optimization.h" +#include "absl/profiling/internal/exponential_biased.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +// PeriodicSamplerBase provides the basic period sampler implementation. +// +// This is the base class for the templated PeriodicSampler class, which holds +// a global std::atomic value identified by a user defined tag, such that +// each specific PeriodSampler implementation holds its own global period. +// +// PeriodicSamplerBase is thread-compatible except where stated otherwise. +class PeriodicSamplerBase { + public: + // PeriodicSamplerBase is trivial / copyable / movable / destructible. + PeriodicSamplerBase() = default; + PeriodicSamplerBase(PeriodicSamplerBase&&) = default; + PeriodicSamplerBase(const PeriodicSamplerBase&) = default; + + // Returns true roughly once every `period` calls. This is established by a + // randomly picked `stride` that is counted down on each call to `Sample`. + // This stride is picked such that the probability of `Sample()` returning + // true is 1 in `period`. + inline bool Sample() noexcept; + + // The below methods are intended for optimized use cases where the + // size of the inlined fast path code is highly important. Applications + // should use the `Sample()` method unless they have proof that their + // specific use case requires the optimizations offered by these methods. + // + // An example of such a use case is SwissTable sampling. All sampling checks + // are in inlined SwissTable methods, and the number of call sites is huge. + // In this case, the inlined code size added to each translation unit calling + // SwissTable methods is non-trivial. + // + // The `SubtleMaybeSample()` function spuriously returns true even if the + // function should not be sampled, applications MUST match each call to + // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call, + // and use the result of the latter as the sampling decision. + // In other words: the code should logically be equivalent to: + // + // if (SubtleMaybeSample() && SubtleConfirmSample()) { + // // Sample this call + // } + // + // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can + // be placed out of line, for example, the typical use case looks as follows: + // + // // --- frobber.h ----------- + // void FrobberSampled(); + // + // inline void FrobberImpl() { + // // ... + // } + // + // inline void Frobber() { + // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) { + // FrobberSampled(); + // } else { + // FrobberImpl(); + // } + // } + // + // // --- frobber.cc ----------- + // void FrobberSampled() { + // if (!sampler.SubtleConfirmSample())) { + // // Spurious false positive + // FrobberImpl(); + // return; + // } + // + // // Sampled execution + // // ... + // } + inline bool SubtleMaybeSample() noexcept; + bool SubtleConfirmSample() noexcept; + + protected: + // We explicitly don't use a virtual destructor as this class is never + // virtually destroyed, and it keeps the class trivial, which avoids TLS + // prologue and epilogue code for our TLS instances. + ~PeriodicSamplerBase() = default; + + // Returns the next stride for our sampler. + // This function is virtual for testing purposes only. + virtual int64_t GetExponentialBiased(int period) noexcept; + + private: + // Returns the current period of this sampler. Thread-safe. + virtual int period() const noexcept = 0; + + // Keep and decrement stride_ as an unsigned integer, but compare the value + // to zero casted as a signed int. clang and msvc do not create optimum code + // if we use signed for the combined decrement and sign comparison. + // + // Below 3 alternative options, all compiles generate the best code + // using the unsigned increment <---> signed int comparison option. + // + // Option 1: + // int64_t stride_; + // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA + // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt + // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd + // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W + // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS + // + // Option 2: + // int64_t stride_ = 0; + // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK + // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA + // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX + // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd + // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE + // + // Option 3: + // uint64_t stride_; + // if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy + // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE + // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4 + // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD + // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 + uint64_t stride_ = 0; + absl::profiling_internal::ExponentialBiased rng_; +}; + +inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { + // See comments on `stride_` for the unsigned increment / signed compare. + if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { + return false; + } + return true; +} + +inline bool PeriodicSamplerBase::Sample() noexcept { + return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() + : false; +} + +// PeriodicSampler is a concreted periodic sampler implementation. +// The user provided Tag identifies the implementation, and is required to +// isolate the global state of this instance from other instances. +// +// Typical use case: +// +// struct HashTablezTag {}; +// thread_local PeriodicSampler sampler; +// +// void HashTableSamplingLogic(...) { +// if (sampler.Sample()) { +// HashTableSlowSamplePath(...); +// } +// } +// +template +class PeriodicSampler final : public PeriodicSamplerBase { + public: + ~PeriodicSampler() = default; + + int period() const noexcept final { + return period_.load(std::memory_order_relaxed); + } + + // Sets the global period for this sampler. Thread-safe. + // Setting a period of 0 disables the sampler, i.e., every call to Sample() + // will return false. Setting a period of 1 puts the sampler in 'always on' + // mode, i.e., every call to Sample() returns true. + static void SetGlobalPeriod(int period) { + period_.store(period, std::memory_order_relaxed); + } + + private: + static std::atomic period_; +}; + +template +std::atomic PeriodicSampler::period_(default_period); + +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ diff --git a/absl/profiling/internal/periodic_sampler_benchmark.cc b/absl/profiling/internal/periodic_sampler_benchmark.cc new file mode 100644 index 00000000..8f0e5574 --- /dev/null +++ b/absl/profiling/internal/periodic_sampler_benchmark.cc @@ -0,0 +1,79 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/profiling/internal/periodic_sampler.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { +namespace { + +template +void BM_Sample(Sampler* sampler, benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(sampler); + benchmark::DoNotOptimize(sampler->Sample()); + } +} + +template +void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(sampler); + if (ABSL_PREDICT_FALSE(sampler->SubtleMaybeSample())) { + benchmark::DoNotOptimize(sampler->SubtleConfirmSample()); + } + } +} + +void BM_PeriodicSampler_TinySample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_TinySample); + +void BM_PeriodicSampler_ShortSample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_ShortSample); + +void BM_PeriodicSampler_LongSample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_LongSample); + +void BM_PeriodicSampler_LongSampleMinunumInlined(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_SampleMinunumInlined(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_LongSampleMinunumInlined); + +void BM_PeriodicSampler_Disabled(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_Disabled); + +} // namespace +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/profiling/internal/periodic_sampler_test.cc b/absl/profiling/internal/periodic_sampler_test.cc new file mode 100644 index 00000000..ef986f38 --- /dev/null +++ b/absl/profiling/internal/periodic_sampler_test.cc @@ -0,0 +1,177 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/profiling/internal/periodic_sampler.h" + +#include // NOLINT(build/c++11) + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/macros.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { +namespace { + +using testing::Eq; +using testing::Return; +using testing::StrictMock; + +class MockPeriodicSampler : public PeriodicSamplerBase { + public: + virtual ~MockPeriodicSampler() = default; + + MOCK_METHOD(int, period, (), (const, noexcept)); + MOCK_METHOD(int64_t, GetExponentialBiased, (int), (noexcept)); +}; + +TEST(PeriodicSamplerBaseTest, Sample) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .WillOnce(Return(2)) + .WillOnce(Return(3)) + .WillOnce(Return(4)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, ImmediatelySample) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .WillOnce(Return(3)); + + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Disabled) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(0)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, AlwaysOn) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(1)); + + EXPECT_TRUE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Disable) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).WillOnce(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(3)); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(0)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Enable) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).WillOnce(Return(0)); + EXPECT_FALSE(sampler.Sample()); + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .Times(2) + .WillRepeatedly(Return(3)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerTest, ConstructConstInit) { + struct Tag {}; + ABSL_CONST_INIT static PeriodicSampler sampler; + (void)sampler; +} + +TEST(PeriodicSamplerTest, DefaultPeriod0) { + struct Tag {}; + PeriodicSampler sampler; + EXPECT_THAT(sampler.period(), Eq(0)); +} + +TEST(PeriodicSamplerTest, DefaultPeriod) { + struct Tag {}; + PeriodicSampler sampler; + EXPECT_THAT(sampler.period(), Eq(100)); +} + +TEST(PeriodicSamplerTest, SetGlobalPeriod) { + struct Tag1 {}; + struct Tag2 {}; + PeriodicSampler sampler1; + PeriodicSampler sampler2; + + EXPECT_THAT(sampler1.period(), Eq(25)); + EXPECT_THAT(sampler2.period(), Eq(50)); + + std::thread thread([] { + PeriodicSampler sampler1; + PeriodicSampler sampler2; + EXPECT_THAT(sampler1.period(), Eq(25)); + EXPECT_THAT(sampler2.period(), Eq(50)); + sampler1.SetGlobalPeriod(10); + sampler2.SetGlobalPeriod(20); + }); + thread.join(); + + EXPECT_THAT(sampler1.period(), Eq(10)); + EXPECT_THAT(sampler2.period(), Eq(20)); +} + +} // namespace +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/status/status.h b/absl/status/status.h index 638b9ca4..9bb45382 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -494,7 +494,7 @@ class Status final { // Returns the error message associated with this error code, if available. // Note that this message rarely describes the error code. It is not unusual // for the error message to be the empty string. As a result, prefer - // `Status::ToString()` for debug logging. + // `operator<<` or `Status::ToString()` for debug logging. absl::string_view message() const; friend bool operator==(const Status&, const Status&); diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index f9735b4b..c45a671e 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -508,8 +508,8 @@ cc_library( deps = [ "//absl/base:config", "//absl/base:core_headers", - "//absl/base:exponential_biased", "//absl/base:raw_logging_internal", + "//absl/profiling:exponential_biased", ], ) diff --git a/absl/strings/internal/cordz_functions.cc b/absl/strings/internal/cordz_functions.cc index 48369933..20d314f0 100644 --- a/absl/strings/internal/cordz_functions.cc +++ b/absl/strings/internal/cordz_functions.cc @@ -21,8 +21,8 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" -#include "absl/base/internal/exponential_biased.h" #include "absl/base/internal/raw_logging.h" +#include "absl/profiling/internal/exponential_biased.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -48,7 +48,7 @@ constexpr int64_t kIntervalIfDisabled = 1 << 16; ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() { - thread_local absl::base_internal::ExponentialBiased + thread_local absl::profiling_internal::ExponentialBiased exponential_biased_generator; int32_t mean_interval = get_cordz_mean_interval(); diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc index f8fbf948..4f403176 100644 --- a/absl/synchronization/mutex_test.cc +++ b/absl/synchronization/mutex_test.cc @@ -26,6 +26,7 @@ #include #include #include // NOLINT(build/c++11) +#include #include #include "gtest/gtest.h" @@ -870,33 +871,6 @@ TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { } } -// -------------------------------------------------------- -// Test for bug with pattern of readers using a condvar. The bug was that if a -// reader went to sleep on a condition variable while one or more other readers -// held the lock, but there were no waiters, the reader count (held in the -// mutex word) would be lost. (This is because Enqueue() had at one time -// always placed the thread on the Mutex queue. Later (CL 4075610), to -// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was -// changed so that it could also place a thread on a condition-variable. This -// introduced the case where Enqueue() returned with an empty queue, and this -// case was handled incorrectly in one place.) - -static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv, - int *running) { - std::random_device dev; - std::mt19937 gen(dev()); - std::uniform_int_distribution random_millis(0, 15); - mu->ReaderLock(); - while (*running == 3) { - absl::SleepFor(absl::Milliseconds(random_millis(gen))); - cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen))); - } - mu->ReaderUnlock(); - mu->Lock(); - (*running)--; - mu->Unlock(); -} - struct True { template bool operator()(Args...) const { @@ -945,6 +919,33 @@ TEST(Mutex, FunctorCondition) { } } +// -------------------------------------------------------- +// Test for bug with pattern of readers using a condvar. The bug was that if a +// reader went to sleep on a condition variable while one or more other readers +// held the lock, but there were no waiters, the reader count (held in the +// mutex word) would be lost. (This is because Enqueue() had at one time +// always placed the thread on the Mutex queue. Later (CL 4075610), to +// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was +// changed so that it could also place a thread on a condition-variable. This +// introduced the case where Enqueue() returned with an empty queue, and this +// case was handled incorrectly in one place.) + +static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv, + int *running) { + std::random_device dev; + std::mt19937 gen(dev()); + std::uniform_int_distribution random_millis(0, 15); + mu->ReaderLock(); + while (*running == 3) { + absl::SleepFor(absl::Milliseconds(random_millis(gen))); + cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen))); + } + mu->ReaderUnlock(); + mu->Lock(); + (*running)--; + mu->Unlock(); +} + static bool IntIsZero(int *x) { return *x == 0; } // Test for reader waiting condition variable when there are other readers diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc index 4e39188f..6770ad6b 100644 --- a/absl/time/internal/cctz/src/cctz_benchmark.cc +++ b/absl/time/internal/cctz/src/cctz_benchmark.cc @@ -648,6 +648,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Johnston", + "Pacific/Kanton", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 6948c3ea..8751b346 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -579,6 +579,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Johnston", + "Pacific/Kanton", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index ba5601ef..51191b57 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2021b +2021c diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen index 465546bd..dfc50957 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen and b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen differ -- cgit v1.2.3 From 59672bec983cb49050363cb47c261a450603409a Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Fri, 8 Oct 2021 07:50:33 -0700 Subject: Use FreeBSD macro definition for ElfW macro for compatibility. (#1037) --- absl/debugging/internal/elf_mem_image.h | 4 ++++ absl/debugging/internal/vdso_support.cc | 5 +++++ absl/debugging/symbolize_elf.inc | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index 86474819..a894bd42 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -40,6 +40,10 @@ #include // for ElfW +#if defined(__FreeBSD__) && !defined(ElfW) +#define ElfW(x) __ElfN(x) +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index 0dfe9ca6..977a9f6b 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -50,6 +50,11 @@ #define AT_SYSINFO_EHDR 33 // for crosstoolv10 #endif +#if defined(__FreeBSD__) +using Elf64_auxv_t = Elf64_Auxinfo; +using Elf32_auxv_t = Elf32_Auxinfo; +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 87dbd078..3ff343d6 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -77,6 +77,10 @@ #include "absl/debugging/internal/vdso_support.h" #include "absl/strings/string_view.h" +#if defined(__FreeBSD__) && !defined(ElfW) +#define ElfW(x) __ElfN(x) +#endif + namespace absl { ABSL_NAMESPACE_BEGIN -- cgit v1.2.3 From 4a995b1eaa4a602f0d3a9ff8eac89d4649cd2fe8 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 8 Oct 2021 07:49:05 -0700 Subject: Export of internal Abseil changes -- 35df3686d19c2864fea8796e109c76e179c3375e by Derek Mauro : Internal change PiperOrigin-RevId: 401769948 -- fcfe13483862a7c11c0bee789ca8d6f47d31860a by Derek Mauro : Update Google Benchmark dependency PiperOrigin-RevId: 401528577 GitOrigin-RevId: 35df3686d19c2864fea8796e109c76e179c3375e Change-Id: I7a5123ea37de10e5bde9c9c74faa7e43d80490ca --- WORKSPACE | 8 ++++---- absl/synchronization/mutex_benchmark.cc | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index af307872..84ab3ed4 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -29,10 +29,10 @@ http_archive( # Google benchmark. http_archive( - name = "com_github_google_benchmark", # 2021-07-01T09:02:54Z - sha256 = "1cb4b97a90aa1fd9c8e412a6bc29fc13fc140162a4a0db3811af40befd8c9ea5", - strip_prefix = "benchmark-e451e50e9b8af453f076dec10bd6890847f1624e", - urls = ["https://github.com/google/benchmark/archive/e451e50e9b8af453f076dec10bd6890847f1624e.zip"], + name = "com_github_google_benchmark", # 2021-09-20T09:19:51Z + sha256 = "62e2f2e6d8a744d67e4bbc212fcfd06647080de4253c97ad5c6749e09faf2cb0", + strip_prefix = "benchmark-0baacde3618ca617da95375e0af13ce1baadea47", + urls = ["https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.zip"], ) # C++ rules for Bazel. diff --git a/absl/synchronization/mutex_benchmark.cc b/absl/synchronization/mutex_benchmark.cc index e35aed8b..b5d2fbc4 100644 --- a/absl/synchronization/mutex_benchmark.cc +++ b/absl/synchronization/mutex_benchmark.cc @@ -97,7 +97,7 @@ void BM_MutexEnqueue(benchmark::State& state) { // Mutex queueing behavior is modified. const bool multiple_priorities = state.range(0); ScopedThreadMutexPriority priority_setter( - (multiple_priorities && state.thread_index != 0) ? 1 : 0); + (multiple_priorities && state.thread_index() != 0) ? 1 : 0); struct Shared { absl::Mutex mu; @@ -176,7 +176,7 @@ BENCHMARK(BM_MutexEnqueue) template void BM_Contended(benchmark::State& state) { - int priority = state.thread_index % state.range(1); + int priority = state.thread_index() % state.range(1); ScopedThreadMutexPriority priority_setter(priority); struct Shared { @@ -196,7 +196,7 @@ void BM_Contended(benchmark::State& state) { // To achieve this amount of local work is multiplied by number of threads // to keep ratio between local work and critical section approximately // equal regardless of number of threads. - DelayNs(100 * state.threads, &local); + DelayNs(100 * state.threads(), &local); RaiiLocker locker(&shared->mu); DelayNs(state.range(0), &shared->data); } -- cgit v1.2.3 From 084aa074c6e3994471fcdb8f542d3a24916792de Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 8 Oct 2021 15:00:10 -0700 Subject: Export of internal Abseil changes -- 42dc250867db8816381a38596e00a3b27b7dbc37 by Gennadiy Rozental : Import of CCTZ from GitHub. PiperOrigin-RevId: 401863616 GitOrigin-RevId: 42dc250867db8816381a38596e00a3b27b7dbc37 Change-Id: Ia1f100293e0c0845de76d986a170b5ca8d15f1a3 --- absl/time/internal/cctz/include/cctz/time_zone.h | 113 +++++++++++++++++---- .../internal/cctz/src/time_zone_format_test.cc | 81 ++++++++++++++- absl/time/internal/cctz/src/time_zone_if.h | 3 +- 3 files changed, 172 insertions(+), 25 deletions(-) diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h index 5562a37b..6e382dc6 100644 --- a/absl/time/internal/cctz/include/cctz/time_zone.h +++ b/absl/time/internal/cctz/include/cctz/time_zone.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -41,20 +42,9 @@ using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead. namespace detail { template -inline std::pair, D> split_seconds( - const time_point& tp) { - auto sec = std::chrono::time_point_cast(tp); - auto sub = tp - sec; - if (sub.count() < 0) { - sec -= seconds(1); - sub += seconds(1); - } - return {sec, std::chrono::duration_cast(sub)}; -} -inline std::pair, seconds> split_seconds( - const time_point& tp) { - return {tp, seconds::zero()}; -} +std::pair, D> split_seconds(const time_point& tp); +std::pair, seconds> split_seconds( + const time_point& tp); } // namespace detail // cctz::time_zone is an opaque, small, value-type class representing a @@ -279,6 +269,20 @@ std::string format(const std::string&, const time_point&, const femtoseconds&, const time_zone&); bool parse(const std::string&, const std::string&, const time_zone&, time_point*, femtoseconds*, std::string* err = nullptr); +template +bool join_seconds( + const time_point& sec, const femtoseconds& fs, + time_point>>* tpp); +template +bool join_seconds( + const time_point& sec, const femtoseconds& fs, + time_point>>* tpp); +template +bool join_seconds( + const time_point& sec, const femtoseconds& fs, + time_point>>* tpp); +bool join_seconds(const time_point& sec, const femtoseconds&, + time_point* tpp); } // namespace detail // Formats the given time_point in the given cctz::time_zone according to @@ -369,15 +373,84 @@ inline bool parse(const std::string& fmt, const std::string& input, const time_zone& tz, time_point* tpp) { time_point sec; detail::femtoseconds fs; - const bool b = detail::parse(fmt, input, tz, &sec, &fs); - if (b) { - // TODO: Return false if unrepresentable as a time_point. - *tpp = std::chrono::time_point_cast(sec); - *tpp += std::chrono::duration_cast(fs); + return detail::parse(fmt, input, tz, &sec, &fs) && + detail::join_seconds(sec, fs, tpp); +} + +namespace detail { + +// Split a time_point into a time_point and a D subseconds. +// Undefined behavior if time_point is not of sufficient range. +// Note that this means it is UB to call cctz::time_zone::lookup(tp) or +// cctz::format(fmt, tp, tz) with a time_point that is outside the range +// of a 64-bit std::time_t. +template +std::pair, D> split_seconds(const time_point& tp) { + auto sec = std::chrono::time_point_cast(tp); + auto sub = tp - sec; + if (sub.count() < 0) { + sec -= seconds(1); + sub += seconds(1); } - return b; + return {sec, std::chrono::duration_cast(sub)}; +} + +inline std::pair, seconds> split_seconds( + const time_point& tp) { + return {tp, seconds::zero()}; } +// Join a time_point and femto subseconds into a time_point. +// Floors to the resolution of time_point. Returns false if time_point +// is not of sufficient range. +template +bool join_seconds( + const time_point& sec, const femtoseconds& fs, + time_point>>* tpp) { + using D = std::chrono::duration>; + // TODO(#199): Return false if result unrepresentable as a time_point. + *tpp = std::chrono::time_point_cast(sec); + *tpp += std::chrono::duration_cast(fs); + return true; +} + +template +bool join_seconds( + const time_point& sec, const femtoseconds&, + time_point>>* tpp) { + using D = std::chrono::duration>; + auto count = sec.time_since_epoch().count(); + if (count >= 0 || count % Num == 0) { + count /= Num; + } else { + count /= Num; + count -= 1; + } + if (count > (std::numeric_limits::max)()) return false; + if (count < (std::numeric_limits::min)()) return false; + *tpp = time_point() + D{static_cast(count)}; + return true; +} + +template +bool join_seconds( + const time_point& sec, const femtoseconds&, + time_point>>* tpp) { + using D = std::chrono::duration>; + auto count = sec.time_since_epoch().count(); + if (count > (std::numeric_limits::max)()) return false; + if (count < (std::numeric_limits::min)()) return false; + *tpp = time_point() + D{static_cast(count)}; + return true; +} + +inline bool join_seconds(const time_point& sec, const femtoseconds&, + time_point* tpp) { + *tpp = sec; + return true; +} + +} // namespace detail } // namespace cctz } // namespace time_internal ABSL_NAMESPACE_END diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc index 294f2e22..6487fa93 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include #include #include @@ -1504,7 +1505,7 @@ TEST(Parse, MaxRange) { parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp)); EXPECT_EQ(tp, time_point::max()); EXPECT_FALSE( - parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp)); + parse(RFC3339_sec, "292277026596-12-04T14:30:08-01:00", utc, &tp)); // tests the lower limit using +00:00 offset EXPECT_TRUE( @@ -1525,10 +1526,82 @@ TEST(Parse, MaxRange) { parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp)); EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01", utc, &tp)); +} + +TEST(Parse, TimePointOverflow) { + const time_zone utc = utc_time_zone(); + + using D = chrono::duration; + time_point tp; + + EXPECT_TRUE( + parse(RFC3339_full, "2262-04-11T23:47:16.8547758079+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point::max()); + EXPECT_EQ("2262-04-11T23:47:16.854775807+00:00", + format(RFC3339_full, tp, utc)); +#if 0 + // TODO(#199): Will fail until cctz::parse() properly detects overflow. + EXPECT_FALSE( + parse(RFC3339_full, "2262-04-11T23:47:16.8547758080+00:00", utc, &tp)); + EXPECT_TRUE( + parse(RFC3339_full, "1677-09-21T00:12:43.1452241920+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point::min()); + EXPECT_EQ("1677-09-21T00:12:43.145224192+00:00", + format(RFC3339_full, tp, utc)); + EXPECT_FALSE( + parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp)); +#endif + + using DS = chrono::duration; + time_point stp; + + EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T00:02:07.9+00:00", utc, &stp)); + EXPECT_EQ(stp, time_point::max()); + EXPECT_EQ("1970-01-01T00:02:07+00:00", format(RFC3339_full, stp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T00:02:08+00:00", utc, &stp)); + + EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T23:57:52+00:00", utc, &stp)); + EXPECT_EQ(stp, time_point::min()); + EXPECT_EQ("1969-12-31T23:57:52+00:00", format(RFC3339_full, stp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp)); - // TODO: Add tests that parsing times with fractional seconds overflow - // appropriately. This can't be done until cctz::parse() properly detects - // overflow when combining the chrono seconds and femto. + using DM = chrono::duration; + time_point mtp; + + EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T02:07:59+00:00", utc, &mtp)); + EXPECT_EQ(mtp, time_point::max()); + EXPECT_EQ("1970-01-01T02:07:00+00:00", format(RFC3339_full, mtp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T02:08:00+00:00", utc, &mtp)); + + EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T21:52:00+00:00", utc, &mtp)); + EXPECT_EQ(mtp, time_point::min()); + EXPECT_EQ("1969-12-31T21:52:00+00:00", format(RFC3339_full, mtp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp)); +} + +TEST(Parse, TimePointOverflowFloor) { + const time_zone utc = utc_time_zone(); + + using D = chrono::duration; + time_point tp; + + EXPECT_TRUE( + parse(RFC3339_full, "294247-01-10T04:00:54.7758079+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point::max()); + EXPECT_EQ("294247-01-10T04:00:54.775807+00:00", + format(RFC3339_full, tp, utc)); +#if 0 + // TODO(#199): Will fail until cctz::parse() properly detects overflow. + EXPECT_FALSE( + parse(RFC3339_full, "294247-01-10T04:00:54.7758080+00:00", utc, &tp)); + EXPECT_TRUE( + parse(RFC3339_full, "-290308-12-21T19:59:05.2241920+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point::min()); + EXPECT_EQ("-290308-12-21T19:59:05.224192+00:00", + format(RFC3339_full, tp, utc)); + EXPECT_FALSE( + parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp)); +#endif } // diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h index 32c0891c..7d3e42d3 100644 --- a/absl/time/internal/cctz/src/time_zone_if.h +++ b/absl/time/internal/cctz/src/time_zone_if.h @@ -56,7 +56,8 @@ class TimeZoneIf { // Convert between time_point and a count of seconds since the // Unix epoch. We assume that the std::chrono::system_clock and the -// Unix clock are second aligned, but not that they share an epoch. +// Unix clock are second aligned, and that the results are representable. +// (That is, that they share an epoch, which is required since C++20.) inline std::int_fast64_t ToUnixSeconds(const time_point& tp) { return (tp - std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0))) -- cgit v1.2.3 From d6f40f4e1f424677a88eee1dade4f3ae2ea5f743 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 11 Oct 2021 15:32:11 -0700 Subject: Export of internal Abseil changes -- 566c422ef23a01a71ca7a02d15a3f23ab5135f47 by Abseil Team : Fix a bug in the right shift of negative numbers for the no_intrinsic code path If the right shift was greater than or equal to 64 bits the the most significant 64 bit would always be initialized to zero. The code now initializes the most significant int64 to all ones if the architecture does arithmetic rights shifts and the number was negative. PiperOrigin-RevId: 402407698 GitOrigin-RevId: 566c422ef23a01a71ca7a02d15a3f23ab5135f47 Change-Id: I01c57a87a91a2c89f62bb952661e3a5dcdca6234 --- absl/numeric/int128_no_intrinsic.inc | 12 +++++++++--- absl/numeric/int128_test.cc | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc index 66f6809f..8834804c 100644 --- a/absl/numeric/int128_no_intrinsic.inc +++ b/absl/numeric/int128_no_intrinsic.inc @@ -279,7 +279,7 @@ constexpr int128 operator^(int128 lhs, int128 rhs) { } constexpr int128 operator<<(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + // int64_t shifts of >= 64 are undefined, so we need some special-casing. return amount >= 64 ? MakeInt128( static_cast(Int128Low64(lhs) << (amount - 64)), 0) @@ -292,10 +292,16 @@ constexpr int128 operator<<(int128 lhs, int amount) { } constexpr int128 operator>>(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + // int64_t shifts of >= 64 are undefined, so we need some special-casing. + // The (Int128High64(lhs) >> 32) >> 32 "trick" causes the the most significant + // int64 to be inititialized with all zeros or all ones correctly. It takes + // into account whether the number is negative or positive, and whether the + // current architecture does arithmetic or logical right shifts for negative + // numbers. return amount >= 64 ? MakeInt128( - 0, static_cast(Int128High64(lhs) >> (amount - 64))) + (Int128High64(lhs) >> 32) >> 32, + static_cast(Int128High64(lhs) >> (amount - 64))) : amount == 0 ? lhs : MakeInt128(Int128High64(lhs) >> amount, diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc index c445d89a..dd9425d7 100644 --- a/absl/numeric/int128_test.cc +++ b/absl/numeric/int128_test.cc @@ -239,6 +239,24 @@ TEST(Uint128, AllTests) { EXPECT_EQ(absl::Uint128Max(), absl::kuint128max); } +TEST(Int128, RightShiftOfNegativeNumbers) { + absl::int128 minus_six = -6; + absl::int128 minus_three = -3; + absl::int128 minus_two = -2; + absl::int128 minus_one = -1; + if ((-6 >> 1) == -3) { + // Right shift is arithmetic (sign propagates) + EXPECT_EQ(minus_six >> 1, minus_three); + EXPECT_EQ(minus_six >> 2, minus_two); + EXPECT_EQ(minus_six >> 65, minus_one); + } else { + // Right shift is logical (zeros shifted in at MSB) + EXPECT_EQ(minus_six >> 1, absl::int128(absl::uint128(minus_six) >> 1)); + EXPECT_EQ(minus_six >> 2, absl::int128(absl::uint128(minus_six) >> 2)); + EXPECT_EQ(minus_six >> 65, absl::int128(absl::uint128(minus_six) >> 65)); + } +} + TEST(Uint128, ConversionTests) { EXPECT_TRUE(absl::MakeUint128(1, 0)); -- cgit v1.2.3 From 22f482f0fc25825dd3783f2e8642a8226b7293d4 Mon Sep 17 00:00:00 2001 From: Vertexwahn Date: Thu, 14 Oct 2021 19:01:52 +0200 Subject: Remove bazelbuild/rules_cc dependency (#1038) --- WORKSPACE | 8 -------- absl/algorithm/BUILD.bazel | 1 - absl/base/BUILD.bazel | 1 - absl/cleanup/BUILD.bazel | 1 - absl/container/BUILD.bazel | 1 - absl/debugging/BUILD.bazel | 1 - absl/flags/BUILD.bazel | 1 - absl/functional/BUILD.bazel | 1 - absl/hash/BUILD.bazel | 1 - absl/memory/BUILD.bazel | 1 - absl/meta/BUILD.bazel | 1 - absl/numeric/BUILD.bazel | 1 - absl/random/BUILD.bazel | 1 - absl/random/internal/BUILD.bazel | 2 -- absl/status/BUILD.bazel | 1 - absl/strings/BUILD.bazel | 1 - absl/synchronization/BUILD.bazel | 1 - absl/time/BUILD.bazel | 1 - absl/time/internal/cctz/BUILD.bazel | 2 -- absl/types/BUILD.bazel | 1 - absl/utility/BUILD.bazel | 1 - 21 files changed, 30 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 84ab3ed4..c9aa8caf 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -35,14 +35,6 @@ http_archive( urls = ["https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.zip"], ) -# C++ rules for Bazel. -http_archive( - name = "rules_cc", # 2021-06-07T16:41:49Z - sha256 = "b295cad8c5899e371dde175079c0a2cdc0151f5127acc92366a8c986beb95c76", - strip_prefix = "rules_cc-daf6ace7cfeacd6a83e9ff2ed659f416537b6c74", - urls = ["https://github.com/bazelbuild/rules_cc/archive/daf6ace7cfeacd6a83e9ff2ed659f416537b6c74.zip"], -) - # Bazel platform rules. http_archive( name = "platforms", diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index a3002b7d..afc52639 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 8b09b6e3..4769efda 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/cleanup/BUILD.bazel b/absl/cleanup/BUILD.bazel index 5cca898f..2154d9f1 100644 --- a/absl/cleanup/BUILD.bazel +++ b/absl/cleanup/BUILD.bazel @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 9e3bc06a..d1df524a 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index b503da51..3c4ea8dc 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 7957c6dd..d20deab4 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel index 4555cd59..f9f2b9c2 100644 --- a/absl/functional/BUILD.bazel +++ b/absl/functional/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 392b68f1..f0640d34 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel index d2824a05..c16bf8a9 100644 --- a/absl/memory/BUILD.bazel +++ b/absl/memory/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel index 5585fcca..fb379251 100644 --- a/absl/meta/BUILD.bazel +++ b/absl/meta/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index ea587bfa..1f9e0f20 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index 66ffcbc7..fdde78b6 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -16,7 +16,6 @@ # ABSL random-number generation libraries. -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index df6e42fc..e93eebb6 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -14,8 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") - # Internal-only implementation classes for Abseil Random load( "//absl:copts/configure_copts.bzl", diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 30df22ae..0e5b3508 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -17,7 +17,6 @@ # It will expand later to have utilities around `Status` like `StatusOr`, # `StatusBuilder` and macros. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index c45a671e..c046366c 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 92e2448d..d7195473 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index 3e25ca26..e8c49660 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index f549af66..bdc2e309 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") - package(features = ["-parse_headers"]) licenses(["notice"]) diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 83be9360..38ed2286 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel index 02b2c407..ca4bc0a6 100644 --- a/absl/utility/BUILD.bazel +++ b/absl/utility/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", -- cgit v1.2.3 From 7e446075d4aff4601c1e7627c7c0be2c4833a53a Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 14 Oct 2021 10:01:40 -0700 Subject: Export of internal Abseil changes -- 58affd6378c47993f5408f7b8a8863fa5bcc2c47 by Derek Mauro : Internal change PiperOrigin-RevId: 403120541 -- 27dc5d5f87bca6254585cca69058c14e0a2f3ce6 by Chris Kennelly : Prefetch while hashing. While we may not need to access the cacheline *ctrl_ is on once we've computed the hash, we can begin to resolve the TLB required for the hashtable's heap allocation simultaneously with computing the hash value for the key. PiperOrigin-RevId: 402954725 GitOrigin-RevId: 58affd6378c47993f5408f7b8a8863fa5bcc2c47 Change-Id: Id04297de823ad5c5a867c46065fa3a9ef0ada3dd --- absl/container/internal/raw_hash_set.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 5c5db12e..47e5a228 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1446,6 +1446,7 @@ class raw_hash_set { void prefetch(const key_arg& key) const { (void)key; #if defined(__GNUC__) + prefetch_heap_block(); auto seq = probe(ctrl_, hash_ref()(key), capacity_); __builtin_prefetch(static_cast(ctrl_ + seq.offset())); __builtin_prefetch(static_cast(slots_ + seq.offset())); @@ -1477,6 +1478,7 @@ class raw_hash_set { } template iterator find(const key_arg& key) { + prefetch_heap_block(); return find(key, hash_ref()(key)); } @@ -1486,6 +1488,7 @@ class raw_hash_set { } template const_iterator find(const key_arg& key) const { + prefetch_heap_block(); return find(key, hash_ref()(key)); } @@ -1856,6 +1859,7 @@ class raw_hash_set { protected: template std::pair find_or_prepare_insert(const K& key) { + prefetch_heap_block(); auto hash = hash_ref()(key); auto seq = probe(ctrl_, hash, capacity_); while (true) { @@ -1918,6 +1922,15 @@ class raw_hash_set { size_t& growth_left() { return settings_.template get<0>(); } + void prefetch_heap_block() const { + // Prefetch the heap-allocated memory region to resolve potential TLB + // misses. This is intended to overlap with execution of calculating the + // hash for a key. +#if defined(__GNUC__) + __builtin_prefetch(static_cast(ctrl_), 0, 1); +#endif // __GNUC__ + } + HashtablezInfoHandle& infoz() { return settings_.template get<1>(); } hasher& hash_ref() { return settings_.template get<2>(); } -- cgit v1.2.3 From a2f52e1177b87bdc747f8d0b7745c8f967bceb9d Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 18 Oct 2021 08:24:39 -0700 Subject: Export of internal Abseil changes -- b008f3aaab60f1f0eb5987b50336543cca7151be by Martijn Vels : Enable Cord Btree as the default Cord implementation. The Cord Btree implementation has been extensively tested by google. This changes makes the Cord Btree implementation the default implementation. This change should have no impact on current use cases and does not effect any public API or behavior. PiperOrigin-RevId: 403966449 -- 3d82052b5819d1244942c59d5cb4e51d75ada287 by Derek Mauro : Add missing CMake alias for gmock_main. Remove unused variable ABSL_TEST_COMMON_LIBRARIES Fixes #709 Fixes #1043 PiperOrigin-RevId: 403950358 -- 86695f6d06915b56d807303c37ee81487998cd58 by Abseil Team : FunctionRef is cheaper to construct than a std::function, and the argument is only used during the call, so this is safe. This has the added advantage of allowing the caller to provide a non-movable callable, which can't be converted to a std::function. PiperOrigin-RevId: 403457615 -- 196c1109ba519d4ff00c856bbb4bff754468359f by Abseil Team : Internal optimization. PiperOrigin-RevId: 403413291 GitOrigin-RevId: b008f3aaab60f1f0eb5987b50336543cca7151be Change-Id: I1665f0efd484f53e0ed22d8940ef2fa60b03cc5b --- CMakeLists.txt | 10 ++-------- absl/debugging/internal/stacktrace_x86-inl.inc | 25 ++++++++++++++++++++++--- absl/status/BUILD.bazel | 1 + absl/status/CMakeLists.txt | 1 + absl/status/status.cc | 2 +- absl/status/status.h | 3 ++- absl/strings/internal/cord_internal.h | 2 +- 7 files changed, 30 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c8bfff4..d59c2ade 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,22 +160,16 @@ if(BUILD_TESTING) if (NOT ABSL_FIND_GOOGLETEST) # When Google Test is included directly rather than through find_package, the aliases are missing. - add_library(GTest::gtest_main ALIAS gtest_main) add_library(GTest::gtest ALIAS gtest) + add_library(GTest::gtest_main ALIAS gtest_main) add_library(GTest::gmock ALIAS gmock) + add_library(GTest::gmock_main ALIAS gmock_main) endif() check_target(GTest::gtest) check_target(GTest::gtest_main) check_target(GTest::gmock) check_target(GTest::gmock_main) - - list(APPEND ABSL_TEST_COMMON_LIBRARIES - GTest::gtest_main - GTest::gtest - GTest::gmock - ${CMAKE_THREAD_LIBS_INIT} - ) endif() add_subdirectory(absl) diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc index 70f79dfc..847a5473 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -27,6 +27,7 @@ #include #include +#include #include "absl/base/macros.h" #include "absl/base/port.h" @@ -158,7 +159,8 @@ static uintptr_t GetFP(const void *vuc) { template ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. -static void **NextStackFrame(void **old_fp, const void *uc) { +static void **NextStackFrame(void **old_fp, const void *uc, + size_t stack_low, size_t stack_high) { void **new_fp = (void **)*old_fp; #if defined(__linux__) && defined(__i386__) @@ -257,6 +259,18 @@ static void **NextStackFrame(void **old_fp, const void *uc) { // at a greater address that the current one. if (new_fp_u <= old_fp_u) return nullptr; if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr; + + if (stack_low < old_fp_u && old_fp_u <= stack_high) { + // Old BP was in the expected stack region... + if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) { + // ... but new BP is outside of expected stack region. + // It is most likely bogus. + return nullptr; + } + } else { + // We may be here if we are executing in a co-routine with a + // separate stack. We can't do safety checks in this case. + } } else { if (new_fp == nullptr) return nullptr; // skip AddressIsReadable() below // In the non-strict mode, allow discontiguous stack frames. @@ -296,13 +310,17 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, int n = 0; void **fp = reinterpret_cast(__builtin_frame_address(0)); + size_t stack_low = getpagesize(); // Assume that the first page is not stack. + size_t stack_high = std::numeric_limits::max() - sizeof(void *); + while (fp && n < max_depth) { if (*(fp + 1) == reinterpret_cast(0)) { // In 64-bit code, we often see a frame that // points to itself and has a return address of 0. break; } - void **next_fp = NextStackFrame(fp, ucp); + void **next_fp = NextStackFrame( + fp, ucp, stack_low, stack_high); if (skip_count > 0) { skip_count--; } else { @@ -325,7 +343,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, const int kMaxUnwind = 1000; int j = 0; for (; fp != nullptr && j < kMaxUnwind; j++) { - fp = NextStackFrame(fp, ucp); + fp = NextStackFrame(fp, ucp, stack_low, + stack_high); } *min_dropped_frames = j; } diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 0e5b3508..bae5156f 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -47,6 +47,7 @@ cc_library( "//absl/container:inlined_vector", "//absl/debugging:stacktrace", "//absl/debugging:symbolize", + "//absl/functional:function_ref", "//absl/strings", "//absl/strings:cord", "//absl/strings:str_format", diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index 43898564..f107c85b 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt @@ -29,6 +29,7 @@ absl_cc_library( absl::atomic_hook absl::config absl::core_headers + absl::function_ref absl::raw_logging_internal absl::inlined_vector absl::stacktrace diff --git a/absl/status/status.cc b/absl/status/status.cc index 53c198e1..bcf3413e 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -161,7 +161,7 @@ bool Status::ErasePayload(absl::string_view type_url) { } void Status::ForEachPayload( - const std::function& visitor) + absl::FunctionRef visitor) const { if (auto* payloads = GetPayloads()) { bool in_reverse = diff --git a/absl/status/status.h b/absl/status/status.h index 9bb45382..39071e5f 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -55,6 +55,7 @@ #include #include "absl/container/inlined_vector.h" +#include "absl/functional/function_ref.h" #include "absl/status/internal/status_internal.h" #include "absl/strings/cord.h" #include "absl/strings/string_view.h" @@ -590,7 +591,7 @@ class Status final { // NOTE: Any mutation on the same 'absl::Status' object during visitation is // forbidden and could result in undefined behavior. void ForEachPayload( - const std::function& visitor) + absl::FunctionRef visitor) const; private: diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 7172b147..0300ac40 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -37,7 +37,7 @@ class CordzInfo; // Default feature enable states for cord ring buffers enum CordFeatureDefaults { - kCordEnableBtreeDefault = false, + kCordEnableBtreeDefault = true, kCordEnableRingBufferDefault = false, kCordShallowSubcordsDefault = false }; -- cgit v1.2.3 From ddb842f583e560bbde497bc96cfebe25f9089e11 Mon Sep 17 00:00:00 2001 From: Jérôme Duval Date: Thu, 21 Oct 2021 22:01:48 +0200 Subject: Initial support for Haiku (#1045) This provides the baseline needed to build Abseil on Haiku systems. --- absl/base/config.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/absl/base/config.h b/absl/base/config.h index 0562cddb..d3cad682 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -413,7 +413,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ - defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) + defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ + defined(__HAIKU__) #define ABSL_HAVE_MMAP 1 #endif -- cgit v1.2.3 From 46d939a918092ef94498a126dc1244b437100b31 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 21 Oct 2021 22:06:17 -0700 Subject: Export of internal Abseil changes -- 30bbe72e8c32d8680bbe6c8473a884d485e9f684 by Laramie Leavitt : Convert randen golden test to use byte arrays. PiperOrigin-RevId: 404948438 -- 4690d9ebd2c518708ad155e913b2140ebdf01234 by Laramie Leavitt : Add golden tests for internal::ExplicitSeedSeeq against stable std:: random URBGs. PiperOrigin-RevId: 404912608 -- 52e9b24276ee3a21cc8a026f9d152ef8ced7f507 by Derek Mauro : Internal change PiperOrigin-RevId: 404861043 -- 9e966a2a680970fd27d927cc6420e333b1378b1e by Abseil Team : Roll back import of CCTZ from GitHub. PiperOrigin-RevId: 404343587 -- 9c4eedec00105b8288c0b91d96434aea08b8e3c3 by Abseil Team : Import of CCTZ from GitHub. PiperOrigin-RevId: 404314597 -- f171375fd7bec35cc34815be8faf5e000828b7bb by Abseil Team : Internal change PiperOrigin-RevId: 404098156 GitOrigin-RevId: 30bbe72e8c32d8680bbe6c8473a884d485e9f684 Change-Id: Ic7b5a3c0659e321e2f642800930a8de014253e2a --- absl/BUILD.bazel | 8 ++++ absl/random/internal/explicit_seed_seq_test.cc | 49 +++++++++++++++++---- absl/random/internal/randen_hwaes_test.cc | 59 ++++++++++++-------------- absl/random/internal/randen_slow_test.cc | 57 ++++++++++++------------- 4 files changed, 102 insertions(+), 71 deletions(-) diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index 7239b5bf..d799b7fe 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -70,3 +70,11 @@ config_setting( }, visibility = [":__subpackages__"], ) + +config_setting( + name = "fuchsia", + values = { + "cpu": "fuchsia", + }, + visibility = [":__subpackages__"], +) diff --git a/absl/random/internal/explicit_seed_seq_test.cc b/absl/random/internal/explicit_seed_seq_test.cc index a55ad739..f867f610 100644 --- a/absl/random/internal/explicit_seed_seq_test.cc +++ b/absl/random/internal/explicit_seed_seq_test.cc @@ -24,6 +24,8 @@ namespace { +using ::absl::random_internal::ExplicitSeedSeq; + template bool ConformsToInterface() { // Check that the SeedSequence can be default-constructed. @@ -64,14 +66,14 @@ TEST(SeedSequences, CheckInterfaces) { EXPECT_TRUE(ConformsToInterface()); // Abseil classes - EXPECT_TRUE(ConformsToInterface()); + EXPECT_TRUE(ConformsToInterface()); } TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) { const size_t kNumBlocks = 128; uint32_t outputs[kNumBlocks]; - absl::random_internal::ExplicitSeedSeq seq; + ExplicitSeedSeq seq; seq.generate(outputs, &outputs[kNumBlocks]); for (uint32_t& seed : outputs) { @@ -87,8 +89,7 @@ TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) { for (uint32_t& seed : seed_material) { seed = urandom(); } - absl::random_internal::ExplicitSeedSeq seq(seed_material, - &seed_material[kNumBlocks]); + ExplicitSeedSeq seq(seed_material, &seed_material[kNumBlocks]); // Check that output is same as seed-material provided to constructor. { @@ -133,11 +134,10 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { for (uint32_t& entry : entropy) { entry = urandom(); } - absl::random_internal::ExplicitSeedSeq seq_from_entropy(std::begin(entropy), - std::end(entropy)); + ExplicitSeedSeq seq_from_entropy(std::begin(entropy), std::end(entropy)); // Copy constructor. { - absl::random_internal::ExplicitSeedSeq seq_copy(seq_from_entropy); + ExplicitSeedSeq seq_copy(seq_from_entropy); EXPECT_EQ(seq_copy.size(), seq_from_entropy.size()); std::vector seeds_1; @@ -155,8 +155,7 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { for (uint32_t& entry : entropy) { entry = urandom(); } - absl::random_internal::ExplicitSeedSeq another_seq(std::begin(entropy), - std::end(entropy)); + ExplicitSeedSeq another_seq(std::begin(entropy), std::end(entropy)); std::vector seeds_1; seeds_1.resize(1000, 0); @@ -202,3 +201,35 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { EXPECT_THAT(seeds_1, Each(Eq(0))); } } + +TEST(ExplicitSeedSeq, StdURBGGoldenTests) { + // Verify that for std::- URBG instances the results are stable across + // platforms (these should have deterministic output). + { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + std::minstd_rand rng(seed_sequence); + + std::minstd_rand::result_type values[4] = {rng(), rng(), rng(), rng()}; + EXPECT_THAT(values, + testing::ElementsAre(579252, 43785881, 464353103, 1501811174)); + } + + { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + std::mt19937 rng(seed_sequence); + + std::mt19937::result_type values[4] = {rng(), rng(), rng(), rng()}; + EXPECT_THAT(values, testing::ElementsAre(138416803, 151130212, 33817739, + 138416803)); + } + + { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + std::mt19937_64 rng(seed_sequence); + + std::mt19937_64::result_type values[4] = {rng(), rng(), rng(), rng()}; + EXPECT_THAT(values, + testing::ElementsAre(19738651785169348, 1464811352364190456, + 18054685302720800, 19738651785169348)); + } +} diff --git a/absl/random/internal/randen_hwaes_test.cc b/absl/random/internal/randen_hwaes_test.cc index 66ddb43f..2348b55c 100644 --- a/absl/random/internal/randen_hwaes_test.cc +++ b/absl/random/internal/randen_hwaes_test.cc @@ -27,44 +27,39 @@ namespace { using absl::random_internal::RandenHwAes; using absl::random_internal::RandenTraits; -// Local state parameters. -constexpr size_t kSeedBytes = - RandenTraits::kStateBytes - RandenTraits::kCapacityBytes; -constexpr size_t kStateSizeT = RandenTraits::kStateBytes / sizeof(uint64_t); -constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t); - -struct alignas(16) randen { - uint64_t state[kStateSizeT]; - uint32_t seed[kSeedSizeT]; -}; - TEST(RandenHwAesTest, Default) { EXPECT_TRUE(absl::random_internal::CPUSupportsRandenHwAes()); - constexpr uint64_t kGolden[] = { - 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, - 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, - 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, - 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, - 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, - 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, - 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, - 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, - 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, - 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, - 0x026ff374c101da7e, 0x811ef0821c3de851, + constexpr uint8_t kGolden[] = { + 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d, + 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3, + 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f, + 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0, + 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff, + 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2, + 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5, + 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c, + 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56, + 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4, + 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91, + 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5, + 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d, + 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58, + 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e, + 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49, + 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0, + 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6, + 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61, + 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35, + 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c, + 0x82, 0xf0, 0x1e, 0x81, }; - alignas(16) randen d; - memset(d.state, 0, sizeof(d.state)); - RandenHwAes::Generate(RandenHwAes::GetKeys(), d.state); + alignas(16) uint8_t state[RandenTraits::kStateBytes]; + std::memset(state, 0, sizeof(state)); - uint64_t* id = d.state; - for (const auto& elem : kGolden) { - auto a = absl::StrFormat("%#x", elem); - auto b = absl::StrFormat("%#x", *id++); - EXPECT_EQ(a, b); - } + RandenHwAes::Generate(RandenHwAes::GetKeys(), state); + EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state))); } } // namespace diff --git a/absl/random/internal/randen_slow_test.cc b/absl/random/internal/randen_slow_test.cc index 4861ffa4..ed603958 100644 --- a/absl/random/internal/randen_slow_test.cc +++ b/absl/random/internal/randen_slow_test.cc @@ -25,40 +25,37 @@ namespace { using absl::random_internal::RandenSlow; using absl::random_internal::RandenTraits; -// Local state parameters. -constexpr size_t kSeedBytes = - RandenTraits::kStateBytes - RandenTraits::kCapacityBytes; -constexpr size_t kStateSizeT = RandenTraits::kStateBytes / sizeof(uint64_t); -constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t); - -struct alignas(16) randen { - uint64_t state[kStateSizeT]; - uint32_t seed[kSeedSizeT]; -}; - TEST(RandenSlowTest, Default) { - constexpr uint64_t kGolden[] = { - 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, - 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, - 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, - 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, - 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, - 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, - 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, - 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, - 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, - 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, - 0x026ff374c101da7e, 0x811ef0821c3de851, + constexpr uint8_t kGolden[] = { + 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d, + 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3, + 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f, + 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0, + 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff, + 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2, + 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5, + 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c, + 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56, + 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4, + 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91, + 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5, + 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d, + 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58, + 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e, + 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49, + 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0, + 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6, + 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61, + 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35, + 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c, + 0x82, 0xf0, 0x1e, 0x81, }; - alignas(16) randen d; - std::memset(d.state, 0, sizeof(d.state)); - RandenSlow::Generate(RandenSlow::GetKeys(), d.state); + alignas(16) uint8_t state[RandenTraits::kStateBytes]; + std::memset(state, 0, sizeof(state)); - uint64_t* id = d.state; - for (const auto& elem : kGolden) { - EXPECT_EQ(absl::little_endian::FromHost64(elem), *id++); - } + RandenSlow::Generate(RandenSlow::GetKeys(), state); + EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state))); } } // namespace -- cgit v1.2.3 From f70eadadd7767c3a97774b63c4c23981fa89af9f Mon Sep 17 00:00:00 2001 From: Milad Fa <46688537+miladfarca@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:09:11 -0400 Subject: Fix over-aligned layout test with older gcc compilers (#1049) --- absl/container/internal/layout_test.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc index 1d7158ff..54e5d5bb 100644 --- a/absl/container/internal/layout_test.cc +++ b/absl/container/internal/layout_test.cc @@ -1350,7 +1350,13 @@ TEST(Layout, CustomAlignment) { TEST(Layout, OverAligned) { constexpr size_t M = alignof(max_align_t); constexpr Layout> x(1, 3); +#ifdef __GNUC__ + // Using __attribute__ ((aligned ())) instead of alignas to bypass a gcc bug: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357 + __attribute__((aligned(2 * M))) unsigned char p[x.AllocSize()]; +#else alignas(2 * M) unsigned char p[x.AllocSize()]; +#endif EXPECT_EQ(2 * M + 3, x.AllocSize()); EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M)); } -- cgit v1.2.3 From cc413f8b674d61e3aa948386432e526e051afca0 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 27 Oct 2021 13:22:18 -0700 Subject: Export of internal Abseil changes -- 05a099a580753f8e96cee38572e94dcdc079361b by Abseil Team : Import of CCTZ from GitHub. PiperOrigin-RevId: 405966217 -- c6b81e9ebc183d8389f14ecd091c8bad08cfe0aa by Abseil Team : Add `inline_element_size` to hashtablez (so that we can compute the weighted load factors properly e.g., in b/187896534). PiperOrigin-RevId: 405917711 -- 3e3673de4e54e4142c54b09e1644dfa3de4bb296 by Abseil Team : align indent of code comment in mutex.h PiperOrigin-RevId: 405871997 -- 2248301a5b14f8d2be5b2e9088f3528a353ea491 by Derek Mauro : Internal change PiperOrigin-RevId: 405639236 -- bc7d3c56fdad3dde4b89324af142529f2afe5f1b by Abseil Team : Import of CCTZ from GitHub. PiperOrigin-RevId: 405508045 -- 66472387276ef02505d99195747be862768bb35b by Laramie Leavitt : Also use uint8_t golden values in randen_test.cc This makes randen_test, randen_slow_test, and randen_hwaes_test essentially identical, as is the intent. PiperOrigin-RevId: 405484423 GitOrigin-RevId: 05a099a580753f8e96cee38572e94dcdc079361b Change-Id: I3dd5b0cfdb98d6e1ab02266194ba67d15428c2f8 --- absl/container/BUILD.bazel | 16 +++ absl/container/CMakeLists.txt | 15 +++ absl/container/internal/hashtablez_sampler.cc | 15 ++- absl/container/internal/hashtablez_sampler.h | 9 +- absl/container/internal/hashtablez_sampler_test.cc | 19 +++- absl/container/internal/raw_hash_set.h | 2 +- absl/container/internal/raw_hash_set_test.cc | 1 + absl/container/sample_element_size_test.cc | 114 +++++++++++++++++++++ absl/random/internal/randen_test.cc | 45 ++++---- absl/synchronization/mutex.h | 6 +- absl/time/internal/cctz/src/time_zone_info.cc | 69 ++++++++++++- absl/time/internal/cctz/src/time_zone_lookup.cc | 49 +++++++++ .../internal/cctz/src/time_zone_lookup_test.cc | 4 + absl/time/internal/cctz/testdata/version | 2 +- .../time/internal/cctz/testdata/zoneinfo/Asia/Gaza | Bin 1213 -> 1230 bytes .../internal/cctz/testdata/zoneinfo/Asia/Hebron | Bin 1231 -> 1248 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Fiji | Bin 419 -> 428 bytes 17 files changed, 329 insertions(+), 37 deletions(-) create mode 100644 absl/container/sample_element_size_test.cc diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index d1df524a..ffaee19c 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -876,6 +876,22 @@ cc_test( ], ) +cc_test( + name = "sample_element_size_test", + srcs = ["sample_element_size_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = NOTEST_TAGS_NONMOBILE, + visibility = ["//visibility:private"], + deps = [ + ":flat_hash_map", + ":flat_hash_set", + ":node_hash_map", + ":node_hash_set", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "btree", srcs = [ diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 9b8a7509..78584d2c 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -894,3 +894,18 @@ absl_cc_test( absl::unordered_map_modifiers_test GTest::gmock_main ) + +absl_cc_test( + NAME + sample_element_size_test + SRCS + "sample_element_size_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flat_hash_map + absl::flat_hash_set + absl::node_hash_map + absl::node_hash_set + GTest::gmock_main +) diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 7070912e..40cce047 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -55,6 +55,9 @@ HashtablezSampler& GlobalHashtablezSampler() { return *sampler; } +// TODO(bradleybear): The comments at this constructors declaration say that the +// fields are not initialized, but this definition does initialize the fields. +// Something needs to be cleaned up. HashtablezInfo::HashtablezInfo() { PrepareForSampling(); } HashtablezInfo::~HashtablezInfo() = default; @@ -98,10 +101,12 @@ static bool ShouldForceSampling() { return state == kForce; } -HashtablezInfo* SampleSlow(int64_t* next_sample) { +HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; - return GlobalHashtablezSampler().Register(); + HashtablezInfo* result = GlobalHashtablezSampler().Register(); + result->inline_element_size = inline_element_size; + return result; } #if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) @@ -123,10 +128,12 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { // that case. if (first) { if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr; - return SampleSlow(next_sample); + return SampleSlow(next_sample, inline_element_size); } - return GlobalHashtablezSampler().Register(); + HashtablezInfo* result = GlobalHashtablezSampler().Register(); + result->inline_element_size = inline_element_size; + return result; #endif } diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 812118e3..91fcdb34 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -91,6 +91,7 @@ struct HashtablezInfo : public profiling_internal::Sample { absl::Time create_time; int32_t depth; void* stack[kMaxStackDepth]; + size_t inline_element_size; }; inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { @@ -143,7 +144,7 @@ inline void RecordEraseSlow(HashtablezInfo* info) { std::memory_order_relaxed); } -HashtablezInfo* SampleSlow(int64_t* next_sample); +HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size); void UnsampleSlow(HashtablezInfo* info); #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) @@ -238,12 +239,14 @@ extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample; // Returns an RAII sampling handle that manages registration and unregistation // with the global sampler. -inline HashtablezInfoHandle Sample() { +inline HashtablezInfoHandle Sample( + size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) { #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { return HashtablezInfoHandle(nullptr); } - return HashtablezInfoHandle(SampleSlow(&global_next_sample)); + return HashtablezInfoHandle( + SampleSlow(&global_next_sample, inline_element_size)); #else return HashtablezInfoHandle(nullptr); #endif // !ABSL_PER_THREAD_TLS diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index f053c19b..449619a3 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -78,10 +78,12 @@ HashtablezInfo* Register(HashtablezSampler* s, size_t size) { TEST(HashtablezInfoTest, PrepareForSampling) { absl::Time test_start = absl::Now(); + const size_t test_element_size = 17; HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); + info.inline_element_size = test_element_size; EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 0); @@ -93,6 +95,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); EXPECT_GE(info.create_time, test_start); + EXPECT_EQ(info.inline_element_size, test_element_size); info.capacity.store(1, std::memory_order_relaxed); info.size.store(1, std::memory_order_relaxed); @@ -116,6 +119,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); + EXPECT_EQ(info.inline_element_size, test_element_size); EXPECT_GE(info.create_time, test_start); } @@ -154,9 +158,11 @@ TEST(HashtablezInfoTest, RecordInsert) { } TEST(HashtablezInfoTest, RecordErase) { + const size_t test_element_size = 29; HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); + info.inline_element_size = test_element_size; EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.size.load(), 0); RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); @@ -164,12 +170,15 @@ TEST(HashtablezInfoTest, RecordErase) { RecordEraseSlow(&info); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 1); + EXPECT_EQ(info.inline_element_size, test_element_size); } TEST(HashtablezInfoTest, RecordRehash) { + const size_t test_element_size = 31; HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); + info.inline_element_size = test_element_size; RecordInsertSlow(&info, 0x1, 0); RecordInsertSlow(&info, 0x2, kProbeLength); RecordInsertSlow(&info, 0x4, kProbeLength); @@ -188,6 +197,7 @@ TEST(HashtablezInfoTest, RecordRehash) { EXPECT_EQ(info.total_probe_length.load(), 3); EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.num_rehashes.load(), 1); + EXPECT_EQ(info.inline_element_size, test_element_size); } TEST(HashtablezInfoTest, RecordReservation) { @@ -208,12 +218,13 @@ TEST(HashtablezInfoTest, RecordReservation) { #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) TEST(HashtablezSamplerTest, SmallSampleParameter) { + const size_t test_element_size = 31; SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); for (int i = 0; i < 1000; ++i) { int64_t next_sample = 0; - HashtablezInfo* sample = SampleSlow(&next_sample); + HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size); EXPECT_GT(next_sample, 0); EXPECT_NE(sample, nullptr); UnsampleSlow(sample); @@ -221,12 +232,13 @@ TEST(HashtablezSamplerTest, SmallSampleParameter) { } TEST(HashtablezSamplerTest, LargeSampleParameter) { + const size_t test_element_size = 31; SetHashtablezEnabled(true); SetHashtablezSampleParameter(std::numeric_limits::max()); for (int i = 0; i < 1000; ++i) { int64_t next_sample = 0; - HashtablezInfo* sample = SampleSlow(&next_sample); + HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size); EXPECT_GT(next_sample, 0); EXPECT_NE(sample, nullptr); UnsampleSlow(sample); @@ -234,13 +246,14 @@ TEST(HashtablezSamplerTest, LargeSampleParameter) { } TEST(HashtablezSamplerTest, Sample) { + const size_t test_element_size = 31; SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); int64_t num_sampled = 0; int64_t total = 0; double sample_rate = 0.0; for (int i = 0; i < 1000000; ++i) { - HashtablezInfoHandle h = Sample(); + HashtablezInfoHandle h = Sample(test_element_size); ++total; if (HashtablezInfoHandlePeer::IsSampled(h)) { ++num_sampled; diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 47e5a228..12682b35 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1643,7 +1643,7 @@ class raw_hash_set { // bound more carefully. if (std::is_same>::value && slots_ == nullptr) { - infoz() = Sample(); + infoz() = Sample(sizeof(slot_type)); } char* mem = static_cast(Allocate( diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index b46c4920..362b3cae 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -2075,6 +2075,7 @@ TEST(RawHashSamplerTest, Sample) { std::memory_order_relaxed)]++; reservations[info.max_reserve.load(std::memory_order_relaxed)]++; } + EXPECT_EQ(info.inline_element_size, sizeof(int64_t)); ++end_size; }); diff --git a/absl/container/sample_element_size_test.cc b/absl/container/sample_element_size_test.cc new file mode 100644 index 00000000..b23626b4 --- /dev/null +++ b/absl/container/sample_element_size_test.cc @@ -0,0 +1,114 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/container/node_hash_map.h" +#include "absl/container/node_hash_set.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace { + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +// Create some tables of type `Table`, then look at all the new +// `HashtablezInfo`s to make sure that the `inline_element_size == +// expected_element_size`. The `inline_element_size` is the amount of memory +// allocated for each slot of a hash table, that is `sizeof(slot_type)`. Add +// the new `HashtablezInfo`s to `preexisting_info`. Store all the new tables +// into `tables`. +template +void TestInlineElementSize( + HashtablezSampler& sampler, + // clang-tidy gives a false positive on this declaration. This unordered + // set cannot be flat_hash_set, however, since that would introduce a mutex + // deadlock. + std::unordered_set& preexisting_info, // NOLINT + std::vector& tables, const typename Table::value_type& elt, + size_t expected_element_size) { + for (int i = 0; i < 10; ++i) { + // We create a new table and must store it somewhere so that when we store + // a pointer to the resulting `HashtablezInfo` into `preexisting_info` + // that we aren't storing a dangling pointer. + tables.emplace_back(); + // We must insert an element to get a hashtablez to instantiate. + tables.back().insert(elt); + } + size_t new_count = 0; + sampler.Iterate([&](const HashtablezInfo& info) { + if (preexisting_info.insert(&info).second) { + EXPECT_EQ(info.inline_element_size, expected_element_size); + ++new_count; + } + }); + // Make sure we actually did get a new hashtablez. + EXPECT_GT(new_count, 0); +} + +struct bigstruct { + char a[1000]; + friend bool operator==(const bigstruct& x, const bigstruct& y) { + return memcmp(x.a, y.a, sizeof(x.a)) == 0; + } + template + friend H AbslHashValue(H h, const bigstruct& c) { + return H::combine_contiguous(std::move(h), c.a, sizeof(c.a)); + } +}; +#endif + +TEST(FlatHashMap, SampleElementSize) { +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + // Enable sampling even if the prod default is off. + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(1); + + auto& sampler = GlobalHashtablezSampler(); + std::vector> flat_map_tables; + std::vector> flat_set_tables; + std::vector> node_map_tables; + std::vector> node_set_tables; + + // It takes thousands of new tables after changing the sampling parameters + // before you actually get some instrumentation. And if you must actually + // put something into those tables. + for (int i = 0; i < 10000; ++i) { + flat_map_tables.emplace_back(); + flat_map_tables.back()[i] = bigstruct{}; + } + + // clang-tidy gives a false positive on this declaration. This unordered set + // cannot be a flat_hash_set, however, since that would introduce a mutex + // deadlock. + std::unordered_set preexisting_info; // NOLINT + sampler.Iterate( + [&](const HashtablezInfo& info) { preexisting_info.insert(&info); }); + TestInlineElementSize(sampler, preexisting_info, flat_map_tables, + {0, bigstruct{}}, sizeof(int) + sizeof(bigstruct)); + TestInlineElementSize(sampler, preexisting_info, node_map_tables, + {0, bigstruct{}}, sizeof(void*)); + TestInlineElementSize(sampler, preexisting_info, flat_set_tables, // + bigstruct{}, sizeof(bigstruct)); + TestInlineElementSize(sampler, preexisting_info, node_set_tables, // + bigstruct{}, sizeof(void*)); +#endif +} + +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/random/internal/randen_test.cc b/absl/random/internal/randen_test.cc index c186fe0d..92773b8d 100644 --- a/absl/random/internal/randen_test.cc +++ b/absl/random/internal/randen_test.cc @@ -23,9 +23,6 @@ namespace { using absl::random_internal::Randen; -// Local state parameters. -constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t); - TEST(RandenTest, CopyAndMove) { static_assert(std::is_copy_constructible::value, "Randen must be copy constructible"); @@ -41,30 +38,38 @@ TEST(RandenTest, CopyAndMove) { } TEST(RandenTest, Default) { - constexpr uint64_t kGolden[] = { - 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, - 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, - 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, - 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, - 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, - 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, - 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, - 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, - 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, - 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, - 0x026ff374c101da7e, 0x811ef0821c3de851, + constexpr uint8_t kGolden[] = { + 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d, + 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3, + 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f, + 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0, + 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff, + 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2, + 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5, + 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c, + 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56, + 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4, + 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91, + 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5, + 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d, + 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58, + 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e, + 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49, + 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0, + 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6, + 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61, + 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35, + 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c, + 0x82, 0xf0, 0x1e, 0x81, }; - alignas(16) uint64_t state[kStateSizeT]; + alignas(16) uint8_t state[Randen::kStateBytes]; std::memset(state, 0, sizeof(state)); Randen r; r.Generate(state); - auto id = std::begin(state); - for (const auto& elem : kGolden) { - EXPECT_EQ(elem, *id++); - } + EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state))); } } // namespace diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index f49e0c83..38338f24 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -778,9 +778,9 @@ class Condition { // // Usage to wake T is: // mu.Lock(); -// // process data, possibly establishing C -// if (C) { cv->Signal(); } -// mu.Unlock(); +// // process data, possibly establishing C +// if (C) { cv->Signal(); } +// mu.Unlock(); // // If C may be useful to more than one waiter, use `SignalAll()` instead of // `Signal()`. diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc index f2777d91..4f175d95 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -649,7 +650,7 @@ std::unique_ptr FileZoneInfoSource::Open( // Open the zoneinfo file. auto fp = FOpen(path.c_str(), "rb"); - if (fp.get() == nullptr) return nullptr; + if (fp == nullptr) return nullptr; return std::unique_ptr(new FileZoneInfoSource(std::move(fp))); } @@ -674,7 +675,7 @@ std::unique_ptr AndroidZoneInfoSource::Open( for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", "/system/usr/share/zoneinfo/tzdata"}) { auto fp = FOpen(tzdata, "rb"); - if (fp.get() == nullptr) continue; + if (fp == nullptr) continue; char hbuf[24]; // covers header.zonetab_offset too if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; @@ -708,6 +709,69 @@ std::unique_ptr AndroidZoneInfoSource::Open( return nullptr; } +// A zoneinfo source for use inside Fuchsia components. This attempts to +// read zoneinfo files from one of several known paths in a component's +// incoming namespace. [Config data][1] is preferred, but package-specific +// resources are also supported. +// +// Fuchsia's implementation supports `FileZoneInfoSource::Version()`. +// +// [1]: +// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component +class FuchsiaZoneInfoSource : public FileZoneInfoSource { + public: + static std::unique_ptr Open(const std::string& name); + std::string Version() const override { return version_; } + + private: + explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version) + : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {} + std::string version_; +}; + +std::unique_ptr FuchsiaZoneInfoSource::Open( + const std::string& name) { + // Use of the "file:" prefix is intended for testing purposes only. + const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; + + // Prefixes where a Fuchsia component might find zoneinfo files, + // in descending order of preference. + const auto kTzdataPrefixes = { + "/config/data/tzdata/", + "/pkg/data/tzdata/", + "/data/tzdata/", + }; + const auto kEmptyPrefix = {""}; + const bool name_absolute = (pos != name.size() && name[pos] == '/'); + const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes; + + // Fuchsia builds place zoneinfo files at "". + for (const std::string prefix : prefixes) { + std::string path = prefix; + if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format + path.append(name, pos, std::string::npos); + + auto fp = FOpen(path.c_str(), "rb"); + if (fp == nullptr) continue; + + std::string version; + if (!prefix.empty()) { + // Fuchsia builds place the version in "revision.txt". + std::ifstream version_stream(prefix + "revision.txt"); + if (version_stream.is_open()) { + // revision.txt should contain no newlines, but to be + // defensive we read just the first line. + std::getline(version_stream, version); + } + } + + return std::unique_ptr( + new FuchsiaZoneInfoSource(std::move(fp), std::move(version))); + } + + return nullptr; +} + } // namespace bool TimeZoneInfo::Load(const std::string& name) { @@ -725,6 +789,7 @@ bool TimeZoneInfo::Load(const std::string& name) { name, [](const std::string& n) -> std::unique_ptr { if (auto z = FileZoneInfoSource::Open(n)) return z; if (auto z = AndroidZoneInfoSource::Open(n)) return z; + if (auto z = FuchsiaZoneInfoSource::Open(n)) return z; return nullptr; }); return zip != nullptr && Load(zip.get()); diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc index efdea64b..898d04c1 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -28,6 +28,13 @@ #include #endif +#if defined(__Fuchsia__) +#include +#include +#include +#include +#endif + #include #include #include @@ -140,6 +147,48 @@ time_zone local_time_zone() { } CFRelease(tz_default); #endif +#if defined(__Fuchsia__) + std::string primary_tz; + [&]() { + // Note: We can't use the synchronous FIDL API here because it doesn't + // allow timeouts; if the FIDL call failed, local_time_zone() would never + // return. + + const zx::duration kTimeout = zx::msec(500); + + // Don't attach to the thread because otherwise the thread's dispatcher + // would be set to null when the loop is destroyed, causing any other FIDL + // code running on the same thread to crash. + async::Loop loop(&kAsyncLoopConfigNeverAttachToThread); + std::unique_ptr context = + sys::ComponentContext::Create(); + + fuchsia::intl::PropertyProviderHandle handle; + zx_status_t status = context->svc()->Connect(handle.NewRequest()); + if (status != ZX_OK) { + return; + } + + fuchsia::intl::PropertyProviderPtr intl_provider; + status = intl_provider.Bind(std::move(handle), loop.dispatcher()); + if (status != ZX_OK) { + return; + } + + intl_provider->GetProfile( + [&loop, &primary_tz](fuchsia::intl::Profile profile) { + if (!profile.time_zones().empty()) { + primary_tz = profile.time_zones()[0].id; + } + loop.Quit(); + }); + loop.Run(zx::deadline_after(kTimeout)); + }(); + + if (!primary_tz.empty()) { + zone = primary_tz.c_str(); + } +#endif // Allow ${TZ} to override to default zone. char* tz_env = nullptr; diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 8751b346..0226ab71 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -1027,7 +1027,11 @@ TEST(MakeTime, SysSecondsLimits) { #endif const year_t min_tm_year = year_t{std::numeric_limits::min()} + 1900; tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut); +#if defined(__Fuchsia__) + // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527). +#else EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut)); +#endif #endif } } diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index 51191b57..8ee898ba 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2021c +2021e diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza index 58e9fdf4..ccc57c9c 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza and b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron index aeda06b5..906d8d5c 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron and b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji index e3934e42..8b2dd52b 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji and b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji differ -- cgit v1.2.3 From 022527c50e0e2bc937f9fa3c516e3e36cbba0845 Mon Sep 17 00:00:00 2001 From: Milad Fa <46688537+miladfarca@users.noreply.github.com> Date: Fri, 29 Oct 2021 15:51:54 -0400 Subject: Fix Randen and PCG on Big Endian platforms (#1031) --- absl/random/internal/explicit_seed_seq.h | 2 +- absl/random/internal/randen_engine.h | 7 +++++++ absl/random/internal/randen_slow.cc | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h index e3aa31a1..25f79153 100644 --- a/absl/random/internal/explicit_seed_seq.h +++ b/absl/random/internal/explicit_seed_seq.h @@ -74,7 +74,7 @@ class ExplicitSeedSeq { template void generate(OutIterator begin, OutIterator end) { for (size_t index = 0; begin != end; begin++) { - *begin = state_.empty() ? 0 : little_endian::FromHost32(state_[index++]); + *begin = state_.empty() ? 0 : state_[index++]; if (index >= state_.size()) { index = 0; } diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h index 92bb8905..372c3ac2 100644 --- a/absl/random/internal/randen_engine.h +++ b/absl/random/internal/randen_engine.h @@ -121,6 +121,13 @@ class alignas(16) randen_engine { const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size; std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0); seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy); +#ifdef ABSL_IS_BIG_ENDIAN + // Randen expects the seed buffer to be in Little Endian; reverse it on + // Big Endian platforms. + for (sequence_result_type& e : buffer) { + e = absl::little_endian::FromHost(e); + } +#endif // The Randen paper suggests preferentially initializing even-numbered // 128-bit vectors of the randen state (there are 16 such vectors). // The seed data is merged into the state offset by 128-bits, which diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc index d5c9347b..9bfd2a40 100644 --- a/absl/random/internal/randen_slow.cc +++ b/absl/random/internal/randen_slow.cc @@ -395,6 +395,23 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute( } } +// Enables native loads in the round loop by pre-swapping. +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void SwapEndian( + absl::uint128* state) { +#ifdef ABSL_IS_BIG_ENDIAN + for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) { + uint64_t new_lo = absl::little_endian::ToHost64( + static_cast(state[block] >> 64)); + uint64_t new_hi = absl::little_endian::ToHost64( + static_cast((state[block] << 64) >> 64)); + state[block] = (static_cast(new_hi) << 64) | new_lo; + } +#else + // Avoid warning about unused variable. + (void)state; +#endif +} + } // namespace namespace absl { @@ -439,8 +456,12 @@ void RandenSlow::Generate(const void* keys_void, void* state_void) { const absl::uint128 prev_inner = state[0]; + SwapEndian(state); + Permute(state, keys); + SwapEndian(state); + // Ensure backtracking resistance. *state ^= prev_inner; } -- cgit v1.2.3 From b6b62c72b4209f953923354bfb1eb40ed867b5c3 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 1 Nov 2021 16:01:36 -0700 Subject: Export of internal Abseil changes -- f49e405201d2ffd5955503fa8ad0f08ec0cdfb2b by Martijn Vels : Add common [container.requirements] type definitions to `CharRange` and `ChunkRange` The presence of these allow these range classes to be used in various utility functions which require some minimum type of container. For example, this change allows tests to use `EXPECT_THAT(cord.Chunks(), ElementsAre(...))` PiperOrigin-RevId: 406941278 -- 0c195f073632e21d9a4bce158047b2ba8551c2d1 by Evan Brown : Use explicit exponential growth in SubstituteAndAppendArray. PiperOrigin-RevId: 406931952 -- afb043bccd809a55cab78abadb7548a057d9eda0 by Jorg Brown : Use longer var names in macro to avoid clang-tidy warning PiperOrigin-RevId: 406930978 -- 80397e2604e6b3d929a34742c3a32581b34d3ac4 by Martijn Vels : Add future kAppendBuffer and kPrependBuffer API trackers for Cordz sampling PiperOrigin-RevId: 406912759 -- e910ce919ef83933f08a690e8b7325c7cc5b6d5d by Martijn Vels : Implement Prepend(string_view) in terms of PrependArray(string_view, MethodIdentifier). PiperOrigin-RevId: 406891665 -- c9cff43d4c0568ed01f2fca0f6ef038ae03112b5 by Martijn Vels : Add 'Rebuild' logic to CordRepBtree There are btree hostile scenarios where an application could perform repeated split/insert/merge operations on a cord leading to a tree exceeding the maximum height. While this should be rare in practice, this change adds a Rebuild() method that will rebuild a tree with a 100% fill factor, and we will invoke this rebuild when a tree exceeds the maximum height. This basically follows the similar 'balance' logic in Concat trees (although the latter is common in Concat uses) PiperOrigin-RevId: 406875739 -- 5b2b8fb88f1ebfdc1c670088152da2cb2ea4c376 by Martijn Vels : Add 'in place' enabled RemoveSuffix An in-place RemoveSuffix is more efficient than SubTree() as it can directly modify privately owned nodes and flats allowing easy re-use of free capacity in right-most flats that may turn into Substring edges when using SubTree. PiperOrigin-RevId: 406431230 -- f09903c0a3d7344f59aaf1380a16ea10829217d4 by Derek Mauro : Internal change PiperOrigin-RevId: 406430373 -- 9957af575c33bb18dc170572a4ee8cc5901df6b2 by Greg Falcon : Initial groundwork to allow storing checksum data inside CordRep instances. This uses a RefcountAndFlags bit that was reserved for this purpose, and will be leveraged in a follow-up change to allow attaching checksums to a Cord's value. This change splits RefcountAndFlags::IsOne() into two distinct operations: * IsOne(): This returns true when the associated CordRep is not shared with other threads. This is useful for functions that consume CordRep instances; for example, code that consumes an unshared CordRep can assume ownership of its children without modifying those refcounts. * IsMutable(): This returns true when the associated CordRep reference is not shared with other threads, *and* does not store an associated checksum value. This is useful for functions that modify a CordRep's contents: code may modify the bytes of a mutable-unshared CordRep without fear of races with other threads, or of invalidating a stored checksum. The tricky part of this CL is ensuring that the correct choice between IsMutable() and IsOne() was made at each point. An incorrect application of IsOne() could lead to correctness bugs in the future. Code conditioned on IsOne() may delete the CordRep in question, or assume ownership of its children, but must not modify the CordRep's data without explicitly adjusting the CRC. PiperOrigin-RevId: 406191103 -- 686544814079e5ab6d4593cca0c068b510be400a by Martijn Vels : Reduce the size in the LargeString test when running with Sanitizers PiperOrigin-RevId: 406186945 -- 735b4490bdb695c35731f06ce4b8de14ce2be6ed by Alex Strelnikov : Release absl::SimpleHexAtoi. PiperOrigin-RevId: 406143188 GitOrigin-RevId: f49e405201d2ffd5955503fa8ad0f08ec0cdfb2b Change-Id: Ic6527ac40fa03ea02ca813e8bb7868a219544de4 --- absl/flags/flag.h | 8 +- absl/strings/BUILD.bazel | 12 ++ absl/strings/CMakeLists.txt | 13 ++ absl/strings/cord.cc | 20 +- absl/strings/cord.h | 28 +++ absl/strings/cord_test.cc | 43 ++++ absl/strings/internal/cord_internal.h | 41 +++- absl/strings/internal/cord_internal_test.cc | 116 +++++++++++ absl/strings/internal/cord_rep_btree.cc | 227 ++++++++++++++++++--- absl/strings/internal/cord_rep_btree.h | 95 +++++++-- absl/strings/internal/cord_rep_btree_test.cc | 136 ++++++++++-- absl/strings/internal/cord_rep_ring.cc | 20 +- absl/strings/internal/cord_rep_ring.h | 4 +- absl/strings/internal/cord_rep_test_util.h | 35 ++++ absl/strings/internal/cordz_update_tracker.h | 2 + absl/strings/internal/cordz_update_tracker_test.cc | 2 + absl/strings/numbers.h | 34 +++ absl/strings/numbers_test.cc | 143 +++++++++++++ absl/strings/str_split_test.cc | 8 +- absl/strings/substitute.cc | 3 +- 20 files changed, 890 insertions(+), 100 deletions(-) create mode 100644 absl/strings/internal/cord_internal_test.cc diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 1a7e4d4d..a724ccc9 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -241,8 +241,8 @@ ABSL_NAMESPACE_END /* default value argument. That keeps temporaries alive */ \ /* long enough for NonConst to work correctly. */ \ static constexpr absl::string_view Value( \ - absl::string_view v = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \ - return v; \ + absl::string_view absl_flag_help = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \ + return absl_flag_help; \ } \ static std::string NonConst() { return std::string(Value()); } \ }; \ @@ -254,8 +254,8 @@ ABSL_NAMESPACE_END #define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ struct AbslFlagDefaultGenFor##name { \ Type value = absl::flags_internal::InitDefaultValue(default_value); \ - static void Gen(void* p) { \ - new (p) Type(AbslFlagDefaultGenFor##name{}.value); \ + static void Gen(void* absl_flag_default_loc) { \ + new (absl_flag_default_loc) Type(AbslFlagDefaultGenFor##name{}.value); \ } \ }; diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index c046366c..090fc58a 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -305,6 +305,17 @@ cc_library( ], ) +cc_test( + name = "cord_internal_test", + srcs = ["internal/cord_internal_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "cord_rep_btree_test", size = "medium", @@ -684,6 +695,7 @@ cc_test( "//absl/base:endian", "//absl/base:raw_logging_internal", "//absl/container:fixed_array", + "//absl/random", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 8ad5f9c7..d6801fe6 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -920,6 +920,7 @@ absl_cc_test( absl::cordz_test_helpers absl::core_headers absl::endian + absl::random_random absl::raw_logging_internal absl::fixed_array GTest::gmock_main @@ -943,6 +944,18 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + cord_internal_test + SRCS + "internal/cord_internal_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::cord_internal + GTest::gmock_main +) + absl_cc_test( NAME cord_rep_btree_test diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 29af9782..854047ca 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -419,7 +419,7 @@ void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { // written to region and the actual size increase will be written to size. static inline bool PrepareAppendRegion(CordRep* root, char** region, size_t* size, size_t max_length) { - if (root->IsBtree() && root->refcount.IsOne()) { + if (root->IsBtree() && root->refcount.IsMutable()) { Span span = root->btree()->GetAppendBuffer(max_length); if (!span.empty()) { *region = span.data(); @@ -430,11 +430,11 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, // Search down the right-hand path for a non-full FLAT node. CordRep* dst = root; - while (dst->IsConcat() && dst->refcount.IsOne()) { + while (dst->IsConcat() && dst->refcount.IsMutable()) { dst = dst->concat()->right; } - if (!dst->IsFlat() || !dst->refcount.IsOne()) { + if (!dst->IsFlat() || !dst->refcount.IsMutable()) { *region = nullptr; *size = 0; return false; @@ -649,7 +649,7 @@ Cord& Cord::operator=(absl::string_view src) { if (tree != nullptr) { CordzUpdateScope scope(contents_.cordz_info(), method); if (tree->IsFlat() && tree->flat()->Capacity() >= length && - tree->refcount.IsOne()) { + tree->refcount.IsMutable()) { // Copy in place if the existing FLAT node is reusable. memmove(tree->flat()->Data(), data, length); tree->length = length; @@ -819,7 +819,7 @@ void Cord::Prepend(const Cord& src) { return Prepend(src_contents); } -void Cord::Prepend(absl::string_view src) { +void Cord::PrependArray(absl::string_view src, MethodIdentifier method) { if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined. if (!contents_.is_tree()) { size_t cur_size = contents_.inline_size(); @@ -834,7 +834,7 @@ void Cord::Prepend(absl::string_view src) { } } CordRep* rep = NewTree(src.data(), src.size(), 0); - contents_.PrependTree(rep, CordzUpdateTracker::kPrependString); + contents_.PrependTree(rep, method); } template > @@ -894,7 +894,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { if (n >= node->length) return nullptr; if (n == 0) return CordRep::Ref(node); absl::InlinedVector lhs_stack; - bool inplace_ok = node->refcount.IsOne(); + bool inplace_ok = node->refcount.IsMutable(); while (node->IsConcat()) { assert(n <= node->length); @@ -907,7 +907,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { n -= node->concat()->right->length; node = node->concat()->left; } - inplace_ok = inplace_ok && node->refcount.IsOne(); + inplace_ok = inplace_ok && node->refcount.IsMutable(); } assert(n <= node->length); @@ -968,9 +968,7 @@ void Cord::RemoveSuffix(size_t n) { auto constexpr method = CordzUpdateTracker::kRemoveSuffix; CordzUpdateScope scope(contents_.cordz_info(), method); if (tree->IsBtree()) { - CordRep* old = tree; - tree = tree->btree()->SubTree(0, tree->length - n); - CordRep::Unref(old); + tree = CordRepBtree::RemoveSuffix(tree->btree(), n); } else { CordRep* newrep = RemoveSuffixFrom(tree, n); CordRep::Unref(tree); diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 175d7b90..f0a19914 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -460,6 +460,16 @@ class Cord { // `Cord::chunk_begin()` and `Cord::chunk_end()`. class ChunkRange { public: + // Fulfill minimum c++ container requirements [container.requirements] + // Theses (partial) container type definitions allow ChunkRange to be used + // in various utilities expecting a subset of [container.requirements]. + // For example, the below enables using `::testing::ElementsAre(...)` + using value_type = absl::string_view; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = ChunkIterator; + using const_iterator = ChunkIterator; + explicit ChunkRange(const Cord* cord) : cord_(cord) {} ChunkIterator begin() const; @@ -591,6 +601,16 @@ class Cord { // `Cord::char_begin()` and `Cord::char_end()`. class CharRange { public: + // Fulfill minimum c++ container requirements [container.requirements] + // Theses (partial) container type definitions allow CharRange to be used + // in various utilities expecting a subset of [container.requirements]. + // For example, the below enables using `::testing::ElementsAre(...)` + using value_type = char; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = CharIterator; + using const_iterator = CharIterator; + explicit CharRange(const Cord* cord) : cord_(cord) {} CharIterator begin() const; @@ -881,6 +901,10 @@ class Cord { template void AppendImpl(C&& src); + // Prepends the provided data to this instance. `method` contains the public + // API method for this action which is tracked for Cordz sampling purposes. + void PrependArray(absl::string_view src, MethodIdentifier method); + // Assigns the value in 'src' to this instance, 'stealing' its contents. // Requires src.length() > kMaxBytesToCopy. Cord& AssignLargeString(std::string&& src); @@ -1220,6 +1244,10 @@ inline void Cord::Append(absl::string_view src) { contents_.AppendArray(src, CordzUpdateTracker::kAppendString); } +inline void Cord::Prepend(absl::string_view src) { + PrependArray(src, CordzUpdateTracker::kPrependString); +} + extern template void Cord::Append(std::string&& src); extern template void Cord::Prepend(std::string&& src); diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 50079b7c..cced9bba 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -34,6 +34,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/container/fixed_array.h" +#include "absl/random/random.h" #include "absl/strings/cord_test_helpers.h" #include "absl/strings/cordz_test_helpers.h" #include "absl/strings/str_cat.h" @@ -1833,6 +1834,48 @@ TEST_P(CordTest, Hardening) { EXPECT_DEATH_IF_SUPPORTED(++cord.chunk_end(), ""); } +// This test mimics a specific (and rare) application repeatedly splitting a +// cord, inserting (overwriting) a string value, and composing a new cord from +// the three pieces. This is hostile towards a Btree implementation: A split of +// a node at any level is likely to have the right-most edge of the left split, +// and the left-most edge of the right split shared. For example, splitting a +// leaf node with 6 edges will result likely in a 1-6, 2-5, 3-4, etc. split, +// sharing the 'split node'. When recomposing such nodes, we 'injected' an edge +// in that node. As this happens with some probability on each level of the +// tree, this will quickly grow the tree until it reaches maximum height. +TEST_P(CordTest, BtreeHostileSplitInsertJoin) { + absl::BitGen bitgen; + + // Start with about 1GB of data + std::string data(1 << 10, 'x'); + absl::Cord buffer(data); + absl::Cord cord; + for (int i = 0; i < 1000000; ++i) { + cord.Append(buffer); + } + + for (int j = 0; j < 1000; ++j) { + size_t offset = absl::Uniform(bitgen, 0u, cord.size()); + size_t length = absl::Uniform(bitgen, 100u, data.size()); + if (cord.size() == offset) { + cord.Append(absl::string_view(data.data(), length)); + } else { + absl::Cord suffix; + if (offset + length < cord.size()) { + suffix = cord; + suffix.RemovePrefix(offset + length); + } + if (cord.size() > offset) { + cord.RemoveSuffix(cord.size() - offset); + } + cord.Append(absl::string_view(data.data(), length)); + if (!suffix.empty()) { + cord.Append(suffix); + } + } + } +} + class AfterExitCordTester { public: bool Set(absl::Cord* cord, absl::string_view expected) { diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 0300ac40..bfe5564e 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -87,6 +87,9 @@ class RefcountAndFlags { constexpr RefcountAndFlags() : count_{kRefIncrement} {} struct Immortal {}; explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {} + struct WithCrc {}; + explicit constexpr RefcountAndFlags(WithCrc) + : count_(kCrcFlag | kRefIncrement) {} // Increments the reference count. Imposes no memory ordering. inline void Increment() { @@ -122,14 +125,32 @@ class RefcountAndFlags { return count_.load(std::memory_order_acquire) >> kNumFlags; } - // Returns whether the atomic integer is 1. - // If the reference count is used in the conventional way, a - // reference count of 1 implies that the current thread owns the - // reference and no other thread shares it. - // This call performs the test for a reference count of one, and - // performs the memory barrier needed for the owning thread - // to act on the object, knowing that it has exclusive access to the - // object. Always returns false when the immortal bit is set. + // Returns true if the referenced object carries a CRC value. + bool HasCrc() const { + return (count_.load(std::memory_order_relaxed) & kCrcFlag) != 0; + } + + // Returns true iff the atomic integer is 1 and this node does not store + // a CRC. When both these conditions are met, the current thread owns + // the reference and no other thread shares it, so its contents may be + // safely mutated. + // + // If the referenced item is shared, carries a CRC, or is immortal, + // it should not be modified in-place, and this function returns false. + // + // This call performs the memory barrier needed for the owning thread + // to act on the object, so that if it returns true, it may safely + // assume exclusive access to the object. + inline bool IsMutable() { + return (count_.load(std::memory_order_acquire)) == kRefIncrement; + } + + // Returns whether the atomic integer is 1. Similar to IsMutable(), + // but does not check for a stored CRC. (An unshared node with a CRC is not + // mutable, because changing its data would invalidate the CRC.) + // + // When this returns true, there are no other references, and data sinks + // may safely adopt the children of the CordRep. inline bool IsOne() { return (count_.load(std::memory_order_acquire) & kRefcountMask) == kRefIncrement; @@ -149,14 +170,14 @@ class RefcountAndFlags { kNumFlags = 2, kImmortalFlag = 0x1, - kReservedFlag = 0x2, + kCrcFlag = 0x2, kRefIncrement = (1 << kNumFlags), // Bitmask to use when checking refcount by equality. This masks out // all flags except kImmortalFlag, which is part of the refcount for // purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1 // if the immortal bit is set.) - kRefcountMask = ~kReservedFlag, + kRefcountMask = ~kCrcFlag, }; std::atomic count_; diff --git a/absl/strings/internal/cord_internal_test.cc b/absl/strings/internal/cord_internal_test.cc new file mode 100644 index 00000000..0758dfef --- /dev/null +++ b/absl/strings/internal/cord_internal_test.cc @@ -0,0 +1,116 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_internal.h" + +#include "gmock/gmock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +TEST(RefcountAndFlags, NormalRefcount) { + for (bool expect_high_refcount : {false, true}) { + SCOPED_TRACE(expect_high_refcount); + RefcountAndFlags refcount; + // count = 1 + + EXPECT_FALSE(refcount.HasCrc()); + EXPECT_TRUE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + refcount.Increment(); + // count = 2 + + EXPECT_FALSE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_FALSE(refcount.IsOne()); + + // Decrementing should return true, since a reference is outstanding. + if (expect_high_refcount) { + EXPECT_TRUE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_TRUE(refcount.Decrement()); + } + // count = 1 + + EXPECT_FALSE(refcount.HasCrc()); + EXPECT_TRUE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + // One more decremnt will return false, as no references remain. + if (expect_high_refcount) { + EXPECT_FALSE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_FALSE(refcount.Decrement()); + } + } +} + +TEST(RefcountAndFlags, CrcRefcount) { + for (bool expect_high_refcount : {false, true}) { + SCOPED_TRACE(expect_high_refcount); + RefcountAndFlags refcount(RefcountAndFlags::WithCrc{}); + // count = 1 + + // A CRC-carrying node is never mutable, but can be unshared + EXPECT_TRUE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + refcount.Increment(); + // count = 2 + + EXPECT_TRUE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_FALSE(refcount.IsOne()); + + // Decrementing should return true, since a reference is outstanding. + if (expect_high_refcount) { + EXPECT_TRUE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_TRUE(refcount.Decrement()); + } + // count = 1 + + EXPECT_TRUE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + // One more decremnt will return false, as no references remain. + if (expect_high_refcount) { + EXPECT_FALSE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_FALSE(refcount.Decrement()); + } + } +} + +TEST(RefcountAndFlags, ImmortalRefcount) { + RefcountAndFlags immortal_refcount(RefcountAndFlags::Immortal{}); + + for (int i = 0; i < 100; ++i) { + // An immortal refcount is never unshared, and decrementing never causes + // a collection. + EXPECT_FALSE(immortal_refcount.HasCrc()); + EXPECT_FALSE(immortal_refcount.IsMutable()); + EXPECT_FALSE(immortal_refcount.IsOne()); + EXPECT_TRUE(immortal_refcount.Decrement()); + EXPECT_TRUE(immortal_refcount.DecrementExpectHighRefcount()); + } +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 6d53ab61..4404f33a 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -140,6 +140,26 @@ inline CordRep* MakeSubstring(CordRep* rep, size_t offset) { return CreateSubstring(rep, offset, rep->length - offset); } +// Resizes `edge` to the provided `length`. Adopts a reference on `edge`. +// This method directly returns `edge` if `length` equals `edge->length`. +// If `is_mutable` is set to true, this function may return `edge` with +// `edge->length` set to the new length depending on the type and size of +// `edge`. Otherwise, this function returns a new CordRepSubstring value. +// Requires `length > 0 && length <= edge->length`. +CordRep* ResizeEdge(CordRep* edge, size_t length, bool is_mutable) { + assert(length > 0); + assert(length <= edge->length); + assert(CordRepBtree::IsDataEdge(edge)); + if (length >= edge->length) return edge; + + if (is_mutable && (edge->tag >= FLAT || edge->tag == SUBSTRING)) { + edge->length = length; + return edge; + } + + return CreateSubstring(edge, 0, length); +} + template inline absl::string_view Consume(absl::string_view s, size_t n) { return edge_type == kBack ? s.substr(n) : s.substr(0, s.size() - n); @@ -196,8 +216,8 @@ void DeleteLeafEdge(CordRep* rep) { // propagate node changes up the stack. template struct StackOperations { - // Returns true if the node at 'depth' is not shared, i.e. has a refcount - // of one and all of its parent nodes have a refcount of one. + // Returns true if the node at 'depth' is mutable, i.e. has a refcount + // of one, carries no CRC, and all of its parent nodes have a refcount of one. inline bool owned(int depth) const { return depth < share_depth; } // Returns the node at 'depth'. @@ -208,11 +228,11 @@ struct StackOperations { inline CordRepBtree* BuildStack(CordRepBtree* tree, int depth) { assert(depth <= tree->height()); int current_depth = 0; - while (current_depth < depth && tree->refcount.IsOne()) { + while (current_depth < depth && tree->refcount.IsMutable()) { stack[current_depth++] = tree; tree = tree->Edge(edge_type)->btree(); } - share_depth = current_depth + (tree->refcount.IsOne() ? 1 : 0); + share_depth = current_depth + (tree->refcount.IsMutable() ? 1 : 0); while (current_depth < depth) { stack[current_depth++] = tree; tree = tree->Edge(edge_type)->btree(); @@ -221,17 +241,17 @@ struct StackOperations { } // Builds a stack with the invariant that all nodes are private owned / not - // shared. This is used in iterative updates where a previous propagation - // guaranteed all nodes are owned / private. + // shared and carry no CRC data. This is used in iterative updates where a + // previous propagation guaranteed all nodes have this property. inline void BuildOwnedStack(CordRepBtree* tree, int height) { assert(height <= CordRepBtree::kMaxHeight); int depth = 0; while (depth < height) { - assert(tree->refcount.IsOne()); + assert(tree->refcount.IsMutable()); stack[depth++] = tree; tree = tree->Edge(edge_type)->btree(); } - assert(tree->refcount.IsOne()); + assert(tree->refcount.IsMutable()); share_depth = depth + 1; } @@ -240,11 +260,14 @@ struct StackOperations { static inline CordRepBtree* Finalize(CordRepBtree* tree, OpResult result) { switch (result.action) { case CordRepBtree::kPopped: - if (ABSL_PREDICT_FALSE(tree->height() >= CordRepBtree::kMaxHeight)) { - ABSL_RAW_LOG(FATAL, "Max height exceeded"); - } - return edge_type == kBack ? CordRepBtree::New(tree, result.tree) + tree = edge_type == kBack ? CordRepBtree::New(tree, result.tree) : CordRepBtree::New(result.tree, tree); + if (ABSL_PREDICT_FALSE(tree->height() > CordRepBtree::kMaxHeight)) { + tree = CordRepBtree::Rebuild(tree); + ABSL_RAW_CHECK(tree->height() <= CordRepBtree::kMaxHeight, + "Max height exceeded"); + } + return tree; case CordRepBtree::kCopied: CordRep::Unref(tree); ABSL_FALLTHROUGH_INTENDED; @@ -313,12 +336,12 @@ struct StackOperations { return Unwind(tree, depth, length, result); } - // `share_depth` contains the depth at which the nodes in the stack become - // shared. I.e., if the top most level is shared (i.e.: `!refcount.IsOne()`), - // then `share_depth` is 0. If the 2nd node is shared (and implicitly all - // nodes below that) then `share_depth` is 1, etc. A `share_depth` greater - // than the depth of the stack indicates that none of the nodes in the stack - // are shared. + // `share_depth` contains the depth at which the nodes in the stack cannot + // be mutated. I.e., if the top most level is shared (i.e.: + // `!refcount.IsMutable()`), then `share_depth` is 0. If the 2nd node + // is shared (and implicitly all nodes below that) then `share_depth` is 1, + // etc. A `share_depth` greater than the depth of the stack indicates that + // none of the nodes in the stack are shared. int share_depth; NodeStack stack; @@ -692,7 +715,7 @@ CopyResult CordRepBtree::CopySuffix(size_t offset) { return result; } -CopyResult CordRepBtree::CopyPrefix(size_t n) { +CopyResult CordRepBtree::CopyPrefix(size_t n, bool allow_folding) { assert(n > 0); assert(n <= this->length); @@ -704,10 +727,12 @@ CopyResult CordRepBtree::CopyPrefix(size_t n) { int height = this->height(); CordRepBtree* node = this; CordRep* front = node->Edge(kFront); - while (front->length >= n) { - if (--height < 0) return {MakeSubstring(CordRep::Ref(front), 0, n), -1}; - node = front->btree(); - front = node->Edge(kFront); + if (allow_folding) { + while (front->length >= n) { + if (--height < 0) return {MakeSubstring(CordRep::Ref(front), 0, n), -1}; + node = front->btree(); + front = node->Edge(kFront); + } } if (node->length == n) return {CordRep::Ref(node), height}; @@ -746,6 +771,97 @@ CopyResult CordRepBtree::CopyPrefix(size_t n) { return result; } +CordRep* CordRepBtree::ExtractFront(CordRepBtree* tree) { + CordRep* front = tree->Edge(tree->begin()); + if (tree->refcount.IsMutable()) { + Unref(tree->Edges(tree->begin() + 1, tree->end())); + CordRepBtree::Delete(tree); + } else { + CordRep::Ref(front); + CordRep::Unref(tree); + } + return front; +} + +CordRepBtree* CordRepBtree::ConsumeBeginTo(CordRepBtree* tree, size_t end, + size_t new_length) { + assert(end <= tree->end()); + if (tree->refcount.IsMutable()) { + Unref(tree->Edges(end, tree->end())); + tree->set_end(end); + tree->length = new_length; + } else { + CordRepBtree* old = tree; + tree = tree->CopyBeginTo(end, new_length); + CordRep::Unref(old); + } + return tree; +} + +CordRep* CordRepBtree::RemoveSuffix(CordRepBtree* tree, size_t n) { + // Check input and deal with trivial cases 'Remove all/none' + assert(tree != nullptr); + assert(n <= tree->length); + const size_t len = tree->length; + if (ABSL_PREDICT_FALSE(n == 0)) { + return tree; + } + if (ABSL_PREDICT_FALSE(n >= len)) { + CordRepBtree::Unref(tree); + return nullptr; + } + + size_t length = len - n; + int height = tree->height(); + bool is_mutable = tree->refcount.IsMutable(); + + // Extract all top nodes which are reduced to size = 1 + Position pos = tree->IndexOfLength(length); + while (pos.index == tree->begin()) { + CordRep* edge = ExtractFront(tree); + is_mutable &= edge->refcount.IsMutable(); + if (height-- == 0) return ResizeEdge(edge, length, is_mutable); + tree = edge->btree(); + pos = tree->IndexOfLength(length); + } + + // Repeat the following sequence traversing down the tree: + // - Crop the top node to the 'last remaining edge' adjusting length. + // - Set the length for down edges to the partial length in that last edge. + // - Repeat this until the last edge is 'included in full' + // - If we hit the data edge level, resize and return the last data edge + CordRepBtree* top = tree = ConsumeBeginTo(tree, pos.index + 1, length); + CordRep* edge = tree->Edge(pos.index); + length = pos.n; + while (length != edge->length) { + // ConsumeBeginTo guarantees `tree` is a clean, privately owned copy. + assert(tree->refcount.IsMutable()); + const bool edge_is_mutable = edge->refcount.IsMutable(); + + if (height-- == 0) { + tree->edges_[pos.index] = ResizeEdge(edge, length, edge_is_mutable); + return AssertValid(top); + } + + if (!edge_is_mutable) { + // We can't 'in place' remove any suffixes down this edge. + // Replace this edge with a prefix copy instead. + tree->edges_[pos.index] = edge->btree()->CopyPrefix(length, false).edge; + CordRep::Unref(edge); + return AssertValid(top); + } + + // Move down one level, rinse repeat. + tree = edge->btree(); + pos = tree->IndexOfLength(length); + tree = ConsumeBeginTo(edge->btree(), pos.index + 1, length); + edge = tree->Edge(pos.index); + length = pos.n; + } + + return AssertValid(top); +} + CordRep* CordRepBtree::SubTree(size_t offset, size_t n) { assert(n <= this->length); assert(offset <= this->length - n); @@ -857,7 +973,7 @@ char CordRepBtree::GetCharacter(size_t offset) const { Span CordRepBtree::GetAppendBufferSlow(size_t size) { // The inlined version in `GetAppendBuffer()` deals with all heights <= 3. assert(height() >= 4); - assert(refcount.IsOne()); + assert(refcount.IsMutable()); // Build a stack of nodes we may potentially need to update if we find a // non-shared FLAT with capacity at the leaf level. @@ -866,13 +982,13 @@ Span CordRepBtree::GetAppendBufferSlow(size_t size) { CordRepBtree* stack[kMaxDepth]; for (int i = 0; i < depth; ++i) { node = node->Edge(kBack)->btree(); - if (!node->refcount.IsOne()) return {}; + if (!node->refcount.IsMutable()) return {}; stack[i] = node; } - // Must be a privately owned flat. + // Must be a privately owned, mutable flat. CordRep* const edge = node->Edge(kBack); - if (!edge->refcount.IsOne() || edge->tag < FLAT) return {}; + if (!edge->refcount.IsMutable() || edge->tag < FLAT) return {}; // Must have capacity. const size_t avail = edge->flat()->Capacity() - edge->length; @@ -950,6 +1066,63 @@ template CordRepBtree* CordRepBtree::AddData(CordRepBtree* tree, absl::string_view data, size_t extra); +void CordRepBtree::Rebuild(CordRepBtree** stack, CordRepBtree* tree, + bool consume) { + bool owned = consume && tree->refcount.IsOne(); + if (tree->height() == 0) { + for (CordRep* edge : tree->Edges()) { + if (!owned) edge = CordRep::Ref(edge); + size_t height = 0; + size_t length = edge->length; + CordRepBtree* node = stack[0]; + OpResult result = node->AddEdge(true, edge, length); + while (result.action == CordRepBtree::kPopped) { + stack[height] = result.tree; + if (stack[++height] == nullptr) { + result.action = CordRepBtree::kSelf; + stack[height] = CordRepBtree::New(node, result.tree); + } else { + node = stack[height]; + result = node->AddEdge(true, result.tree, length); + } + } + while (stack[++height] != nullptr) { + stack[height]->length += length; + } + } + } else { + for (CordRep* rep : tree->Edges()) { + Rebuild(stack, rep->btree(), owned); + } + } + if (consume) { + if (owned) { + CordRepBtree::Delete(tree); + } else { + CordRepBtree::Unref(tree); + } + } +} + +CordRepBtree* CordRepBtree::Rebuild(CordRepBtree* tree) { + // Set up initial stack with empty leaf node. + CordRepBtree* node = CordRepBtree::New(); + CordRepBtree* stack[CordRepBtree::kMaxDepth + 1] = {node}; + + // Recursively build the tree, consuming the input tree. + Rebuild(stack, tree, /* consume reference */ true); + + // Return top most node + for (CordRepBtree* parent : stack) { + if (parent == nullptr) return node; + node = parent; + } + + // Unreachable + assert(false); + return nullptr; +} + } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index bbaa7934..bb38f0c3 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -163,6 +163,12 @@ class CordRepBtree : public CordRep { // typically after a ref_count.Decrement() on the last reference count. static void Destroy(CordRepBtree* tree); + // Use CordRep::Unref() as we overload for absl::Span. + using CordRep::Unref; + + // Unrefs all edges in `edges` which are assumed to be 'likely one'. + static void Unref(absl::Span edges); + // Appends / Prepends an existing CordRep instance to this tree. // The below methods accept three types of input: // 1) `rep` is a data node (See `IsDataNode` for valid data edges). @@ -198,6 +204,19 @@ class CordRepBtree : public CordRep { // Requires `offset + n <= length`. Returns `nullptr` if `n` is zero. CordRep* SubTree(size_t offset, size_t n); + // Removes `n` trailing bytes from `tree`, and returns the resulting tree + // or data edge. Returns `tree` if n is zero, and nullptr if n == length. + // This function is logically identical to: + // result = tree->SubTree(0, tree->length - n); + // Unref(tree); + // return result; + // However, the actual implementation will as much as possible perform 'in + // place' modifications on the tree on all nodes and edges that are mutable. + // For example, in a fully privately owned tree with the last edge being a + // flat of length 12, RemoveSuffix(1) will simply set the length of that data + // edge to 11, and reduce the length of all nodes on the edge path by 1. + static CordRep* RemoveSuffix(CordRepBtree* tree, size_t n); + // Returns the character at the given offset. char GetCharacter(size_t offset) const; @@ -221,9 +240,9 @@ class CordRepBtree : public CordRep { // length of the flat node and involved tree nodes have been increased by // `span.length()`. The caller is responsible for immediately assigning values // to all uninitialized data reference by the returned span. - // Requires `this->refcount.IsOne()`: this function forces the caller to do - // this fast path check on the top level node, as this is the most commonly - // shared node of a cord tree. + // Requires `this->refcount.IsMutable()`: this function forces the + // caller to do this fast path check on the top level node, as this is the + // most commonly shared node of a cord tree. Span GetAppendBuffer(size_t size); // Returns the `height` of the tree. The height of a tree is limited to @@ -324,6 +343,11 @@ class CordRepBtree : public CordRep { // `front.height() + 1`. Requires `back.height() == front.height()`. static CordRepBtree* New(CordRepBtree* front, CordRepBtree* back); + // Creates a fully balanced tree from the provided tree by rebuilding a new + // tree from all data edges in the input. This function is automatically + // invoked internally when the tree exceeds the maximum height. + static CordRepBtree* Rebuild(CordRepBtree* tree); + private: CordRepBtree() = default; ~CordRepBtree() = default; @@ -368,6 +392,12 @@ class CordRepBtree : public CordRep { // Requires 0 < `offset` <= length. Position IndexBefore(size_t offset) const; + // Returns the index of the edge ending at (or on) length `length`, and the + // number of bytes inside that edge up to `length`. For example, if we have a + // Node with 2 edges, one of 10 and one of 20 long, then IndexOfLength(27) + // will return {1, 17}, and IndexOfLength(10) will return {0, 10}. + Position IndexOfLength(size_t n) const; + // Identical to the above function except starting from the position `front`. // This function is equivalent to `IndexBefore(front.n + offset)`, with // the difference that this function is optimized to start at `front.index`. @@ -406,11 +436,28 @@ class CordRepBtree : public CordRep { // created copy to `new_length`. CordRepBtree* CopyBeginTo(size_t end, size_t new_length) const; + // Returns a tree containing the edges [tree->begin(), end) and length + // of `new_length`. This method consumes a reference on the provided + // tree, and logically performs the following operation: + // result = tree->CopyBeginTo(end, new_length); + // CordRep::Unref(tree); + // return result; + static CordRepBtree* ConsumeBeginTo(CordRepBtree* tree, size_t end, + size_t new_length); + // Creates a partial copy of this Btree node, copying all edges starting at // `begin`, adding a reference on each copied edge, and sets the length of // the newly created copy to `new_length`. CordRepBtree* CopyToEndFrom(size_t begin, size_t new_length) const; + // Extracts and returns the front edge from the provided tree. + // This method consumes a reference on the provided tree, and logically + // performs the following operation: + // edge = CordRep::Ref(tree->Edge(kFront)); + // CordRep::Unref(tree); + // return edge; + static CordRep* ExtractFront(CordRepBtree* tree); + // Returns a tree containing the result of appending `right` to `left`. static CordRepBtree* MergeTrees(CordRepBtree* left, CordRepBtree* right); @@ -420,6 +467,12 @@ class CordRepBtree : public CordRep { static CordRepBtree* AppendSlow(CordRepBtree*, CordRep* rep); static CordRepBtree* PrependSlow(CordRepBtree*, CordRep* rep); + // Recursively rebuilds `tree` into `stack`. If 'consume` is set to true, the + // function will consume a reference on `tree`. `stack` is a null terminated + // array containing the new tree's state, with the current leaf node at + // stack[0], and parent nodes above that, or null for 'top of tree'. + static void Rebuild(CordRepBtree** stack, CordRepBtree* tree, bool consume); + // Aligns existing edges to start at index 0, to allow for a new edge to be // added to the back of the current edges. inline void AlignBegin(); @@ -459,11 +512,11 @@ class CordRepBtree : public CordRep { // Returns a partial copy of the current tree containing the first `n` bytes // of data. `CopyResult` contains both the resulting edge and its height. The // resulting tree may be less high than the current tree, or even be a single - // matching data edge. For example, if `n == 1`, then the result will be the - // single data edge, and height will be set to -1 (one below the owning leaf - // node). If n == 0, this function returns null. - // Requires `n <= length` - CopyResult CopyPrefix(size_t n); + // matching data edge if `allow_folding` is set to true. + // For example, if `n == 1`, then the result will be the single data edge, and + // height will be set to -1 (one below the owning leaf node). If n == 0, this + // function returns null. Requires `n <= length` + CopyResult CopyPrefix(size_t n, bool allow_folding = true); // Returns a partial copy of the current tree containing all data starting // after `offset`. `CopyResult` contains both the resulting edge and its @@ -619,6 +672,14 @@ inline void CordRepBtree::Destroy(CordRepBtree* tree) { DestroyTree(tree, tree->begin(), tree->end()); } +inline void CordRepBtree::Unref(absl::Span edges) { + for (CordRep* edge : edges) { + if (ABSL_PREDICT_FALSE(!edge->refcount.Decrement())) { + CordRep::Destroy(edge); + } + } +} + inline CordRepBtree* CordRepBtree::CopyRaw() const { auto* tree = static_cast(::operator new(sizeof(CordRepBtree))); memcpy(static_cast(tree), this, sizeof(CordRepBtree)); @@ -762,6 +823,14 @@ inline CordRepBtree::Position CordRepBtree::IndexBefore(Position front, return {index, offset}; } +inline CordRepBtree::Position CordRepBtree::IndexOfLength(size_t n) const { + assert(n <= length); + size_t index = back(); + size_t strip = length - n; + while (strip >= edges_[index]->length) strip -= edges_[index--]->length; + return {index, edges_[index]->length - strip}; +} + inline CordRepBtree::Position CordRepBtree::IndexBeyond( const size_t offset) const { // We need to find the edge which `starting offset` is beyond (>=)`offset`. @@ -780,7 +849,7 @@ inline CordRepBtree* CordRepBtree::Create(CordRep* rep) { } inline Span CordRepBtree::GetAppendBuffer(size_t size) { - assert(refcount.IsOne()); + assert(refcount.IsMutable()); CordRepBtree* tree = this; const int height = this->height(); CordRepBtree* n1 = tree; @@ -789,21 +858,21 @@ inline Span CordRepBtree::GetAppendBuffer(size_t size) { switch (height) { case 3: tree = tree->Edge(kBack)->btree(); - if (!tree->refcount.IsOne()) return {}; + if (!tree->refcount.IsMutable()) return {}; n2 = tree; ABSL_FALLTHROUGH_INTENDED; case 2: tree = tree->Edge(kBack)->btree(); - if (!tree->refcount.IsOne()) return {}; + if (!tree->refcount.IsMutable()) return {}; n1 = tree; ABSL_FALLTHROUGH_INTENDED; case 1: tree = tree->Edge(kBack)->btree(); - if (!tree->refcount.IsOne()) return {}; + if (!tree->refcount.IsMutable()) return {}; ABSL_FALLTHROUGH_INTENDED; case 0: CordRep* edge = tree->Edge(kBack); - if (!edge->refcount.IsOne()) return {}; + if (!edge->refcount.IsMutable()) return {}; if (edge->tag < FLAT) return {}; size_t avail = edge->flat()->Capacity() - edge->length; if (avail == 0) return {}; diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc index 073a7d45..be9473d4 100644 --- a/absl/strings/internal/cord_rep_btree_test.cc +++ b/absl/strings/internal/cord_rep_btree_test.cc @@ -47,7 +47,9 @@ class CordRepBtreeTestPeer { namespace { using ::absl::cordrep_testing::AutoUnref; +using ::absl::cordrep_testing::CordCollectRepsIf; using ::absl::cordrep_testing::CordToString; +using ::absl::cordrep_testing::CordVisitReps; using ::absl::cordrep_testing::CreateFlatsFromString; using ::absl::cordrep_testing::CreateRandomString; using ::absl::cordrep_testing::MakeConcat; @@ -62,6 +64,7 @@ using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::Eq; using ::testing::HasSubstr; +using ::testing::Le; using ::testing::Ne; using ::testing::Not; using ::testing::SizeIs; @@ -205,14 +208,17 @@ CordRepBtree* MakeTree(size_t size, bool append = true) { return tree; } -CordRepBtree* CreateTree(absl::string_view data, size_t chunk_size) { - std::vector flats = CreateFlatsFromString(data, chunk_size); - auto it = flats.begin(); +CordRepBtree* CreateTree(absl::Span reps) { + auto it = reps.begin(); CordRepBtree* tree = CordRepBtree::Create(*it); - while (++it != flats.end()) tree = CordRepBtree::Append(tree, *it); + while (++it != reps.end()) tree = CordRepBtree::Append(tree, *it); return tree; } +CordRepBtree* CreateTree(absl::string_view data, size_t chunk_size) { + return CreateTree(CreateFlatsFromString(data, chunk_size)); +} + CordRepBtree* CreateTreeReverse(absl::string_view data, size_t chunk_size) { std::vector flats = CreateFlatsFromString(data, chunk_size); auto rit = flats.rbegin(); @@ -759,6 +765,63 @@ TEST(CordRepBtreeTest, MergeFuzzTest) { } } +TEST_P(CordRepBtreeTest, RemoveSuffix) { + // Create tree of 1, 2 and 3 levels high + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + for (size_t cap : {max_cap - 1, max_cap * 2, max_cap * max_cap * 2}) { + const std::string data = CreateRandomString(cap * 512); + + { + // Verify RemoveSuffix() + AutoUnref refs; + CordRepBtree* node = refs.RefIf(shared(), CreateTree(data, 512)); + EXPECT_THAT(CordRepBtree::RemoveSuffix(node, data.length()), Eq(nullptr)); + + // Verify RemoveSuffix() + node = refs.RefIf(shared(), CreateTree(data, 512)); + EXPECT_THAT(CordRepBtree::RemoveSuffix(node, 0), Eq(node)); + CordRep::Unref(node); + } + + for (int n = 1; n < data.length(); ++n) { + AutoUnref refs; + auto flats = CreateFlatsFromString(data, 512); + CordRepBtree* node = refs.RefIf(shared(), CreateTree(flats)); + CordRep* rep = refs.Add(CordRepBtree::RemoveSuffix(node, n)); + EXPECT_THAT(CordToString(rep), Eq(data.substr(0, data.length() - n))); + + // Collect all flats + auto is_flat = [](CordRep* rep) { return rep->tag >= FLAT; }; + std::vector edges = CordCollectRepsIf(is_flat, rep); + ASSERT_THAT(edges.size(), Le(flats.size())); + + // Isolate last edge + CordRep* last_edge = edges.back(); + edges.pop_back(); + const size_t last_length = rep->length - edges.size() * 512; + + // All flats except the last edge must be kept or copied 'as is' + int index = 0; + for (CordRep* edge : edges) { + ASSERT_THAT(edge, Eq(flats[index++])); + ASSERT_THAT(edge->length, Eq(512)); + } + + // CordRepBtree may optimize small substrings to avoid waste, so only + // check for flat sharing / updates where the code should always do this. + if (last_length >= 500) { + EXPECT_THAT(last_edge, Eq(flats[index++])); + if (shared()) { + EXPECT_THAT(last_edge->length, Eq(512)); + } else { + EXPECT_TRUE(last_edge->refcount.IsOne()); + EXPECT_THAT(last_edge->length, Eq(last_length)); + } + } + } + } +} + TEST(CordRepBtreeTest, SubTree) { // Create tree of at least 2 levels high constexpr size_t max_cap = CordRepBtree::kMaxCapacity; @@ -986,23 +1049,6 @@ TEST_P(CordRepBtreeTest, CreateFromTreeReturnsTree) { CordRep::Unref(result); } -TEST_P(CordRepBtreeTest, ExceedMaxHeight) { - AutoUnref refs; - CordRepBtree* tree = MakeLeaf(); - for (int h = 1; h <= CordRepBtree::kMaxHeight; ++h) { - CordRepBtree* newtree = CordRepBtree::New(tree); - for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { - newtree = CordRepBtree::Append(newtree, CordRep::Ref(tree)); - } - tree = newtree; - } - refs.RefIf(shared(), tree); -#if defined(GTEST_HAS_DEATH_TEST) - EXPECT_DEATH(tree = CordRepBtree::Append(tree, MakeFlat("Boom")), ".*"); -#endif - CordRep::Unref(tree); -} - TEST(CordRepBtreeTest, GetCharacter) { size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2; std::string data = CreateRandomString(n * 3); @@ -1389,6 +1435,54 @@ TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) { CordRep::Unref(tree); } +TEST_P(CordRepBtreeTest, Rebuild) { + for (size_t size : {3, 8, 100, 10000, 1000000}) { + SCOPED_TRACE(absl::StrCat("Rebuild @", size)); + + std::vector flats; + for (int i = 0; i < size; ++i) { + flats.push_back(CordRepFlat::New(2)); + flats.back()->Data()[0] = 'x'; + flats.back()->length = 1; + } + + // Build the tree into 'right', and each so many 'split_limit' edges, + // combine 'left' + 'right' into a new 'left', and start a new 'right'. + // This guarantees we get a reasonable amount of chaos in the tree. + size_t split_count = 0; + size_t split_limit = 3; + auto it = flats.begin(); + CordRepBtree* left = nullptr; + CordRepBtree* right = CordRepBtree::New(*it); + while (++it != flats.end()) { + if (++split_count >= split_limit) { + split_limit += split_limit / 16; + left = left ? CordRepBtree::Append(left, right) : right; + right = CordRepBtree::New(*it); + } else { + right = CordRepBtree::Append(right, *it); + } + } + + // Finalize tree + left = left ? CordRepBtree::Append(left, right) : right; + + // Rebuild + AutoUnref ref; + left = ref.Add(CordRepBtree::Rebuild(ref.RefIf(shared(), left))); + ASSERT_TRUE(CordRepBtree::IsValid(left)); + + // Verify we have the exact same edges in the exact same order. + bool ok = true; + it = flats.begin(); + CordVisitReps(left, [&](CordRep* edge) { + if (edge->tag < FLAT) return; + ok = ok && (it != flats.end() && *it++ == edge); + }); + EXPECT_TRUE(ok && it == flats.end()) << "Rebuild edges mismatch"; + } +} + } // namespace } // namespace cord_internal ABSL_NAMESPACE_END diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index db1f63fa..07c77eb3 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -277,7 +277,7 @@ CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) { // Get current number of entries, and check for max capacity. size_t entries = rep->entries(); - if (!rep->refcount.IsOne()) { + if (!rep->refcount.IsMutable()) { return Copy(rep, rep->head(), rep->tail(), extra); } else if (entries + extra > rep->capacity()) { const size_t min_grow = rep->capacity() + rep->capacity() / 2; @@ -292,10 +292,10 @@ CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) { } Span CordRepRing::GetAppendBuffer(size_t size) { - assert(refcount.IsOne()); + assert(refcount.IsMutable()); index_type back = retreat(tail_); CordRep* child = entry_child(back); - if (child->tag >= FLAT && child->refcount.IsOne()) { + if (child->tag >= FLAT && child->refcount.IsMutable()) { size_t capacity = child->flat()->Capacity(); pos_type end_pos = entry_end_pos(back); size_t data_offset = entry_data_offset(back); @@ -312,10 +312,10 @@ Span CordRepRing::GetAppendBuffer(size_t size) { } Span CordRepRing::GetPrependBuffer(size_t size) { - assert(refcount.IsOne()); + assert(refcount.IsMutable()); CordRep* child = entry_child(head_); size_t data_offset = entry_data_offset(head_); - if (data_offset && child->refcount.IsOne() && child->tag >= FLAT) { + if (data_offset && child->refcount.IsMutable() && child->tag >= FLAT) { size_t n = (std::min)(data_offset, size); this->length += n; begin_pos_ -= n; @@ -504,7 +504,7 @@ CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) { CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data, size_t extra) { - if (rep->refcount.IsOne()) { + if (rep->refcount.IsMutable()) { Span avail = rep->GetAppendBuffer(data.length()); if (!avail.empty()) { memcpy(avail.data(), data.data(), avail.length()); @@ -538,7 +538,7 @@ CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data, CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data, size_t extra) { - if (rep->refcount.IsOne()) { + if (rep->refcount.IsMutable()) { Span avail = rep->GetPrependBuffer(data.length()); if (!avail.empty()) { const char* tail = data.data() + data.length() - avail.length(); @@ -678,7 +678,7 @@ CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset, Position tail = rep->FindTail(head.index, offset + len); const size_t new_entries = rep->entries(head.index, tail.index); - if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) { + if (rep->refcount.IsMutable() && extra <= (rep->capacity() - new_entries)) { // We adopt a privately owned rep and no extra entries needed. if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); @@ -715,7 +715,7 @@ CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len, } Position head = rep->Find(len); - if (rep->refcount.IsOne()) { + if (rep->refcount.IsMutable()) { if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); rep->head_ = head.index; } else { @@ -745,7 +745,7 @@ CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len, } Position tail = rep->FindTail(rep->length - len); - if (rep->refcount.IsOne()) { + if (rep->refcount.IsMutable()) { // We adopt a privately owned rep, scrub. if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); rep->tail_ = tail.index; diff --git a/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h index 44db8494..2000e21e 100644 --- a/absl/strings/internal/cord_rep_ring.h +++ b/absl/strings/internal/cord_rep_ring.h @@ -383,8 +383,8 @@ class CordRepRing : public CordRep { // Destroys the provided ring buffer, decrementing the reference count of all // contained child CordReps. The provided 1\`rep` should have a ref count of - // one (pre decrement destroy call observing `refcount.IsOne()`) or zero (post - // decrement destroy call observing `!refcount.Decrement()`). + // one (pre decrement destroy call observing `refcount.IsOne()`) or zero + // (post decrement destroy call observing `!refcount.Decrement()`). static void Destroy(CordRepRing* rep); // Returns a mutable reference to the logical end position array. diff --git a/absl/strings/internal/cord_rep_test_util.h b/absl/strings/internal/cord_rep_test_util.h index bc500064..ad828af2 100644 --- a/absl/strings/internal/cord_rep_test_util.h +++ b/absl/strings/internal/cord_rep_test_util.h @@ -115,6 +115,41 @@ inline cord_internal::CordRepBtree* CordRepBtreeFromFlats( return node; } +template +inline void CordVisitReps(cord_internal::CordRep* rep, Fn&& fn) { + fn(rep); + while (rep->tag == cord_internal::SUBSTRING) { + rep = rep->substring()->child; + fn(rep); + } + if (rep->tag == cord_internal::BTREE) { + for (cord_internal::CordRep* edge : rep->btree()->Edges()) { + CordVisitReps(edge, fn); + } + } else if (rep->tag == cord_internal::CONCAT) { + CordVisitReps(rep->concat()->left, fn); + CordVisitReps(rep->concat()->right, fn); + } +} + +template +inline std::vector CordCollectRepsIf( + Predicate&& predicate, cord_internal::CordRep* rep) { + std::vector reps; + CordVisitReps(rep, [&reps, &predicate](cord_internal::CordRep* rep) { + if (predicate(rep)) reps.push_back(rep); + }); + return reps; +} + +inline std::vector CordCollectReps( + cord_internal::CordRep* rep) { + std::vector reps; + auto fn = [&reps](cord_internal::CordRep* rep) { reps.push_back(rep); }; + CordVisitReps(rep, fn); + return reps; +} + inline void CordToString(cord_internal::CordRep* rep, std::string& s) { size_t offset = 0; size_t length = rep->length; diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index 02efcc3a..1f764486 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -39,6 +39,7 @@ class CordzUpdateTracker { // Tracked update methods. enum MethodIdentifier { kUnknown, + kAppendBuffer, kAppendCord, kAppendExternalMemory, kAppendString, @@ -54,6 +55,7 @@ class CordzUpdateTracker { kMoveAppendCord, kMoveAssignCord, kMovePrependCord, + kPrependBuffer, kPrependCord, kPrependString, kRemovePrefix, diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc index fcd17df7..2348a175 100644 --- a/absl/strings/internal/cordz_update_tracker_test.cc +++ b/absl/strings/internal/cordz_update_tracker_test.cc @@ -37,6 +37,7 @@ using Methods = std::array; // Returns an array of all methods defined in `MethodIdentifier` Methods AllMethods() { return Methods{Method::kUnknown, + Method::kAppendBuffer, Method::kAppendCord, Method::kAppendExternalMemory, Method::kAppendString, @@ -52,6 +53,7 @@ Methods AllMethods() { Method::kMoveAppendCord, Method::kMoveAssignCord, Method::kMovePrependCord, + Method::kPrependBuffer, Method::kPrependCord, Method::kPrependString, Method::kRemovePrefix, diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 1780bb44..4ae07c2d 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -96,6 +96,25 @@ ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); // unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out); +// SimpleHexAtoi() +// +// Converts a hexadecimal string (optionally followed or preceded by ASCII +// whitespace) to an integer, returning `true` if successful. Only valid base-16 +// hexadecimal integers whose value falls within the range of the integer type +// (optionally preceded by a `+` or `-`) can be converted. A valid hexadecimal +// value may include both upper and lowercase character symbols, and may +// optionally include a leading "0x" (or "0X") number prefix, which is ignored +// by this function. If any errors are encountered, this function returns +// `false`, leaving `out` in an unspecified state. +template +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out); + +// Overloads of SimpleHexAtoi() for 128 bit integers. +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::int128* out); +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::uint128* out); + ABSL_NAMESPACE_END } // namespace absl @@ -260,6 +279,21 @@ ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, return numbers_internal::safe_strtou128_base(str, out, 10); } +template +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out) { + return numbers_internal::safe_strtoi_base(str, out, 16); +} + +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::int128* out) { + return numbers_internal::safe_strto128_base(str, out, 16); +} + +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::uint128* out) { + return numbers_internal::safe_strtou128_base(str, out, 16); +} + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index f3103106..498c210d 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -47,6 +47,7 @@ namespace { using absl::SimpleAtoi; +using absl::SimpleHexAtoi; using absl::numbers_internal::kSixDigitsToBufferSize; using absl::numbers_internal::safe_strto32_base; using absl::numbers_internal::safe_strto64_base; @@ -468,6 +469,148 @@ TEST(NumbersTest, Atoenum) { VerifySimpleAtoiGood(E_biguint_max32, E_biguint_max32); } +template +void VerifySimpleHexAtoiGood(in_val_type in_value, int_type exp_value) { + std::string s; + // uint128 can be streamed but not StrCat'd + absl::strings_internal::OStringStream strm(&s); + if (in_value >= 0) { + strm << std::hex << in_value; + } else { + // Inefficient for small integers, but works with all integral types. + strm << "-" << std::hex << -absl::uint128(in_value); + } + int_type x = static_cast(~exp_value); + EXPECT_TRUE(SimpleHexAtoi(s, &x)) + << "in_value=" << std::hex << in_value << " s=" << s << " x=" << x; + EXPECT_EQ(exp_value, x); + x = static_cast(~exp_value); + EXPECT_TRUE(SimpleHexAtoi( + s.c_str(), &x)); // NOLINT: readability-redundant-string-conversions + EXPECT_EQ(exp_value, x); +} + +template +void VerifySimpleHexAtoiBad(in_val_type in_value) { + std::string s; + // uint128 can be streamed but not StrCat'd + absl::strings_internal::OStringStream strm(&s); + if (in_value >= 0) { + strm << std::hex << in_value; + } else { + // Inefficient for small integers, but works with all integral types. + strm << "-" << std::hex << -absl::uint128(in_value); + } + int_type x; + EXPECT_FALSE(SimpleHexAtoi(s, &x)); + EXPECT_FALSE(SimpleHexAtoi( + s.c_str(), &x)); // NOLINT: readability-redundant-string-conversions +} + +TEST(NumbersTest, HexAtoi) { + // SimpleHexAtoi(absl::string_view, int32_t) + VerifySimpleHexAtoiGood(0, 0); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiGood(-0x42, -0x42); + + VerifySimpleHexAtoiGood(std::numeric_limits::min(), + std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + + // SimpleHexAtoi(absl::string_view, uint32_t) + VerifySimpleHexAtoiGood(0, 0); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiBad(-0x42); + + VerifySimpleHexAtoiBad(std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiBad(std::numeric_limits::min()); + VerifySimpleHexAtoiBad(std::numeric_limits::max()); + VerifySimpleHexAtoiBad(std::numeric_limits::max()); + + // SimpleHexAtoi(absl::string_view, int64_t) + VerifySimpleHexAtoiGood(0, 0); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiGood(-0x42, -0x42); + + VerifySimpleHexAtoiGood(std::numeric_limits::min(), + std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood(std::numeric_limits::min(), + std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiBad(std::numeric_limits::max()); + + // SimpleHexAtoi(absl::string_view, uint64_t) + VerifySimpleHexAtoiGood(0, 0); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiBad(-0x42); + + VerifySimpleHexAtoiBad(std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiBad(std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + + // SimpleHexAtoi(absl::string_view, absl::uint128) + VerifySimpleHexAtoiGood(0, 0); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiBad(-0x42); + + VerifySimpleHexAtoiBad(std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiBad(std::numeric_limits::min()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleHexAtoiGood( + std::numeric_limits::max(), + std::numeric_limits::max()); + + // Some other types + VerifySimpleHexAtoiGood(-0x42, -0x42); + VerifySimpleHexAtoiGood(-0x42, -0x42); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiGood(-0x42, -0x42); + VerifySimpleHexAtoiGood(-0x42, -0x42); // NOLINT: runtime-int + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiGood(0x42, 0x42); + VerifySimpleHexAtoiGood(0x42, 0x42); + + // Number prefix + int32_t value; + EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16)); + EXPECT_EQ(0x34234324, value); + + EXPECT_TRUE(safe_strto32_base("0X34234324", &value, 16)); + EXPECT_EQ(0x34234324, value); + + // ASCII whitespace + EXPECT_TRUE(safe_strto32_base(" \t\n 34234324", &value, 16)); + EXPECT_EQ(0x34234324, value); + + EXPECT_TRUE(safe_strto32_base("34234324 \t\n ", &value, 16)); + EXPECT_EQ(0x34234324, value); +} + TEST(stringtest, safe_strto32_base) { int32_t value; EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16)); diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc index f472f9ed..1b4427b8 100644 --- a/absl/strings/str_split_test.cc +++ b/absl/strings/str_split_test.cc @@ -943,8 +943,14 @@ TEST(Delimiter, ByLength) { } TEST(Split, WorksWithLargeStrings) { +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER) + constexpr size_t kSize = (uint32_t{1} << 26) + 1; // 64M + 1 byte +#else + constexpr size_t kSize = (uint32_t{1} << 31) + 1; // 2G + 1 byte +#endif if (sizeof(size_t) > 4) { - std::string s((uint32_t{1} << 31) + 1, 'x'); // 2G + 1 byte + std::string s(kSize, 'x'); s.back() = '-'; std::vector v = absl::StrSplit(s, '-'); EXPECT_EQ(2, v.size()); diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index 1f3c7409..8980b198 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc @@ -75,7 +75,8 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, // Build the string. size_t original_size = output->size(); - strings_internal::STLStringResizeUninitialized(output, original_size + size); + strings_internal::STLStringResizeUninitializedAmortized(output, + original_size + size); char* target = &(*output)[original_size]; for (size_t i = 0; i < format.size(); i++) { if (format[i] == '$') { -- cgit v1.2.3 From 3b22e57740b8aec4920c4cfd76b78b3a4fcb2bb5 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 2 Nov 2021 06:58:37 -0700 Subject: Export of internal Abseil changes -- 9ab15354ad0462f51e078b87d73a68f789644b24 by Derek Mauro : Import of CCTZ from GitHub. PiperOrigin-RevId: 407072980 GitOrigin-RevId: 9ab15354ad0462f51e078b87d73a68f789644b24 Change-Id: Ibed92d989dd877efa11073ee981033d7598d0295 --- absl/time/internal/cctz/src/zone_info_source.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc index 72095339..5ab5a59e 100644 --- a/absl/time/internal/cctz/src/zone_info_source.cc +++ b/absl/time/internal/cctz/src/zone_info_source.cc @@ -65,7 +65,7 @@ ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) = extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; -#if defined(_M_IX86) +#if defined(_M_IX86) || defined(_M_ARM) #pragma comment( \ linker, \ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ @@ -83,8 +83,7 @@ ZoneInfoSourceFactory default_factory = DefaultFactory; "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \ "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \ "@@ZA") -#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM) || \ - defined(_M_ARM64) +#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64) #pragma comment( \ linker, \ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ -- cgit v1.2.3 From d6c75d9dd895922bf5dc6c38245ac8887d3e68fc Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 3 Nov 2021 08:46:25 -0700 Subject: Export of internal Abseil changes -- 9f6ef337f282272bc098330c9ccacd9d39155db4 by Abseil Team : Make DestroyElements an empty function if the value types are trivially deconstructible. PiperOrigin-RevId: 407345743 -- a639bbf6f22446d6ba3da9e2c205e26cda4bebc5 by Derek Mauro : Add ctest --output-on-failure to the CMake install test PiperOrigin-RevId: 407333671 -- 54d0577fa6ba1159c6a86410ae185c52f4afaff6 by Derek Mauro : Fix Android build of elf_mem_image.cc PiperOrigin-RevId: 407331032 GitOrigin-RevId: 9f6ef337f282272bc098330c9ccacd9d39155db4 Change-Id: I7e13f01cd804aec50f280ac25934c9ec48ef3c5f --- CMake/install_test_project/test.sh | 2 +- absl/container/inlined_vector.h | 12 +++--- absl/container/internal/inlined_vector.h | 65 +++++++++++++++++++++----------- absl/debugging/internal/elf_mem_image.cc | 2 +- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/CMake/install_test_project/test.sh b/CMake/install_test_project/test.sh index 5a78c92c..aecbb8fe 100755 --- a/CMake/install_test_project/test.sh +++ b/CMake/install_test_project/test.sh @@ -58,7 +58,7 @@ cmake "${absl_dir}" \ -DBUILD_TESTING=ON \ -DBUILD_SHARED_LIBS="${build_shared_libs}" make -j $(nproc) -ctest -j $(nproc) +ctest -j $(nproc) --output-on-failure make install ldconfig popd diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index df9e0991..318c4679 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -233,8 +233,8 @@ class InlinedVector { // specified allocator is also `noexcept`. InlinedVector( InlinedVector&& other, - const allocator_type& allocator) - noexcept(absl::allocator_is_nothrow::value) + const allocator_type& + allocator) noexcept(absl::allocator_is_nothrow::value) : storage_(allocator) { if (IsMemcpyOk::value) { storage_.MemcpyFrom(other.storage_); @@ -486,8 +486,8 @@ class InlinedVector { InlinedVector& operator=(InlinedVector&& other) { if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { - inlined_vector_internal::DestroyElements(storage_.GetAllocator(), - data(), size()); + inlined_vector_internal::DestroyAdapter::DestroyElements( + storage_.GetAllocator(), data(), size()); storage_.DeallocateIfAllocated(); storage_.MemcpyFrom(other.storage_); @@ -721,8 +721,8 @@ class InlinedVector { // Destroys all elements in the inlined vector, setting the size to `0` and // deallocating any held memory. void clear() noexcept { - inlined_vector_internal::DestroyElements(storage_.GetAllocator(), data(), - size()); + inlined_vector_internal::DestroyAdapter::DestroyElements( + storage_.GetAllocator(), data(), size()); storage_.DeallocateIfAllocated(); storage_.SetInlinedSize(0); diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 1d7d6cda..26caa49a 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -94,16 +94,30 @@ struct TypeIdentity { template using NoTypeDeduction = typename TypeIdentity::type; +template >::value> +struct DestroyAdapter; + template -void DestroyElements(NoTypeDeduction& allocator, Pointer destroy_first, - SizeType destroy_size) { - if (destroy_first != nullptr) { +struct DestroyAdapter { + static void DestroyElements(A& allocator, Pointer destroy_first, + SizeType destroy_size) { for (SizeType i = destroy_size; i != 0;) { --i; AllocatorTraits::destroy(allocator, destroy_first + i); } } -} +}; + +template +struct DestroyAdapter { + static void DestroyElements(A& allocator, Pointer destroy_first, + SizeType destroy_size) { + static_cast(allocator); + static_cast(destroy_first); + static_cast(destroy_size); + } +}; template struct Allocation { @@ -133,7 +147,7 @@ void ConstructElements(NoTypeDeduction& allocator, for (SizeType i = 0; i < construct_size; ++i) { ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); } ABSL_INTERNAL_CATCH_ANY { - DestroyElements(allocator, construct_first, i); + DestroyAdapter::DestroyElements(allocator, construct_first, i); ABSL_INTERNAL_RETHROW; } } @@ -253,7 +267,7 @@ class ConstructionTransaction { ~ConstructionTransaction() { if (DidConstruct()) { - DestroyElements(GetAllocator(), GetData(), GetSize()); + DestroyAdapter::DestroyElements(GetAllocator(), GetData(), GetSize()); } } @@ -469,7 +483,7 @@ class Storage { template void Storage::DestroyContents() { Pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); - DestroyElements(GetAllocator(), data, GetSize()); + DestroyAdapter::DestroyElements(GetAllocator(), data, GetSize()); DeallocateIfAllocated(); } @@ -566,7 +580,8 @@ auto Storage::Assign(ValueAdapter values, SizeType new_size) ConstructElements(GetAllocator(), construct_loop.data(), values, construct_loop.size()); - DestroyElements(GetAllocator(), destroy_loop.data(), destroy_loop.size()); + DestroyAdapter::DestroyElements(GetAllocator(), destroy_loop.data(), + destroy_loop.size()); if (allocation_tx.DidAllocate()) { DeallocateIfAllocated(); @@ -587,7 +602,7 @@ auto Storage::Resize(ValueAdapter values, SizeType new_size) A& alloc = GetAllocator(); if (new_size <= size) { // Destroy extra old elements. - DestroyElements(alloc, base + new_size, size - new_size); + DestroyAdapter::DestroyElements(alloc, base + new_size, size - new_size); } else if (new_size <= storage_view.capacity) { // Construct new elements in place. ConstructElements(alloc, base + size, values, new_size - size); @@ -611,7 +626,7 @@ auto Storage::Resize(ValueAdapter values, SizeType new_size) (MoveIterator(base))); ConstructElements(alloc, new_data, move_values, size); - DestroyElements(alloc, base, size); + DestroyAdapter::DestroyElements(alloc, base, size); std::move(construction_tx).Commit(); DeallocateIfAllocated(); SetAllocation(std::move(allocation_tx).Release()); @@ -650,7 +665,8 @@ auto Storage::Insert(ConstIterator pos, ValueAdapter values, ConstructElements(GetAllocator(), new_data + insert_end_index, move_values, storage_view.size - insert_index); - DestroyElements(GetAllocator(), storage_view.data, storage_view.size); + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); std::move(construction_tx).Commit(); std::move(move_construction_tx).Commit(); @@ -753,7 +769,8 @@ auto Storage::EmplaceBackSlow(Args&&... args) -> Reference { ABSL_INTERNAL_RETHROW; } // Destroy elements in old backing store. - DestroyElements(GetAllocator(), storage_view.data, storage_view.size); + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); DeallocateIfAllocated(); SetAllocation(std::move(allocation_tx).Release()); @@ -778,9 +795,9 @@ auto Storage::Erase(ConstIterator from, ConstIterator to) AssignElements(storage_view.data + erase_index, move_values, storage_view.size - erase_end_index); - DestroyElements(GetAllocator(), - storage_view.data + (storage_view.size - erase_size), - erase_size); + DestroyAdapter::DestroyElements( + GetAllocator(), storage_view.data + (storage_view.size - erase_size), + erase_size); SubtractSize(erase_size); return Iterator(storage_view.data + erase_index); @@ -804,7 +821,8 @@ auto Storage::Reserve(SizeType requested_capacity) -> void { ConstructElements(GetAllocator(), new_data, move_values, storage_view.size); - DestroyElements(GetAllocator(), storage_view.data, storage_view.size); + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); DeallocateIfAllocated(); SetAllocation(std::move(allocation_tx).Release()); @@ -847,7 +865,8 @@ auto Storage::ShrinkToFit() -> void { ABSL_INTERNAL_RETHROW; } - DestroyElements(GetAllocator(), storage_view.data, storage_view.size); + DestroyAdapter::DestroyElements(GetAllocator(), storage_view.data, + storage_view.size); MallocAdapter::Deallocate(GetAllocator(), storage_view.data, storage_view.capacity); @@ -883,9 +902,10 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { move_values, large_ptr->GetSize() - small_ptr->GetSize()); - DestroyElements(large_ptr->GetAllocator(), - large_ptr->GetInlinedData() + small_ptr->GetSize(), - large_ptr->GetSize() - small_ptr->GetSize()); + DestroyAdapter::DestroyElements( + large_ptr->GetAllocator(), + large_ptr->GetInlinedData() + small_ptr->GetSize(), + large_ptr->GetSize() - small_ptr->GetSize()); } else { Storage* allocated_ptr = this; Storage* inlined_ptr = other_storage_ptr; @@ -909,8 +929,9 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { ABSL_INTERNAL_RETHROW; } - DestroyElements(inlined_ptr->GetAllocator(), - inlined_ptr->GetInlinedData(), inlined_ptr->GetSize()); + DestroyAdapter::DestroyElements(inlined_ptr->GetAllocator(), + inlined_ptr->GetInlinedData(), + inlined_ptr->GetSize()); inlined_ptr->SetAllocation( {allocated_storage_view.data, allocated_storage_view.capacity}); diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index d6832eaf..29a28181 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -222,7 +222,7 @@ void ElfMemImage::Init(const void *base) { reinterpret_cast(dynamic_program_header->p_vaddr + relocation); for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) { - const ElfW(Xword) value = dynamic_entry->d_un.d_val + relocation; + const auto value = dynamic_entry->d_un.d_val + relocation; switch (dynamic_entry->d_tag) { case DT_HASH: hash_ = reinterpret_cast(value); -- cgit v1.2.3 From c86347d4cec43074e64e225a8753728f4bfc5ed6 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 4 Nov 2021 14:12:39 -0700 Subject: Export of internal Abseil changes -- 5e45aadfb89e366dedd1fcad5034a76c5c10ad76 by James Y Knight : Correct the conditions for std::{optional,variant,any} availability on Apple platforms. Before XCode 12.5, the availability declarations incorrectly excluded one release on which the features were in fact available. This was corrected in https://github.com/llvm/llvm-project/commit/7fb40e1569dd66292b647f4501b85517e9247953 Unfortunately, we cannot simply switch abseil to the corrected versions, as that will cause build failures when using the older XCode toolchain. So, we check the version, and choose the correct set of versions for the toolchain in use. PiperOrigin-RevId: 407667302 -- 376fa06cde048e536e9447336b27bebf598ed4ea by Greg Falcon : Cord implementation change: Add a new CordRepCrc node type. This is an implementation detail that will allow storing expected CRCs alongside cord data. This node is intended only to live at the top of Cord trees. PiperOrigin-RevId: 407587140 GitOrigin-RevId: 5e45aadfb89e366dedd1fcad5034a76c5c10ad76 Change-Id: Iea3ca001c0cbb4deec8286b5581b30dc172a9918 --- CMake/AbseilDll.cmake | 6 +- absl/base/config.h | 43 ++++++++--- absl/strings/BUILD.bazel | 16 ++++ absl/strings/CMakeLists.txt | 16 ++++ absl/strings/cord.cc | 20 ++++- absl/strings/internal/cord_internal.cc | 4 + absl/strings/internal/cord_internal.h | 17 +++-- absl/strings/internal/cord_rep_crc.cc | 54 ++++++++++++++ absl/strings/internal/cord_rep_crc.h | 93 +++++++++++++++++++++++ absl/strings/internal/cord_rep_crc_test.cc | 115 +++++++++++++++++++++++++++++ absl/strings/internal/cord_rep_flat.h | 10 +-- 11 files changed, 368 insertions(+), 26 deletions(-) create mode 100644 absl/strings/internal/cord_rep_crc.cc create mode 100644 absl/strings/internal/cord_rep_crc.h create mode 100644 absl/strings/internal/cord_rep_crc_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index fa323ff8..77c6f9e9 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -204,14 +204,16 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/charconv_parse.h" "strings/internal/cord_internal.cc" "strings/internal/cord_internal.h" - "strings/internal/cord_rep_consume.h" - "strings/internal/cord_rep_consume.cc" "strings/internal/cord_rep_btree.cc" "strings/internal/cord_rep_btree.h" "strings/internal/cord_rep_btree_navigator.cc" "strings/internal/cord_rep_btree_navigator.h" "strings/internal/cord_rep_btree_reader.cc" "strings/internal/cord_rep_btree_reader.h" + "strings/internal/cord_rep_crc.cc" + "strings/internal/cord_rep_crc.h" + "strings/internal/cord_rep_consume.h" + "strings/internal/cord_rep_consume.cc" "strings/internal/cord_rep_flat.h" "strings/internal/cord_rep_ring.cc" "strings/internal/cord_rep_ring.h" diff --git a/absl/base/config.h b/absl/base/config.h index d3cad682..ae7a5a15 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -520,22 +520,41 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #error "absl endian detection needs to be set up for your compiler" #endif -// macOS 10.13 and iOS 10.11 don't let you use , , or -// even though the headers exist and are publicly noted to work. See -// https://github.com/abseil/abseil-cpp/issues/207 and +// macOS < 10.13 and iOS < 11 don't let you use , , or +// even though the headers exist and are publicly noted to work, because the +// libc++ shared library shipped on the system doesn't have the requisite +// exported symbols. See https://github.com/abseil/abseil-cpp/issues/207 and // https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// // libc++ spells out the availability requirements in the file // llvm-project/libcxx/include/__config via the #define // _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. -#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ - ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ - (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ - (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ - (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)) +// +// Unfortunately, Apple initially mis-stated the requirements as macOS < 10.14 +// and iOS < 12 in the libc++ headers. This was corrected by +// https://github.com/llvm/llvm-project/commit/7fb40e1569dd66292b647f4501b85517e9247953 +// which subsequently made it into the XCode 12.5 release. We need to match the +// old (incorrect) conditions when built with old XCode, but can use the +// corrected earlier versions with new XCode. +#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ + ((_LIBCPP_VERSION >= 11000 && /* XCode 12.5 or later: */ \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 110000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 40000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 110000))) || \ + (_LIBCPP_VERSION < 11000 && /* Pre-XCode 12.5: */ \ + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)))) #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 #else #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 090fc58a..1948fe32 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -272,6 +272,7 @@ cc_library( "internal/cord_rep_btree_navigator.cc", "internal/cord_rep_btree_reader.cc", "internal/cord_rep_consume.cc", + "internal/cord_rep_crc.cc", "internal/cord_rep_ring.cc", ], hdrs = [ @@ -280,6 +281,7 @@ cc_library( "internal/cord_rep_btree_navigator.h", "internal/cord_rep_btree_reader.h", "internal/cord_rep_consume.h", + "internal/cord_rep_crc.h", "internal/cord_rep_flat.h", "internal/cord_rep_ring.h", "internal/cord_rep_ring_reader.h", @@ -366,6 +368,20 @@ cc_test( ], ) +cc_test( + name = "cord_rep_crc_test", + size = "small", + srcs = ["internal/cord_rep_crc_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":cord_rep_test_util", + "//absl/base:config", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "cordz_update_tracker", hdrs = ["internal/cordz_update_tracker.h"], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index d6801fe6..1ef3332c 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -558,6 +558,7 @@ absl_cc_library( "internal/cord_rep_btree.h" "internal/cord_rep_btree_navigator.h" "internal/cord_rep_btree_reader.h" + "internal/cord_rep_crc.h" "internal/cord_rep_consume.h" "internal/cord_rep_flat.h" "internal/cord_rep_ring.h" @@ -567,6 +568,7 @@ absl_cc_library( "internal/cord_rep_btree.cc" "internal/cord_rep_btree_navigator.cc" "internal/cord_rep_btree_reader.cc" + "internal/cord_rep_crc.cc" "internal/cord_rep_consume.cc" "internal/cord_rep_ring.cc" COPTS @@ -1011,6 +1013,20 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + cord_rep_crc_test + SRCS + "internal/cord_rep_crc_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cord_rep_test_util + GTest::gmock_main +) + absl_cc_test( NAME cord_ring_test diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 854047ca..63a82133 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -37,6 +37,7 @@ #include "absl/strings/escaping.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_crc.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cordz_statistics.h" #include "absl/strings/internal/cordz_update_scope.h" @@ -53,6 +54,7 @@ ABSL_NAMESPACE_BEGIN using ::absl::cord_internal::CordRep; using ::absl::cord_internal::CordRepBtree; using ::absl::cord_internal::CordRepConcat; +using ::absl::cord_internal::CordRepCrc; using ::absl::cord_internal::CordRepExternal; using ::absl::cord_internal::CordRepFlat; using ::absl::cord_internal::CordRepSubstring; @@ -1870,7 +1872,11 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, *os << "]"; *os << " " << (IsRootBalanced(rep) ? 'b' : 'u'); *os << " " << std::setw(indent) << ""; - if (rep->IsConcat()) { + if (rep->IsCrc()) { + *os << "CRC crc=" << rep->crc()->crc << "\n"; + indent += kIndentStep; + rep = rep->crc()->child; + } else if (rep->IsConcat()) { *os << "CONCAT depth=" << Depth(rep) << "\n"; indent += kIndentStep; indents.push_back(indent); @@ -1922,6 +1928,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, ABSL_INTERNAL_CHECK(node != nullptr, ReportError(root, node)); if (node != root) { ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node)); + ABSL_INTERNAL_CHECK(!node->IsCrc(), ReportError(root, node)); } if (node->IsConcat()) { @@ -1949,6 +1956,12 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, ABSL_INTERNAL_CHECK(node->substring()->start + node->length <= node->substring()->child->length, ReportError(root, node)); + } else if (node->IsCrc()) { + ABSL_INTERNAL_CHECK(node->crc()->child != nullptr, + ReportError(root, node)); + ABSL_INTERNAL_CHECK(node->crc()->length == node->crc()->child->length, + ReportError(root, node)); + worklist.push_back(node->crc()->child); } } while (!worklist.empty()); return true; @@ -1958,6 +1971,11 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, /* static */ size_t Cord::MemoryUsageAux(const CordRep* rep) { size_t total_mem_usage = 0; + if (rep->IsCrc()) { + total_mem_usage += sizeof(CordRepCrc); + rep = rep->crc()->child; + } + // Allow a quick exit for the common case that the root is a leaf. if (RepMemoryUsageLeaf(rep, &total_mem_usage)) { return total_mem_usage; diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index 1767e6fc..c9ceac90 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -19,6 +19,7 @@ #include "absl/container/inlined_vector.h" #include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_crc.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" @@ -70,6 +71,9 @@ void CordRep::Destroy(CordRep* rep) { rep = child; continue; } + } else if (rep->tag == CRC) { + CordRepCrc::Destroy(rep->crc()); + rep = nullptr; } else { CordRepFlat::Delete(rep); rep = nullptr; diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index bfe5564e..bf135ae2 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -192,6 +192,7 @@ struct CordRepConcat; struct CordRepExternal; struct CordRepFlat; struct CordRepSubstring; +struct CordRepCrc; class CordRepRing; class CordRepBtree; @@ -199,18 +200,19 @@ class CordRepBtree; enum CordRepKind { CONCAT = 0, SUBSTRING = 1, - BTREE = 2, - RING = 3, - EXTERNAL = 4, + CRC = 2, + BTREE = 3, + RING = 4, + EXTERNAL = 5, // We have different tags for different sized flat arrays, - // starting with FLAT, and limited to MAX_FLAT_TAG. The 225 value is based on + // starting with FLAT, and limited to MAX_FLAT_TAG. The 226 value is based on // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well // as the Tag <---> Size logic so that FLAT stil represents the minimum flat // allocation size. (32 bytes as of now). - FLAT = 5, - MAX_FLAT_TAG = 225 + FLAT = 6, + MAX_FLAT_TAG = 226 }; // There are various locations where we want to check if some rep is a 'plain' @@ -251,6 +253,7 @@ struct CordRep { constexpr bool IsRing() const { return tag == RING; } constexpr bool IsConcat() const { return tag == CONCAT; } constexpr bool IsSubstring() const { return tag == SUBSTRING; } + constexpr bool IsCrc() const { return tag == CRC; } constexpr bool IsExternal() const { return tag == EXTERNAL; } constexpr bool IsFlat() const { return tag >= FLAT; } constexpr bool IsBtree() const { return tag == BTREE; } @@ -261,6 +264,8 @@ struct CordRep { inline const CordRepConcat* concat() const; inline CordRepSubstring* substring(); inline const CordRepSubstring* substring() const; + inline CordRepCrc* crc(); + inline const CordRepCrc* crc() const; inline CordRepExternal* external(); inline const CordRepExternal* external() const; inline CordRepFlat* flat(); diff --git a/absl/strings/internal/cord_rep_crc.cc b/absl/strings/internal/cord_rep_crc.cc new file mode 100644 index 00000000..ee140354 --- /dev/null +++ b/absl/strings/internal/cord_rep_crc.cc @@ -0,0 +1,54 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_crc.h" + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +CordRepCrc* CordRepCrc::New(CordRep* child, uint32_t crc) { + assert(child != nullptr); + if (child->IsCrc()) { + if (child->refcount.IsOne()) { + child->crc()->crc = crc; + return child->crc(); + } + CordRep* old = child; + child = old->crc()->child; + CordRep::Ref(child); + CordRep::Unref(old); + } + auto* new_cordrep = new CordRepCrc; + new_cordrep->length = child->length; + new_cordrep->tag = cord_internal::CRC; + new_cordrep->child = child; + new_cordrep->crc = crc; + return new_cordrep; +} + +void CordRepCrc::Destroy(CordRepCrc* node) { + CordRep::Unref(node->child); + delete node; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_crc.h b/absl/strings/internal/cord_rep_crc.h new file mode 100644 index 00000000..3ead94a1 --- /dev/null +++ b/absl/strings/internal/cord_rep_crc.h @@ -0,0 +1,93 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_CRC_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_CRC_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepCrc is a CordRep node intended only to appear at the top level of a +// cord tree. It associates an "expected CRC" with the contained data, to allow +// for easy passage of checksum data in Cord data flows. +// +// From Cord's perspective, the crc value has no semantics; any validation of +// the contained checksum is the user's responsibility. +struct CordRepCrc : public CordRep { + CordRep* child; + uint32_t crc; + + // Consumes `child` and returns a CordRepCrc prefixed tree containing `child`. + // If the specified `child` is itself a CordRepCrc node, then this method + // either replaces the existing node, or directly updates the crc value in it + // depending on the node being shared or not, i.e.: refcount.IsOne(). + // `child` must not be null. Never returns null. + static CordRepCrc* New(CordRep* child, uint32_t crc); + + // Destroys (deletes) the provided node. `node` must not be null. + static void Destroy(CordRepCrc* node); +}; + +// Consumes `rep` and returns a CordRep* with any outer CordRepCrc wrapper +// removed. This is usually a no-op (returning `rep`), but this will remove and +// unref an outer CordRepCrc node. +inline CordRep* RemoveCrcNode(CordRep* rep) { + assert(rep != nullptr); + if (ABSL_PREDICT_FALSE(rep->IsCrc())) { + CordRep* child = rep->crc()->child; + if (rep->refcount.IsOne()) { + delete rep->crc(); + } else { + CordRep::Ref(child); + CordRep::Unref(rep); + } + return child; + } + return rep; +} + +// Returns `rep` if it is not a CordRepCrc node, or its child if it is. +// Does not consume or create a reference on `rep` or the returned value. +inline CordRep* SkipCrcNode(CordRep* rep) { + assert(rep != nullptr); + if (ABSL_PREDICT_FALSE(rep->IsCrc())) { + return rep->crc()->child; + } else { + return rep; + } +} + +inline CordRepCrc* CordRep::crc() { + assert(IsCrc()); + return static_cast(this); +} + +inline const CordRepCrc* CordRep::crc() const { + assert(IsCrc()); + return static_cast(this); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_CRC_H_ diff --git a/absl/strings/internal/cord_rep_crc_test.cc b/absl/strings/internal/cord_rep_crc_test.cc new file mode 100644 index 00000000..80f53485 --- /dev/null +++ b/absl/strings/internal/cord_rep_crc_test.cc @@ -0,0 +1,115 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_rep_crc.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_test_util.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::absl::cordrep_testing::MakeFlat; +using ::testing::Eq; +using ::testing::Ne; + +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + +TEST(CordRepCrc, NewWithNullPtr) { + EXPECT_DEATH(CordRepCrc::New(nullptr, 0), ""); +} + +TEST(CordRepCrc, RemoveCrcWithNullptr) { + EXPECT_DEATH(RemoveCrcNode(nullptr), ""); +} + +#endif // !NDEBUG && GTEST_HAS_DEATH_TEST + +TEST(CordRepCrc, NewDestroy) { + CordRep* rep = cordrep_testing::MakeFlat("Hello world"); + CordRepCrc* crc = CordRepCrc::New(rep, 12345); + EXPECT_TRUE(crc->refcount.IsOne()); + EXPECT_THAT(crc->child, Eq(rep)); + EXPECT_THAT(crc->crc, Eq(12345)); + EXPECT_TRUE(rep->refcount.IsOne()); + CordRepCrc::Destroy(crc); +} + +TEST(CordRepCrc, NewExistingCrcNotShared) { + CordRep* rep = cordrep_testing::MakeFlat("Hello world"); + CordRepCrc* crc = CordRepCrc::New(rep, 12345); + CordRepCrc* new_crc = CordRepCrc::New(crc, 54321); + EXPECT_THAT(new_crc, Eq(crc)); + EXPECT_TRUE(new_crc->refcount.IsOne()); + EXPECT_THAT(new_crc->child, Eq(rep)); + EXPECT_THAT(new_crc->crc, Eq(54321)); + EXPECT_TRUE(rep->refcount.IsOne()); + CordRepCrc::Destroy(new_crc); +} + +TEST(CordRepCrc, NewExistingCrcShared) { + CordRep* rep = cordrep_testing::MakeFlat("Hello world"); + CordRepCrc* crc = CordRepCrc::New(rep, 12345); + CordRep::Ref(crc); + CordRepCrc* new_crc = CordRepCrc::New(crc, 54321); + + EXPECT_THAT(new_crc, Ne(crc)); + EXPECT_TRUE(new_crc->refcount.IsOne()); + EXPECT_TRUE(crc->refcount.IsOne()); + EXPECT_FALSE(rep->refcount.IsOne()); + EXPECT_THAT(crc->child, Eq(rep)); + EXPECT_THAT(new_crc->child, Eq(rep)); + EXPECT_THAT(crc->crc, Eq(12345)); + EXPECT_THAT(new_crc->crc, Eq(54321)); + + CordRep::Unref(crc); + CordRep::Unref(new_crc); +} + +TEST(CordRepCrc, RemoveCrcNotCrc) { + CordRep* rep = cordrep_testing::MakeFlat("Hello world"); + CordRep* nocrc = RemoveCrcNode(rep); + EXPECT_THAT(nocrc, Eq(rep)); + CordRep::Unref(nocrc); +} + +TEST(CordRepCrc, RemoveCrcNotShared) { + CordRep* rep = cordrep_testing::MakeFlat("Hello world"); + CordRepCrc* crc = CordRepCrc::New(rep, 12345); + CordRep* nocrc = RemoveCrcNode(crc); + EXPECT_THAT(nocrc, Eq(rep)); + EXPECT_TRUE(rep->refcount.IsOne()); + CordRep::Unref(nocrc); +} + +TEST(CordRepCrc, RemoveCrcShared) { + CordRep* rep = cordrep_testing::MakeFlat("Hello world"); + CordRepCrc* crc = CordRepCrc::New(rep, 12345); + CordRep::Ref(crc); + CordRep* nocrc = RemoveCrcNode(crc); + EXPECT_THAT(nocrc, Eq(rep)); + EXPECT_FALSE(rep->refcount.IsOne()); + CordRep::Unref(nocrc); + CordRep::Unref(crc); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index 4d0f9886..62cf5840 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -44,11 +44,11 @@ static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead; constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) { - return static_cast((size <= 1024) ? size / 8 + 1 - : 129 + size / 32 - 1024 / 32); + return static_cast((size <= 1024) ? size / 8 + 2 + : 130 + size / 32 - 1024 / 32); } -static_assert(kMinFlatSize / 8 + 1 >= FLAT, ""); +static_assert(kMinFlatSize / 8 + 2 >= FLAT, ""); static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, ""); // Helper functions for rounded div, and rounding to exact sizes. @@ -73,7 +73,7 @@ inline uint8_t AllocatedSizeToTag(size_t size) { // Converts the provided tag to the corresponding allocated size constexpr size_t TagToAllocatedSize(uint8_t tag) { - return (tag <= 129) ? ((tag - 1) * 8) : (1024 + (tag - 129) * 32); + return (tag <= 130) ? ((tag - 2) * 8) : (1024 + (tag - 130) * 32); } // Converts the provided tag to the corresponding available data length @@ -82,7 +82,7 @@ constexpr size_t TagToLength(uint8_t tag) { } // Enforce that kMaxFlatSize maps to a well-known exact tag value. -static_assert(TagToAllocatedSize(225) == kMaxFlatSize, "Bad tag logic"); +static_assert(TagToAllocatedSize(226) == kMaxFlatSize, "Bad tag logic"); struct CordRepFlat : public CordRep { // Creates a new flat node. -- cgit v1.2.3 From 39f46faa69614b429b97bdad737097fa0497b06d Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 9 Nov 2021 10:34:06 -0800 Subject: Export of internal Abseil changes -- 83e4cdf03a4d702b30e69204060de09e462e23c6 by Greg Falcon : Revert the crc addition to RefcountAndFlags, and restore related comments to their original state. In development, the implementation of SetExpectedCrc() changed, and there is no longer a need to track the CRC status in the refcount. Since the distinction between IsOne() and IsMutable() is subtle *and unused*, removing it now can help avoid subtle bugs in the future. This distinction can always be added back later, if it proves necessary. Keep the reserved bit for now; all it costs is one extra mask instruction in the refcount checks, and space for extra state in Cord is always hard to find. PiperOrigin-RevId: 408647038 -- ee67585cf66954176615271f50f8b278119dd138 by Greg Falcon : Implement Cord::SetExpectedChecksum() and Cord::ExpectedChecksum(). SetExpectedChecksum() will store a uint32_t out-of-band alongside a Cord's data. This value persists through copies and assignments. Mutating operations on a Cord cause the value to be forgotten. ExpectedChecksum() retrieves the stored value, if present. This API is intended for storing a CRC32C checksum alongside data, allowing checksums to be passed through dataflows and validated at the final step. However, this API is agnostic to the meaning of the stored value. No CRC32C validation is performed by these new APIs. This implementation adds a new CordRep node, CordRepCrc. A CordRepCrc may (currently) only live at the top of a tree. This allows traversal logic to be agnostic to these nodes, instead putting the needed branches at the mutation level. This also implements the property requested from API review, that any mutation is guaranteed to permanently forget the stored CRC. PiperOrigin-RevId: 408611221 -- a86f592402b37c854ebdc77d2b9b425451a7a675 by Martijn Vels : Move 'ExtractResult' into CordRep The result of an extract operation is logically identical for any tree implementation, and having a single type makes 'tree independent' implementation in cord.cc more concise. PiperOrigin-RevId: 408332408 -- baa7647e21db59a87f75af9cac62172ce38a0f71 by Abseil Team : Replace usages of `assert` macros with `ABSL_HARDENING_ASSERT`. PiperOrigin-RevId: 408272133 -- c7658133d8662c39fa5035fc93a364c7c3d327e0 by Martijn Vels : Add CordRepBtree::ExtractAppendBuffer PiperOrigin-RevId: 407944179 -- 5775100363b5890ebfe710fadebf040445eab991 by Martijn Vels : Add CordRepConcat::ExtractAppendBuffer PiperOrigin-RevId: 407932968 -- 9f520ba1600a93352c78f644a369c7c76195ee86 by Greg Falcon : Add cordz tracking for crc nodes. This also adds a new kSetExpectedChecksum method to the list of tracked methods. This is presently unused but will be used soon. PiperOrigin-RevId: 407884120 GitOrigin-RevId: 83e4cdf03a4d702b30e69204060de09e462e23c6 Change-Id: I134ace2d87215813eaa60a282996a33884676c06 --- CMake/AbseilDll.cmake | 1 + absl/container/inlined_vector.h | 1 - absl/container/internal/inlined_vector.h | 15 +- absl/strings/BUILD.bazel | 9 +- absl/strings/CMakeLists.txt | 10 +- absl/strings/cord.cc | 74 ++- absl/strings/cord.h | 25 + absl/strings/cord_test.cc | 529 ++++++++++++++++++--- absl/strings/internal/cord_internal.h | 60 ++- absl/strings/internal/cord_internal_test.cc | 116 ----- absl/strings/internal/cord_rep_btree.cc | 119 ++++- absl/strings/internal/cord_rep_btree.h | 46 +- absl/strings/internal/cord_rep_btree_test.cc | 129 +++++ absl/strings/internal/cord_rep_concat.cc | 63 +++ absl/strings/internal/cord_rep_concat_test.cc | 143 ++++++ absl/strings/internal/cord_rep_crc.h | 9 + absl/strings/internal/cord_rep_ring.cc | 20 +- absl/strings/internal/cordz_info.cc | 9 + .../strings/internal/cordz_info_statistics_test.cc | 22 + absl/strings/internal/cordz_statistics.h | 1 + absl/strings/internal/cordz_update_tracker.h | 1 + absl/strings/internal/cordz_update_tracker_test.cc | 1 + 22 files changed, 1123 insertions(+), 280 deletions(-) delete mode 100644 absl/strings/internal/cord_internal_test.cc create mode 100644 absl/strings/internal/cord_rep_concat.cc create mode 100644 absl/strings/internal/cord_rep_concat_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 77c6f9e9..7c827251 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -210,6 +210,7 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cord_rep_btree_navigator.h" "strings/internal/cord_rep_btree_reader.cc" "strings/internal/cord_rep_btree_reader.h" + "strings/internal/cord_rep_concat.cc" "strings/internal/cord_rep_crc.cc" "strings/internal/cord_rep_crc.h" "strings/internal/cord_rep_consume.h" diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 318c4679..5597d43e 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -36,7 +36,6 @@ #define ABSL_CONTAINER_INLINED_VECTOR_H_ #include -#include #include #include #include diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 26caa49a..98c26afc 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -430,7 +430,7 @@ class Storage { } void SubtractSize(SizeType count) { - assert(count <= GetSize()); + ABSL_HARDENING_ASSERT(count <= GetSize()); GetSizeAndIsAllocated() -= count << static_cast>(1); } @@ -441,7 +441,8 @@ class Storage { } void MemcpyFrom(const Storage& other_storage) { - assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); + ABSL_HARDENING_ASSERT(IsMemcpyOk::value || + other_storage.GetIsAllocated()); GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); data_ = other_storage.data_; @@ -490,7 +491,7 @@ void Storage::DestroyContents() { template void Storage::InitFrom(const Storage& other) { const SizeType n = other.GetSize(); - assert(n > 0); // Empty sources handled handled in caller. + ABSL_HARDENING_ASSERT(n > 0); // Empty sources handled handled in caller. ConstPointer src; Pointer dst; if (!other.GetIsAllocated()) { @@ -522,8 +523,8 @@ template auto Storage::Initialize(ValueAdapter values, SizeType new_size) -> void { // Only callable from constructors! - assert(!GetIsAllocated()); - assert(GetSize() == 0); + ABSL_HARDENING_ASSERT(!GetIsAllocated()); + ABSL_HARDENING_ASSERT(GetSize() == 0); Pointer construct_data; if (new_size > GetInlinedCapacity()) { @@ -832,7 +833,7 @@ auto Storage::Reserve(SizeType requested_capacity) -> void { template auto Storage::ShrinkToFit() -> void { // May only be called on allocated instances! - assert(GetIsAllocated()); + ABSL_HARDENING_ASSERT(GetIsAllocated()); StorageView storage_view{GetAllocatedData(), GetSize(), GetAllocatedCapacity()}; @@ -881,7 +882,7 @@ auto Storage::ShrinkToFit() -> void { template auto Storage::Swap(Storage* other_storage_ptr) -> void { using std::swap; - assert(this != other_storage_ptr); + ABSL_HARDENING_ASSERT(this != other_storage_ptr); if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { swap(data_.allocated, other_storage_ptr->data_.allocated); diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 1948fe32..0c47ca54 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -271,6 +271,7 @@ cc_library( "internal/cord_rep_btree.cc", "internal/cord_rep_btree_navigator.cc", "internal/cord_rep_btree_reader.cc", + "internal/cord_rep_concat.cc", "internal/cord_rep_consume.cc", "internal/cord_rep_crc.cc", "internal/cord_rep_ring.cc", @@ -308,12 +309,15 @@ cc_library( ) cc_test( - name = "cord_internal_test", - srcs = ["internal/cord_internal_test.cc"], + name = "cord_rep_concat_test", + size = "small", + srcs = ["internal/cord_rep_concat_test.cc"], copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ ":cord_internal", + ":cord_rep_test_util", + "//absl/base:config", "@com_google_googletest//:gtest_main", ], ) @@ -711,6 +715,7 @@ cc_test( "//absl/base:endian", "//absl/base:raw_logging_internal", "//absl/container:fixed_array", + "//absl/hash", "//absl/random", "@com_google_googletest//:gtest_main", ], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 1ef3332c..aab97efd 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -568,6 +568,7 @@ absl_cc_library( "internal/cord_rep_btree.cc" "internal/cord_rep_btree_navigator.cc" "internal/cord_rep_btree_reader.cc" + "internal/cord_rep_concat.cc" "internal/cord_rep_crc.cc" "internal/cord_rep_consume.cc" "internal/cord_rep_ring.cc" @@ -922,6 +923,7 @@ absl_cc_test( absl::cordz_test_helpers absl::core_headers absl::endian + absl::hash absl::random_random absl::raw_logging_internal absl::fixed_array @@ -948,13 +950,17 @@ absl_cc_test( absl_cc_test( NAME - cord_internal_test + cord_rep_concat_test SRCS - "internal/cord_internal_test.cc" + "internal/cord_rep_concat_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS + absl::base + absl::config absl::cord_internal + absl::cord_rep_test_util + absl::core_headers GTest::gmock_main ) diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 63a82133..0015bb9f 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -267,6 +267,7 @@ static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) { return nullptr; } else { CordRepSubstring* rep = new CordRepSubstring(); + assert(child->IsExternal() || child->IsFlat()); assert((offset + length) <= child->length); rep->length = length; rep->tag = cord_internal::SUBSTRING; @@ -343,7 +344,9 @@ inline void Cord::InlineRep::remove_prefix(size_t n) { // Returns `rep` converted into a CordRepBtree. // Directly returns `rep` if `rep` is already a CordRepBtree. static CordRepBtree* ForceBtree(CordRep* rep) { - return rep->IsBtree() ? rep->btree() : CordRepBtree::Create(rep); + return rep->IsBtree() + ? rep->btree() + : CordRepBtree::Create(cord_internal::RemoveCrcNode(rep)); } void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, @@ -366,13 +369,14 @@ void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) { if (btree_enabled()) { tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree); } else { - tree = Concat(data_.as_tree(), tree); + tree = Concat(cord_internal::RemoveCrcNode(data_.as_tree()), tree); } SetTree(tree, scope); } void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) { if (tree == nullptr) return; + assert(!tree->IsCrc()); if (data_.is_tree()) { AppendTreeToTree(tree, method); } else { @@ -401,13 +405,14 @@ void Cord::InlineRep::PrependTreeToTree(CordRep* tree, if (btree_enabled()) { tree = CordRepBtree::Prepend(ForceBtree(data_.as_tree()), tree); } else { - tree = Concat(tree, data_.as_tree()); + tree = Concat(tree, cord_internal::RemoveCrcNode(data_.as_tree())); } SetTree(tree, scope); } void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { assert(tree != nullptr); + assert(!tree->IsCrc()); if (data_.is_tree()) { PrependTreeToTree(tree, method); } else { @@ -421,7 +426,7 @@ void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { // written to region and the actual size increase will be written to size. static inline bool PrepareAppendRegion(CordRep* root, char** region, size_t* size, size_t max_length) { - if (root->IsBtree() && root->refcount.IsMutable()) { + if (root->IsBtree() && root->refcount.IsOne()) { Span span = root->btree()->GetAppendBuffer(max_length); if (!span.empty()) { *region = span.data(); @@ -432,11 +437,11 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, // Search down the right-hand path for a non-full FLAT node. CordRep* dst = root; - while (dst->IsConcat() && dst->refcount.IsMutable()) { + while (dst->IsConcat() && dst->refcount.IsOne()) { dst = dst->concat()->right; } - if (!dst->IsFlat() || !dst->refcount.IsMutable()) { + if (!dst->IsFlat() || !dst->refcount.IsOne()) { *region = nullptr; *size = 0; return false; @@ -481,8 +486,9 @@ void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, } size_t extra = has_length ? length : (std::max)(sz, kMinFlatLength); - CordRep* rep = root ? root : MakeFlatWithExtraCapacity(extra); CordzUpdateScope scope(root ? data_.cordz_info() : nullptr, method); + CordRep* rep = root ? cord_internal::RemoveCrcNode(root) + : MakeFlatWithExtraCapacity(extra); if (PrepareAppendRegion(rep, region, size, length)) { CommitTree(root, rep, scope, method); return; @@ -651,7 +657,7 @@ Cord& Cord::operator=(absl::string_view src) { if (tree != nullptr) { CordzUpdateScope scope(contents_.cordz_info(), method); if (tree->IsFlat() && tree->flat()->Capacity() >= length && - tree->refcount.IsMutable()) { + tree->refcount.IsOne()) { // Copy in place if the existing FLAT node is reusable. memmove(tree->flat()->Data(), data, length); tree->length = length; @@ -677,6 +683,7 @@ void Cord::InlineRep::AppendArray(absl::string_view src, const CordRep* const root = rep; CordzUpdateScope scope(root ? cordz_info() : nullptr, method); if (root != nullptr) { + rep = cord_internal::RemoveCrcNode(rep); char* region; if (PrepareAppendRegion(rep, ®ion, &appended, src.size())) { memcpy(region, src.data(), appended); @@ -748,7 +755,8 @@ inline void Cord::AppendImpl(C&& src) { // Since destination is empty, we can avoid allocating a node, if (src.contents_.is_tree()) { // by taking the tree directly - CordRep* rep = std::forward(src).TakeRep(); + CordRep* rep = + cord_internal::RemoveCrcNode(std::forward(src).TakeRep()); contents_.EmplaceTree(rep, method); } else { // or copying over inline data @@ -784,7 +792,7 @@ inline void Cord::AppendImpl(C&& src) { } // Guaranteed to be a tree (kMaxBytesToCopy > kInlinedSize) - CordRep* rep = std::forward(src).TakeRep(); + CordRep* rep = cord_internal::RemoveCrcNode(std::forward(src).TakeRep()); contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord); } @@ -812,7 +820,8 @@ void Cord::Prepend(const Cord& src) { CordRep* src_tree = src.contents_.tree(); if (src_tree != nullptr) { CordRep::Ref(src_tree); - contents_.PrependTree(src_tree, CordzUpdateTracker::kPrependCord); + contents_.PrependTree(cord_internal::RemoveCrcNode(src_tree), + CordzUpdateTracker::kPrependCord); return; } @@ -856,6 +865,7 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { if (n == 0) return CordRep::Ref(node); absl::InlinedVector rhs_stack; + assert(!node->IsCrc()); while (node->IsConcat()) { assert(n <= node->length); if (n < node->concat()->left->length) { @@ -896,7 +906,8 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { if (n >= node->length) return nullptr; if (n == 0) return CordRep::Ref(node); absl::InlinedVector lhs_stack; - bool inplace_ok = node->refcount.IsMutable(); + bool inplace_ok = node->refcount.IsOne(); + assert(!node->IsCrc()); while (node->IsConcat()) { assert(n <= node->length); @@ -909,7 +920,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { n -= node->concat()->right->length; node = node->concat()->left; } - inplace_ok = inplace_ok && node->refcount.IsMutable(); + inplace_ok = inplace_ok && node->refcount.IsOne(); } assert(n <= node->length); @@ -946,6 +957,7 @@ void Cord::RemovePrefix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemovePrefix; CordzUpdateScope scope(contents_.cordz_info(), method); + tree = cord_internal::RemoveCrcNode(tree); if (tree->IsBtree()) { CordRep* old = tree; tree = tree->btree()->SubTree(n, tree->length - n); @@ -969,6 +981,7 @@ void Cord::RemoveSuffix(size_t n) { } else { auto constexpr method = CordzUpdateTracker::kRemoveSuffix; CordzUpdateScope scope(contents_.cordz_info(), method); + tree = cord_internal::RemoveCrcNode(tree); if (tree->IsBtree()) { tree = CordRepBtree::RemoveSuffix(tree->btree(), n); } else { @@ -992,6 +1005,7 @@ struct SubRange { static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { absl::InlinedVector results; absl::InlinedVector todo; + assert(!node->IsCrc()); todo.push_back(SubRange(node, pos, n)); do { const SubRange& sr = todo.back(); @@ -1062,6 +1076,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { return sub_cord; } + tree = cord_internal::SkipCrcNode(tree); if (tree->IsBtree()) { tree = tree->btree()->SubTree(pos, new_size); } else { @@ -1082,6 +1097,7 @@ class CordForest { void Build(CordRep* cord_root) { std::vector pending = {cord_root}; + assert(cord_root->IsConcat()); while (!pending.empty()) { CordRep* node = pending.back(); @@ -1258,7 +1274,7 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { return absl::string_view(data_.as_chars(), data_.inline_size()); } - CordRep* node = tree(); + CordRep* node = cord_internal::SkipCrcNode(tree()); if (node->IsFlat()) { return absl::string_view(node->flat()->Data(), node->length); } @@ -1300,6 +1316,28 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { return absl::string_view(node->external()->base + offset, length); } +void Cord::SetExpectedChecksum(uint32_t crc) { + auto constexpr method = CordzUpdateTracker::kSetExpectedChecksum; + if (empty()) return; + + if (!contents_.is_tree()) { + CordRep* rep = contents_.MakeFlatWithExtraCapacity(0); + rep = CordRepCrc::New(rep, crc); + contents_.EmplaceTree(rep, method); + } else { + const CordzUpdateScope scope(contents_.data_.cordz_info(), method); + CordRep* rep = CordRepCrc::New(contents_.data_.as_tree(), crc); + contents_.SetTree(rep, scope); + } +} + +absl::optional Cord::ExpectedChecksum() const { + if (!contents_.is_tree() || !contents_.tree()->IsCrc()) { + return absl::nullopt; + } + return contents_.tree()->crc()->crc; +} + inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size, size_t size_to_compare) const { auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) { @@ -1720,6 +1758,7 @@ char Cord::operator[](size_t i) const { if (rep == nullptr) { return contents_.data()[i]; } + rep = cord_internal::SkipCrcNode(rep); while (true) { assert(rep != nullptr); assert(offset < rep->length); @@ -1780,6 +1819,7 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) { assert(rep != nullptr); + rep = cord_internal::SkipCrcNode(rep); if (rep->IsFlat()) { *fragment = absl::string_view(rep->flat()->Data(), rep->length); return true; @@ -1809,6 +1849,9 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ void Cord::ForEachChunkAux( absl::cord_internal::CordRep* rep, absl::FunctionRef callback) { + assert(rep != nullptr); + rep = cord_internal::SkipCrcNode(rep); + if (rep->IsBtree()) { ChunkIterator it(rep), end; while (it != end) { @@ -1818,12 +1861,11 @@ absl::string_view Cord::FlattenSlowPath() { return; } - assert(rep != nullptr); int stack_pos = 0; constexpr int stack_max = 128; // Stack of right branches for tree traversal absl::cord_internal::CordRep* stack[stack_max]; - absl::cord_internal::CordRep* current_node = rep; + absl::cord_internal::CordRep* current_node = cord_internal::SkipCrcNode(rep); while (true) { if (current_node->IsConcat()) { if (stack_pos == stack_max) { diff --git a/absl/strings/cord.h b/absl/strings/cord.h index f0a19914..3f0633bd 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -81,6 +81,7 @@ #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_btree_reader.h" +#include "absl/strings/internal/cord_rep_crc.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_functions.h" #include "absl/strings/internal/cordz_info.h" @@ -671,6 +672,29 @@ class Cord { cord->Append(part); } + // Cord::SetExpectedChecksum() + // + // Stores a checksum value with this non-empty cord instance, for later + // retrieval. + // + // The expected checksum is a number stored out-of-band, alongside the data. + // It is preserved across copies and assignments, but any mutations to a cord + // will cause it to lose its expected checksum. + // + // The expected checksum is not part of a Cord's value, and does not affect + // operations such as equality or hashing. + // + // This field is intended to store a CRC32C checksum for later validation, to + // help support end-to-end checksum workflows. However, the Cord API itself + // does no CRC validation, and assigns no meaning to this number. + // + // This call has no effect if this cord is empty. + void SetExpectedChecksum(uint32_t crc); + + // Returns this cord's expected checksum, if it has one. Otherwise, returns + // nullopt. + absl::optional ExpectedChecksum() const; + template friend H AbslHashValue(H hash_state, const absl::Cord& c) { absl::optional maybe_flat = c.TryFlat(); @@ -1274,6 +1298,7 @@ inline bool Cord::StartsWith(absl::string_view rhs) const { } inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) { + tree = cord_internal::SkipCrcNode(tree); if (tree->tag == cord_internal::BTREE) { current_chunk_ = btree_reader_.Init(tree->btree()); return; diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index cced9bba..188fbc2e 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -34,9 +34,11 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/container/fixed_array.h" +#include "absl/hash/hash.h" #include "absl/random/random.h" #include "absl/strings/cord_test_helpers.h" #include "absl/strings/cordz_test_helpers.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" @@ -192,10 +194,13 @@ class CordTestPeer { static Cord MakeSubstring(Cord src, size_t offset, size_t length) { ABSL_RAW_CHECK(src.contents_.is_tree(), "Can not be inlined"); + ABSL_RAW_CHECK(src.ExpectedChecksum() == absl::nullopt, + "Can not be hardened"); Cord cord; auto* rep = new cord_internal::CordRepSubstring; rep->tag = cord_internal::SUBSTRING; - rep->child = cord_internal::CordRep::Ref(src.contents_.tree()); + rep->child = cord_internal::CordRep::Ref( + cord_internal::SkipCrcNode(src.contents_.tree())); rep->start = offset; rep->length = length; cord.contents_.EmplaceTree(rep, @@ -207,8 +212,9 @@ class CordTestPeer { ABSL_NAMESPACE_END } // namespace absl -// The CordTest fixture runs all tests with and without Cord Btree enabled. -class CordTest : public testing::TestWithParam { +// The CordTest fixture runs all tests with and without Cord Btree enabled, +// and with our without expected CRCs being set on the subject Cords. +class CordTest : public testing::TestWithParam { public: CordTest() : was_btree_(absl::cord_internal::cord_btree_enabled.load()) { absl::cord_internal::cord_btree_enabled.store(UseBtree()); @@ -218,18 +224,40 @@ class CordTest : public testing::TestWithParam { } // Returns true if test is running with btree enabled. - bool UseBtree() const { return GetParam(); } + bool UseBtree() const { return GetParam() == 1 || GetParam() == 3; } + bool UseCrc() const { return GetParam() == 2 || GetParam() == 3; } + void MaybeHarden(absl::Cord& c) { + if (UseCrc()) { + c.SetExpectedChecksum(1); + } + } + absl::Cord MaybeHardened(absl::Cord c) { + MaybeHarden(c); + return c; + } // Returns human readable string representation of the test parameter. - static std::string ToString(testing::TestParamInfo param) { - return param.param ? "Btree" : "Concat"; + static std::string ToString(testing::TestParamInfo param) { + switch (param.param) { + case 0: + return "Concat"; + case 1: + return "Btree"; + case 2: + return "ConcatHardened"; + case 3: + return "BtreeHardened"; + default: + assert(false); + return "???"; + } } private: const bool was_btree_; }; -INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Bool(), +INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1, 2, 3), CordTest::ToString); TEST_P(CordTest, AllFlatSizes) { @@ -243,6 +271,7 @@ TEST_P(CordTest, AllFlatSizes) { } absl::Cord dst(src); + MaybeHarden(dst); EXPECT_EQ(std::string(dst), src) << s; } } @@ -274,6 +303,7 @@ TEST_P(CordTest, GigabyteCordFromExternal) { c.Append(from); c.Append(from); c.Append(from); + MaybeHarden(c); } for (int i = 0; i < 1024; ++i) { @@ -302,6 +332,8 @@ bool my_unique_true_boolean = true; TEST_P(CordTest, Assignment) { absl::Cord x(absl::string_view("hi there")); absl::Cord y(x); + MaybeHarden(y); + ASSERT_EQ(x.ExpectedChecksum(), absl::nullopt); ASSERT_EQ(std::string(x), "hi there"); ASSERT_EQ(std::string(y), "hi there"); ASSERT_TRUE(x == y); @@ -355,6 +387,7 @@ TEST_P(CordTest, Assignment) { TEST_P(CordTest, StartsEndsWith) { absl::Cord x(absl::string_view("abcde")); + MaybeHarden(x); absl::Cord empty(""); ASSERT_TRUE(x.StartsWith(absl::Cord("abcde"))); @@ -392,6 +425,7 @@ TEST_P(CordTest, Subcord) { absl::Cord a; AppendWithFragments(s, &rng, &a); + MaybeHarden(a); ASSERT_EQ(s, std::string(a)); // Check subcords of a, from a variety of interesting points. @@ -413,6 +447,9 @@ TEST_P(CordTest, Subcord) { ASSERT_EQ(absl::string_view(s).substr(pos, end_pos - pos), std::string(sa)) << a; + if (pos != 0 || end_pos != a.size()) { + ASSERT_EQ(sa.ExpectedChecksum(), absl::nullopt); + } } } @@ -452,10 +489,19 @@ TEST_P(CordTest, Swap) { absl::string_view b("Mandark"); absl::Cord x(a); absl::Cord y(b); + MaybeHarden(x); swap(x, y); + if (UseCrc()) { + ASSERT_EQ(x.ExpectedChecksum(), absl::nullopt); + ASSERT_EQ(y.ExpectedChecksum(), 1); + } ASSERT_EQ(x, absl::Cord(b)); ASSERT_EQ(y, absl::Cord(a)); x.swap(y); + if (UseCrc()) { + ASSERT_EQ(x.ExpectedChecksum(), 1); + ASSERT_EQ(y.ExpectedChecksum(), absl::nullopt); + } ASSERT_EQ(x, absl::Cord(a)); ASSERT_EQ(y, absl::Cord(b)); } @@ -480,11 +526,11 @@ static void VerifyCopyToString(const absl::Cord& cord) { } TEST_P(CordTest, CopyToString) { - VerifyCopyToString(absl::Cord()); - VerifyCopyToString(absl::Cord("small cord")); - VerifyCopyToString( + VerifyCopyToString(absl::Cord()); // empty cords cannot carry CRCs + VerifyCopyToString(MaybeHardened(absl::Cord("small cord"))); + VerifyCopyToString(MaybeHardened( absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ", - "copying ", "to ", "a ", "string."})); + "copying ", "to ", "a ", "string."}))); } TEST_P(CordTest, TryFlatEmpty) { @@ -494,40 +540,47 @@ TEST_P(CordTest, TryFlatEmpty) { TEST_P(CordTest, TryFlatFlat) { absl::Cord c("hello"); + MaybeHarden(c); EXPECT_EQ(c.TryFlat(), "hello"); } TEST_P(CordTest, TryFlatSubstrInlined) { absl::Cord c("hello"); c.RemovePrefix(1); + MaybeHarden(c); EXPECT_EQ(c.TryFlat(), "ello"); } TEST_P(CordTest, TryFlatSubstrFlat) { absl::Cord c("longer than 15 bytes"); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); + MaybeHarden(sub); EXPECT_EQ(sub.TryFlat(), "onger than 15 bytes"); } TEST_P(CordTest, TryFlatConcat) { absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"}); + MaybeHarden(c); EXPECT_EQ(c.TryFlat(), absl::nullopt); } TEST_P(CordTest, TryFlatExternal) { absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); + MaybeHarden(c); EXPECT_EQ(c.TryFlat(), "hell"); } TEST_P(CordTest, TryFlatSubstrExternal) { absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); + MaybeHarden(sub); EXPECT_EQ(sub.TryFlat(), "ell"); } TEST_P(CordTest, TryFlatSubstrConcat) { absl::Cord c = absl::MakeFragmentedCord({"hello", " world"}); absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); + MaybeHarden(sub); EXPECT_EQ(sub.TryFlat(), absl::nullopt); c.RemovePrefix(1); EXPECT_EQ(c.TryFlat(), absl::nullopt); @@ -547,6 +600,7 @@ TEST_P(CordTest, TryFlatCommonlyAssumedInvariants) { "returned by the ", "iterator"}; absl::Cord c = absl::MakeFragmentedCord(fragments); + MaybeHarden(c); int fragment = 0; int offset = 0; absl::Cord::CharIterator itc = c.char_begin(); @@ -591,13 +645,15 @@ static void VerifyFlatten(absl::Cord c) { TEST_P(CordTest, Flatten) { VerifyFlatten(absl::Cord()); - VerifyFlatten(absl::Cord("small cord")); - VerifyFlatten(absl::Cord("larger than small buffer optimization")); - VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"})); + VerifyFlatten(MaybeHardened(absl::Cord("small cord"))); + VerifyFlatten( + MaybeHardened(absl::Cord("larger than small buffer optimization"))); + VerifyFlatten(MaybeHardened( + absl::MakeFragmentedCord({"small ", "fragmented ", "cord"}))); // Test with a cord that is longer than the largest flat buffer RandomEngine rng(GTEST_FLAG_GET(random_seed)); - VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192))); + VerifyFlatten(MaybeHardened(absl::Cord(RandomLowercaseString(&rng, 8192)))); } // Test data @@ -651,22 +707,26 @@ TEST_P(CordTest, MultipleLengths) { { // Construct from Cord absl::Cord tmp(a); absl::Cord x(tmp); + MaybeHarden(x); EXPECT_EQ(a, std::string(x)) << "'" << a << "'"; } { // Construct from absl::string_view absl::Cord x(a); + MaybeHarden(x); EXPECT_EQ(a, std::string(x)) << "'" << a << "'"; } { // Append cord to self absl::Cord self(a); + MaybeHarden(self); self.Append(self); EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'"; } { // Prepend cord to self absl::Cord self(a); + MaybeHarden(self); self.Prepend(self); EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'"; } @@ -678,12 +738,14 @@ TEST_P(CordTest, MultipleLengths) { { // CopyFrom Cord absl::Cord x(a); absl::Cord y(b); + MaybeHarden(x); x = y; EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'"; } { // CopyFrom absl::string_view absl::Cord x(a); + MaybeHarden(x); x = b; EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'"; } @@ -691,12 +753,14 @@ TEST_P(CordTest, MultipleLengths) { { // Cord::Append(Cord) absl::Cord x(a); absl::Cord y(b); + MaybeHarden(x); x.Append(y); EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'"; } { // Cord::Append(absl::string_view) absl::Cord x(a); + MaybeHarden(x); x.Append(b); EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'"; } @@ -704,12 +768,14 @@ TEST_P(CordTest, MultipleLengths) { { // Cord::Prepend(Cord) absl::Cord x(a); absl::Cord y(b); + MaybeHarden(x); x.Prepend(y); EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'"; } { // Cord::Prepend(absl::string_view) absl::Cord x(a); + MaybeHarden(x); x.Prepend(b); EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'"; } @@ -722,13 +788,16 @@ namespace { TEST_P(CordTest, RemoveSuffixWithExternalOrSubstring) { absl::Cord cord = absl::MakeCordFromExternal( "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); }); - EXPECT_EQ("foo bar baz", std::string(cord)); + MaybeHarden(cord); + // This RemoveSuffix() will wrap the EXTERNAL node in a SUBSTRING node. cord.RemoveSuffix(4); EXPECT_EQ("foo bar", std::string(cord)); + MaybeHarden(cord); + // This RemoveSuffix() will adjust the SUBSTRING node in-place. cord.RemoveSuffix(4); EXPECT_EQ("foo", std::string(cord)); @@ -738,6 +807,7 @@ TEST_P(CordTest, RemoveSuffixMakesZeroLengthNode) { absl::Cord c; c.Append(absl::Cord(std::string(100, 'x'))); absl::Cord other_ref = c; // Prevent inplace appends + MaybeHarden(c); c.Append(absl::Cord(std::string(200, 'y'))); c.RemoveSuffix(200); EXPECT_EQ(std::string(100, 'x'), std::string(c)); @@ -763,6 +833,7 @@ absl::Cord CordWithZedBlock(size_t size) { // Establish that ZedBlock does what we think it does. TEST_P(CordTest, CordSpliceTestZedBlock) { absl::Cord blob = CordWithZedBlock(10); + MaybeHarden(blob); EXPECT_EQ(10, blob.size()); std::string s; absl::CopyCordToString(blob, &s); @@ -771,6 +842,7 @@ TEST_P(CordTest, CordSpliceTestZedBlock) { TEST_P(CordTest, CordSpliceTestZedBlock0) { absl::Cord blob = CordWithZedBlock(0); + MaybeHarden(blob); EXPECT_EQ(0, blob.size()); std::string s; absl::CopyCordToString(blob, &s); @@ -779,6 +851,7 @@ TEST_P(CordTest, CordSpliceTestZedBlock0) { TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) { absl::Cord blob = CordWithZedBlock(10); + MaybeHarden(blob); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); suffix.RemovePrefix(9); @@ -791,6 +864,7 @@ TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) { // Remove all of a prefix block TEST_P(CordTest, CordSpliceTestZedBlockSuffix0) { absl::Cord blob = CordWithZedBlock(10); + MaybeHarden(blob); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); suffix.RemovePrefix(10); @@ -823,6 +897,7 @@ absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset, // Taking an empty suffix of a block breaks appending. TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) { absl::Cord zero = CordWithZedBlock(10); + MaybeHarden(zero); absl::Cord suffix(zero); suffix.RemovePrefix(10); absl::Cord result; @@ -831,6 +906,7 @@ TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) { TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) { absl::Cord zero = CordWithZedBlock(10); + MaybeHarden(zero); absl::Cord prefix(zero); prefix.RemoveSuffix(10); absl::Cord suffix(zero); @@ -842,13 +918,19 @@ TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) { TEST_P(CordTest, CordSpliceTestRemoveEntireBlock3) { absl::Cord blob = CordWithZedBlock(10); absl::Cord block = BigCord(10, 'b'); + MaybeHarden(blob); + MaybeHarden(block); blob = SpliceCord(blob, 0, block); } struct CordCompareTestCase { template - CordCompareTestCase(const LHS& lhs, const RHS& rhs) - : lhs_cord(lhs), rhs_cord(rhs) {} + CordCompareTestCase(const LHS& lhs, const RHS& rhs, bool use_crc) + : lhs_cord(lhs), rhs_cord(rhs) { + if (use_crc) { + lhs_cord.SetExpectedChecksum(1); + } + } absl::Cord lhs_cord; absl::Cord rhs_cord; @@ -885,47 +967,54 @@ TEST_P(CordTest, Compare) { concat2.Append("cccccccccccDDDDDDDDDDDDDD"); concat2.Append("DD"); + const bool use_crc = UseCrc(); + std::vector test_cases = {{ // Inline cords - {"abcdef", "abcdef"}, - {"abcdef", "abcdee"}, - {"abcdef", "abcdeg"}, - {"bbcdef", "abcdef"}, - {"bbcdef", "abcdeg"}, - {"abcdefa", "abcdef"}, - {"abcdef", "abcdefa"}, + {"abcdef", "abcdef", use_crc}, + {"abcdef", "abcdee", use_crc}, + {"abcdef", "abcdeg", use_crc}, + {"bbcdef", "abcdef", use_crc}, + {"bbcdef", "abcdeg", use_crc}, + {"abcdefa", "abcdef", use_crc}, + {"abcdef", "abcdefa", use_crc}, // Small flat cords - {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD"}, - {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD"}, - {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD"}, - {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX"}, - {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD"}, - {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa"}, + {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD", use_crc}, + {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD", use_crc}, + {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD", use_crc}, + {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX", use_crc}, + {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD", use_crc}, + {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa", use_crc}, // Subcords - {subcord, subcord}, - {subcord, "aaBBBBBccc"}, - {subcord, "aaBBBBBccd"}, - {subcord, "aaBBBBBccb"}, - {subcord, "aaBBBBBxcb"}, - {subcord, "aaBBBBBccca"}, - {subcord, "aaBBBBBcc"}, + {subcord, subcord, use_crc}, + {subcord, "aaBBBBBccc", use_crc}, + {subcord, "aaBBBBBccd", use_crc}, + {subcord, "aaBBBBBccb", use_crc}, + {subcord, "aaBBBBBxcb", use_crc}, + {subcord, "aaBBBBBccca", use_crc}, + {subcord, "aaBBBBBcc", use_crc}, // Concats - {concat, concat}, + {concat, concat, use_crc}, {concat, - "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD"}, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD", + use_crc}, {concat, - "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD"}, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD", + use_crc}, {concat, - "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD"}, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD", + use_crc}, {concat, - "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD"}, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD", + use_crc}, {concat, - "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe"}, + "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe", + use_crc}, - {concat, concat2}, + {concat, concat2, use_crc}, }}; for (const auto& tc : test_cases) { @@ -936,6 +1025,7 @@ TEST_P(CordTest, Compare) { TEST_P(CordTest, CompareAfterAssign) { absl::Cord a("aaaaaa1111111"); absl::Cord b("aaaaaa2222222"); + MaybeHarden(a); a = "cccccc"; b = "cccccc"; EXPECT_EQ(a, b); @@ -994,6 +1084,8 @@ TEST_P(CordTest, CompareRandomComparisons) { d.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]); } std::bernoulli_distribution coin_flip(0.5); + MaybeHarden(c); + MaybeHarden(d); TestCompare(coin_flip(rng) ? c : absl::Cord(std::string(c)), coin_flip(rng) ? d : absl::Cord(std::string(d)), &rng); } @@ -1119,6 +1211,7 @@ TEST_P(CordTest, ConstructFromExternalCompareContents) { EXPECT_EQ(external->size(), sv.size()); delete external; }); + MaybeHarden(cord); EXPECT_EQ(data, cord); } } @@ -1134,7 +1227,7 @@ TEST_P(CordTest, ConstructFromExternalLargeReleaser) { EXPECT_EQ(data, absl::string_view(data_array.data(), data_array.size())); invoked = true; }; - (void)absl::MakeCordFromExternal(data, releaser); + (void)MaybeHardened(absl::MakeCordFromExternal(data, releaser)); EXPECT_TRUE(invoked); } @@ -1147,11 +1240,11 @@ TEST_P(CordTest, ConstructFromExternalFunctionPointerReleaser) { invoked = true; }); invoked = false; - (void)absl::MakeCordFromExternal(data, releaser); + (void)MaybeHardened(absl::MakeCordFromExternal(data, releaser)); EXPECT_TRUE(invoked); invoked = false; - (void)absl::MakeCordFromExternal(data, *releaser); + (void)MaybeHardened(absl::MakeCordFromExternal(data, *releaser)); EXPECT_TRUE(invoked); } @@ -1165,20 +1258,21 @@ TEST_P(CordTest, ConstructFromExternalMoveOnlyReleaser) { }; bool invoked = false; - (void)absl::MakeCordFromExternal("dummy", Releaser(&invoked)); + (void)MaybeHardened(absl::MakeCordFromExternal("dummy", Releaser(&invoked))); EXPECT_TRUE(invoked); } TEST_P(CordTest, ConstructFromExternalNoArgLambda) { bool invoked = false; - (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; }); + (void)MaybeHardened( + absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; })); EXPECT_TRUE(invoked); } TEST_P(CordTest, ConstructFromExternalStringViewArgLambda) { bool invoked = false; - (void)absl::MakeCordFromExternal( - "dummy", [&invoked](absl::string_view) { invoked = true; }); + (void)MaybeHardened(absl::MakeCordFromExternal( + "dummy", [&invoked](absl::string_view) { invoked = true; })); EXPECT_TRUE(invoked); } @@ -1193,7 +1287,7 @@ TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) { bool destroyed = false; Releaser releaser(&destroyed); - (void)absl::MakeCordFromExternal("dummy", releaser); + (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser)); EXPECT_TRUE(destroyed); } @@ -1209,18 +1303,18 @@ TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) { bool lvalue_invoked = false; bool rvalue_invoked = false; Releaser releaser = {&lvalue_invoked, &rvalue_invoked}; - (void)absl::MakeCordFromExternal("", releaser); + (void)MaybeHardened(absl::MakeCordFromExternal("", releaser)); EXPECT_FALSE(lvalue_invoked); EXPECT_TRUE(rvalue_invoked); rvalue_invoked = false; - (void)absl::MakeCordFromExternal("dummy", releaser); + (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser)); EXPECT_FALSE(lvalue_invoked); EXPECT_TRUE(rvalue_invoked); rvalue_invoked = false; // NOLINTNEXTLINE: suppress clang-tidy std::move on trivially copyable type. - (void)absl::MakeCordFromExternal("dummy", std::move(releaser)); + (void)MaybeHardened(absl::MakeCordFromExternal("dummy", std::move(releaser))); EXPECT_FALSE(lvalue_invoked); EXPECT_TRUE(rvalue_invoked); } @@ -1229,7 +1323,9 @@ TEST_P(CordTest, ExternalMemoryBasicUsage) { static const char* strings[] = {"", "hello", "there"}; for (const char* str : strings) { absl::Cord dst("(prefix)"); + MaybeHarden(dst); AddExternalMemory(str, &dst); + MaybeHarden(dst); dst.Append("(suffix)"); EXPECT_EQ((std::string("(prefix)") + str + std::string("(suffix)")), std::string(dst)); @@ -1243,7 +1339,9 @@ TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) { for (int offset = 0; offset <= s.size(); offset++) { for (int length = 0; length <= s.size() - offset; length++) { absl::Cord result(cord); + MaybeHarden(result); result.RemovePrefix(offset); + MaybeHarden(result); result.RemoveSuffix(result.size() - length); EXPECT_EQ(s.substr(offset, length), std::string(result)) << offset << " " << length; @@ -1254,8 +1352,10 @@ TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) { TEST_P(CordTest, ExternalMemoryGet) { absl::Cord cord("hello"); AddExternalMemory(" world!", &cord); + MaybeHarden(cord); AddExternalMemory(" how are ", &cord); cord.Append(" you?"); + MaybeHarden(cord); std::string s = std::string(cord); for (int i = 0; i < s.size(); i++) { EXPECT_EQ(s[i], cord[i]); @@ -1354,11 +1454,13 @@ TEST_P(CordTest, CordMemoryUsageInlineRep) { TEST_P(CordTest, Concat_Append) { // Create a rep of type CONCAT absl::Cord s1("foobarbarbarbarbar"); + MaybeHarden(s1); s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg"); size_t size = s1.size(); // Create a copy of s1 and append to it. absl::Cord s2 = s1; + MaybeHarden(s2); s2.Append("x"); // 7465150 modifies s1 when it shouldn't. @@ -1378,6 +1480,7 @@ TEST_P(CordTest, DiabolicalGrowth) { for (char c : expected) { absl::Cord shared(cord); cord.Append(absl::string_view(&c, 1)); + MaybeHarden(cord); } std::string value; absl::CopyCordToString(cord, &value); @@ -1422,8 +1525,12 @@ static absl::Cord MakeHuge(absl::string_view prefix) { TEST_P(CordTest, HugeCord) { absl::Cord cord = MakeHuge("huge cord"); + MaybeHarden(cord); + + const size_t acceptable_delta = + 100 + (UseCrc() ? sizeof(absl::cord_internal::CordRepCrc) : 0); EXPECT_LE(cord.size(), cord.EstimatedMemoryUsage()); - EXPECT_GE(cord.size() + 100, cord.EstimatedMemoryUsage()); + EXPECT_GE(cord.size() + acceptable_delta, cord.EstimatedMemoryUsage()); } // Tests that Append() works ok when handed a self reference @@ -1433,6 +1540,7 @@ TEST_P(CordTest, AppendSelf) { std::string control_data = "Abc"; absl::Cord data(control_data); while (control_data.length() < 0x4000) { + MaybeHarden(data); data.Append(data); control_data.append(control_data); ASSERT_EQ(control_data, data); @@ -1443,6 +1551,8 @@ TEST_P(CordTest, MakeFragmentedCordFromInitializerList) { absl::Cord fragmented = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); + MaybeHarden(fragmented); + EXPECT_EQ("A fragmented Cord", fragmented); auto chunk_it = fragmented.chunk_begin(); @@ -1463,6 +1573,8 @@ TEST_P(CordTest, MakeFragmentedCordFromVector) { std::vector chunks = {"A ", "fragmented ", "Cord"}; absl::Cord fragmented = absl::MakeFragmentedCord(chunks); + MaybeHarden(fragmented); + EXPECT_EQ("A fragmented Cord", fragmented); auto chunk_it = fragmented.chunk_begin(); @@ -1565,22 +1677,26 @@ TEST_P(CordTest, CordChunkIteratorOperations) { VerifyChunkIterator(empty_cord, 0); absl::Cord small_buffer_cord("small cord"); + MaybeHarden(small_buffer_cord); VerifyChunkIterator(small_buffer_cord, 1); absl::Cord flat_node_cord("larger than small buffer optimization"); + MaybeHarden(flat_node_cord); VerifyChunkIterator(flat_node_cord, 1); - VerifyChunkIterator( - absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ", - "testing ", "chunk ", "iterations."}), - 8); + VerifyChunkIterator(MaybeHardened(absl::MakeFragmentedCord( + {"a ", "small ", "fragmented ", "cord ", "for ", + "testing ", "chunk ", "iterations."})), + 8); absl::Cord reused_nodes_cord(std::string(40, 'c')); reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'b'))); + MaybeHarden(reused_nodes_cord); reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'a'))); size_t expected_chunks = 3; for (int i = 0; i < 8; ++i) { reused_nodes_cord.Prepend(reused_nodes_cord); + MaybeHarden(reused_nodes_cord); expected_chunks *= 2; VerifyChunkIterator(reused_nodes_cord, expected_chunks); } @@ -1706,27 +1822,33 @@ TEST_P(CordTest, CharIteratorOperations) { VerifyCharIterator(empty_cord); absl::Cord small_buffer_cord("small cord"); + MaybeHarden(small_buffer_cord); VerifyCharIterator(small_buffer_cord); absl::Cord flat_node_cord("larger than small buffer optimization"); + MaybeHarden(flat_node_cord); VerifyCharIterator(flat_node_cord); - VerifyCharIterator( + VerifyCharIterator(MaybeHardened( absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ", - "testing ", "character ", "iteration."})); + "testing ", "character ", "iteration."}))); absl::Cord reused_nodes_cord("ghi"); reused_nodes_cord.Prepend(absl::Cord("def")); reused_nodes_cord.Prepend(absl::Cord("abc")); for (int i = 0; i < 4; ++i) { reused_nodes_cord.Prepend(reused_nodes_cord); + MaybeHarden(reused_nodes_cord); VerifyCharIterator(reused_nodes_cord); } RandomEngine rng(GTEST_FLAG_GET(random_seed)); absl::Cord flat_cord(RandomLowercaseString(&rng, 256)); absl::Cord subcords; - for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128)); + for (int i = 0; i < 4; ++i) { + subcords.Prepend(flat_cord.Subcord(16 * i, 128)); + MaybeHarden(subcords); + } VerifyCharIterator(subcords); } @@ -1751,6 +1873,8 @@ TEST_P(CordTest, CharIteratorAdvanceAndRead) { cord.Append(absl::Cord(block)); } + MaybeHarden(cord); + for (size_t chunk_size : {kChunkSize1, kChunkSize2, kChunkSize3, kChunkSize4}) { absl::Cord::CharIterator it = cord.char_begin(); @@ -1768,6 +1892,7 @@ TEST_P(CordTest, CharIteratorAdvanceAndRead) { TEST_P(CordTest, StreamingOutput) { absl::Cord c = absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."}); + MaybeHarden(c); std::stringstream output; output << c; EXPECT_EQ("A small fragmented Cord.", output.str()); @@ -1781,6 +1906,7 @@ TEST_P(CordTest, ForEachChunk) { cord_chunks.push_back(absl::StrCat("[", i, "]")); } absl::Cord c = absl::MakeFragmentedCord(cord_chunks); + MaybeHarden(c); std::vector iterated_chunks; absl::CordTestPeer::ForEachChunk(c, @@ -1798,6 +1924,7 @@ TEST_P(CordTest, SmallBufferAssignFromOwnData) { for (size_t pos = 0; pos < contents.size(); ++pos) { for (size_t count = contents.size() - pos; count > 0; --count) { absl::Cord c(contents); + MaybeHarden(c); absl::string_view flat = c.Flatten(); c = flat.substr(pos, count); EXPECT_EQ(c, contents.substr(pos, count)) @@ -1810,12 +1937,16 @@ TEST_P(CordTest, Format) { absl::Cord c; absl::Format(&c, "There were %04d little %s.", 3, "pigs"); EXPECT_EQ(c, "There were 0003 little pigs."); + MaybeHarden(c); absl::Format(&c, "And %-3llx bad wolf!", 1); + MaybeHarden(c); EXPECT_EQ(c, "There were 0003 little pigs.And 1 bad wolf!"); } TEST_P(CordTest, Hardening) { absl::Cord cord("hello"); + MaybeHarden(cord); + // These statement should abort the program in all builds modes. EXPECT_DEATH_IF_SUPPORTED(cord.RemovePrefix(6), ""); EXPECT_DEATH_IF_SUPPORTED(cord.RemoveSuffix(6), ""); @@ -1855,6 +1986,7 @@ TEST_P(CordTest, BtreeHostileSplitInsertJoin) { } for (int j = 0; j < 1000; ++j) { + MaybeHarden(cord); size_t offset = absl::Uniform(bitgen, 0u, cord.size()); size_t length = absl::Uniform(bitgen, 100u, data.size()); if (cord.size() == offset) { @@ -1955,3 +2087,272 @@ TEST_P(CordTest, ConstinitConstructor) { TestConstinitConstructor( absl::strings_internal::MakeStringConstant(LongView{})); } + +namespace { + +// Test helper that generates a populated cord for future manipulation. +// +// By test convention, all generated cords begin with the characters "abcde" at +// the start of the first chunk. +class PopulatedCordFactory { + public: + constexpr PopulatedCordFactory(absl::string_view name, + absl::Cord (*generator)()) + : name_(name), generator_(generator) {} + + absl::string_view Name() const { return name_; } + absl::Cord Generate() const { return generator_(); } + + private: + absl::string_view name_; + absl::Cord (*generator_)(); +}; + +// clang-format off +// This array is constant-initialized in conformant compilers. +PopulatedCordFactory cord_factories[] = { + {"sso", [] { return absl::Cord("abcde"); }}, + {"flat", [] { + // Too large to live in SSO space, but small enough to be a simple FLAT. + absl::Cord flat(absl::StrCat("abcde", std::string(1000, 'x'))); + flat.Flatten(); + return flat; + }}, + {"external", [] { + // A cheat: we are using a string literal as the external storage, so a + // no-op releaser is correct here. + return absl::MakeCordFromExternal("abcde External!", []{}); + }}, + {"external substring", [] { + // A cheat: we are using a string literal as the external storage, so a + // no-op releaser is correct here. + absl::Cord ext = absl::MakeCordFromExternal("-abcde External!", []{}); + return absl::CordTestPeer::MakeSubstring(ext, 1, ext.size() - 1); + }}, + {"substring", [] { + absl::Cord flat(absl::StrCat("-abcde", std::string(1000, 'x'))); + flat.Flatten(); + return flat.Subcord(1, 998); + }}, + {"fragmented", [] { + std::string fragment = absl::StrCat("abcde", std::string(195, 'x')); + std::vector fragments(200, fragment); + absl::Cord cord = absl::MakeFragmentedCord(fragments); + assert(cord.size() == 40000); + return cord; + }}, +}; +// clang-format on + +// Test helper that can mutate a cord, and possibly undo the mutation, for +// testing. +class CordMutator { + public: + constexpr CordMutator(absl::string_view name, void (*mutate)(absl::Cord&), + void (*undo)(absl::Cord&) = nullptr) + : name_(name), mutate_(mutate), undo_(undo) {} + + absl::string_view Name() const { return name_; } + void Mutate(absl::Cord& cord) const { mutate_(cord); } + bool CanUndo() const { return undo_ != nullptr; } + void Undo(absl::Cord& cord) const { undo_(cord); } + + private: + absl::string_view name_; + void (*mutate_)(absl::Cord&); + void (*undo_)(absl::Cord&); +}; + +// clang-format off +// This array is constant-initialized in conformant compilers. +CordMutator cord_mutators[] ={ + {"clear", [](absl::Cord& c) { c.Clear(); }}, + {"overwrite", [](absl::Cord& c) { c = "overwritten"; }}, + { + "append string", + [](absl::Cord& c) { c.Append("0123456789"); }, + [](absl::Cord& c) { c.RemoveSuffix(10); } + }, + { + "append cord", + [](absl::Cord& c) { + c.Append(absl::MakeFragmentedCord({"12345", "67890"})); + }, + [](absl::Cord& c) { c.RemoveSuffix(10); } + }, + { + "append checksummed cord", + [](absl::Cord& c) { + absl::Cord to_append = absl::MakeFragmentedCord({"12345", "67890"}); + to_append.SetExpectedChecksum(999); + c.Append(to_append); + }, + [](absl::Cord& c) { c.RemoveSuffix(10); } + }, + { + "append self", + [](absl::Cord& c) { c.Append(c); }, + [](absl::Cord& c) { c.RemoveSuffix(c.size() / 2); } + }, + { + "prepend string", + [](absl::Cord& c) { c.Prepend("9876543210"); }, + [](absl::Cord& c) { c.RemovePrefix(10); } + }, + { + "prepend cord", + [](absl::Cord& c) { + c.Prepend(absl::MakeFragmentedCord({"98765", "43210"})); + }, + [](absl::Cord& c) { c.RemovePrefix(10); } + }, + { + "prepend checksummed cord", + [](absl::Cord& c) { + absl::Cord to_prepend = absl::MakeFragmentedCord({"98765", "43210"}); + to_prepend.SetExpectedChecksum(999); + c.Prepend(to_prepend); + }, + [](absl::Cord& c) { c.RemovePrefix(10); } + }, + { + "prepend self", + [](absl::Cord& c) { c.Prepend(c); }, + [](absl::Cord& c) { c.RemovePrefix(c.size() / 2); } + }, + {"remove prefix", [](absl::Cord& c) { c.RemovePrefix(2); }}, + {"remove suffix", [](absl::Cord& c) { c.RemoveSuffix(2); }}, + {"subcord", [](absl::Cord& c) { c = c.Subcord(1, c.size() - 2); }}, + { + "swap inline", + [](absl::Cord& c) { + absl::Cord other("swap"); + c.swap(other); + } + }, + { + "swap tree", + [](absl::Cord& c) { + absl::Cord other(std::string(10000, 'x')); + c.swap(other); + } + }, +}; +// clang-format on +} // namespace + +TEST_P(CordTest, ExpectedChecksum) { + for (const PopulatedCordFactory& factory : cord_factories) { + SCOPED_TRACE(factory.Name()); + for (bool shared : {false, true}) { + SCOPED_TRACE(shared); + + absl::Cord shared_cord_source = factory.Generate(); + auto make_instance = [=] { + return shared ? shared_cord_source : factory.Generate(); + }; + + const absl::Cord base_value = factory.Generate(); + const std::string base_value_as_string(factory.Generate().Flatten()); + + absl::Cord c1 = make_instance(); + EXPECT_FALSE(c1.ExpectedChecksum().has_value()); + + // Setting an expected checksum works, and retains the cord's bytes + c1.SetExpectedChecksum(12345); + EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345); + EXPECT_EQ(c1, base_value); + + // CRC persists through copies, assignments, and moves: + absl::Cord c1_copy_construct = c1; + EXPECT_EQ(c1_copy_construct.ExpectedChecksum().value_or(0), 12345); + + absl::Cord c1_copy_assign; + c1_copy_assign = c1; + EXPECT_EQ(c1_copy_assign.ExpectedChecksum().value_or(0), 12345); + + absl::Cord c1_move(std::move(c1_copy_assign)); + EXPECT_EQ(c1_move.ExpectedChecksum().value_or(0), 12345); + + EXPECT_EQ(c1.ExpectedChecksum().value_or(0), 12345); + + // A CRC Cord compares equal to its non-CRC value. + EXPECT_EQ(c1, make_instance()); + + for (const CordMutator& mutator : cord_mutators) { + SCOPED_TRACE(mutator.Name()); + + // Test that mutating a cord removes its stored checksum + absl::Cord c2 = make_instance(); + c2.SetExpectedChecksum(24680); + + mutator.Mutate(c2); + EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt); + + if (mutator.CanUndo()) { + // Undoing an operation should not restore the checksum + mutator.Undo(c2); + EXPECT_EQ(c2, base_value); + EXPECT_EQ(c2.ExpectedChecksum(), absl::nullopt); + } + } + + absl::Cord c3 = make_instance(); + c3.SetExpectedChecksum(999); + const absl::Cord& cc3 = c3; + + // Test that all cord reading operations function in the face of an + // expected checksum. + + // Test data precondition + ASSERT_TRUE(cc3.StartsWith("abcde")); + + EXPECT_EQ(cc3.size(), base_value_as_string.size()); + EXPECT_FALSE(cc3.empty()); + EXPECT_EQ(cc3.Compare(base_value), 0); + EXPECT_EQ(cc3.Compare(base_value_as_string), 0); + EXPECT_EQ(cc3.Compare("wxyz"), -1); + EXPECT_EQ(cc3.Compare(absl::Cord("wxyz")), -1); + EXPECT_EQ(cc3.Compare("aaaa"), 1); + EXPECT_EQ(cc3.Compare(absl::Cord("aaaa")), 1); + EXPECT_EQ(absl::Cord("wxyz").Compare(cc3), 1); + EXPECT_EQ(absl::Cord("aaaa").Compare(cc3), -1); + EXPECT_TRUE(cc3.StartsWith("abcd")); + EXPECT_EQ(std::string(cc3), base_value_as_string); + + std::string dest; + absl::CopyCordToString(cc3, &dest); + EXPECT_EQ(dest, base_value_as_string); + + bool first_pass = true; + for (absl::string_view chunk : cc3.Chunks()) { + if (first_pass) { + EXPECT_TRUE(absl::StartsWith(chunk, "abcde")); + } + first_pass = false; + } + first_pass = true; + for (char ch : cc3.Chars()) { + if (first_pass) { + EXPECT_EQ(ch, 'a'); + } + first_pass = false; + } + EXPECT_TRUE(absl::StartsWith(*cc3.chunk_begin(), "abcde")); + EXPECT_EQ(*cc3.char_begin(), 'a'); + + auto char_it = cc3.char_begin(); + absl::Cord::Advance(&char_it, 2); + EXPECT_EQ(absl::Cord::AdvanceAndRead(&char_it, 2), "cd"); + EXPECT_EQ(*char_it, 'e'); + char_it = cc3.char_begin(); + absl::Cord::Advance(&char_it, 2); + EXPECT_TRUE(absl::StartsWith(absl::Cord::ChunkRemaining(char_it), "cde")); + + EXPECT_EQ(cc3[0], 'a'); + EXPECT_EQ(cc3[4], 'e'); + EXPECT_EQ(absl::HashOf(cc3), absl::HashOf(base_value)); + EXPECT_EQ(absl::HashOf(cc3), absl::HashOf(base_value_as_string)); + } + } +} diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index bf135ae2..672bf178 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -87,9 +87,6 @@ class RefcountAndFlags { constexpr RefcountAndFlags() : count_{kRefIncrement} {} struct Immortal {}; explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {} - struct WithCrc {}; - explicit constexpr RefcountAndFlags(WithCrc) - : count_(kCrcFlag | kRefIncrement) {} // Increments the reference count. Imposes no memory ordering. inline void Increment() { @@ -125,32 +122,14 @@ class RefcountAndFlags { return count_.load(std::memory_order_acquire) >> kNumFlags; } - // Returns true if the referenced object carries a CRC value. - bool HasCrc() const { - return (count_.load(std::memory_order_relaxed) & kCrcFlag) != 0; - } - - // Returns true iff the atomic integer is 1 and this node does not store - // a CRC. When both these conditions are met, the current thread owns - // the reference and no other thread shares it, so its contents may be - // safely mutated. - // - // If the referenced item is shared, carries a CRC, or is immortal, - // it should not be modified in-place, and this function returns false. - // - // This call performs the memory barrier needed for the owning thread - // to act on the object, so that if it returns true, it may safely - // assume exclusive access to the object. - inline bool IsMutable() { - return (count_.load(std::memory_order_acquire)) == kRefIncrement; - } - - // Returns whether the atomic integer is 1. Similar to IsMutable(), - // but does not check for a stored CRC. (An unshared node with a CRC is not - // mutable, because changing its data would invalidate the CRC.) - // - // When this returns true, there are no other references, and data sinks - // may safely adopt the children of the CordRep. + // Returns whether the atomic integer is 1. + // If the reference count is used in the conventional way, a + // reference count of 1 implies that the current thread owns the + // reference and no other thread shares it. + // This call performs the test for a reference count of one, and + // performs the memory barrier needed for the owning thread + // to act on the object, knowing that it has exclusive access to the + // object. Always returns false when the immortal bit is set. inline bool IsOne() { return (count_.load(std::memory_order_acquire) & kRefcountMask) == kRefIncrement; @@ -170,14 +149,14 @@ class RefcountAndFlags { kNumFlags = 2, kImmortalFlag = 0x1, - kCrcFlag = 0x2, + kReservedFlag = 0x2, kRefIncrement = (1 << kNumFlags), // Bitmask to use when checking refcount by equality. This masks out // all flags except kImmortalFlag, which is part of the refcount for // purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1 // if the immortal bit is set.) - kRefcountMask = ~kCrcFlag, + kRefcountMask = ~kReservedFlag, }; std::atomic count_; @@ -227,6 +206,18 @@ static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive"); static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive"); struct CordRep { + // Result from an `extract edge` operation. Contains the (possibly changed) + // tree node as well as the extracted edge, or {tree, nullptr} if no edge + // could be extracted. + // On success, the returned `tree` value is null if `extracted` was the only + // data edge inside the tree, a data edge if there were only two data edges in + // the tree, or the (possibly new / smaller) remaining tree with the extracted + // data edge removed. + struct ExtractResult { + CordRep* tree; + CordRep* extracted; + }; + CordRep() = default; constexpr CordRep(RefcountAndFlags::Immortal immortal, size_t l) : length(l), refcount(immortal), tag(EXTERNAL), storage{} {} @@ -294,6 +285,13 @@ struct CordRepConcat : public CordRep { uint8_t depth() const { return storage[0]; } void set_depth(uint8_t depth) { storage[0] = depth; } + + // Extracts the right-most flat in the provided concat tree if the entire path + // to that flat is not shared, and the flat has the requested extra capacity. + // Returns the (potentially new) top level tree node and the extracted flat, + // or {tree, nullptr} if no flat was extracted. + static ExtractResult ExtractAppendBuffer(CordRepConcat* tree, + size_t extra_capacity); }; struct CordRepSubstring : public CordRep { diff --git a/absl/strings/internal/cord_internal_test.cc b/absl/strings/internal/cord_internal_test.cc deleted file mode 100644 index 0758dfef..00000000 --- a/absl/strings/internal/cord_internal_test.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2021 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/cord_internal.h" - -#include "gmock/gmock.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { - -TEST(RefcountAndFlags, NormalRefcount) { - for (bool expect_high_refcount : {false, true}) { - SCOPED_TRACE(expect_high_refcount); - RefcountAndFlags refcount; - // count = 1 - - EXPECT_FALSE(refcount.HasCrc()); - EXPECT_TRUE(refcount.IsMutable()); - EXPECT_TRUE(refcount.IsOne()); - - refcount.Increment(); - // count = 2 - - EXPECT_FALSE(refcount.HasCrc()); - EXPECT_FALSE(refcount.IsMutable()); - EXPECT_FALSE(refcount.IsOne()); - - // Decrementing should return true, since a reference is outstanding. - if (expect_high_refcount) { - EXPECT_TRUE(refcount.DecrementExpectHighRefcount()); - } else { - EXPECT_TRUE(refcount.Decrement()); - } - // count = 1 - - EXPECT_FALSE(refcount.HasCrc()); - EXPECT_TRUE(refcount.IsMutable()); - EXPECT_TRUE(refcount.IsOne()); - - // One more decremnt will return false, as no references remain. - if (expect_high_refcount) { - EXPECT_FALSE(refcount.DecrementExpectHighRefcount()); - } else { - EXPECT_FALSE(refcount.Decrement()); - } - } -} - -TEST(RefcountAndFlags, CrcRefcount) { - for (bool expect_high_refcount : {false, true}) { - SCOPED_TRACE(expect_high_refcount); - RefcountAndFlags refcount(RefcountAndFlags::WithCrc{}); - // count = 1 - - // A CRC-carrying node is never mutable, but can be unshared - EXPECT_TRUE(refcount.HasCrc()); - EXPECT_FALSE(refcount.IsMutable()); - EXPECT_TRUE(refcount.IsOne()); - - refcount.Increment(); - // count = 2 - - EXPECT_TRUE(refcount.HasCrc()); - EXPECT_FALSE(refcount.IsMutable()); - EXPECT_FALSE(refcount.IsOne()); - - // Decrementing should return true, since a reference is outstanding. - if (expect_high_refcount) { - EXPECT_TRUE(refcount.DecrementExpectHighRefcount()); - } else { - EXPECT_TRUE(refcount.Decrement()); - } - // count = 1 - - EXPECT_TRUE(refcount.HasCrc()); - EXPECT_FALSE(refcount.IsMutable()); - EXPECT_TRUE(refcount.IsOne()); - - // One more decremnt will return false, as no references remain. - if (expect_high_refcount) { - EXPECT_FALSE(refcount.DecrementExpectHighRefcount()); - } else { - EXPECT_FALSE(refcount.Decrement()); - } - } -} - -TEST(RefcountAndFlags, ImmortalRefcount) { - RefcountAndFlags immortal_refcount(RefcountAndFlags::Immortal{}); - - for (int i = 0; i < 100; ++i) { - // An immortal refcount is never unshared, and decrementing never causes - // a collection. - EXPECT_FALSE(immortal_refcount.HasCrc()); - EXPECT_FALSE(immortal_refcount.IsMutable()); - EXPECT_FALSE(immortal_refcount.IsOne()); - EXPECT_TRUE(immortal_refcount.Decrement()); - EXPECT_TRUE(immortal_refcount.DecrementExpectHighRefcount()); - } -} - -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 4404f33a..7cefcc52 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -216,8 +216,8 @@ void DeleteLeafEdge(CordRep* rep) { // propagate node changes up the stack. template struct StackOperations { - // Returns true if the node at 'depth' is mutable, i.e. has a refcount - // of one, carries no CRC, and all of its parent nodes have a refcount of one. + // Returns true if the node at 'depth' is not shared, i.e. has a refcount + // of one and all of its parent nodes have a refcount of one. inline bool owned(int depth) const { return depth < share_depth; } // Returns the node at 'depth'. @@ -228,11 +228,11 @@ struct StackOperations { inline CordRepBtree* BuildStack(CordRepBtree* tree, int depth) { assert(depth <= tree->height()); int current_depth = 0; - while (current_depth < depth && tree->refcount.IsMutable()) { + while (current_depth < depth && tree->refcount.IsOne()) { stack[current_depth++] = tree; tree = tree->Edge(edge_type)->btree(); } - share_depth = current_depth + (tree->refcount.IsMutable() ? 1 : 0); + share_depth = current_depth + (tree->refcount.IsOne() ? 1 : 0); while (current_depth < depth) { stack[current_depth++] = tree; tree = tree->Edge(edge_type)->btree(); @@ -241,17 +241,17 @@ struct StackOperations { } // Builds a stack with the invariant that all nodes are private owned / not - // shared and carry no CRC data. This is used in iterative updates where a - // previous propagation guaranteed all nodes have this property. + // shared. This is used in iterative updates where a previous propagation + // guaranteed all nodes are owned / private. inline void BuildOwnedStack(CordRepBtree* tree, int height) { assert(height <= CordRepBtree::kMaxHeight); int depth = 0; while (depth < height) { - assert(tree->refcount.IsMutable()); + assert(tree->refcount.IsOne()); stack[depth++] = tree; tree = tree->Edge(edge_type)->btree(); } - assert(tree->refcount.IsMutable()); + assert(tree->refcount.IsOne()); share_depth = depth + 1; } @@ -336,12 +336,12 @@ struct StackOperations { return Unwind(tree, depth, length, result); } - // `share_depth` contains the depth at which the nodes in the stack cannot - // be mutated. I.e., if the top most level is shared (i.e.: - // `!refcount.IsMutable()`), then `share_depth` is 0. If the 2nd node - // is shared (and implicitly all nodes below that) then `share_depth` is 1, - // etc. A `share_depth` greater than the depth of the stack indicates that - // none of the nodes in the stack are shared. + // `share_depth` contains the depth at which the nodes in the stack become + // shared. I.e., if the top most level is shared (i.e.: `!refcount.IsOne()`), + // then `share_depth` is 0. If the 2nd node is shared (and implicitly all + // nodes below that) then `share_depth` is 1, etc. A `share_depth` greater + // than the depth of the stack indicates that none of the nodes in the stack + // are shared. int share_depth; NodeStack stack; @@ -773,7 +773,7 @@ CopyResult CordRepBtree::CopyPrefix(size_t n, bool allow_folding) { CordRep* CordRepBtree::ExtractFront(CordRepBtree* tree) { CordRep* front = tree->Edge(tree->begin()); - if (tree->refcount.IsMutable()) { + if (tree->refcount.IsOne()) { Unref(tree->Edges(tree->begin() + 1, tree->end())); CordRepBtree::Delete(tree); } else { @@ -786,7 +786,7 @@ CordRep* CordRepBtree::ExtractFront(CordRepBtree* tree) { CordRepBtree* CordRepBtree::ConsumeBeginTo(CordRepBtree* tree, size_t end, size_t new_length) { assert(end <= tree->end()); - if (tree->refcount.IsMutable()) { + if (tree->refcount.IsOne()) { Unref(tree->Edges(end, tree->end())); tree->set_end(end); tree->length = new_length; @@ -813,13 +813,13 @@ CordRep* CordRepBtree::RemoveSuffix(CordRepBtree* tree, size_t n) { size_t length = len - n; int height = tree->height(); - bool is_mutable = tree->refcount.IsMutable(); + bool is_mutable = tree->refcount.IsOne(); // Extract all top nodes which are reduced to size = 1 Position pos = tree->IndexOfLength(length); while (pos.index == tree->begin()) { CordRep* edge = ExtractFront(tree); - is_mutable &= edge->refcount.IsMutable(); + is_mutable &= edge->refcount.IsOne(); if (height-- == 0) return ResizeEdge(edge, length, is_mutable); tree = edge->btree(); pos = tree->IndexOfLength(length); @@ -835,8 +835,8 @@ CordRep* CordRepBtree::RemoveSuffix(CordRepBtree* tree, size_t n) { length = pos.n; while (length != edge->length) { // ConsumeBeginTo guarantees `tree` is a clean, privately owned copy. - assert(tree->refcount.IsMutable()); - const bool edge_is_mutable = edge->refcount.IsMutable(); + assert(tree->refcount.IsOne()); + const bool edge_is_mutable = edge->refcount.IsOne(); if (height-- == 0) { tree->edges_[pos.index] = ResizeEdge(edge, length, edge_is_mutable); @@ -973,7 +973,7 @@ char CordRepBtree::GetCharacter(size_t offset) const { Span CordRepBtree::GetAppendBufferSlow(size_t size) { // The inlined version in `GetAppendBuffer()` deals with all heights <= 3. assert(height() >= 4); - assert(refcount.IsMutable()); + assert(refcount.IsOne()); // Build a stack of nodes we may potentially need to update if we find a // non-shared FLAT with capacity at the leaf level. @@ -982,13 +982,13 @@ Span CordRepBtree::GetAppendBufferSlow(size_t size) { CordRepBtree* stack[kMaxDepth]; for (int i = 0; i < depth; ++i) { node = node->Edge(kBack)->btree(); - if (!node->refcount.IsMutable()) return {}; + if (!node->refcount.IsOne()) return {}; stack[i] = node; } // Must be a privately owned, mutable flat. CordRep* const edge = node->Edge(kBack); - if (!edge->refcount.IsMutable() || edge->tag < FLAT) return {}; + if (!edge->refcount.IsOne() || edge->tag < FLAT) return {}; // Must have capacity. const size_t avail = edge->flat()->Capacity() - edge->length; @@ -1123,6 +1123,79 @@ CordRepBtree* CordRepBtree::Rebuild(CordRepBtree* tree) { return nullptr; } +CordRepBtree::ExtractResult CordRepBtree::ExtractAppendBuffer( + CordRepBtree* tree, size_t extra_capacity) { + int depth = 0; + NodeStack stack; + + // Set up default 'no success' result which is {tree, nullptr}. + ExtractResult result; + result.tree = tree; + result.extracted = nullptr; + + // Dive down the right side of the tree, making sure no edges are shared. + while (tree->height() > 0) { + if (!tree->refcount.IsOne()) return result; + stack[depth++] = tree; + tree = tree->Edge(kBack)->btree(); + } + if (!tree->refcount.IsOne()) return result; + + // Validate we ended on a non shared flat. + CordRep* rep = tree->Edge(kBack); + if (!(rep->IsFlat() && rep->refcount.IsOne())) return result; + + // Verify it has at least the requested extra capacity. + CordRepFlat* flat = rep->flat(); + const size_t length = flat->length; + const size_t avail = flat->Capacity() - flat->length; + if (extra_capacity > avail) return result; + + // Set the extracted flat in the result. + result.extracted = flat; + + // Cascading delete all nodes that become empty. + while (tree->size() == 1) { + CordRepBtree::Delete(tree); + if (--depth < 0) { + // We consumed the entire tree: return nullptr for new tree. + result.tree = nullptr; + return result; + } + rep = tree; + tree = stack[depth]; + } + + // Remove the edge or cascaded up parent node. + tree->set_end(tree->end() - 1); + tree->length -= length; + + // Adjust lengths up the tree. + while (depth > 0) { + tree = stack[--depth]; + tree->length -= length; + } + + // Remove unnecessary top nodes with size = 1. This may iterate all the way + // down to the leaf node in which case we simply return the remaining last + // edge in that node and the extracted flat. + while (tree->size() == 1) { + int height = tree->height(); + rep = tree->Edge(kBack); + Delete(tree); + if (height == 0) { + // We consumed the leaf: return the sole data edge as the new tree. + result.tree = rep; + return result; + } + tree = rep->btree(); + } + + // Done: return the (new) top level node and extracted flat. + result.tree = tree; + return result; +} + } // namespace cord_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index bb38f0c3..0b44509e 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -240,11 +240,41 @@ class CordRepBtree : public CordRep { // length of the flat node and involved tree nodes have been increased by // `span.length()`. The caller is responsible for immediately assigning values // to all uninitialized data reference by the returned span. - // Requires `this->refcount.IsMutable()`: this function forces the - // caller to do this fast path check on the top level node, as this is the - // most commonly shared node of a cord tree. + // Requires `this->refcount.IsOne()`: this function forces the caller to do + // this fast path check on the top level node, as this is the most commonly + // shared node of a cord tree. Span GetAppendBuffer(size_t size); + // Extracts the right-most data edge from this tree iff: + // - the tree and all internal edges to the right-most node are not shared. + // - the right-most node is a FLAT node and not shared. + // - the right-most node has at least the desired extra capacity. + // + // Returns {tree, nullptr} if any of the above conditions are not met. + // This method effectively removes data from the tree. The intent of this + // method is to allow applications appending small string data to use + // pre-existing capacity, and add the modified rep back to the tree. + // + // Simplified such code would look similar to this: + // void MyTreeBuilder::Append(string_view data) { + // ExtractResult result = CordRepBtree::ExtractAppendBuffer(tree_, 1); + // if (CordRep* rep = result.extracted) { + // size_t available = rep->Capacity() - rep->length; + // size_t n = std::min(data.size(), n); + // memcpy(rep->Data(), data.data(), n); + // rep->length += n; + // data.remove_prefix(n); + // if (!result.tree->IsBtree()) { + // tree_ = CordRepBtree::Create(result.tree); + // } + // tree_ = CordRepBtree::Append(tree_, rep); + // } + // ... + // // Remaining edge in `result.tree`. + // } + static ExtractResult ExtractAppendBuffer(CordRepBtree* tree, + size_t extra_capacity = 1); + // Returns the `height` of the tree. The height of a tree is limited to // kMaxHeight. `height` is implemented as an `int` as in some places we // use negative (-1) values for 'data edges'. @@ -849,7 +879,7 @@ inline CordRepBtree* CordRepBtree::Create(CordRep* rep) { } inline Span CordRepBtree::GetAppendBuffer(size_t size) { - assert(refcount.IsMutable()); + assert(refcount.IsOne()); CordRepBtree* tree = this; const int height = this->height(); CordRepBtree* n1 = tree; @@ -858,21 +888,21 @@ inline Span CordRepBtree::GetAppendBuffer(size_t size) { switch (height) { case 3: tree = tree->Edge(kBack)->btree(); - if (!tree->refcount.IsMutable()) return {}; + if (!tree->refcount.IsOne()) return {}; n2 = tree; ABSL_FALLTHROUGH_INTENDED; case 2: tree = tree->Edge(kBack)->btree(); - if (!tree->refcount.IsMutable()) return {}; + if (!tree->refcount.IsOne()) return {}; n1 = tree; ABSL_FALLTHROUGH_INTENDED; case 1: tree = tree->Edge(kBack)->btree(); - if (!tree->refcount.IsMutable()) return {}; + if (!tree->refcount.IsOne()) return {}; ABSL_FALLTHROUGH_INTENDED; case 0: CordRep* edge = tree->Edge(kBack); - if (!edge->refcount.IsMutable()) return {}; + if (!edge->refcount.IsOne()) return {}; if (edge->tag < FLAT) return {}; size_t avail = edge->flat()->Capacity() - edge->length; if (avail == 0) return {}; diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc index be9473d4..1d0c68b2 100644 --- a/absl/strings/internal/cord_rep_btree_test.cc +++ b/absl/strings/internal/cord_rep_btree_test.cc @@ -128,6 +128,16 @@ MATCHER_P2(IsSubstring, start, length, return true; } +MATCHER_P2(EqExtractResult, tree, rep, "Equals ExtractResult") { + if (arg.tree != tree || arg.extracted != rep) { + *result_listener << "Expected {" << static_cast(tree) << ", " + << static_cast(rep) << "}, got {" << arg.tree + << ", " << arg.extracted << "}"; + return false; + } + return true; +} + // DataConsumer is a simple helper class used by tests to 'consume' string // fragments from the provided input in forward or backward direction. class DataConsumer { @@ -1483,6 +1493,125 @@ TEST_P(CordRepBtreeTest, Rebuild) { } } +// Convenience helper for CordRepBtree::ExtractAppendBuffer +CordRepBtree::ExtractResult ExtractLast(CordRepBtree* input, size_t cap = 1) { + return CordRepBtree::ExtractAppendBuffer(input, cap); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferLeafSingleFlat) { + CordRep* flat = MakeFlat("Abc"); + CordRepBtree* leaf = CordRepBtree::Create(flat); + EXPECT_THAT(ExtractLast(leaf), EqExtractResult(nullptr, flat)); + CordRep::Unref(flat); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferNodeSingleFlat) { + CordRep* flat = MakeFlat("Abc"); + CordRepBtree* leaf = CordRepBtree::Create(flat); + CordRepBtree* node = CordRepBtree::New(leaf); + EXPECT_THAT(ExtractLast(node), EqExtractResult(nullptr, flat)); + CordRep::Unref(flat); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferLeafTwoFlats) { + std::vector flats = CreateFlatsFromString("abcdef", 3); + CordRepBtree* leaf = CreateTree(flats); + EXPECT_THAT(ExtractLast(leaf), EqExtractResult(flats[0], flats[1])); + CordRep::Unref(flats[0]); + CordRep::Unref(flats[1]); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferNodeTwoFlats) { + std::vector flats = CreateFlatsFromString("abcdef", 3); + CordRepBtree* leaf = CreateTree(flats); + CordRepBtree* node = CordRepBtree::New(leaf); + EXPECT_THAT(ExtractLast(node), EqExtractResult(flats[0], flats[1])); + CordRep::Unref(flats[0]); + CordRep::Unref(flats[1]); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferNodeTwoFlatsInTwoLeafs) { + std::vector flats = CreateFlatsFromString("abcdef", 3); + CordRepBtree* leaf1 = CordRepBtree::Create(flats[0]); + CordRepBtree* leaf2 = CordRepBtree::Create(flats[1]); + CordRepBtree* node = CordRepBtree::New(leaf1, leaf2); + EXPECT_THAT(ExtractLast(node), EqExtractResult(flats[0], flats[1])); + CordRep::Unref(flats[0]); + CordRep::Unref(flats[1]); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferLeafThreeFlats) { + std::vector flats = CreateFlatsFromString("abcdefghi", 3); + CordRepBtree* leaf = CreateTree(flats); + EXPECT_THAT(ExtractLast(leaf), EqExtractResult(leaf, flats[2])); + CordRep::Unref(flats[2]); + CordRep::Unref(leaf); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferNodeThreeFlatsRightNoFolding) { + CordRep* flat = MakeFlat("Abc"); + std::vector flats = CreateFlatsFromString("defghi", 3); + CordRepBtree* leaf1 = CordRepBtree::Create(flat); + CordRepBtree* leaf2 = CreateTree(flats); + CordRepBtree* node = CordRepBtree::New(leaf1, leaf2); + EXPECT_THAT(ExtractLast(node), EqExtractResult(node, flats[1])); + EXPECT_THAT(node->Edges(), ElementsAre(leaf1, leaf2)); + EXPECT_THAT(leaf1->Edges(), ElementsAre(flat)); + EXPECT_THAT(leaf2->Edges(), ElementsAre(flats[0])); + CordRep::Unref(node); + CordRep::Unref(flats[1]); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferNodeThreeFlatsRightLeafFolding) { + CordRep* flat = MakeFlat("Abc"); + std::vector flats = CreateFlatsFromString("defghi", 3); + CordRepBtree* leaf1 = CreateTree(flats); + CordRepBtree* leaf2 = CordRepBtree::Create(flat); + CordRepBtree* node = CordRepBtree::New(leaf1, leaf2); + EXPECT_THAT(ExtractLast(node), EqExtractResult(leaf1, flat)); + EXPECT_THAT(leaf1->Edges(), ElementsAreArray(flats)); + CordRep::Unref(leaf1); + CordRep::Unref(flat); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferNoCapacity) { + std::vector flats = CreateFlatsFromString("abcdef", 3); + CordRepBtree* leaf = CreateTree(flats); + size_t avail = flats[1]->flat()->Capacity() - flats[1]->length; + EXPECT_THAT(ExtractLast(leaf, avail + 1), EqExtractResult(leaf, nullptr)); + EXPECT_THAT(ExtractLast(leaf, avail), EqExtractResult(flats[0], flats[1])); + CordRep::Unref(flats[0]); + CordRep::Unref(flats[1]); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferNotFlat) { + std::vector flats = CreateFlatsFromString("abcdef", 3); + auto substr = MakeSubstring(1, 2, flats[1]); + CordRepBtree* leaf = CreateTree({flats[0], substr}); + EXPECT_THAT(ExtractLast(leaf), EqExtractResult(leaf, nullptr)); + CordRep::Unref(leaf); +} + +TEST(CordRepBtreeTest, ExtractAppendBufferShared) { + std::vector flats = CreateFlatsFromString("abcdef", 3); + CordRepBtree* leaf = CreateTree(flats); + + CordRep::Ref(flats[1]); + EXPECT_THAT(ExtractLast(leaf), EqExtractResult(leaf, nullptr)); + CordRep::Unref(flats[1]); + + CordRep::Ref(leaf); + EXPECT_THAT(ExtractLast(leaf), EqExtractResult(leaf, nullptr)); + CordRep::Unref(leaf); + + CordRepBtree* node = CordRepBtree::New(leaf); + CordRep::Ref(node); + EXPECT_THAT(ExtractLast(node), EqExtractResult(node, nullptr)); + CordRep::Unref(node); + + CordRep::Unref(node); +} + } // namespace } // namespace cord_internal ABSL_NAMESPACE_END diff --git a/absl/strings/internal/cord_rep_concat.cc b/absl/strings/internal/cord_rep_concat.cc new file mode 100644 index 00000000..3457b55c --- /dev/null +++ b/absl/strings/internal/cord_rep_concat.cc @@ -0,0 +1,63 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "absl/base/config.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +CordRepConcat::ExtractResult CordRepConcat::ExtractAppendBuffer( + CordRepConcat* tree, size_t extra_capacity) { + absl::InlinedVector stack; + CordRepConcat* concat = tree; + CordRep* rep = concat->right; + + // Dive down the tree, making sure no edges are shared + while (concat->refcount.IsOne() && rep->IsConcat()) { + stack.push_back(concat); + concat = rep->concat(); + rep = concat->right; + } + // Validate we ended on a non shared flat. + if (concat->refcount.IsOne() && rep->IsFlat() && + rep->refcount.IsOne()) { + // Verify it has at least the requested extra capacity + CordRepFlat* flat = rep->flat(); + size_t remaining = flat->Capacity() - flat->length; + if (extra_capacity > remaining) return {tree, nullptr}; + + // Check if we have a parent to adjust, or if we must return the left node. + rep = concat->left; + if (!stack.empty()) { + stack.back()->right = rep; + for (CordRepConcat* parent : stack) { + parent->length -= flat->length; + } + rep = tree; + } + delete concat; + return {rep, flat}; + } + return {tree, nullptr}; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_concat_test.cc b/absl/strings/internal/cord_rep_concat_test.cc new file mode 100644 index 00000000..32bab975 --- /dev/null +++ b/absl/strings/internal/cord_rep_concat_test.cc @@ -0,0 +1,143 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_test_util.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::MakeSubstring; +using ::testing::Eq; + +MATCHER_P2(EqExtractResult, tree, rep, "Equals ExtractResult") { + if (arg.tree != tree || arg.extracted != rep) { + *result_listener << "Expected {" << static_cast(tree) << ", " + << static_cast(rep) << "}, got {" << arg.tree + << ", " << arg.extracted << "}"; + return false; + } + return true; +} + +CordRepConcat* MakeConcat(CordRep* left, CordRep* right, int depth) { + CordRepConcat* concat = new CordRepConcat; + concat->tag = CONCAT; + concat->set_depth(depth); + concat->length = left->length + right->length; + concat->left = left; + concat->right = right; + return concat; +} + +CordRepConcat::ExtractResult ExtractLast(CordRepConcat* concat, + size_t extra_capacity = 1) { + return CordRepConcat::ExtractAppendBuffer(concat, extra_capacity); +} + +TEST(CordRepConcatTest, ExtractAppendBufferTwoFlats) { + CordRepFlat* flat1 = MakeFlat("abc"); + CordRepFlat* flat2 = MakeFlat("defg"); + CordRepConcat* concat = MakeConcat(flat1, flat2, 0); + EXPECT_THAT(ExtractLast(concat), EqExtractResult(flat1, flat2)); + CordRep::Unref(flat1); + CordRep::Unref(flat2); +} + +TEST(CordRepConcatTest, ExtractAppendBufferThreeFlatsOne) { + CordRepFlat* flat1 = MakeFlat("abc"); + CordRepFlat* flat2 = MakeFlat("defg"); + CordRepFlat* flat3 = MakeFlat("hijkl"); + CordRepConcat* lconcat = MakeConcat(flat1, flat2, 0); + CordRepConcat* concat = MakeConcat(lconcat, flat3, 1); + EXPECT_THAT(ExtractLast(concat), EqExtractResult(lconcat, flat3)); + ASSERT_THAT(lconcat->length, Eq(7)); + CordRep::Unref(lconcat); + CordRep::Unref(flat3); +} + +TEST(CordRepConcatTest, ExtractAppendBufferThreeFlatsTwo) { + CordRepFlat* flat1 = MakeFlat("hijkl"); + CordRepFlat* flat2 = MakeFlat("abc"); + CordRepFlat* flat3 = MakeFlat("defg"); + CordRepConcat* rconcat = MakeConcat(flat2, flat3, 0); + CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); + EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, flat3)); + ASSERT_THAT(concat->length, Eq(8)); + CordRep::Unref(concat); + CordRep::Unref(flat3); +} + +TEST(CordRepConcatTest, ExtractAppendBufferShared) { + CordRepFlat* flat1 = MakeFlat("hijkl"); + CordRepFlat* flat2 = MakeFlat("abc"); + CordRepFlat* flat3 = MakeFlat("defg"); + CordRepConcat* rconcat = MakeConcat(flat2, flat3, 0); + CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); + + CordRep::Ref(concat); + EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); + CordRep::Unref(concat); + + CordRep::Ref(rconcat); + EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); + CordRep::Unref(rconcat); + + CordRep::Ref(flat3); + EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); + CordRep::Unref(flat3); + + CordRep::Unref(concat); +} + +TEST(CordRepConcatTest, ExtractAppendBufferNotFlat) { + CordRepFlat* flat1 = MakeFlat("hijkl"); + CordRepFlat* flat2 = MakeFlat("abc"); + CordRepFlat* flat3 = MakeFlat("defg"); + auto substr = MakeSubstring(1, 2, flat3); + CordRepConcat* rconcat = MakeConcat(flat2, substr, 0); + CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); + EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); + CordRep::Unref(concat); +} + +TEST(CordRepConcatTest, ExtractAppendBufferNoCapacity) { + CordRepFlat* flat1 = MakeFlat("hijkl"); + CordRepFlat* flat2 = MakeFlat("abc"); + CordRepFlat* flat3 = MakeFlat("defg"); + size_t avail = flat3->Capacity() - flat3->length; + CordRepConcat* rconcat = MakeConcat(flat2, flat3, 0); + CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); + + // Should fail if 1 byte over, success if exactly matching + EXPECT_THAT(ExtractLast(concat, avail + 1), EqExtractResult(concat, nullptr)); + EXPECT_THAT(ExtractLast(concat, avail), EqExtractResult(concat, flat3)); + + CordRep::Unref(concat); + CordRep::Unref(flat3); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_crc.h b/absl/strings/internal/cord_rep_crc.h index 3ead94a1..5294b0d1 100644 --- a/absl/strings/internal/cord_rep_crc.h +++ b/absl/strings/internal/cord_rep_crc.h @@ -76,6 +76,15 @@ inline CordRep* SkipCrcNode(CordRep* rep) { } } +inline const CordRep* SkipCrcNode(const CordRep* rep) { + assert(rep != nullptr); + if (ABSL_PREDICT_FALSE(rep->IsCrc())) { + return rep->crc()->child; + } else { + return rep; + } +} + inline CordRepCrc* CordRep::crc() { assert(IsCrc()); return static_cast(this); diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index 07c77eb3..db1f63fa 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -277,7 +277,7 @@ CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) { // Get current number of entries, and check for max capacity. size_t entries = rep->entries(); - if (!rep->refcount.IsMutable()) { + if (!rep->refcount.IsOne()) { return Copy(rep, rep->head(), rep->tail(), extra); } else if (entries + extra > rep->capacity()) { const size_t min_grow = rep->capacity() + rep->capacity() / 2; @@ -292,10 +292,10 @@ CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) { } Span CordRepRing::GetAppendBuffer(size_t size) { - assert(refcount.IsMutable()); + assert(refcount.IsOne()); index_type back = retreat(tail_); CordRep* child = entry_child(back); - if (child->tag >= FLAT && child->refcount.IsMutable()) { + if (child->tag >= FLAT && child->refcount.IsOne()) { size_t capacity = child->flat()->Capacity(); pos_type end_pos = entry_end_pos(back); size_t data_offset = entry_data_offset(back); @@ -312,10 +312,10 @@ Span CordRepRing::GetAppendBuffer(size_t size) { } Span CordRepRing::GetPrependBuffer(size_t size) { - assert(refcount.IsMutable()); + assert(refcount.IsOne()); CordRep* child = entry_child(head_); size_t data_offset = entry_data_offset(head_); - if (data_offset && child->refcount.IsMutable() && child->tag >= FLAT) { + if (data_offset && child->refcount.IsOne() && child->tag >= FLAT) { size_t n = (std::min)(data_offset, size); this->length += n; begin_pos_ -= n; @@ -504,7 +504,7 @@ CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) { CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data, size_t extra) { - if (rep->refcount.IsMutable()) { + if (rep->refcount.IsOne()) { Span avail = rep->GetAppendBuffer(data.length()); if (!avail.empty()) { memcpy(avail.data(), data.data(), avail.length()); @@ -538,7 +538,7 @@ CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data, CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data, size_t extra) { - if (rep->refcount.IsMutable()) { + if (rep->refcount.IsOne()) { Span avail = rep->GetPrependBuffer(data.length()); if (!avail.empty()) { const char* tail = data.data() + data.length() - avail.length(); @@ -678,7 +678,7 @@ CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset, Position tail = rep->FindTail(head.index, offset + len); const size_t new_entries = rep->entries(head.index, tail.index); - if (rep->refcount.IsMutable() && extra <= (rep->capacity() - new_entries)) { + if (rep->refcount.IsOne() && extra <= (rep->capacity() - new_entries)) { // We adopt a privately owned rep and no extra entries needed. if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); @@ -715,7 +715,7 @@ CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len, } Position head = rep->Find(len); - if (rep->refcount.IsMutable()) { + if (rep->refcount.IsOne()) { if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); rep->head_ = head.index; } else { @@ -745,7 +745,7 @@ CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len, } Position tail = rep->FindTail(rep->length - len); - if (rep->refcount.IsMutable()) { + if (rep->refcount.IsOne()) { // We adopt a privately owned rep, scrub. if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); rep->tail_ = tail.index; diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 5c18bbc5..4d52a802 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -20,6 +20,7 @@ #include "absl/debugging/stacktrace.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_crc.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_handle.h" #include "absl/strings/internal/cordz_statistics.h" @@ -81,6 +82,14 @@ class CordRepAnalyzer { size_t refcount = rep->refcount.Get(); RepRef repref{rep, (refcount > 1) ? refcount - 1 : 1}; + // Process the top level CRC node, if present. + if (repref.rep->tag == CRC) { + statistics_.node_count++; + statistics_.node_counts.crc++; + memory_usage_.Add(sizeof(CordRepCrc), repref.refcount); + repref = repref.Child(repref.rep->crc()->child); + } + // Process all top level linear nodes (substrings and flats). repref = CountLinearReps(repref, memory_usage_); diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc index 7430d281..5277c3c1 100644 --- a/absl/strings/internal/cordz_info_statistics_test.cc +++ b/absl/strings/internal/cordz_info_statistics_test.cc @@ -22,6 +22,7 @@ #include "absl/strings/cord.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_crc.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" #include "absl/strings/internal/cordz_info.h" @@ -535,6 +536,27 @@ TEST(CordzInfoStatisticsTest, BtreeNodeShared) { EXPECT_THAT(SampleCord(tree), EqStatistics(expected)); } +TEST(CordzInfoStatisticsTest, Crc) { + RefHelper ref; + auto* left = Flat(1000); + auto* right = Flat(1000); + auto* concat = Concat(left, right); + auto* crc = ref.NeedsUnref(CordRepCrc::New(concat, 12345)); + + CordzStatistics expected; + expected.size = concat->length; + expected.estimated_memory_usage = + SizeOf(crc) + SizeOf(concat) + SizeOf(left) + SizeOf(right); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 4; + expected.node_counts.flat = 2; + expected.node_counts.flat_1k = 2; + expected.node_counts.concat = 1; + expected.node_counts.crc = 1; + + EXPECT_THAT(SampleCord(crc), EqStatistics(expected)); +} + TEST(CordzInfoStatisticsTest, ThreadSafety) { Notification stop; static constexpr int kNumThreads = 8; diff --git a/absl/strings/internal/cordz_statistics.h b/absl/strings/internal/cordz_statistics.h index da4c7dbb..57071905 100644 --- a/absl/strings/internal/cordz_statistics.h +++ b/absl/strings/internal/cordz_statistics.h @@ -41,6 +41,7 @@ struct CordzStatistics { size_t concat = 0; // #concat reps size_t ring = 0; // #ring buffer reps size_t btree = 0; // #btree reps + size_t crc = 0; // #crc reps }; // The size of the cord in bytes. This matches the result of Cord::size(). diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index 1f764486..7a41c180 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -60,6 +60,7 @@ class CordzUpdateTracker { kPrependString, kRemovePrefix, kRemoveSuffix, + kSetExpectedChecksum, kSubCord, // kNumMethods defines the number of entries: must be the last entry. diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc index 2348a175..8eda529e 100644 --- a/absl/strings/internal/cordz_update_tracker_test.cc +++ b/absl/strings/internal/cordz_update_tracker_test.cc @@ -58,6 +58,7 @@ Methods AllMethods() { Method::kPrependString, Method::kRemovePrefix, Method::kRemoveSuffix, + Method::kSetExpectedChecksum, Method::kSubCord}; } -- cgit v1.2.3 From 732b5580f089101ce4b8cdff55bb6461c59a6720 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 9 Nov 2021 14:14:51 -0800 Subject: Export of internal Abseil changes -- 4324716dc5384f03dcd7e36e8cca0e944e4dac74 by Evan Brown : Clarify comments about API differences from std::{set,map} in btree headers. We note that the most important API differences are mentioned in the next paragraph and that other API differences are minor. An example of another minor API difference is the note about std::launder in the comment for `extract`. Motivation: readers shouldn't feel like they need to read the entire header file to understand why b-tree containers are not "drop-in replacements" for STL containers. PiperOrigin-RevId: 408703780 -- 7e8da4f14afd25d11713eee6b743ba31605332bf by Derek Mauro : Remove the test for absl::base_internal::NominalCPUFrequency() from OSS code This is an internal-only function that should never by called by OSS code. By its nature fails on unsupported platforms. Google code has tests for this function on supported internal platforms. Fixes #1053 PiperOrigin-RevId: 408692861 -- 37bad2e003b17e3f82d6fd5d90ae183553c018b0 by Abseil Team : To avoid triggering -Wmissing-variable-declarations warnings, ABSL_FLAG now declares the Flag before defining it. PiperOrigin-RevId: 408673990 GitOrigin-RevId: 4324716dc5384f03dcd7e36e8cca0e944e4dac74 Change-Id: I8c8ea73c4a4e38729c5bfdfa3fefb5d593e0536c --- absl/base/internal/sysinfo_test.cc | 23 ----------------------- absl/container/btree_map.h | 7 +++++-- absl/container/btree_set.h | 4 +++- absl/flags/flag.h | 1 + 4 files changed, 9 insertions(+), 26 deletions(-) diff --git a/absl/base/internal/sysinfo_test.cc b/absl/base/internal/sysinfo_test.cc index 5f9e45f6..f305b6c5 100644 --- a/absl/base/internal/sysinfo_test.cc +++ b/absl/base/internal/sysinfo_test.cc @@ -37,29 +37,6 @@ TEST(SysinfoTest, NumCPUs) { << "NumCPUs() should not have the default value of 0"; } -// Ensure that NominalCPUFrequency returns a reasonable value, or 1.00 on -// platforms where the CPU frequency is not available through sysfs. -// -// POWER is particularly problematic here; some Linux kernels expose the CPU -// frequency, while others do not. Since we can't predict a priori what a given -// machine is going to do, just disable this test on POWER on Linux. -#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__))) -TEST(SysinfoTest, NominalCPUFrequency) { - // Linux only exposes the CPU frequency on certain architectures, and - // Emscripten doesn't expose it at all. -#if defined(__linux__) && \ - (defined(__aarch64__) || defined(__hppa__) || defined(__mips__) || \ - defined(__riscv) || defined(__s390x__)) || \ - defined(__EMSCRIPTEN__) - EXPECT_EQ(NominalCPUFrequency(), 1.0) - << "CPU frequency detection was fixed! Please update unittest."; -#else - EXPECT_GE(NominalCPUFrequency(), 1000.0) - << "NominalCPUFrequency() did not return a reasonable value"; -#endif -} -#endif - TEST(SysinfoTest, GetTID) { EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test. #ifdef __native_client__ diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index f0a8d4a6..96ebcb77 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -35,14 +35,17 @@ // // However, these types should not be considered drop-in replacements for // `std::map` and `std::multimap` as there are some API differences, which are -// noted in this header file. +// noted in this header file. The most consequential differences with respect to +// migrating to b-tree from the STL types are listed in the next paragraph. +// Other API differences are minor. // // Importantly, insertions and deletions may invalidate outstanding iterators, // pointers, and references to elements. Such invalidations are typically only // an issue if insertion and deletion operations are interleaved with the use of // more than one iterator, pointer, or reference simultaneously. For this // reason, `insert()` and `erase()` return a valid iterator at the current -// position. +// position. Another important difference is that key-types must be +// copy-constructible. #ifndef ABSL_CONTAINER_BTREE_MAP_H_ #define ABSL_CONTAINER_BTREE_MAP_H_ diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 89739006..f587ca22 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -35,7 +35,9 @@ // // However, these types should not be considered drop-in replacements for // `std::set` and `std::multiset` as there are some API differences, which are -// noted in this header file. +// noted in this header file. The most consequential differences with respect to +// migrating to b-tree from the STL types are listed in the next paragraph. +// Other API differences are minor. // // Importantly, insertions and deletions may invalidate outstanding iterators, // pointers, and references to elements. Such invalidations are typically only diff --git a/absl/flags/flag.h b/absl/flags/flag.h index a724ccc9..50106082 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -163,6 +163,7 @@ ABSL_NAMESPACE_END // Note: do not construct objects of type `absl::Flag` directly. Only use the // `ABSL_FLAG()` macro for such construction. #define ABSL_FLAG(Type, name, default_value, help) \ + extern ::absl::Flag FLAGS_##name; \ ABSL_FLAG_IMPL(Type, name, default_value, help) // ABSL_FLAG().OnUpdate() -- cgit v1.2.3 From f2dbd918d8d08529800eb72f23bd2829f92104a4 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 10 Nov 2021 13:26:07 -0800 Subject: Export of internal Abseil changes -- 317c7bd0caa12fa0a4dc65dc9c8e645211f85872 by Abseil Team : Elide the Emscripten JS-based stack trace and symbolization functions when building in Standalone Mode. Users will need to set `-DSTANDALONE_WASM=1` in some toolchain(s). PiperOrigin-RevId: 408961649 -- 92ba3ab1ef3edee4b7fb9e151f2061661e7beb0a by Derek Mauro : Suppress MSVC warning "C4127: conditional expression is constant" Fixes #1004 PiperOrigin-RevId: 408920356 GitOrigin-RevId: 317c7bd0caa12fa0a4dc65dc9c8e645211f85872 Change-Id: Ieca0a9e263874ba5bd93110dc437c56bc59ad5a7 --- absl/debugging/internal/stacktrace_config.h | 3 ++- absl/debugging/symbolize.cc | 7 ++++++- absl/strings/numbers.h | 30 +++++++++++++++++------------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h index ff21b719..3929b1b7 100644 --- a/absl/debugging/internal/stacktrace_config.h +++ b/absl/debugging/internal/stacktrace_config.h @@ -37,7 +37,8 @@ "absl/debugging/internal/stacktrace_generic-inl.inc" #endif // defined(ABSL_HAVE_THREAD_LOCAL) -#elif defined(__EMSCRIPTEN__) +// Emscripten stacktraces rely on JS. Do not use them in standalone mode. +#elif defined(__EMSCRIPTEN__) && !defined(STANDALONE_WASM) #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_emscripten-inl.inc" diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index f1abdfda..638d3954 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc @@ -23,6 +23,11 @@ #endif #endif +// Emscripten symbolization relies on JS. Do not use them in standalone mode. +#if defined(__EMSCRIPTEN__) && !defined(STANDALONE_WASM) +#define ABSL_INTERNAL_HAVE_SYMBOLIZE_WASM +#endif + #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) #include "absl/debugging/symbolize_elf.inc" #elif defined(ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32) @@ -31,7 +36,7 @@ #include "absl/debugging/symbolize_win32.inc" #elif defined(__APPLE__) #include "absl/debugging/symbolize_darwin.inc" -#elif defined(__EMSCRIPTEN__) +#elif defined(ABSL_INTERNAL_HAVE_SYMBOLIZE_WASM) #include "absl/debugging/symbolize_emscripten.inc" #else #include "absl/debugging/symbolize_unimplemented.inc" diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 4ae07c2d..e977fc67 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -181,16 +181,19 @@ char* FastIntToBuffer(int_type i, char* buffer) { // TODO(jorg): This signed-ness check is used because it works correctly // with enums, and it also serves to check that int_type is not a pointer. // If one day something like std::is_signed works, switch to it. - if (static_cast(1) - 2 < 0) { // Signed - if (sizeof(i) > 32 / 8) { // 33-bit to 64-bit + // These conditions are constexpr bools to suppress MSVC warning C4127. + constexpr bool kIsSigned = static_cast(1) - 2 < 0; + constexpr bool kUse64Bit = sizeof(i) > 32 / 8; + if (kIsSigned) { + if (kUse64Bit) { return FastIntToBuffer(static_cast(i), buffer); - } else { // 32-bit or less + } else { return FastIntToBuffer(static_cast(i), buffer); } - } else { // Unsigned - if (sizeof(i) > 32 / 8) { // 33-bit to 64-bit + } else { + if (kUse64Bit) { return FastIntToBuffer(static_cast(i), buffer); - } else { // 32-bit or less + } else { return FastIntToBuffer(static_cast(i), buffer); } } @@ -209,22 +212,25 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, // TODO(jorg): This signed-ness check is used because it works correctly // with enums, and it also serves to check that int_type is not a pointer. // If one day something like std::is_signed works, switch to it. - if (static_cast(1) - 2 < 0) { // Signed - if (sizeof(*out) == 64 / 8) { // 64-bit + // These conditions are constexpr bools to suppress MSVC warning C4127. + constexpr bool kIsSigned = static_cast(1) - 2 < 0; + constexpr bool kUse64Bit = sizeof(*out) == 64 / 8; + if (kIsSigned) { + if (kUse64Bit) { int64_t val; parsed = numbers_internal::safe_strto64_base(s, &val, base); *out = static_cast(val); - } else { // 32-bit + } else { int32_t val; parsed = numbers_internal::safe_strto32_base(s, &val, base); *out = static_cast(val); } - } else { // Unsigned - if (sizeof(*out) == 64 / 8) { // 64-bit + } else { + if (kUse64Bit) { uint64_t val; parsed = numbers_internal::safe_strtou64_base(s, &val, base); *out = static_cast(val); - } else { // 32-bit + } else { uint32_t val; parsed = numbers_internal::safe_strtou32_base(s, &val, base); *out = static_cast(val); -- cgit v1.2.3 From d587c966ed86dc3b94362444e2b20fc3c5c4224e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 15 Nov 2021 11:29:23 -0800 Subject: Export of internal Abseil changes -- 355b8f7b0070b005d53d94ee4180e95559ba2c88 by Derek Mauro : Documentation: don't define absl::Status::ok() in terms of itself Fixes #1058 PiperOrigin-RevId: 410035651 -- 31c512c834b3a8979297adc5006c3727a3c6554b by Evan Brown : Cleanup: move set_params/set_slot_policy into btree_set.h and move map_params into btree_map.h. Also change some `sizeof(value_type)`s to `sizeof(slot_type)`s and update some comments/variable names referring to values to refer to slots as appropriate in btree.h. Motivation: preliminary cleanup towards node_btree_*. PiperOrigin-RevId: 409991342 -- 3129ca320d61a82f1c9ee8c02a23d25024eea4ab by Abseil Team : Use simpler implementation for AddressIsReadable. In particular, current solution doesn't work on systems configured with large pid_t space. PiperOrigin-RevId: 409397716 -- f71067f7494b19ce4a2e1df730b934dc931c51b2 by Martijn Vels : Add Span dependency PiperOrigin-RevId: 409198889 GitOrigin-RevId: 355b8f7b0070b005d53d94ee4180e95559ba2c88 Change-Id: I7f4df3ec7739fdfde61d8ba983f07a08f6f1c7d7 --- absl/container/btree_map.h | 60 ++++++++++++ absl/container/btree_set.h | 74 ++++++++++++++ absl/container/internal/btree.h | 122 ++---------------------- absl/debugging/internal/address_is_readable.cc | 127 +++++++++---------------- absl/status/status.h | 5 +- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 1 + 7 files changed, 195 insertions(+), 195 deletions(-) diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index 96ebcb77..4eafe062 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -56,6 +56,14 @@ namespace absl { ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct map_params; + +} // namespace container_internal + // absl::btree_map<> // // An `absl::btree_map` is an ordered associative container of @@ -812,6 +820,58 @@ void erase_if(btree_multimap &map, Pred pred) { } } +namespace container_internal { + +// A parameters structure for holding the type parameters for a btree_map. +// Compare and Alloc should be nothrow copy-constructible. +template +struct map_params : common_params> { + using super_type = typename map_params::common_params; + using mapped_type = Data; + // This type allows us to move keys when it is safe to do so. It is safe + // for maps in which value_type and mutable_value_type are layout compatible. + using slot_policy = typename super_type::slot_policy; + using slot_type = typename super_type::slot_type; + using value_type = typename super_type::value_type; + using init_type = typename super_type::init_type; + + using original_key_compare = typename super_type::original_key_compare; + // Reference: https://en.cppreference.com/w/cpp/container/map/value_compare + class value_compare { + template + friend class btree; + + protected: + explicit value_compare(original_key_compare c) : comp(std::move(c)) {} + + original_key_compare comp; // NOLINT + + public: + auto operator()(const value_type &lhs, const value_type &rhs) const + -> decltype(comp(lhs.first, rhs.first)) { + return comp(lhs.first, rhs.first); + } + }; + using is_map_container = std::true_type; + + template + static auto key(const V &value) -> decltype(value.first) { + return value.first; + } + static const Key &key(const slot_type *s) { return slot_policy::key(s); } + static const Key &key(slot_type *s) { return slot_policy::key(s); } + // For use in node handle. + static auto mutable_key(slot_type *s) + -> decltype(slot_policy::mutable_key(s)) { + return slot_policy::mutable_key(s); + } + static mapped_type &value(value_type *value) { return value->second; } +}; + +} // namespace container_internal + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index f587ca22..8c96e0ed 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -55,6 +55,17 @@ namespace absl { ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct set_slot_policy; + +template +struct set_params; + +} // namespace container_internal + // absl::btree_set<> // // An `absl::btree_set` is an ordered associative container of unique key @@ -724,6 +735,69 @@ void erase_if(btree_multiset &set, Pred pred) { } } +namespace container_internal { + +// This type implements the necessary functions from the +// absl::container_internal::slot_type interface for btree_(multi)set. +template +struct set_slot_policy { + using slot_type = Key; + using value_type = Key; + using mutable_value_type = Key; + + static value_type &element(slot_type *slot) { return *slot; } + static const value_type &element(const slot_type *slot) { return *slot; } + + template + static void construct(Alloc *alloc, slot_type *slot, Args &&...args) { + absl::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { + absl::allocator_traits::construct(*alloc, slot, std::move(*other)); + } + + template + static void destroy(Alloc *alloc, slot_type *slot) { + absl::allocator_traits::destroy(*alloc, slot); + } + + template + static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { + using std::swap; + swap(*a, *b); + } + + template + static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { + *dest = std::move(*src); + } +}; + +// A parameters structure for holding the type parameters for a btree_set. +// Compare and Alloc should be nothrow copy-constructible. +template +struct set_params : common_params> { + using value_type = Key; + using slot_type = typename set_params::common_params::slot_type; + using value_compare = + typename set_params::common_params::original_key_compare; + using is_map_container = std::false_type; + + template + static const V &key(const V &value) { + return value; + } + static const Key &key(const slot_type *slot) { return *slot; } + static const Key &key(slot_type *slot) { return *slot; } +}; + +} // namespace container_internal + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index f636c5fc..bf65a03d 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -270,17 +270,17 @@ struct common_params { enum { kTargetNodeSize = TargetNodeSize, - // Upper bound for the available space for values. This is largest for leaf + // Upper bound for the available space for slots. This is largest for leaf // nodes, which have overhead of at least a pointer + 4 bytes (for storing // 3 field_types and an enum). - kNodeValueSpace = + kNodeSlotSpace = TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4), }; - // This is an integral type large enough to hold as many - // ValueSize-values as will fit a node of TargetNodeSize bytes. + // This is an integral type large enough to hold as many slots as will fit a + // node of TargetNodeSize bytes. using node_count_type = - absl::conditional_t<(kNodeValueSpace / sizeof(value_type) > + absl::conditional_t<(kNodeSlotSpace / sizeof(slot_type) > (std::numeric_limits::max)()), uint16_t, uint8_t>; // NOLINT @@ -314,111 +314,6 @@ struct common_params { } }; -// A parameters structure for holding the type parameters for a btree_map. -// Compare and Alloc should be nothrow copy-constructible. -template -struct map_params : common_params> { - using super_type = typename map_params::common_params; - using mapped_type = Data; - // This type allows us to move keys when it is safe to do so. It is safe - // for maps in which value_type and mutable_value_type are layout compatible. - using slot_policy = typename super_type::slot_policy; - using slot_type = typename super_type::slot_type; - using value_type = typename super_type::value_type; - using init_type = typename super_type::init_type; - - using original_key_compare = typename super_type::original_key_compare; - // Reference: https://en.cppreference.com/w/cpp/container/map/value_compare - class value_compare { - template - friend class btree; - - protected: - explicit value_compare(original_key_compare c) : comp(std::move(c)) {} - - original_key_compare comp; // NOLINT - - public: - auto operator()(const value_type &lhs, const value_type &rhs) const - -> decltype(comp(lhs.first, rhs.first)) { - return comp(lhs.first, rhs.first); - } - }; - using is_map_container = std::true_type; - - template - static auto key(const V &value) -> decltype(value.first) { - return value.first; - } - static const Key &key(const slot_type *s) { return slot_policy::key(s); } - static const Key &key(slot_type *s) { return slot_policy::key(s); } - // For use in node handle. - static auto mutable_key(slot_type *s) - -> decltype(slot_policy::mutable_key(s)) { - return slot_policy::mutable_key(s); - } - static mapped_type &value(value_type *value) { return value->second; } -}; - -// This type implements the necessary functions from the -// absl::container_internal::slot_type interface. -template -struct set_slot_policy { - using slot_type = Key; - using value_type = Key; - using mutable_value_type = Key; - - static value_type &element(slot_type *slot) { return *slot; } - static const value_type &element(const slot_type *slot) { return *slot; } - - template - static void construct(Alloc *alloc, slot_type *slot, Args &&... args) { - absl::allocator_traits::construct(*alloc, slot, - std::forward(args)...); - } - - template - static void construct(Alloc *alloc, slot_type *slot, slot_type *other) { - absl::allocator_traits::construct(*alloc, slot, std::move(*other)); - } - - template - static void destroy(Alloc *alloc, slot_type *slot) { - absl::allocator_traits::destroy(*alloc, slot); - } - - template - static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { - using std::swap; - swap(*a, *b); - } - - template - static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { - *dest = std::move(*src); - } -}; - -// A parameters structure for holding the type parameters for a btree_set. -// Compare and Alloc should be nothrow copy-constructible. -template -struct set_params : common_params> { - using value_type = Key; - using slot_type = typename set_params::common_params::slot_type; - using value_compare = - typename set_params::common_params::original_key_compare; - using is_map_container = std::false_type; - - template - static const V &key(const V &value) { return value; } - static const Key &key(const slot_type *slot) { return *slot; } - static const Key &key(slot_type *slot) { return *slot; } -}; - // An adapter class that converts a lower-bound compare into an upper-bound // compare. Note: there is no need to make a version of this adapter specialized // for key-compare-to functors because the upper-bound (the first value greater @@ -562,9 +457,9 @@ class btree_node { /*children*/ 0) .AllocSize(); } - // A lower bound for the overhead of fields other than values in a leaf node. + // A lower bound for the overhead of fields other than slots in a leaf node. constexpr static size_type MinimumOverhead() { - return SizeWithNSlots(1) - sizeof(value_type); + return SizeWithNSlots(1) - sizeof(slot_type); } // Compute how many values we can fit onto a leaf node taking into account @@ -1397,6 +1292,7 @@ class btree { } // The total number of bytes used by the btree. + // TODO(b/169338300): update to support node_btree_*. size_type bytes_used() const { node_stats stats = internal_stats(root()); if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { @@ -1929,7 +1825,7 @@ constexpr bool btree

::static_assert_validation() { "key comparison function must return absl::{weak,strong}_ordering or " "bool."); - // Test the assumption made in setting kNodeValueSpace. + // Test the assumption made in setting kNodeSlotSpace. static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4, "node space assumption incorrect"); diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 329c285f..26df3289 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -33,12 +33,12 @@ ABSL_NAMESPACE_END #else #include +#include #include +#include #include -#include #include -#include #include "absl/base/internal/errno_saver.h" #include "absl/base/internal/raw_logging.h" @@ -47,89 +47,56 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { -// Pack a pid and two file descriptors into a 64-bit word, -// using 16, 24, and 24 bits for each respectively. -static uint64_t Pack(uint64_t pid, uint64_t read_fd, uint64_t write_fd) { - ABSL_RAW_CHECK((read_fd >> 24) == 0 && (write_fd >> 24) == 0, - "fd out of range"); - return (pid << 48) | ((read_fd & 0xffffff) << 24) | (write_fd & 0xffffff); -} - -// Unpack x into a pid and two file descriptors, where x was created with -// Pack(). -static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) { - *pid = x >> 48; - *read_fd = (x >> 24) & 0xffffff; - *write_fd = x & 0xffffff; -} - -// Return whether the byte at *addr is readable, without faulting. -// Save and restores errno. Returns true on systems where -// unimplemented. -// This is a namespace-scoped variable for correct zero-initialization. -static std::atomic pid_and_fds; // initially 0, an invalid pid. - bool AddressIsReadable(const void *addr) { + int fd = 0; absl::base_internal::ErrnoSaver errno_saver; - // We test whether a byte is readable by using write(). Normally, this would - // be done via a cached file descriptor to /dev/null, but linux fails to - // check whether the byte is readable when the destination is /dev/null, so - // we use a cached pipe. We store the pid of the process that created the - // pipe to handle the case where a process forks, and the child closes all - // the file descriptors and then calls this routine. This is not perfect: - // the child could use the routine, then close all file descriptors and then - // use this routine again. But the likely use of this routine is when - // crashing, to test the validity of pages when dumping the stack. Beware - // that we may leak file descriptors, but we're unlikely to leak many. - int bytes_written; - int current_pid = getpid() & 0xffff; // we use only the low order 16 bits - do { // until we do not get EBADF trying to use file descriptors - int pid; - int read_fd; - int write_fd; - uint64_t local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); - Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); - while (current_pid != pid) { - int p[2]; - // new pipe - if (pipe(p) != 0) { - ABSL_RAW_LOG(FATAL, "Failed to create pipe, errno=%d", errno); - } - fcntl(p[0], F_SETFD, FD_CLOEXEC); - fcntl(p[1], F_SETFD, FD_CLOEXEC); - uint64_t new_pid_and_fds = Pack(current_pid, p[0], p[1]); - if (pid_and_fds.compare_exchange_strong( - local_pid_and_fds, new_pid_and_fds, std::memory_order_release, - std::memory_order_relaxed)) { - local_pid_and_fds = new_pid_and_fds; // fds exposed to other threads - } else { // fds not exposed to other threads; we can close them. - close(p[0]); - close(p[1]); - local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); - } - Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); - } - errno = 0; - // Use syscall(SYS_write, ...) instead of write() to prevent ASAN - // and other checkers from complaining about accesses to arbitrary - // memory. + for (int j = 0; j < 2; j++) { + // Here we probe with some syscall which + // - accepts a one-byte region of user memory as input + // - tests for EFAULT before other validation + // - has no problematic side-effects + // + // connect(2) works for this. It copies the address into kernel + // memory before any validation beyond requiring an open fd. + // But a one byte address is never valid (sa_family is two bytes), + // so the call cannot succeed and change any state. + // + // This strategy depends on Linux implementation details, + // so we rely on the test to alert us if it stops working. + // + // Some discarded past approaches: + // - msync() doesn't reject PROT_NONE regions + // - write() on /dev/null doesn't return EFAULT + // - write() on a pipe requires creating it and draining the writes + // + // Use syscall(SYS_connect, ...) instead of connect() to prevent ASAN + // and other checkers from complaining about accesses to arbitrary memory. do { - bytes_written = syscall(SYS_write, write_fd, addr, 1); - } while (bytes_written == -1 && errno == EINTR); - if (bytes_written == 1) { // remove the byte from the pipe - char c; - while (read(read_fd, &c, 1) == -1 && errno == EINTR) { + ABSL_RAW_CHECK(syscall(SYS_connect, fd, addr, 1) == -1, + "should never succeed"); + } while (errno == EINTR); + if (errno == EFAULT) return false; + if (errno == EBADF) { + if (j != 0) { + // Unclear what happened. + ABSL_RAW_LOG(ERROR, "unexpected EBADF on fd %d", fd); + return false; } + // fd 0 must have been closed. Try opening it again. + // Note: we shouldn't leak too many file descriptors here, since we expect + // to get fd==0 reopened. + fd = open("/dev/null", O_RDONLY); + if (fd == -1) { + ABSL_RAW_LOG(ERROR, "can't open /dev/null"); + return false; + } + } else { + // probably EINVAL or ENOTSOCK; we got past EFAULT validation. + return true; } - if (errno == EBADF) { // Descriptors invalid. - // If pid_and_fds contains the problematic file descriptors we just used, - // this call will forget them, and the loop will try again. - pid_and_fds.compare_exchange_strong(local_pid_and_fds, 0, - std::memory_order_release, - std::memory_order_relaxed); - } - } while (errno == EBADF); - return bytes_written == 1; + } + ABSL_RAW_CHECK(false, "unreachable"); + return false; } } // namespace debugging_internal diff --git a/absl/status/status.h b/absl/status/status.h index 39071e5f..5a09faeb 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -469,8 +469,9 @@ class Status final { // Status::ok() // - // Returns `true` if `this->ok()`. Prefer checking for an OK status using this - // member function. + // Returns `true` if `this->code()` == `absl::StatusCode::kOk`, + // indicating the absence of an error. + // Prefer checking for an OK status using this member function. ABSL_MUST_USE_RESULT bool ok() const; // Status::code() diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 0c47ca54..4e37151e 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -437,6 +437,7 @@ cc_library( "//absl/functional:function_ref", "//absl/meta:type_traits", "//absl/types:optional", + "//absl/types:span", ], ) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index aab97efd..0eef91df 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -851,6 +851,7 @@ absl_cc_library( absl::inlined_vector absl::optional absl::raw_logging_internal + absl::span absl::strings absl::type_traits PUBLIC -- cgit v1.2.3 From 9b924cedb6127dfb8f5d504a419dead2899fa777 Mon Sep 17 00:00:00 2001 From: Federico Scrinzi Date: Tue, 16 Nov 2021 19:32:20 +0100 Subject: Fix googletest URL in CMakeLists.txt (#1062) The git branch was renamed from "master" to "main". --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d59c2ade..a1400b74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,7 +146,7 @@ if(BUILD_TESTING) message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL") endif() if(ABSL_USE_GOOGLETEST_HEAD) - set(absl_gtest_download_url "https://github.com/google/googletest/archive/master.zip") + set(absl_gtest_download_url "https://github.com/google/googletest/archive/main.zip") elseif(ABSL_GOOGLETEST_DOWNLOAD_URL) set(absl_gtest_download_url ${ABSL_GOOGLETEST_DOWNLOAD_URL}) endif() -- cgit v1.2.3 From 299f59cadda78dfaf71b2a35049b63911e8eff47 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 17 Nov 2021 10:01:24 -0800 Subject: Export of internal Abseil changes -- 2130ba98c8359b08d97fb16d84dfd05687005dcf by Abseil Team : Tweaking the documentation of c_all_of to state the effect more directly. PiperOrigin-RevId: 410557900 -- 4732289bf4b56123fed113e36be4710b55c6a6c7 by Greg Falcon : Improve the quality of absl::Hash>. This previously dispatched to std::hash>, which suffers from trivial collisions on many platforms. (They often hash the internal words but no size info, so that, e.g., {1, 1} and {1, 1, 0} collide.) Also extended the unit test to exercise this. PiperOrigin-RevId: 410329943 -- 1c5f3934230a7669f74c96b305251786a265e235 by Greg Falcon : Add broader testing of absl hash contracts in the hash unit test. In particular, test that the hash erasure mechanism works. PiperOrigin-RevId: 410312738 -- 5e1923f527ed3d02f6752a5b38d5e1c17a4a146f by Derek Mauro : Internal change PiperOrigin-RevId: 410290663 -- 8c74bc962b3b98a5908017c345efc592393048ea by Martijn Vels : Add Cord::CreateFlat() function PiperOrigin-RevId: 410260776 -- bd0de4e94c85620d3b8dd60fae367b730fc4cb34 by Evan Brown : Rename node_hash_policy to node_slot_policy. Motivation: we can potentially reuse this code for node_btree_*. PiperOrigin-RevId: 410082271 GitOrigin-RevId: 2130ba98c8359b08d97fb16d84dfd05687005dcf Change-Id: Ie052084cf992dee250d8b2f388d39c4de0dcff40 --- CMake/AbseilDll.cmake | 4 +- absl/algorithm/container.h | 2 +- absl/container/BUILD.bazel | 14 +- absl/container/CMakeLists.txt | 14 +- absl/container/internal/node_hash_policy.h | 92 ----------- absl/container/internal/node_hash_policy_test.cc | 69 -------- absl/container/internal/node_slot_policy.h | 92 +++++++++++ absl/container/internal/node_slot_policy_test.cc | 69 ++++++++ absl/container/node_hash_map.h | 4 +- absl/container/node_hash_set.h | 4 +- absl/hash/hash_test.cc | 195 ++++++++++++++++++----- absl/hash/internal/hash.h | 19 ++- absl/strings/cord.h | 5 + absl/strings/internal/cord_rep_flat.h | 11 ++ 14 files changed, 368 insertions(+), 226 deletions(-) delete mode 100644 absl/container/internal/node_hash_policy.h delete mode 100644 absl/container/internal/node_hash_policy_test.cc create mode 100644 absl/container/internal/node_slot_policy.h create mode 100644 absl/container/internal/node_slot_policy_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 7c827251..07a4576e 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -81,7 +81,7 @@ set(ABSL_INTERNAL_DLL_FILES "container/internal/have_sse.h" "container/internal/inlined_vector.h" "container/internal/layout.h" - "container/internal/node_hash_policy.h" + "container/internal/node_slot_policy.h" "container/internal/raw_hash_map.h" "container/internal/raw_hash_set.cc" "container/internal/raw_hash_set.h" @@ -455,7 +455,7 @@ set(ABSL_INTERNAL_DLL_TARGETS "hashtable_debug" "hashtable_debug_hooks" "have_sse" - "node_hash_policy" + "node_slot_policy" "raw_hash_map" "container_common" "raw_hash_set" diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index c38a4a63..26b19529 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -166,7 +166,7 @@ container_algorithm_internal::ContainerDifferenceType c_distance( // c_all_of() // // Container-based version of the `std::all_of()` function to -// test a condition on all elements within a container. +// test if all elements within a container satisfy a condition. template bool c_all_of(const C& c, Pred&& pred) { return std::all_of(container_algorithm_internal::c_begin(c), diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index ffaee19c..ea2c98b8 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -307,7 +307,7 @@ cc_library( deps = [ ":container_memory", ":hash_function_defaults", - ":node_hash_policy", + ":node_slot_policy", ":raw_hash_map", "//absl/algorithm:container", "//absl/memory", @@ -339,7 +339,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_function_defaults", - ":node_hash_policy", + ":node_slot_policy", ":raw_hash_set", "//absl/algorithm:container", "//absl/memory", @@ -535,21 +535,21 @@ cc_test( ) cc_library( - name = "node_hash_policy", - hdrs = ["internal/node_hash_policy.h"], + name = "node_slot_policy", + hdrs = ["internal/node_slot_policy.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = ["//absl/base:config"], ) cc_test( - name = "node_hash_policy_test", - srcs = ["internal/node_hash_policy_test.cc"], + name = "node_slot_policy_test", + srcs = ["internal/node_slot_policy_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_traits", - ":node_hash_policy", + ":node_slot_policy", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 78584d2c..bb718cf8 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -348,7 +348,7 @@ absl_cc_library( DEPS absl::container_memory absl::hash_function_defaults - absl::node_hash_policy + absl::node_slot_policy absl::raw_hash_map absl::algorithm_container absl::memory @@ -382,7 +382,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::hash_function_defaults - absl::node_hash_policy + absl::node_slot_policy absl::raw_hash_set absl::algorithm_container absl::memory @@ -599,9 +599,9 @@ absl_cc_library( absl_cc_library( NAME - node_hash_policy + node_slot_policy HDRS - "internal/node_hash_policy.h" + "internal/node_slot_policy.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -611,14 +611,14 @@ absl_cc_library( absl_cc_test( NAME - node_hash_policy_test + node_slot_policy_test SRCS - "internal/node_hash_policy_test.cc" + "internal/node_slot_policy_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS absl::hash_policy_traits - absl::node_hash_policy + absl::node_slot_policy GTest::gmock_main ) diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h deleted file mode 100644 index 4617162f..00000000 --- a/absl/container/internal/node_hash_policy.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Adapts a policy for nodes. -// -// The node policy should model: -// -// struct Policy { -// // Returns a new node allocated and constructed using the allocator, using -// // the specified arguments. -// template -// value_type* new_element(Alloc* alloc, Args&&... args) const; -// -// // Destroys and deallocates node using the allocator. -// template -// void delete_element(Alloc* alloc, value_type* node) const; -// }; -// -// It may also optionally define `value()` and `apply()`. For documentation on -// these, see hash_policy_traits.h. - -#ifndef ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ -#define ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ - -#include -#include -#include -#include -#include - -#include "absl/base/config.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace container_internal { - -template -struct node_hash_policy { - static_assert(std::is_lvalue_reference::value, ""); - - using slot_type = typename std::remove_cv< - typename std::remove_reference::type>::type*; - - template - static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { - *slot = Policy::new_element(alloc, std::forward(args)...); - } - - template - static void destroy(Alloc* alloc, slot_type* slot) { - Policy::delete_element(alloc, *slot); - } - - template - static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) { - *new_slot = *old_slot; - } - - static size_t space_used(const slot_type* slot) { - if (slot == nullptr) return Policy::element_space_used(nullptr); - return Policy::element_space_used(*slot); - } - - static Reference element(slot_type* slot) { return **slot; } - - template - static auto value(T* elem) -> decltype(P::value(elem)) { - return P::value(elem); - } - - template - static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward(ts)...)) { - return P::apply(std::forward(ts)...); - } -}; - -} // namespace container_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ diff --git a/absl/container/internal/node_hash_policy_test.cc b/absl/container/internal/node_hash_policy_test.cc deleted file mode 100644 index 84aabba9..00000000 --- a/absl/container/internal/node_hash_policy_test.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/container/internal/node_hash_policy.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/container/internal/hash_policy_traits.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace container_internal { -namespace { - -using ::testing::Pointee; - -struct Policy : node_hash_policy { - using key_type = int; - using init_type = int; - - template - static int* new_element(Alloc* alloc, int value) { - return new int(value); - } - - template - static void delete_element(Alloc* alloc, int* elem) { - delete elem; - } -}; - -using NodePolicy = hash_policy_traits; - -struct NodeTest : ::testing::Test { - std::allocator alloc; - int n = 53; - int* a = &n; -}; - -TEST_F(NodeTest, ConstructDestroy) { - NodePolicy::construct(&alloc, &a, 42); - EXPECT_THAT(a, Pointee(42)); - NodePolicy::destroy(&alloc, &a); -} - -TEST_F(NodeTest, transfer) { - int s = 42; - int* b = &s; - NodePolicy::transfer(&alloc, &a, &b); - EXPECT_EQ(&s, a); -} - -} // namespace -} // namespace container_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/container/internal/node_slot_policy.h b/absl/container/internal/node_slot_policy.h new file mode 100644 index 00000000..baba5743 --- /dev/null +++ b/absl/container/internal/node_slot_policy.h @@ -0,0 +1,92 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Adapts a policy for nodes. +// +// The node policy should model: +// +// struct Policy { +// // Returns a new node allocated and constructed using the allocator, using +// // the specified arguments. +// template +// value_type* new_element(Alloc* alloc, Args&&... args) const; +// +// // Destroys and deallocates node using the allocator. +// template +// void delete_element(Alloc* alloc, value_type* node) const; +// }; +// +// It may also optionally define `value()` and `apply()`. For documentation on +// these, see hash_policy_traits.h. + +#ifndef ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ +#define ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +template +struct node_slot_policy { + static_assert(std::is_lvalue_reference::value, ""); + + using slot_type = typename std::remove_cv< + typename std::remove_reference::type>::type*; + + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + *slot = Policy::new_element(alloc, std::forward(args)...); + } + + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::delete_element(alloc, *slot); + } + + template + static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) { + *new_slot = *old_slot; + } + + static size_t space_used(const slot_type* slot) { + if (slot == nullptr) return Policy::element_space_used(nullptr); + return Policy::element_space_used(*slot); + } + + static Reference element(slot_type* slot) { return **slot; } + + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + template + static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward(ts)...)) { + return P::apply(std::forward(ts)...); + } +}; + +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ diff --git a/absl/container/internal/node_slot_policy_test.cc b/absl/container/internal/node_slot_policy_test.cc new file mode 100644 index 00000000..51b7467b --- /dev/null +++ b/absl/container/internal/node_slot_policy_test.cc @@ -0,0 +1,69 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/node_slot_policy.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_policy_traits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace { + +using ::testing::Pointee; + +struct Policy : node_slot_policy { + using key_type = int; + using init_type = int; + + template + static int* new_element(Alloc* alloc, int value) { + return new int(value); + } + + template + static void delete_element(Alloc* alloc, int* elem) { + delete elem; + } +}; + +using NodePolicy = hash_policy_traits; + +struct NodeTest : ::testing::Test { + std::allocator alloc; + int n = 53; + int* a = &n; +}; + +TEST_F(NodeTest, ConstructDestroy) { + NodePolicy::construct(&alloc, &a, 42); + EXPECT_THAT(a, Pointee(42)); + NodePolicy::destroy(&alloc, &a); +} + +TEST_F(NodeTest, transfer) { + int s = 42; + int* b = &s; + NodePolicy::transfer(&alloc, &a, &b); + EXPECT_EQ(&s, a); +} + +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index 7a39f628..302dafa2 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -43,7 +43,7 @@ #include "absl/algorithm/container.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export -#include "absl/container/internal/node_hash_policy.h" +#include "absl/container/internal/node_slot_policy.h" #include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export #include "absl/memory/memory.h" @@ -535,7 +535,7 @@ namespace container_internal { template class NodeHashMapPolicy - : public absl::container_internal::node_hash_policy< + : public absl::container_internal::node_slot_policy< std::pair&, NodeHashMapPolicy> { using value_type = std::pair; diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 93b15f46..4247288d 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -39,7 +39,7 @@ #include "absl/algorithm/container.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export -#include "absl/container/internal/node_hash_policy.h" +#include "absl/container/internal/node_slot_policy.h" #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export #include "absl/memory/memory.h" @@ -442,7 +442,7 @@ namespace container_internal { template struct NodeHashSetPolicy - : absl::container_internal::node_hash_policy> { + : absl::container_internal::node_slot_policy> { using key_type = T; using init_type = T; using constant_iterators = std::true_type; diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index b3ddebdd..dbfacb2d 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -14,12 +14,14 @@ #include "absl/hash/hash.h" +#include #include #include #include #include #include #include +#include #include #include #include @@ -46,6 +48,56 @@ namespace { +// Utility wrapper of T for the purposes of testing the `AbslHash` type erasure +// mechanism. `TypeErasedValue` can be constructed with a `T`, and can +// be compared and hashed. However, all hashing goes through the hashing +// type-erasure framework. +template +class TypeErasedValue { + public: + TypeErasedValue() = default; + TypeErasedValue(const TypeErasedValue&) = default; + TypeErasedValue(TypeErasedValue&&) = default; + explicit TypeErasedValue(const T& n) : n_(n) {} + + template + friend H AbslHashValue(H hash_state, const TypeErasedValue& v) { + v.HashValue(absl::HashState::Create(&hash_state)); + return hash_state; + } + + void HashValue(absl::HashState state) const { + absl::HashState::combine(std::move(state), n_); + } + + bool operator==(const TypeErasedValue& rhs) const { return n_ == rhs.n_; } + bool operator!=(const TypeErasedValue& rhs) const { return !(*this == rhs); } + + private: + T n_; +}; + +// A TypeErasedValue refinement, for containers. It exposes the wrapped +// `value_type` and is constructible from an initializer list. +template +class TypeErasedContainer : public TypeErasedValue { + public: + using value_type = typename T::value_type; + TypeErasedContainer() = default; + TypeErasedContainer(const TypeErasedContainer&) = default; + TypeErasedContainer(TypeErasedContainer&&) = default; + explicit TypeErasedContainer(const T& n) : TypeErasedValue(n) {} + TypeErasedContainer(std::initializer_list init_list) + : TypeErasedContainer(T(init_list.begin(), init_list.end())) {} + // one-argument constructor of value type T, to appease older toolchains that + // get confused by one-element initializer lists in some contexts + explicit TypeErasedContainer(const value_type& v) + : TypeErasedContainer(T(&v, &v + 1)) {} +}; + +template +using TypeErasedVector = TypeErasedContainer>; + using absl::Hash; using absl::hash_internal::SpyHashState; @@ -389,23 +441,60 @@ TYPED_TEST_SUITE_P(HashValueSequenceTest); TYPED_TEST_P(HashValueSequenceTest, BasicUsage) { EXPECT_TRUE((is_hashable::value)); - using ValueType = typename TypeParam::value_type; - auto a = static_cast(0); - auto b = static_cast(23); - auto c = static_cast(42); - - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( - std::make_tuple(TypeParam(), TypeParam{}, TypeParam{a, b, c}, - TypeParam{a, b}, TypeParam{b, c}))); + using IntType = typename TypeParam::value_type; + auto a = static_cast(0); + auto b = static_cast(23); + auto c = static_cast(42); + + std::vector exemplars = { + TypeParam(), TypeParam(), TypeParam{a, b, c}, + TypeParam{a, c, b}, TypeParam{c, a, b}, TypeParam{a}, + TypeParam{a, a}, TypeParam{a, a, a}, TypeParam{a, a, b}, + TypeParam{a, b, a}, TypeParam{b, a, a}, TypeParam{a, b}, + TypeParam{b, c}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); } REGISTER_TYPED_TEST_CASE_P(HashValueSequenceTest, BasicUsage); using IntSequenceTypes = testing::Types, std::forward_list, std::list, - std::vector, std::vector, std::set, - std::multiset>; + std::vector, std::vector, TypeErasedVector, + TypeErasedVector, std::set, std::multiset>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueSequenceTest, IntSequenceTypes); +template +class HashValueNestedSequenceTest : public testing::Test {}; +TYPED_TEST_SUITE_P(HashValueNestedSequenceTest); + +TYPED_TEST_P(HashValueNestedSequenceTest, BasicUsage) { + using T = TypeParam; + using V = typename T::value_type; + std::vector exemplars = { + // empty case + T{}, + // sets of empty sets + T{V{}}, T{V{}, V{}}, T{V{}, V{}, V{}}, + // multisets of different values + T{V{1}}, T{V{1, 1}, V{1, 1}}, T{V{1, 1, 1}, V{1, 1, 1}, V{1, 1, 1}}, + // various orderings of same nested sets + T{V{}, V{1, 2}}, T{V{}, V{2, 1}}, T{V{1, 2}, V{}}, T{V{2, 1}, V{}}, + // various orderings of various nested sets, case 2 + T{V{1, 2}, V{3, 4}}, T{V{1, 2}, V{4, 3}}, T{V{1, 3}, V{2, 4}}, + T{V{1, 3}, V{4, 2}}, T{V{1, 4}, V{2, 3}}, T{V{1, 4}, V{3, 2}}, + T{V{2, 3}, V{1, 4}}, T{V{2, 3}, V{4, 1}}, T{V{2, 4}, V{1, 3}}, + T{V{2, 4}, V{3, 1}}, T{V{3, 4}, V{1, 2}}, T{V{3, 4}, V{2, 1}}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); +} + +REGISTER_TYPED_TEST_CASE_P(HashValueNestedSequenceTest, BasicUsage); +using NestedIntSequenceTypes = + testing::Types>, + std::vector>, + TypeErasedVector>, + TypeErasedVector>>; +INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueNestedSequenceTest, + NestedIntSequenceTypes); + // Private type that only supports AbslHashValue to make sure our chosen hash // implementation is recursive within absl::Hash. // It uses std::abs() on the value to provide different bitwise representations @@ -564,23 +653,59 @@ TEST(HashValueTest, Variant) { #endif } -TEST(HashValueTest, Maps) { - EXPECT_TRUE((is_hashable>::value)); +template +class HashValueAssociativeMapTest : public testing::Test {}; +TYPED_TEST_SUITE_P(HashValueAssociativeMapTest); + +TYPED_TEST_P(HashValueAssociativeMapTest, BasicUsage) { + using M = TypeParam; + using V = typename M::value_type; + std::vector exemplars{M{}, + M{V{0, "foo"}}, + M{V{1, "foo"}}, + M{V{0, "bar"}}, + M{V{1, "bar"}}, + M{V{0, "foo"}, V{42, "bar"}}, + M{V{42, "bar"}, V{0, "foo"}}, + M{V{1, "foo"}, V{42, "bar"}}, + M{V{1, "foo"}, V{43, "bar"}}, + M{V{1, "foo"}, V{43, "baz"}}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); +} - using M = std::map; - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( - M{}, M{{0, "foo"}}, M{{1, "foo"}}, M{{0, "bar"}}, M{{1, "bar"}}, - M{{0, "foo"}, {42, "bar"}}, M{{1, "foo"}, {42, "bar"}}, - M{{1, "foo"}, {43, "bar"}}, M{{1, "foo"}, {43, "baz"}}))); +REGISTER_TYPED_TEST_CASE_P(HashValueAssociativeMapTest, BasicUsage); +using AssociativeMapTypes = testing::Types>; +INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueAssociativeMapTest, + AssociativeMapTypes); - using MM = std::multimap; - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( - MM{}, MM{{0, "foo"}}, MM{{1, "foo"}}, MM{{0, "bar"}}, MM{{1, "bar"}}, - MM{{0, "foo"}, {0, "bar"}}, MM{{0, "bar"}, {0, "foo"}}, - MM{{0, "foo"}, {42, "bar"}}, MM{{1, "foo"}, {42, "bar"}}, - MM{{1, "foo"}, {1, "foo"}, {43, "bar"}}, MM{{1, "foo"}, {43, "baz"}}))); +template +class HashValueAssociativeMultimapTest : public testing::Test {}; +TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest); + +TYPED_TEST_P(HashValueAssociativeMultimapTest, BasicUsage) { + using MM = TypeParam; + using V = typename MM::value_type; + std::vector exemplars{MM{}, + MM{V{0, "foo"}}, + MM{V{1, "foo"}}, + MM{V{0, "bar"}}, + MM{V{1, "bar"}}, + MM{V{0, "foo"}, V{0, "bar"}}, + MM{V{0, "bar"}, V{0, "foo"}}, + MM{V{0, "foo"}, V{42, "bar"}}, + MM{V{1, "foo"}, V{42, "bar"}}, + MM{V{1, "foo"}, V{1, "foo"}, V{43, "bar"}}, + MM{V{1, "foo"}, V{43, "bar"}, V{1, "foo"}}, + MM{V{1, "foo"}, V{43, "baz"}}}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); } +REGISTER_TYPED_TEST_CASE_P(HashValueAssociativeMultimapTest, BasicUsage); +using AssociativeMultimapTypes = + testing::Types>; +INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueAssociativeMultimapTest, + AssociativeMultimapTypes); + TEST(HashValueTest, ReferenceWrapper) { EXPECT_TRUE(is_hashable>::value); @@ -928,28 +1053,14 @@ TEST(HashTest, SmallValueOn64ByteBoundary) { Hash()(IntAndString{0, std::string(63, '0')}); } -struct TypeErased { - size_t n; - - template - friend H AbslHashValue(H hash_state, const TypeErased& v) { - v.HashValue(absl::HashState::Create(&hash_state)); - return hash_state; - } - - void HashValue(absl::HashState state) const { - absl::HashState::combine(std::move(state), n); - } -}; - TEST(HashTest, TypeErased) { - EXPECT_TRUE((is_hashable::value)); - EXPECT_TRUE((is_hashable>::value)); + EXPECT_TRUE((is_hashable>::value)); + EXPECT_TRUE((is_hashable, int>>::value)); - EXPECT_EQ(SpyHash(TypeErased{7}), SpyHash(size_t{7})); - EXPECT_NE(SpyHash(TypeErased{7}), SpyHash(size_t{13})); + EXPECT_EQ(SpyHash(TypeErasedValue(7)), SpyHash(size_t{7})); + EXPECT_NE(SpyHash(TypeErasedValue(7)), SpyHash(size_t{13})); - EXPECT_EQ(SpyHash(std::make_pair(TypeErased{7}, 17)), + EXPECT_EQ(SpyHash(std::make_pair(TypeErasedValue(7), 17)), SpyHash(std::make_pair(size_t{7}, 17))); } diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index b1e33caf..97b68bad 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -502,10 +502,9 @@ AbslHashValue(H hash_state, const std::vector& vector) { vector.size()); } +// AbslHashValue special cases for hashing std::vector #if defined(ABSL_IS_BIG_ENDIAN) && \ (defined(__GLIBCXX__) || defined(__GLIBCPP__)) -// AbslHashValue for hashing std::vector -// // std::hash in libstdc++ does not work correctly with vector on Big // Endian platforms therefore we need to implement a custom AbslHashValue for // it. More details on the bug: @@ -521,6 +520,22 @@ AbslHashValue(H hash_state, const std::vector& vector) { } return H::combine(combiner.finalize(std::move(hash_state)), vector.size()); } +#else +// When not working around the libstdc++ bug above, we still have to contend +// with the fact that std::hash> is often poor quality, hashing +// directly on the internal words and on no other state. On these platforms, +// vector{1, 1} and vector{1, 1, 0} hash to the same value. +// +// Mixing in the size (as we do in our other vector<> implementations) on top +// of the library-provided hash implementation avoids this QOI issue. +template +typename std::enable_if::value && std::is_same::value, + H>::type +AbslHashValue(H hash_state, const std::vector& vector) { + return H::combine(std::move(hash_state), + std::hash>{}(vector), + vector.size()); +} #endif // ----------------------------------------------------------------------------- diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 3f0633bd..662e889a 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -855,6 +855,11 @@ class Cord { // Returns true if the Cord is being profiled by cordz. bool is_profiled() const { return data_.is_tree() && data_.is_profiled(); } + // Returns the available inlined capacity, or 0 if is_tree() == true. + size_t inline_capacity() const { + return data_.is_tree() ? 0 : kMaxInline - data_.inline_size(); + } + // Returns the profiled CordzInfo, or nullptr if not sampled. absl::cord_internal::CordzInfo* cordz_info() const { return data_.cordz_info(); diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index 62cf5840..8c254273 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -117,6 +117,17 @@ struct CordRepFlat : public CordRep { #endif } + // Create a CordRepFlat containing `data`, with an optional additional + // extra capacity of up to `extra` bytes. Requires that `data.size()` + // is less than kMaxFlatLength. + static CordRepFlat* Create(absl::string_view data, size_t extra = 0) { + assert(data.size() <= kMaxFlatLength); + CordRepFlat* flat = New(data.size() + (std::min)(extra, kMaxFlatLength)); + memcpy(flat->Data(), data.data(), data.size()); + flat->length = data.size(); + return flat; + } + // Returns a pointer to the data inside this flat rep. char* Data() { return reinterpret_cast(storage); } const char* Data() const { return reinterpret_cast(storage); } -- cgit v1.2.3 From 8a4e6b8cc9baa26ebdc87fd813dc808d77e0640c Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 18 Nov 2021 19:19:30 -0800 Subject: Export of internal Abseil changes -- e400e8c3dc7190b83dec57be9d858ee44e146a42 by Martijn Vels : Optimize Cord destructor through CordRepBtree::Destroy This change inlines tree destruction 2 levels at a time, special casing leaf and leaf + 1 levels. Benchmarks shows this provides up to 8% speed up PiperOrigin-RevId: 410953099 GitOrigin-RevId: e400e8c3dc7190b83dec57be9d858ee44e146a42 Change-Id: Ic1c123fa0fa2b13cb141ee7a6be155d9e57a0466 --- absl/strings/internal/cord_rep_btree.cc | 71 ++++++++++++++++++++++----------- absl/strings/internal/cord_rep_btree.h | 22 ++-------- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 7cefcc52..83cdd5f4 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -190,24 +190,29 @@ inline void FastUnref(R* r, Fn&& fn) { } } -// Deletes a leaf node data edge. Requires `rep` to be an EXTERNAL or FLAT -// node, or a SUBSTRING of an EXTERNAL or FLAT node. -void DeleteLeafEdge(CordRep* rep) { - for (;;) { + +void DeleteSubstring(CordRepSubstring* substring) { + CordRep* rep = substring->child; + if (!rep->refcount.Decrement()) { if (rep->tag >= FLAT) { CordRepFlat::Delete(rep->flat()); - return; - } - if (rep->tag == EXTERNAL) { + } else { + assert(rep->tag == EXTERNAL); CordRepExternal::Delete(rep->external()); - return; } - assert(rep->tag == SUBSTRING); - CordRepSubstring* substring = rep->substring(); - rep = substring->child; - assert(rep->tag == EXTERNAL || rep->tag >= FLAT); - delete substring; - if (rep->refcount.Decrement()) return; + } + delete substring; +} + +// Deletes a leaf node data edge. Requires `IsDataEdge(rep)`. +void DeleteLeafEdge(CordRep* rep) { + assert(CordRepBtree::IsDataEdge(rep)); + if (rep->tag >= FLAT) { + CordRepFlat::Delete(rep->flat()); + } else if (rep->tag == EXTERNAL) { + CordRepExternal::Delete(rep->external()); + } else { + DeleteSubstring(rep->substring()); } } @@ -372,19 +377,39 @@ void CordRepBtree::Dump(const CordRep* rep, std::ostream& stream) { Dump(rep, absl::string_view(), false, stream); } -void CordRepBtree::DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end) { - for (CordRep* edge : tree->Edges(begin, end)) { - FastUnref(edge, DeleteLeafEdge); +template +static void DestroyTree(CordRepBtree* tree) { + for (CordRep* node : tree->Edges()) { + if (!node->refcount.Decrement()) { + for (CordRep* edge : node->btree()->Edges()) { + if (!edge->refcount.Decrement()) { + if (size == 1) { + DeleteLeafEdge(edge); + } else { + CordRepBtree::Destroy(edge->btree()); + } + } + } + CordRepBtree::Delete(node->btree()); + } } - Delete(tree); + CordRepBtree::Delete(tree); } -void CordRepBtree::DestroyNonLeaf(CordRepBtree* tree, size_t begin, - size_t end) { - for (CordRep* edge : tree->Edges(begin, end)) { - FastUnref(edge->btree(), Destroy); +void CordRepBtree::Destroy(CordRepBtree* tree) { + switch (tree->height()) { + case 0: + for (CordRep* edge : tree->Edges()) { + if (!edge->refcount.Decrement()) { + DeleteLeafEdge(edge); + } + } + return CordRepBtree::Delete(tree); + case 1: + return DestroyTree<1>(tree); + default: + return DestroyTree<2>(tree); } - Delete(tree); } bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 0b44509e..df5c994c 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -163,6 +163,9 @@ class CordRepBtree : public CordRep { // typically after a ref_count.Decrement() on the last reference count. static void Destroy(CordRepBtree* tree); + // Destruction + static void Delete(CordRepBtree* tree) { delete tree; } + // Use CordRep::Unref() as we overload for absl::Span. using CordRep::Unref; @@ -440,12 +443,6 @@ class CordRepBtree : public CordRep { // Requires `offset` < length. Position IndexBeyond(size_t offset) const; - // Destruction - static void DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end); - static void DestroyNonLeaf(CordRepBtree* tree, size_t begin, size_t end); - static void DestroyTree(CordRepBtree* tree, size_t begin, size_t end); - static void Delete(CordRepBtree* tree) { delete tree; } - // Creates a new leaf node containing as much data as possible from `data`. // The data is added either forwards or reversed depending on `edge_type`. // Callers must check the length of the returned node to determine if all data @@ -689,19 +686,6 @@ inline CordRepBtree* CordRepBtree::New(CordRepBtree* front, return tree; } -inline void CordRepBtree::DestroyTree(CordRepBtree* tree, size_t begin, - size_t end) { - if (tree->height() == 0) { - DestroyLeaf(tree, begin, end); - } else { - DestroyNonLeaf(tree, begin, end); - } -} - -inline void CordRepBtree::Destroy(CordRepBtree* tree) { - DestroyTree(tree, tree->begin(), tree->end()); -} - inline void CordRepBtree::Unref(absl::Span edges) { for (CordRep* edge : edges) { if (ABSL_PREDICT_FALSE(!edge->refcount.Decrement())) { -- cgit v1.2.3 From 72c765111173a61de6e4184bb837f855b7869952 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 19 Nov 2021 10:46:01 -0800 Subject: Export of internal Abseil changes -- 28ba85cbeb766341969fda677bbd8df8615b349f by Martijn Vels : Internally allow for CordRepFlat sizes up to 256KB The default flat size will remain at 4KB, but this allows for future specializations such as file io and compression where larger buffers can be used providing 'zero copy' streaming optimizations. PiperOrigin-RevId: 411095538 -- 0c1cae5ff376a0955a5b67868723836665737801 by Abseil Team : cord_rep_btree: Reduce nesting in `DestroyTree` using guard clauses PiperOrigin-RevId: 411093741 GitOrigin-RevId: 28ba85cbeb766341969fda677bbd8df8615b349f Change-Id: Ieb84ed409f1e8a8bca74edebbf2d097a9ec828a7 --- absl/strings/cord_test.cc | 72 +++++++++++++++++++++++++++++++++ absl/strings/internal/cord_internal.h | 15 ++++--- absl/strings/internal/cord_rep_btree.cc | 18 ++++----- absl/strings/internal/cord_rep_flat.h | 54 ++++++++++++++++++------- 4 files changed, 129 insertions(+), 30 deletions(-) diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 188fbc2e..8a311799 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -43,6 +43,10 @@ #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" +// convenience local constants +static constexpr auto FLAT = absl::cord_internal::FLAT; +static constexpr auto MAX_FLAT_TAG = absl::cord_internal::MAX_FLAT_TAG; + typedef std::mt19937_64 RandomEngine; static std::string RandomLowercaseString(RandomEngine* rng); @@ -260,6 +264,74 @@ class CordTest : public testing::TestWithParam { INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1, 2, 3), CordTest::ToString); + +TEST(CordRepFlat, AllFlatCapacities) { + using absl::cord_internal::CordRep; + using absl::cord_internal::CordRepFlat; + using absl::cord_internal::kFlatOverhead; + + // Explicitly and redundantly assert built-in min/max limits + static_assert(absl::cord_internal::kFlatOverhead < 32, ""); + static_assert(absl::cord_internal::kMinFlatSize == 32, ""); + static_assert(absl::cord_internal::kMaxLargeFlatSize == 256 << 10, ""); + EXPECT_EQ(absl::cord_internal::TagToAllocatedSize(FLAT), 32); + EXPECT_EQ(absl::cord_internal::TagToAllocatedSize(MAX_FLAT_TAG), 256 << 10); + + // Verify all tags to map perfectly back and forth, and + // that sizes are monotonically increasing. + size_t last_size = 0; + for (int tag = FLAT; tag <= MAX_FLAT_TAG; ++tag) { + size_t size = absl::cord_internal::TagToAllocatedSize(tag); + ASSERT_GT(size, last_size); + ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size); + last_size = size; + } + + // All flat size from 32 - 512 are 8 byte granularity + for (size_t size = 32; size <= 512; size += 8) { + ASSERT_EQ(absl::cord_internal::RoundUpForTag(size), size); + uint8_t tag = absl::cord_internal::AllocatedSizeToTag(size); + ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size); + } + + // All flat sizes from 512 - 8192 are 64 byte granularity + for (size_t size = 512; size <= 8192; size += 64) { + ASSERT_EQ(absl::cord_internal::RoundUpForTag(size), size); + uint8_t tag = absl::cord_internal::AllocatedSizeToTag(size); + ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size); + } + + // All flat sizes from 8KB to 256KB are 4KB granularity + for (size_t size = 8192; size <= 256 * 1024; size += 4 * 1024) { + ASSERT_EQ(absl::cord_internal::RoundUpForTag(size), size); + uint8_t tag = absl::cord_internal::AllocatedSizeToTag(size); + ASSERT_EQ(absl::cord_internal::TagToAllocatedSize(tag), size); + } +} + +TEST(CordRepFlat, MaxFlatSize) { + using absl::cord_internal::CordRep; + using absl::cord_internal::CordRepFlat; + using absl::cord_internal::kMaxFlatLength; + CordRepFlat* flat = CordRepFlat::New(kMaxFlatLength); + EXPECT_EQ(flat->Capacity(), kMaxFlatLength); + CordRep::Unref(flat); + + flat = CordRepFlat::New(kMaxFlatLength * 4); + EXPECT_EQ(flat->Capacity(), kMaxFlatLength); + CordRep::Unref(flat); +} + +TEST(CordRepFlat, MaxLargeFlatSize) { + using absl::cord_internal::CordRep; + using absl::cord_internal::CordRepFlat; + using absl::cord_internal::kFlatOverhead; + const size_t size = 256 * 1024 - kFlatOverhead; + CordRepFlat* flat = CordRepFlat::New(CordRepFlat::Large(), size); + EXPECT_GE(flat->Capacity(), size); + CordRep::Unref(flat); +} + TEST_P(CordTest, AllFlatSizes) { using absl::strings_internal::CordTestAccess; diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 672bf178..b16c8fa5 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -185,13 +185,16 @@ enum CordRepKind { EXTERNAL = 5, // We have different tags for different sized flat arrays, - // starting with FLAT, and limited to MAX_FLAT_TAG. The 226 value is based on - // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed - // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well - // as the Tag <---> Size logic so that FLAT stil represents the minimum flat - // allocation size. (32 bytes as of now). + // starting with FLAT, and limited to MAX_FLAT_TAG. The below values map to an + // allocated range of 32 bytes to 256 KB. The current granularity is: + // - 8 byte granularity for flat sizes in [32 - 512] + // - 64 byte granularity for flat sizes in (512 - 8KiB] + // - 4KiB byte granularity for flat sizes in (8KiB, 256 KiB] + // If a new tag is needed in the future, then 'FLAT' and 'MAX_FLAT_TAG' should + // be adjusted as well as the Tag <---> Size mapping logic so that FLAT still + // represents the minimum flat allocation size. (32 bytes as of now). FLAT = 6, - MAX_FLAT_TAG = 226 + MAX_FLAT_TAG = 248 }; // There are various locations where we want to check if some rep is a 'plain' diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 83cdd5f4..f436bc17 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -380,18 +380,16 @@ void CordRepBtree::Dump(const CordRep* rep, std::ostream& stream) { template static void DestroyTree(CordRepBtree* tree) { for (CordRep* node : tree->Edges()) { - if (!node->refcount.Decrement()) { - for (CordRep* edge : node->btree()->Edges()) { - if (!edge->refcount.Decrement()) { - if (size == 1) { - DeleteLeafEdge(edge); - } else { - CordRepBtree::Destroy(edge->btree()); - } - } + if (node->refcount.Decrement()) continue; + for (CordRep* edge : node->btree()->Edges()) { + if (edge->refcount.Decrement()) continue; + if (size == 1) { + DeleteLeafEdge(edge); + } else { + CordRepBtree::Destroy(edge->btree()); } - CordRepBtree::Delete(node->btree()); } + CordRepBtree::Delete(node->btree()); } CordRepBtree::Delete(tree); } diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index 8c254273..a15c9acd 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -42,14 +42,34 @@ static constexpr size_t kMinFlatSize = 32; static constexpr size_t kMaxFlatSize = 4096; static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead; +static constexpr size_t kMaxLargeFlatSize = 256 * 1024; +static constexpr size_t kMaxLargeFlatLength = kMaxLargeFlatSize - kFlatOverhead; +// kTagBase should make the Size <--> Tag computation resilient +// against changes to the value of FLAT when we add a new tag.. +static constexpr uint8_t kTagBase = FLAT - 4; + +// Converts the provided rounded size to the corresponding tag constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) { - return static_cast((size <= 1024) ? size / 8 + 2 - : 130 + size / 32 - 1024 / 32); + return static_cast(size <= 512 ? kTagBase + size / 8 + : size <= 8192 + ? kTagBase + 512 / 8 + size / 64 - 512 / 64 + : kTagBase + 512 / 8 + ((8192 - 512) / 64) + + size / 4096 - 8192 / 4096); } -static_assert(kMinFlatSize / 8 + 2 >= FLAT, ""); -static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, ""); +// Converts the provided tag to the corresponding allocated size +constexpr size_t TagToAllocatedSize(uint8_t tag) { + return (tag <= kTagBase + 512 / 8) ? tag * 8 - kTagBase * 8 + : (tag <= kTagBase + (512 / 8) + ((8192 - 512) / 64)) + ? 512 + tag * 64 - kTagBase * 64 - 512 / 8 * 64 + : 8192 + tag * 4096 - kTagBase * 4096 - + ((512 / 8) + ((8192 - 512) / 64)) * 4096; +} + +static_assert(AllocatedSizeToTagUnchecked(kMinFlatSize) == FLAT, ""); +static_assert(AllocatedSizeToTagUnchecked(kMaxLargeFlatSize) == MAX_FLAT_TAG, + ""); // Helper functions for rounded div, and rounding to exact sizes. constexpr size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; } @@ -58,7 +78,7 @@ constexpr size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; } // Returns the size to the nearest equal or larger value that can be // expressed exactly as a tag value. inline size_t RoundUpForTag(size_t size) { - return RoundUp(size, (size <= 1024) ? 8 : 32); + return RoundUp(size, (size <= 512) ? 8 : (size <= 8192 ? 64 : 4096)); } // Converts the allocated size to a tag, rounding down if the size @@ -71,26 +91,26 @@ inline uint8_t AllocatedSizeToTag(size_t size) { return tag; } -// Converts the provided tag to the corresponding allocated size -constexpr size_t TagToAllocatedSize(uint8_t tag) { - return (tag <= 130) ? ((tag - 2) * 8) : (1024 + (tag - 130) * 32); -} - // Converts the provided tag to the corresponding available data length constexpr size_t TagToLength(uint8_t tag) { return TagToAllocatedSize(tag) - kFlatOverhead; } // Enforce that kMaxFlatSize maps to a well-known exact tag value. -static_assert(TagToAllocatedSize(226) == kMaxFlatSize, "Bad tag logic"); +static_assert(TagToAllocatedSize(MAX_FLAT_TAG) == kMaxLargeFlatSize, + "Bad tag logic"); struct CordRepFlat : public CordRep { + // Tag for explicit 'large flat' allocation + struct Large {}; + // Creates a new flat node. - static CordRepFlat* New(size_t len) { + template + static CordRepFlat* NewImpl(size_t len) { if (len <= kMinFlatLength) { len = kMinFlatLength; - } else if (len > kMaxFlatLength) { - len = kMaxFlatLength; + } else if (len > max_flat_size - kFlatOverhead) { + len = max_flat_size - kFlatOverhead; } // Round size up so it matches a size we can exactly express in a tag. @@ -101,6 +121,12 @@ struct CordRepFlat : public CordRep { return rep; } + static CordRepFlat* New(size_t len) { return NewImpl(len); } + + static CordRepFlat* New(Large, size_t len) { + return NewImpl(len); + } + // Deletes a CordRepFlat instance created previously through a call to New(). // Flat CordReps are allocated and constructed with raw ::operator new and // placement new, and must be destructed and deallocated accordingly. -- cgit v1.2.3 From ec0d76f1d012cc1a4b3b08dfafcfc5237f5ba2c9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 22 Nov 2021 08:22:28 -0800 Subject: Export of internal Abseil changes -- a0847bf19789c97689f1a8b0133a53b8af5e5caa by Samuel Benzaquen : Add support for absl::(u)int128 to random distributions and generators. PiperOrigin-RevId: 411565479 GitOrigin-RevId: a0847bf19789c97689f1a8b0133a53b8af5e5caa Change-Id: Ide434bdd93fcab8e90f791796498de14833b667c --- absl/random/CMakeLists.txt | 1 + absl/random/distributions.h | 6 +- absl/random/distributions_test.cc | 1 + absl/random/generators_test.cc | 6 ++ absl/random/internal/BUILD.bazel | 8 ++- absl/random/internal/fast_uniform_bits.h | 3 +- absl/random/internal/traits.h | 58 +++++++++++++-- absl/random/internal/uniform_helper.h | 10 +-- absl/random/internal/wide_multiply.h | 81 +++++++++------------ absl/random/internal/wide_multiply_test.cc | 110 +++++++++++++++++++++-------- absl/random/log_uniform_int_distribution.h | 19 +++-- absl/random/poisson_distribution.h | 11 +-- absl/random/uniform_int_distribution.h | 4 +- absl/random/zipf_distribution.h | 5 +- 14 files changed, 214 insertions(+), 109 deletions(-) diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 9d1c67fb..7fbd460d 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -1210,5 +1210,6 @@ absl_cc_test( absl::random_internal_wide_multiply absl::bits absl::int128 + GTest::gmock GTest::gtest_main ) diff --git a/absl/random/distributions.h b/absl/random/distributions.h index 31c79694..37fc3aa7 100644 --- a/absl/random/distributions.h +++ b/absl/random/distributions.h @@ -373,7 +373,7 @@ RealType Gaussian(URBG&& urbg, // NOLINT(runtime/references) template IntType LogUniform(URBG&& urbg, // NOLINT(runtime/references) IntType lo, IntType hi, IntType base = 2) { - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Template-argument 'IntType' must be an integral type, in " "absl::LogUniform(...)"); @@ -403,7 +403,7 @@ IntType LogUniform(URBG&& urbg, // NOLINT(runtime/references) template IntType Poisson(URBG&& urbg, // NOLINT(runtime/references) double mean = 1.0) { - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Template-argument 'IntType' must be an integral type, in " "absl::Poisson(...)"); @@ -435,7 +435,7 @@ template IntType Zipf(URBG&& urbg, // NOLINT(runtime/references) IntType hi = (std::numeric_limits::max)(), double q = 2.0, double v = 1.0) { - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Template-argument 'IntType' must be an integral type, in " "absl::Zipf(...)"); diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc index d3a5dd75..5321a11c 100644 --- a/absl/random/distributions_test.cc +++ b/absl/random/distributions_test.cc @@ -220,6 +220,7 @@ TEST_F(RandomDistributionsTest, UniformNoBounds) { absl::Uniform(gen); absl::Uniform(gen); absl::Uniform(gen); + absl::Uniform(gen); } TEST_F(RandomDistributionsTest, UniformNonsenseRanges) { diff --git a/absl/random/generators_test.cc b/absl/random/generators_test.cc index 41725f13..14fd24e9 100644 --- a/absl/random/generators_test.cc +++ b/absl/random/generators_test.cc @@ -107,6 +107,8 @@ void TestPoisson(URBG* gen) { absl::Poisson(*gen); absl::Poisson(*gen); absl::Poisson(URBG()); + absl::Poisson(*gen); + absl::Poisson(*gen); } template @@ -126,6 +128,8 @@ void TestZipf(URBG* gen) { absl::Zipf(*gen, 1 << 10); absl::Zipf(*gen, 1 << 10); absl::Zipf(URBG(), 1 << 10); + absl::Zipf(*gen, 1 << 10); + absl::Zipf(*gen, 1 << 10); } template @@ -146,6 +150,8 @@ void TestLogNormal(URBG* gen) { absl::LogUniform(*gen, 0, 1 << 10); absl::LogUniform(*gen, 0, 1 << 10); absl::LogUniform(URBG(), 0, 1 << 10); + absl::LogUniform(*gen, 0, 1 << 10); + absl::LogUniform(*gen, 0, 1 << 10); } template diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index e93eebb6..67808aa0 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -35,7 +35,11 @@ cc_library( hdrs = ["traits.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - deps = ["//absl/base:config"], + deps = [ + "//absl/base:config", + "//absl/numeric:bits", + "//absl/numeric:int128", + ], ) cc_library( @@ -58,6 +62,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":traits", "//absl/base:config", "//absl/meta:type_traits", ], @@ -674,6 +679,7 @@ cc_library( ":traits", "//absl/base:config", "//absl/meta:type_traits", + "//absl/numeric:int128", ], ) diff --git a/absl/random/internal/fast_uniform_bits.h b/absl/random/internal/fast_uniform_bits.h index 425aaf7d..f3a5c00f 100644 --- a/absl/random/internal/fast_uniform_bits.h +++ b/absl/random/internal/fast_uniform_bits.h @@ -22,6 +22,7 @@ #include "absl/base/config.h" #include "absl/meta/type_traits.h" +#include "absl/random/internal/traits.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -98,7 +99,7 @@ class FastUniformBits { result_type operator()(URBG& g); // NOLINT(runtime/references) private: - static_assert(std::is_unsigned::value, + static_assert(IsUnsigned::value, "Class-template FastUniformBits<> must be parameterized using " "an unsigned type."); diff --git a/absl/random/internal/traits.h b/absl/random/internal/traits.h index 75772bd9..f874a0f7 100644 --- a/absl/random/internal/traits.h +++ b/absl/random/internal/traits.h @@ -20,6 +20,8 @@ #include #include "absl/base/config.h" +#include "absl/numeric/bits.h" +#include "absl/numeric/int128.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -59,6 +61,31 @@ class is_widening_convertible { rank() <= rank(); }; +template +struct IsIntegral : std::is_integral {}; +template <> +struct IsIntegral : std::true_type {}; +template <> +struct IsIntegral : std::true_type {}; + +template +struct MakeUnsigned : std::make_unsigned {}; +template <> +struct MakeUnsigned { + using type = absl::uint128; +}; +template <> +struct MakeUnsigned { + using type = absl::uint128; +}; + +template +struct IsUnsigned : std::is_unsigned {}; +template <> +struct IsUnsigned : std::false_type {}; +template <> +struct IsUnsigned : std::true_type {}; + // unsigned_bits::type returns the unsigned int type with the indicated // number of bits. template @@ -81,19 +108,40 @@ struct unsigned_bits<64> { using type = uint64_t; }; -#ifdef ABSL_HAVE_INTRINSIC_INT128 template <> struct unsigned_bits<128> { - using type = __uint128_t; + using type = absl::uint128; +}; + +// 256-bit wrapper for wide multiplications. +struct U256 { + uint128 hi; + uint128 lo; +}; +template <> +struct unsigned_bits<256> { + using type = U256; }; -#endif template struct make_unsigned_bits { - using type = typename unsigned_bits::type>::digits>::type; + using type = typename unsigned_bits< + std::numeric_limits::type>::digits>::type; }; +template +int BitWidth(T v) { + // Workaround for bit_width not supporting int128. + // Don't hardcode `64` to make sure this code does not trigger compiler + // warnings in smaller types. + constexpr int half_bits = sizeof(T) * 8 / 2; + if (sizeof(T) == 16 && (v >> half_bits) != 0) { + return bit_width(static_cast(v >> half_bits)) + half_bits; + } else { + return bit_width(static_cast(v)); + } +} + } // namespace random_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h index 1243bc1c..e68b82ee 100644 --- a/absl/random/internal/uniform_helper.h +++ b/absl/random/internal/uniform_helper.h @@ -100,7 +100,7 @@ using uniform_inferred_return_t = template typename absl::enable_if_t< absl::conjunction< - std::is_integral, + IsIntegral, absl::disjunction, std::is_same>>::value, IntType> @@ -131,7 +131,7 @@ uniform_lower_bound(Tag, NumType a, NumType) { template typename absl::enable_if_t< absl::conjunction< - std::is_integral, + IsIntegral, absl::disjunction, std::is_same>>::value, IntType> @@ -153,7 +153,7 @@ uniform_upper_bound(Tag, FloatType, FloatType b) { template typename absl::enable_if_t< absl::conjunction< - std::is_integral, + IsIntegral, absl::disjunction, std::is_same>>::value, IntType> @@ -201,7 +201,7 @@ is_uniform_range_valid(FloatType a, FloatType b) { } template -absl::enable_if_t::value, bool> +absl::enable_if_t::value, bool> is_uniform_range_valid(IntType a, IntType b) { return a <= b; } @@ -210,7 +210,7 @@ is_uniform_range_valid(IntType a, IntType b) { // or absl::uniform_real_distribution depending on the NumType parameter. template using UniformDistribution = - typename std::conditional::value, + typename std::conditional::value, absl::uniform_int_distribution, absl::uniform_real_distribution>::type; diff --git a/absl/random/internal/wide_multiply.h b/absl/random/internal/wide_multiply.h index b6e6c4b6..891e3630 100644 --- a/absl/random/internal/wide_multiply.h +++ b/absl/random/internal/wide_multiply.h @@ -34,43 +34,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// Helper object to multiply two 64-bit values to a 128-bit value. -// MultiplyU64ToU128 multiplies two 64-bit values to a 128-bit value. -// If an intrinsic is available, it is used, otherwise use native 32-bit -// multiplies to construct the result. -inline absl::uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return absl::uint128(static_cast<__uint128_t>(a) * b); -#elif defined(ABSL_INTERNAL_USE_UMUL128) - // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC. - uint64_t high = 0; - const uint64_t low = _umul128(a, b, &high); - return absl::MakeUint128(high, low); -#else - // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit - // multiply. However there are many cases where that is not necessary, and it - // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is - // for those cases. - const uint64_t a00 = static_cast(a); - const uint64_t a32 = a >> 32; - const uint64_t b00 = static_cast(b); - const uint64_t b32 = b >> 32; - - const uint64_t c00 = a00 * b00; - const uint64_t c32a = a00 * b32; - const uint64_t c32b = a32 * b00; - const uint64_t c64 = a32 * b32; - - const uint32_t carry = - static_cast(((c00 >> 32) + static_cast(c32a) + - static_cast(c32b)) >> - 32); - - return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry, - c00 + (c32a << 32) + (c32b << 32)); -#endif -} - // wide_multiply multiplies two N-bit values to a 2N-bit result. template struct wide_multiply { @@ -82,27 +45,49 @@ struct wide_multiply { return static_cast(a) * b; } - static input_type hi(result_type r) { return r >> kN; } - static input_type lo(result_type r) { return r; } + static input_type hi(result_type r) { + return static_cast(r >> kN); + } + static input_type lo(result_type r) { return static_cast(r); } static_assert(std::is_unsigned::value, "Class-template wide_multiply<> argument must be unsigned."); }; -#ifndef ABSL_HAVE_INTRINSIC_INT128 +// MultiplyU128ToU256 multiplies two 128-bit values to a 256-bit value. +inline U256 MultiplyU128ToU256(uint128 a, uint128 b) { + const uint128 a00 = static_cast(a); + const uint128 a64 = a >> 64; + const uint128 b00 = static_cast(b); + const uint128 b64 = b >> 64; + + const uint128 c00 = a00 * b00; + const uint128 c64a = a00 * b64; + const uint128 c64b = a64 * b00; + const uint128 c128 = a64 * b64; + + const uint64_t carry = + static_cast(((c00 >> 64) + static_cast(c64a) + + static_cast(c64b)) >> + 64); + + return {c128 + (c64a >> 64) + (c64b >> 64) + carry, + c00 + (c64a << 64) + (c64b << 64)}; +} + + template <> -struct wide_multiply { - using input_type = uint64_t; - using result_type = absl::uint128; +struct wide_multiply { + using input_type = uint128; + using result_type = U256; - static result_type multiply(uint64_t a, uint64_t b) { - return MultiplyU64ToU128(a, b); + static result_type multiply(input_type a, input_type b) { + return MultiplyU128ToU256(a, b); } - static uint64_t hi(result_type r) { return absl::Uint128High64(r); } - static uint64_t lo(result_type r) { return absl::Uint128Low64(r); } + static input_type hi(result_type r) { return r.hi; } + static input_type lo(result_type r) { return r.lo; } }; -#endif } // namespace random_internal ABSL_NAMESPACE_END diff --git a/absl/random/internal/wide_multiply_test.cc b/absl/random/internal/wide_multiply_test.cc index e276cb51..f8ee35c0 100644 --- a/absl/random/internal/wide_multiply_test.cc +++ b/absl/random/internal/wide_multiply_test.cc @@ -14,52 +14,106 @@ #include "absl/random/internal/wide_multiply.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/numeric/int128.h" -using absl::random_internal::MultiplyU64ToU128; +using absl::random_internal::MultiplyU128ToU256; +using absl::random_internal::U256; namespace { -TEST(WideMultiplyTest, MultiplyU64ToU128Test) { - constexpr uint64_t k1 = 1; - constexpr uint64_t kMax = ~static_cast(0); +U256 LeftShift(U256 v, int s) { + if (s == 0) { + return v; + } else if (s < 128) { + return {(v.hi << s) | (v.lo >> (128 - s)), v.lo << s}; + } else { + return {v.lo << (s - 128), 0}; + } +} + +MATCHER_P2(Eq256, hi, lo, "") { return arg.hi == hi && arg.lo == lo; } +MATCHER_P(Eq256, v, "") { return arg.hi == v.hi && arg.lo == v.lo; } + +TEST(WideMultiplyTest, MultiplyU128ToU256Test) { + using absl::uint128; + constexpr uint128 k1 = 1; + constexpr uint128 kMax = ~static_cast(0); - EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0)); + EXPECT_THAT(MultiplyU128ToU256(0, 0), Eq256(0, 0)); - // Max uint64_t - EXPECT_EQ(MultiplyU64ToU128(kMax, kMax), - absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001)); - EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1)); - EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(1, kMax)); + // Max uin128_t + EXPECT_THAT(MultiplyU128ToU256(kMax, kMax), Eq256(kMax << 1, 1)); + EXPECT_THAT(MultiplyU128ToU256(kMax, 1), Eq256(0, kMax)); + EXPECT_THAT(MultiplyU128ToU256(1, kMax), Eq256(0, kMax)); for (int i = 0; i < 64; ++i) { - EXPECT_EQ(absl::MakeUint128(0, kMax) << i, - MultiplyU64ToU128(kMax, k1 << i)); - EXPECT_EQ(absl::MakeUint128(0, kMax) << i, - MultiplyU64ToU128(k1 << i, kMax)); + SCOPED_TRACE(i); + EXPECT_THAT(MultiplyU128ToU256(kMax, k1 << i), + Eq256(LeftShift({0, kMax}, i))); + EXPECT_THAT(MultiplyU128ToU256(k1 << i, kMax), + Eq256(LeftShift({0, kMax}, i))); } // 1-bit x 1-bit. for (int i = 0; i < 64; ++i) { for (int j = 0; j < 64; ++j) { - EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), - MultiplyU64ToU128(k1 << i, k1 << j)); - EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), - MultiplyU64ToU128(k1 << i, k1 << j)); + EXPECT_THAT(MultiplyU128ToU256(k1 << i, k1 << j), + Eq256(LeftShift({0, 1}, i + j))); } } // Verified multiplies - EXPECT_EQ(MultiplyU64ToU128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888), - absl::MakeUint128(0xbbbb9e2692c5dddc, 0xc28f7531048d2c60)); - EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfedcba9876543210), - absl::MakeUint128(0x0121fa00ad77d742, 0x2236d88fe5618cf0)); - EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfdb97531eca86420), - absl::MakeUint128(0x0120ae99d26725fc, 0xce197f0ecac319e0)); - EXPECT_EQ(MultiplyU64ToU128(0x97a87f4f261ba3f2, 0xfedcba9876543210), - absl::MakeUint128(0x96fbf1a8ae78d0ba, 0x5a6dd4b71f278320)); - EXPECT_EQ(MultiplyU64ToU128(0xfedcba9876543210, 0xfdb97531eca86420), - absl::MakeUint128(0xfc98c6981a413e22, 0x342d0bbf48948200)); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0xc502da0d6ea99fe8, 0xfa3c9141a1f50912), + absl::MakeUint128(0x96bcf1ac37f97bd6, 0x27e2cdeb5fb2299e)), + Eq256(absl::MakeUint128(0x740113d838f96a64, 0x22e8cfa4d71f89ea), + absl::MakeUint128(0x19184a345c62e993, 0x237871b630337b1c))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0x6f29e670cee07230, 0xc3d8e6c3e4d86759), + absl::MakeUint128(0x3227d29fa6386db1, 0x231682bb1e4b764f)), + Eq256(absl::MakeUint128(0x15c779d9d5d3b07c, 0xd7e6c827f0c81cbe), + absl::MakeUint128(0xf88e3914f7fa287a, 0x15b79975137dea77))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0xafb77107215646e1, 0x3b844cb1ac5769e7), + absl::MakeUint128(0x1ff7b2d888b62479, 0x92f758ae96fcba0b)), + Eq256(absl::MakeUint128(0x15f13b70181f6985, 0x2adb36bbabce7d02), + absl::MakeUint128(0x6c470d72e13aad04, 0x63fba3f5841762ed))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0xd85d5558d67ac905, 0xf88c70654dae19b1), + absl::MakeUint128(0x17252c6727db3738, 0x399ff658c511eedc)), + Eq256(absl::MakeUint128(0x138fcdaf8b0421ee, 0x1b465ddf2a0d03f6), + absl::MakeUint128(0x8f573ba68296860f, 0xf327d2738741a21c))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0x46f0421a37ff6bee, 0xa61df89f09d140b1), + absl::MakeUint128(0x3d712ec9f37ca2e1, 0x9658a2cba47ef4b1)), + Eq256(absl::MakeUint128(0x11069cc48ee7c95d, 0xd35fb1c7aa91c978), + absl::MakeUint128(0xbe2f4a6de874b015, 0xd2f7ac1b76746e61))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0x730d27c72d58fa49, 0x3ebeda7498f8827c), + absl::MakeUint128(0xa2c959eca9f503af, 0x189c687eb842bbd8)), + Eq256(absl::MakeUint128(0x4928d0ea356ba022, 0x1546d34a2963393), + absl::MakeUint128(0x7481531e1e0a16d1, 0xdd8025015cf6aca0))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0x6ca41020f856d2f1, 0xb9b0838c04a7f4aa), + absl::MakeUint128(0x9cf41d28a8396f54, 0x1d681695e377ffe6)), + Eq256(absl::MakeUint128(0x429b92934d9be6f1, 0xea182877157c1e7), + absl::MakeUint128(0x7135c23f0a4a475, 0xc1adc366f4a126bc))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0x57472833797c332, 0x6c79272fdec4687a), + absl::MakeUint128(0xb5f022ea3838e46b, 0x16face2f003e27a6)), + Eq256(absl::MakeUint128(0x3e072e0962b3400, 0x5d9fe8fdc3d0e1f4), + absl::MakeUint128(0x7dc0df47cedafd62, 0xbe6501f1acd2551c))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0xf0fb4198322eb1c2, 0xfe7f5f31f3885938), + absl::MakeUint128(0xd99012b71bb7aa31, 0xac7a6f9eb190789)), + Eq256(absl::MakeUint128(0xcccc998cf075ca01, 0x642d144322fb873a), + absl::MakeUint128(0xc79dc12b69d91ed4, 0xa83459132ce046f8))); + EXPECT_THAT(MultiplyU128ToU256( + absl::MakeUint128(0xb5c04120848cdb47, 0x8aa62a827bf52635), + absl::MakeUint128(0x8d07a359be2f1380, 0x467bb90d59da0dea)), + Eq256(absl::MakeUint128(0x64205019d139a9ce, 0x99425c5fb6e7a977), + absl::MakeUint128(0xd3e99628a9e5fca7, 0x9c7824cb7279d72))); } } // namespace diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h index 43e10116..4afff8f6 100644 --- a/absl/random/log_uniform_int_distribution.h +++ b/absl/random/log_uniform_int_distribution.h @@ -69,10 +69,8 @@ class log_uniform_int_distribution { if (base_ == 2) { // Determine where the first set bit is on range(), giving a log2(range) // value which can be used to construct bounds. - log_range_ = - (std::min)(bit_width(range()), - static_cast( - std::numeric_limits::digits)); + log_range_ = (std::min)(random_internal::BitWidth(range()), + std::numeric_limits::digits); } else { // NOTE: Computing the logN(x) introduces error from 2 sources: // 1. Conversion of int to double loses precision for values >= @@ -83,7 +81,7 @@ class log_uniform_int_distribution { // // Thus a result which should equal K may equal K +/- epsilon, // which can eliminate some values depending on where the bounds fall. - const double inv_log_base = 1.0 / std::log(base_); + const double inv_log_base = 1.0 / std::log(static_cast(base_)); const double log_range = std::log(static_cast(range()) + 0.5); log_range_ = static_cast(std::ceil(inv_log_base * log_range)); } @@ -113,7 +111,7 @@ class log_uniform_int_distribution { unsigned_type range_; // max - min int log_range_; // ceil(logN(range_)) - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::log_uniform_int_distribution<> must be " "parameterized using an integral type."); }; @@ -139,7 +137,7 @@ class log_uniform_int_distribution { template result_type operator()(URBG& g, // NOLINT(runtime/references) const param_type& p) { - return (p.min)() + Generate(g, p); + return static_cast((p.min)() + Generate(g, p)); } result_type(min)() const { return (param_.min)(); } @@ -193,8 +191,8 @@ log_uniform_int_distribution::Generate( ? (std::numeric_limits::max)() : (static_cast(1) << e) - 1; } else { - const double r = std::pow(p.base(), d); - const double s = (r * p.base()) - 1.0; + const double r = std::pow(static_cast(p.base()), d); + const double s = (r * static_cast(p.base())) - 1.0; base_e = (r > static_cast((std::numeric_limits::max)())) @@ -211,7 +209,8 @@ log_uniform_int_distribution::Generate( const unsigned_type hi = (top_e >= p.range()) ? p.range() : top_e; // choose uniformly over [lo, hi] - return absl::uniform_int_distribution(lo, hi)(g); + return absl::uniform_int_distribution( + static_cast(lo), static_cast(hi))(g); } template diff --git a/absl/random/poisson_distribution.h b/absl/random/poisson_distribution.h index cb5f5d5d..f4573082 100644 --- a/absl/random/poisson_distribution.h +++ b/absl/random/poisson_distribution.h @@ -26,6 +26,7 @@ #include "absl/random/internal/fastmath.h" #include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/internal/traits.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -80,7 +81,7 @@ class poisson_distribution { double log_k_; int split_; - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::poisson_distribution<> must be " "parameterized using an integral type."); }; @@ -133,7 +134,8 @@ template poisson_distribution::param_type::param_type(double mean) : mean_(mean), split_(0) { assert(mean >= 0); - assert(mean <= (std::numeric_limits::max)()); + assert(mean <= + static_cast((std::numeric_limits::max)())); // As a defensive measure, avoid large values of the mean. The rejection // algorithm used does not support very large values well. It my be worth // changing algorithms to better deal with these cases. @@ -222,8 +224,9 @@ poisson_distribution::operator()( // clang-format on const double lhs = 2.0 * std::log(u) + p.log_k_ + s; if (lhs < rhs) { - return x > (max)() ? (max)() - : static_cast(x); // f(x)/k >= u^2 + return x > static_cast((max)()) + ? (max)() + : static_cast(x); // f(x)/k >= u^2 } } } diff --git a/absl/random/uniform_int_distribution.h b/absl/random/uniform_int_distribution.h index c1f54cce..fae80252 100644 --- a/absl/random/uniform_int_distribution.h +++ b/absl/random/uniform_int_distribution.h @@ -97,7 +97,7 @@ class uniform_int_distribution { result_type lo_; unsigned_type range_; - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::uniform_int_distribution<> must be " "parameterized using an integral type."); }; // param_type @@ -125,7 +125,7 @@ class uniform_int_distribution { template result_type operator()( URBG& gen, const param_type& param) { // NOLINT(runtime/references) - return param.a() + Generate(gen, param.range()); + return static_cast(param.a() + Generate(gen, param.range())); } result_type a() const { return param_.a(); } diff --git a/absl/random/zipf_distribution.h b/absl/random/zipf_distribution.h index 22ebc756..ed4038f1 100644 --- a/absl/random/zipf_distribution.h +++ b/absl/random/zipf_distribution.h @@ -23,6 +23,7 @@ #include #include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/internal/traits.h" #include "absl/random/uniform_real_distribution.h" namespace absl { @@ -94,7 +95,7 @@ class zipf_distribution { double hxm_; // h(k + 0.5) double hx0_minus_hxm_; // h(x0) - h(k + 0.5) - static_assert(std::is_integral::value, + static_assert(random_internal::IsIntegral::value, "Class-template absl::zipf_distribution<> must be " "parameterized using an integral type."); }; @@ -221,7 +222,7 @@ zipf_distribution::operator()( const double u = p.hxm_ + v * p.hx0_minus_hxm_; const double x = p.hinv(u); k = rint(x); // std::floor(x + 0.5); - if (k > p.k()) continue; // reject k > max_k + if (k > static_cast(p.k())) continue; // reject k > max_k if (k - x <= p.s_) break; const double h = p.h(k + 0.5); const double r = p.pow_negative_q(p.v_ + k); -- cgit v1.2.3 From 3e1983c5c07eb8a43ad030e770cbae023a470a04 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 24 Nov 2021 22:01:08 -0800 Subject: Export of internal Abseil changes -- a9ea60e9c0ccd744b6f12fd021dbedfe826dfe84 by Matt Kulukundis : Add an internal hook to allow keeping flags in sync with global state. Rollforward, except continue including hashtablez_flags.h in absl_flags.h so users don't break. PiperOrigin-RevId: 412198044 -- 183e5c440b68c797ce4a82102f94f41c97a14674 by Martijn Vels : Internal cleanups and changes PiperOrigin-RevId: 412083793 -- 3740faf7c5a2e1723e3c7e4d1b3f3db7cbec6e61 by Abseil Team : Mark Cord::Clear() with the ABSL_ATTRIBUTE_REINITIALIZES attribute. This prevents false positives in the clang-tidy check bugprone-use-after-move; it allows Clear() to be called on a moved-from Cord without any warnings, and the Cord will thereafter be regarded as initialized again. PiperOrigin-RevId: 412082757 -- a730d3f4ba06b55ae50386920a0544592069ac01 by Abseil Team : StrJoin: Support iterators that do not have an `operator->` Allows using `StrJoin` with iterators that do not have an `operator->`. The `operator->` requirement for input iterators was dropped in C++20. PiperOrigin-RevId: 412066130 -- 6773c0ced2caa6a7855898298faecc584f3997ec by Andy Soffer : Rollback of internal hook for keeping flags in sync with global state. PiperOrigin-RevId: 411895027 -- 4e7016a2fb88ce97853ef85ad5b4f76998eacca1 by Matt Kulukundis : Add an internal hook to allow keeping flags in sync with global state. PiperOrigin-RevId: 411867376 -- 2a7d4056e467b6b5d8a7aa9398d6cb5454c10fc5 by Martijn Vels : Internal change PiperOrigin-RevId: 411806932 GitOrigin-RevId: a9ea60e9c0ccd744b6f12fd021dbedfe826dfe84 Change-Id: Ib35bb7b40774979ed2ad205bbb1744b1085eae78 --- absl/container/internal/hashtablez_sampler.cc | 37 +++++++ absl/container/internal/hashtablez_sampler.h | 9 ++ absl/profiling/internal/sample_recorder.h | 6 ++ absl/strings/BUILD.bazel | 1 + absl/strings/cord.h | 3 +- absl/strings/cord_test.cc | 26 +++-- absl/strings/internal/cord_rep_flat.h | 6 +- absl/strings/internal/str_join_internal.h | 15 +-- absl/strings/str_join_test.cc | 134 ++++++++++++++++++++++++++ 9 files changed, 218 insertions(+), 19 deletions(-) diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 40cce047..1a3ca7cc 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -38,12 +38,18 @@ ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ false }; ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; +std::atomic g_hashtablez_config_listener{nullptr}; #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased g_exponential_biased_generator; #endif +void TriggerHashtablezConfigListener() { + auto* listener = g_hashtablez_config_listener.load(std::memory_order_acquire); + if (listener != nullptr) listener(); +} + } // namespace #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) @@ -163,11 +169,33 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash, info->size.fetch_add(1, std::memory_order_relaxed); } +void SetHashtablezConfigListener(HashtablezConfigListener l) { + g_hashtablez_config_listener.store(l, std::memory_order_release); +} + +bool IsHashtablezEnabled() { + return g_hashtablez_enabled.load(std::memory_order_acquire); +} + void SetHashtablezEnabled(bool enabled) { + SetHashtablezEnabledInternal(enabled); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezEnabledInternal(bool enabled) { g_hashtablez_enabled.store(enabled, std::memory_order_release); } +int32_t GetHashtablezSampleParameter() { + return g_hashtablez_sample_parameter.load(std::memory_order_acquire); +} + void SetHashtablezSampleParameter(int32_t rate) { + SetHashtablezSampleParameterInternal(rate); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezSampleParameterInternal(int32_t rate) { if (rate > 0) { g_hashtablez_sample_parameter.store(rate, std::memory_order_release); } else { @@ -176,7 +204,16 @@ void SetHashtablezSampleParameter(int32_t rate) { } } +int32_t GetHashtablezMaxSamples() { + return GlobalHashtablezSampler().GetMaxSamples(); +} + void SetHashtablezMaxSamples(int32_t max) { + SetHashtablezMaxSamplesInternal(max); + TriggerHashtablezConfigListener(); +} + +void SetHashtablezMaxSamplesInternal(int32_t max) { if (max > 0) { GlobalHashtablezSampler().SetMaxSamples(max); } else { diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 91fcdb34..ee4d293e 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -258,14 +258,23 @@ using HashtablezSampler = // Returns a global Sampler. HashtablezSampler& GlobalHashtablezSampler(); +using HashtablezConfigListener = void (*)(); +void SetHashtablezConfigListener(HashtablezConfigListener l); + // Enables or disables sampling for Swiss tables. +bool IsHashtablezEnabled(); void SetHashtablezEnabled(bool enabled); +void SetHashtablezEnabledInternal(bool enabled); // Sets the rate at which Swiss tables will be sampled. +int32_t GetHashtablezSampleParameter(); void SetHashtablezSampleParameter(int32_t rate); +void SetHashtablezSampleParameterInternal(int32_t rate); // Sets a soft max for the number of samples that will be kept. +int32_t GetHashtablezMaxSamples(); void SetHashtablezMaxSamples(int32_t max); +void SetHashtablezMaxSamplesInternal(int32_t max); // Configuration override. // This allows process-wide sampling without depending on order of diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h index 5e04a9cd..6c146210 100644 --- a/absl/profiling/internal/sample_recorder.h +++ b/absl/profiling/internal/sample_recorder.h @@ -75,6 +75,7 @@ class SampleRecorder { // samples that have been dropped. int64_t Iterate(const std::function& f); + int32_t GetMaxSamples() const; void SetMaxSamples(int32_t max); private: @@ -223,6 +224,11 @@ void SampleRecorder::SetMaxSamples(int32_t max) { max_samples_.store(max, std::memory_order_release); } +template +int32_t SampleRecorder::GetMaxSamples() const { + return max_samples_.load(std::memory_order_acquire); +} + } // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 4e37151e..5d433cce 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -436,6 +436,7 @@ cc_library( "//absl/container:inlined_vector", "//absl/functional:function_ref", "//absl/meta:type_traits", + "//absl/numeric:bits", "//absl/types:optional", "//absl/types:span", ], diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 662e889a..27d3475f 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -70,6 +70,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/per_thread_tls.h" @@ -215,7 +216,7 @@ class Cord { // // Releases the Cord data. Any nodes that share data with other Cords, if // applicable, will have their reference counts reduced by 1. - void Clear(); + ABSL_ATTRIBUTE_REINITIALIZES void Clear(); // Cord::Append() // diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 8a311799..ea865cca 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -49,6 +49,11 @@ static constexpr auto MAX_FLAT_TAG = absl::cord_internal::MAX_FLAT_TAG; typedef std::mt19937_64 RandomEngine; +using absl::cord_internal::CordRep; +using absl::cord_internal::CordRepFlat; +using absl::cord_internal::kFlatOverhead; +using absl::cord_internal::kMaxFlatLength; + static std::string RandomLowercaseString(RandomEngine* rng); static std::string RandomLowercaseString(RandomEngine* rng, size_t length); @@ -266,10 +271,6 @@ INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1, 2, 3), TEST(CordRepFlat, AllFlatCapacities) { - using absl::cord_internal::CordRep; - using absl::cord_internal::CordRepFlat; - using absl::cord_internal::kFlatOverhead; - // Explicitly and redundantly assert built-in min/max limits static_assert(absl::cord_internal::kFlatOverhead < 32, ""); static_assert(absl::cord_internal::kMinFlatSize == 32, ""); @@ -310,9 +311,6 @@ TEST(CordRepFlat, AllFlatCapacities) { } TEST(CordRepFlat, MaxFlatSize) { - using absl::cord_internal::CordRep; - using absl::cord_internal::CordRepFlat; - using absl::cord_internal::kMaxFlatLength; CordRepFlat* flat = CordRepFlat::New(kMaxFlatLength); EXPECT_EQ(flat->Capacity(), kMaxFlatLength); CordRep::Unref(flat); @@ -323,15 +321,23 @@ TEST(CordRepFlat, MaxFlatSize) { } TEST(CordRepFlat, MaxLargeFlatSize) { - using absl::cord_internal::CordRep; - using absl::cord_internal::CordRepFlat; - using absl::cord_internal::kFlatOverhead; const size_t size = 256 * 1024 - kFlatOverhead; CordRepFlat* flat = CordRepFlat::New(CordRepFlat::Large(), size); EXPECT_GE(flat->Capacity(), size); CordRep::Unref(flat); } +TEST(CordRepFlat, AllFlatSizes) { + const size_t kMaxSize = 256 * 1024; + for (size_t size = 32; size <= kMaxSize; size *=2) { + const size_t length = size - kFlatOverhead - 1; + CordRepFlat* flat = CordRepFlat::New(CordRepFlat::Large(), length); + EXPECT_GE(flat->Capacity(), length); + memset(flat->Data(), 0xCD, flat->Capacity()); + CordRep::Unref(flat); + } +} + TEST_P(CordTest, AllFlatSizes) { using absl::strings_internal::CordTestAccess; diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index a15c9acd..ae8b3a2a 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -20,6 +20,8 @@ #include #include +#include "absl/base/config.h" +#include "absl/base/macros.h" #include "absl/strings/internal/cord_internal.h" namespace absl { @@ -105,8 +107,8 @@ struct CordRepFlat : public CordRep { struct Large {}; // Creates a new flat node. - template - static CordRepFlat* NewImpl(size_t len) { + template + static CordRepFlat* NewImpl(size_t len, Args... args ABSL_ATTRIBUTE_UNUSED) { if (len <= kMinFlatLength) { len = kMinFlatLength; } else if (len > max_flat_size - kFlatOverhead) { diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h index 31dbf672..d97d5033 100644 --- a/absl/strings/internal/str_join_internal.h +++ b/absl/strings/internal/str_join_internal.h @@ -229,10 +229,11 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, std::string result; if (start != end) { // Sums size - size_t result_size = start->size(); + auto&& start_value = *start; + size_t result_size = start_value.size(); for (Iterator it = start; ++it != end;) { result_size += s.size(); - result_size += it->size(); + result_size += (*it).size(); } if (result_size > 0) { @@ -240,13 +241,15 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, // Joins strings char* result_buf = &*result.begin(); - memcpy(result_buf, start->data(), start->size()); - result_buf += start->size(); + + memcpy(result_buf, start_value.data(), start_value.size()); + result_buf += start_value.size(); for (Iterator it = start; ++it != end;) { memcpy(result_buf, s.data(), s.size()); result_buf += s.size(); - memcpy(result_buf, it->data(), it->size()); - result_buf += it->size(); + auto&& value = *it; + memcpy(result_buf, value.data(), value.size()); + result_buf += value.size(); } } } diff --git a/absl/strings/str_join_test.cc b/absl/strings/str_join_test.cc index 2be6256e..c986e863 100644 --- a/absl/strings/str_join_test.cc +++ b/absl/strings/str_join_test.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" namespace { @@ -471,4 +473,136 @@ TEST(StrJoin, Tuple) { "-", absl::DereferenceFormatter(TestFormatter()))); } +// A minimal value type for `StrJoin` inputs. +// Used to ensure we do not excessively require more a specific type, such as a +// `string_view`. +// +// Anything that can be `data()` and `size()` is OK. +class TestValue { + public: + TestValue(const char* data, size_t size) : data_(data), size_(size) {} + const char* data() const { return data_; } + size_t size() const { return size_; } + + private: + const char* data_; + size_t size_; +}; + +// A minimal C++20 forward iterator, used to test that we do not impose +// excessive requirements on StrJoin inputs. +// +// The 2 main differences between pre-C++20 LegacyForwardIterator and the +// C++20 ForwardIterator are: +// 1. `operator->` is not required in C++20. +// 2. `operator*` result does not need to be an lvalue (a reference). +// +// The `operator->` requirement was removed on page 17 in: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1037r0.pdf +// +// See the `[iterator.requirements]` section of the C++ standard. +// +// The value type is a template parameter so that we can test the behaviour +// of `StrJoin` specializations, e.g. the NoFormatter specialization for +// `string_view`. +template +class TestIterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = ValueT; + using pointer = void; + using reference = const value_type&; + using difference_type = int; + + // `data` must outlive the result. + static TestIterator begin(const std::vector& data) { + return TestIterator(&data, 0); + } + + static TestIterator end(const std::vector& data) { + return TestIterator(nullptr, data.size()); + } + + bool operator==(const TestIterator& other) const { + return pos_ == other.pos_; + } + bool operator!=(const TestIterator& other) const { + return pos_ != other.pos_; + } + + // This deliberately returns a `prvalue`. + // The requirement to return a reference was removed in C++20. + value_type operator*() const { + return ValueT((*data_)[pos_].data(), (*data_)[pos_].size()); + } + + // `operator->()` is deliberately omitted. + // The requirement to provide it was removed in C++20. + + TestIterator& operator++() { + ++pos_; + return *this; + } + + TestIterator operator++(int) { + TestIterator result = *this; + ++(*this); + return result; + } + + TestIterator& operator--() { + --pos_; + return *this; + } + + TestIterator operator--(int) { + TestIterator result = *this; + --(*this); + return result; + } + + private: + TestIterator(const std::vector* data, size_t pos) + : data_(data), pos_(pos) {} + + const std::vector* data_; + size_t pos_; +}; + +template +class TestIteratorRange { + public: + // `data` must be non-null and must outlive the result. + explicit TestIteratorRange(const std::vector& data) + : begin_(TestIterator::begin(data)), + end_(TestIterator::end(data)) {} + + const TestIterator& begin() const { return begin_; } + const TestIterator& end() const { return end_; } + + private: + TestIterator begin_; + TestIterator end_; +}; + +TEST(StrJoin, TestIteratorRequirementsNoFormatter) { + const std::vector a = {"a", "b", "c"}; + + // When the value type is string-like (`std::string` or `string_view`), + // the NoFormatter template specialization is used internally. + EXPECT_EQ("a-b-c", + absl::StrJoin(TestIteratorRange(a), "-")); +} + +TEST(StrJoin, TestIteratorRequirementsCustomFormatter) { + const std::vector a = {"a", "b", "c"}; + EXPECT_EQ("a-b-c", + absl::StrJoin(TestIteratorRange(a), "-", + [](std::string* out, const TestValue& value) { + absl::StrAppend( + out, + absl::string_view(value.data(), value.size())); + })); +} + } // namespace -- cgit v1.2.3 From cad715dbf375a336fcb0b2b0ef57fc2b7a1b0b4b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 30 Nov 2021 14:02:51 -0800 Subject: Export of internal Abseil changes -- e2a571b818faaec4185426a8cf71fd2970674423 by Matt Kulukundis : Fix missed use of old RTTI macro PiperOrigin-RevId: 413239579 -- e3c15a3fe0a4e44d6e08d69ad912b2245a403bd6 by Derek Mauro : Makes erase_if return the number of erased elements for compatibility with C++20 https://en.cppreference.com/w/cpp/container/unordered_map/erase_if This may technically be an API break, but no actual breaks were found in Google code. Fixes to open source code should be trivial. Closes #1065 PiperOrigin-RevId: 413204392 -- c1fb1ddbc2def3f3d177e5b80b9934bdbb7b16fc by Matt Kulukundis : Consolidate to a single HAS_RTTI macro PiperOrigin-RevId: 413169336 GitOrigin-RevId: e2a571b818faaec4185426a8cf71fd2970674423 Change-Id: I74b78ebd5fc172e3f5fcbd13a58cf53f7b250ae9 --- absl/base/config.h | 8 ++++++++ absl/container/btree_map.h | 12 ++++++++++-- absl/container/btree_set.h | 12 ++++++++++-- absl/container/btree_test.cc | 17 +++++++++++------ absl/container/flat_hash_map.h | 6 ++++-- absl/container/flat_hash_map_test.cc | 15 +++++++++------ absl/container/flat_hash_set.h | 6 ++++-- absl/container/flat_hash_set_test.cc | 10 +++++----- absl/container/internal/raw_hash_set.h | 5 ++++- absl/container/node_hash_map.h | 6 ++++-- absl/container/node_hash_map_test.cc | 15 +++++++++------ absl/container/node_hash_set.h | 6 ++++-- absl/container/node_hash_set_test.cc | 10 +++++----- absl/flags/config.h | 8 -------- absl/flags/internal/flag.cc | 2 +- absl/flags/internal/flag.h | 2 +- absl/types/any.h | 23 ++++++----------------- 17 files changed, 95 insertions(+), 68 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index ae7a5a15..c29b206a 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -788,4 +788,12 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 #endif +// `ABSL_INTERNAL_HAS_RTTI` determines whether abseil is being compiled with +// RTTI support. +#ifdef ABSL_INTERNAL_HAS_RTTI +#error ABSL_INTERNAL_HAS_RTTI cannot be directly set +#elif !defined(__GNUC__) || defined(__GXX_RTTI) +#define ABSL_INTERNAL_HAS_RTTI 1 +#endif // !defined(__GNUC__) || defined(__GXX_RTTI) + #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index 4eafe062..797b949f 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -478,8 +478,11 @@ void swap(btree_map &x, btree_map &y) { // absl::erase_if(absl::btree_map<>, Pred) // // Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. template -void erase_if(btree_map &map, Pred pred) { +typename btree_map::size_type erase_if( + btree_map &map, Pred pred) { + const auto initial_size = map.size(); for (auto it = map.begin(); it != map.end();) { if (pred(*it)) { it = map.erase(it); @@ -487,6 +490,7 @@ void erase_if(btree_map &map, Pred pred) { ++it; } } + return initial_size - map.size(); } // absl::btree_multimap @@ -809,8 +813,11 @@ void swap(btree_multimap &x, btree_multimap &y) { // absl::erase_if(absl::btree_multimap<>, Pred) // // Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. template -void erase_if(btree_multimap &map, Pred pred) { +typename btree_multimap::size_type erase_if( + btree_multimap &map, Pred pred) { + const auto initial_size = map.size(); for (auto it = map.begin(); it != map.end();) { if (pred(*it)) { it = map.erase(it); @@ -818,6 +825,7 @@ void erase_if(btree_multimap &map, Pred pred) { ++it; } } + return initial_size - map.size(); } namespace container_internal { diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 8c96e0ed..2c217d8f 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -398,8 +398,11 @@ void swap(btree_set &x, btree_set &y) { // absl::erase_if(absl::btree_set<>, Pred) // // Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. template -void erase_if(btree_set &set, Pred pred) { +typename btree_set::size_type erase_if(btree_set &set, + Pred pred) { + const auto initial_size = set.size(); for (auto it = set.begin(); it != set.end();) { if (pred(*it)) { it = set.erase(it); @@ -407,6 +410,7 @@ void erase_if(btree_set &set, Pred pred) { ++it; } } + return initial_size - set.size(); } // absl::btree_multiset<> @@ -724,8 +728,11 @@ void swap(btree_multiset &x, btree_multiset &y) { // absl::erase_if(absl::btree_multiset<>, Pred) // // Erases all elements that satisfy the predicate pred from the container. +// Returns the number of erased elements. template -void erase_if(btree_multiset &set, Pred pred) { +typename btree_multiset::size_type erase_if( + btree_multiset & set, Pred pred) { + const auto initial_size = set.size(); for (auto it = set.begin(); it != set.end();) { if (pred(*it)) { it = set.erase(it); @@ -733,6 +740,7 @@ void erase_if(btree_multiset &set, Pred pred) { ++it; } } + return initial_size - set.size(); } namespace container_internal { diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index d27cf271..650c51db 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -2452,23 +2452,28 @@ TEST(Btree, EraseIf) { // Test that erase_if works with all the container types and supports lambdas. { absl::btree_set s = {1, 3, 5, 6, 100}; - erase_if(s, [](int k) { return k > 3; }); + EXPECT_EQ(erase_if(s, [](int k) { return k > 3; }), 3); EXPECT_THAT(s, ElementsAre(1, 3)); } { absl::btree_multiset s = {1, 3, 3, 5, 6, 6, 100}; - erase_if(s, [](int k) { return k <= 3; }); + EXPECT_EQ(erase_if(s, [](int k) { return k <= 3; }), 3); EXPECT_THAT(s, ElementsAre(5, 6, 6, 100)); } { absl::btree_map m = {{1, 1}, {3, 3}, {6, 6}, {100, 100}}; - erase_if(m, [](std::pair kv) { return kv.first > 3; }); + EXPECT_EQ( + erase_if(m, [](std::pair kv) { return kv.first > 3; }), + 2); EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3))); } { absl::btree_multimap m = {{1, 1}, {3, 3}, {3, 6}, {6, 6}, {6, 7}, {100, 6}}; - erase_if(m, [](std::pair kv) { return kv.second == 6; }); + EXPECT_EQ( + erase_if(m, + [](std::pair kv) { return kv.second == 6; }), + 3); EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3), Pair(6, 7))); } // Test that erasing all elements from a large set works and test support for @@ -2476,13 +2481,13 @@ TEST(Btree, EraseIf) { { absl::btree_set s; for (int i = 0; i < 1000; ++i) s.insert(2 * i); - erase_if(s, IsEven); + EXPECT_EQ(erase_if(s, IsEven), 1000); EXPECT_THAT(s, IsEmpty()); } // Test that erase_if supports other format of function pointers. { absl::btree_set s = {1, 3, 5, 6, 100}; - erase_if(s, &IsEven); + EXPECT_EQ(erase_if(s, &IsEven), 2); EXPECT_THAT(s, ElementsAre(1, 3, 5)); } } diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index 74def0df..69fbf239 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -541,10 +541,12 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // erase_if(flat_hash_map<>, Pred) // // Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. template -void erase_if(flat_hash_map& c, Predicate pred) { - container_internal::EraseIf(pred, &c); +typename flat_hash_map::size_type erase_if( + flat_hash_map& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); } namespace container_internal { diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc index 8dda1d35..263951f1 100644 --- a/absl/container/flat_hash_map_test.cc +++ b/absl/container/flat_hash_map_test.cc @@ -236,33 +236,36 @@ TEST(FlatHashMap, EraseIf) { // Erase all elements. { flat_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, [](std::pair) { return true; }); + EXPECT_EQ(erase_if(s, [](std::pair) { return true; }), 5); EXPECT_THAT(s, IsEmpty()); } // Erase no elements. { flat_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, [](std::pair) { return false; }); + EXPECT_EQ(erase_if(s, [](std::pair) { return false; }), 0); EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(4, 4), Pair(5, 5))); } // Erase specific elements. { flat_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, - [](std::pair kvp) { return kvp.first % 2 == 1; }); + EXPECT_EQ(erase_if(s, + [](std::pair kvp) { + return kvp.first % 2 == 1; + }), + 3); EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4))); } // Predicate is function reference. { flat_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, FirstIsEven); + EXPECT_EQ(erase_if(s, FirstIsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); } // Predicate is function pointer. { flat_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, &FirstIsEven); + EXPECT_EQ(erase_if(s, &FirstIsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); } } diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index 6b89da65..f1c650cc 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -443,9 +443,11 @@ class flat_hash_set // erase_if(flat_hash_set<>, Pred) // // Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. template -void erase_if(flat_hash_set& c, Predicate pred) { - container_internal::EraseIf(pred, &c); +typename flat_hash_set::size_type erase_if( + flat_hash_set& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); } namespace container_internal { diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc index 8f6f9944..b6a72a20 100644 --- a/absl/container/flat_hash_set_test.cc +++ b/absl/container/flat_hash_set_test.cc @@ -143,31 +143,31 @@ TEST(FlatHashSet, EraseIf) { // Erase all elements. { flat_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, [](int) { return true; }); + EXPECT_EQ(erase_if(s, [](int) { return true; }), 5); EXPECT_THAT(s, IsEmpty()); } // Erase no elements. { flat_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, [](int) { return false; }); + EXPECT_EQ(erase_if(s, [](int) { return false; }), 0); EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5)); } // Erase specific elements. { flat_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, [](int k) { return k % 2 == 1; }); + EXPECT_EQ(erase_if(s, [](int k) { return k % 2 == 1; }), 3); EXPECT_THAT(s, UnorderedElementsAre(2, 4)); } // Predicate is function reference. { flat_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, IsEven); + EXPECT_EQ(erase_if(s, IsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); } // Predicate is function pointer. { flat_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, &IsEven); + EXPECT_EQ(erase_if(s, &IsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); } } diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 12682b35..d4b72ab1 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1958,7 +1958,9 @@ class raw_hash_set { // Erases all elements that satisfy the predicate `pred` from the container `c`. template -void EraseIf(Predicate& pred, raw_hash_set* c) { +typename raw_hash_set::size_type EraseIf( + Predicate& pred, raw_hash_set* c) { + const auto initial_size = c->size(); for (auto it = c->begin(), last = c->end(); it != last;) { if (pred(*it)) { c->erase(it++); @@ -1966,6 +1968,7 @@ void EraseIf(Predicate& pred, raw_hash_set* c) { ++it; } } + return initial_size - c->size(); } namespace hashtable_debug_internal { diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index 302dafa2..0d4ebeda 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -525,10 +525,12 @@ class node_hash_map // erase_if(node_hash_map<>, Pred) // // Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. template -void erase_if(node_hash_map& c, Predicate pred) { - container_internal::EraseIf(pred, &c); +typename node_hash_map::size_type erase_if( + node_hash_map& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); } namespace container_internal { diff --git a/absl/container/node_hash_map_test.cc b/absl/container/node_hash_map_test.cc index 8f59a1e4..e941a836 100644 --- a/absl/container/node_hash_map_test.cc +++ b/absl/container/node_hash_map_test.cc @@ -223,33 +223,36 @@ TEST(NodeHashMap, EraseIf) { // Erase all elements. { node_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, [](std::pair) { return true; }); + EXPECT_EQ(erase_if(s, [](std::pair) { return true; }), 5); EXPECT_THAT(s, IsEmpty()); } // Erase no elements. { node_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, [](std::pair) { return false; }); + EXPECT_EQ(erase_if(s, [](std::pair) { return false; }), 0); EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(4, 4), Pair(5, 5))); } // Erase specific elements. { node_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, - [](std::pair kvp) { return kvp.first % 2 == 1; }); + EXPECT_EQ(erase_if(s, + [](std::pair kvp) { + return kvp.first % 2 == 1; + }), + 3); EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4))); } // Predicate is function reference. { node_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, FirstIsEven); + EXPECT_EQ(erase_if(s, FirstIsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); } // Predicate is function pointer. { node_hash_map s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; - erase_if(s, &FirstIsEven); + EXPECT_EQ(erase_if(s, &FirstIsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5))); } } diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 4247288d..3bc5fe6b 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -433,9 +433,11 @@ class node_hash_set // erase_if(node_hash_set<>, Pred) // // Erases all elements that satisfy the predicate `pred` from the container `c`. +// Returns the number of erased elements. template -void erase_if(node_hash_set& c, Predicate pred) { - container_internal::EraseIf(pred, &c); +typename node_hash_set::size_type erase_if( + node_hash_set& c, Predicate pred) { + return container_internal::EraseIf(pred, &c); } namespace container_internal { diff --git a/absl/container/node_hash_set_test.cc b/absl/container/node_hash_set_test.cc index 7ddad202..98a8dbdd 100644 --- a/absl/container/node_hash_set_test.cc +++ b/absl/container/node_hash_set_test.cc @@ -108,31 +108,31 @@ TEST(NodeHashSet, EraseIf) { // Erase all elements. { node_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, [](int) { return true; }); + EXPECT_EQ(erase_if(s, [](int) { return true; }), 5); EXPECT_THAT(s, IsEmpty()); } // Erase no elements. { node_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, [](int) { return false; }); + EXPECT_EQ(erase_if(s, [](int) { return false; }), 0); EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5)); } // Erase specific elements. { node_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, [](int k) { return k % 2 == 1; }); + EXPECT_EQ(erase_if(s, [](int k) { return k % 2 == 1; }), 3); EXPECT_THAT(s, UnorderedElementsAre(2, 4)); } // Predicate is function reference. { node_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, IsEven); + EXPECT_EQ(erase_if(s, IsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); } // Predicate is function pointer. { node_hash_set s = {1, 2, 3, 4, 5}; - erase_if(s, &IsEven); + EXPECT_EQ(erase_if(s, &IsEven), 2); EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5)); } } diff --git a/absl/flags/config.h b/absl/flags/config.h index 5ab1f311..14c4235b 100644 --- a/absl/flags/config.h +++ b/absl/flags/config.h @@ -45,14 +45,6 @@ #define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES #endif -// ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI -// for flag type identification. -#ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI -#error ABSL_FLAGS_INTERNAL_HAS_RTTI cannot be directly set -#elif !defined(__GNUC__) || defined(__GXX_RTTI) -#define ABSL_FLAGS_INTERNAL_HAS_RTTI 1 -#endif // !defined(__GNUC__) || defined(__GXX_RTTI) - // These macros represent the "source of truth" for the list of supported // built-in types. #define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \ diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 1515022d..7102559e 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -205,7 +205,7 @@ void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id, if (lhs_runtime_type_id == rhs_runtime_type_id) return; -#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) +#ifdef ABSL_INTERNAL_HAS_RTTI if (*lhs_runtime_type_id == *rhs_runtime_type_id) return; #endif diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 124a2f1c..2d0a7e9c 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -163,7 +163,7 @@ inline ptrdiff_t ValueOffset(FlagOpFn op) { // Returns an address of RTTI's typeid(T). template inline const std::type_info* GenRuntimeTypeId() { -#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) +#ifdef ABSL_INTERNAL_HAS_RTTI return &typeid(T); #else return nullptr; diff --git a/absl/types/any.h b/absl/types/any.h index fc5a0746..204da26d 100644 --- a/absl/types/any.h +++ b/absl/types/any.h @@ -81,18 +81,9 @@ ABSL_NAMESPACE_END #include #include "absl/base/internal/fast_type_id.h" -#include "absl/base/macros.h" #include "absl/meta/type_traits.h" #include "absl/types/bad_any_cast.h" -// NOTE: This macro is an implementation detail that is undefined at the bottom -// of the file. It is not intended for expansion directly from user code. -#ifdef ABSL_ANY_DETAIL_HAS_RTTI -#error ABSL_ANY_DETAIL_HAS_RTTI cannot be directly set -#elif !defined(__GNUC__) || defined(__GXX_RTTI) -#define ABSL_ANY_DETAIL_HAS_RTTI 1 -#endif // !defined(__GNUC__) || defined(__GXX_RTTI) - namespace absl { ABSL_NAMESPACE_BEGIN @@ -348,7 +339,7 @@ class any { // returns `false`. bool has_value() const noexcept { return obj_ != nullptr; } -#if ABSL_ANY_DETAIL_HAS_RTTI +#ifdef ABSL_INTERNAL_HAS_RTTI // Returns: typeid(T) if *this has a contained object of type T, otherwise // typeid(void). const std::type_info& type() const noexcept { @@ -358,7 +349,7 @@ class any { return typeid(void); } -#endif // ABSL_ANY_DETAIL_HAS_RTTI +#endif // ABSL_INTERNAL_HAS_RTTI private: // Tagged type-erased abstraction for holding a cloneable object. @@ -367,9 +358,9 @@ class any { virtual ~ObjInterface() = default; virtual std::unique_ptr Clone() const = 0; virtual const void* ObjTypeId() const noexcept = 0; -#if ABSL_ANY_DETAIL_HAS_RTTI +#ifdef ABSL_INTERNAL_HAS_RTTI virtual const std::type_info& Type() const noexcept = 0; -#endif // ABSL_ANY_DETAIL_HAS_RTTI +#endif // ABSL_INTERNAL_HAS_RTTI }; // Hold a value of some queryable type, with an ability to Clone it. @@ -386,9 +377,9 @@ class any { const void* ObjTypeId() const noexcept final { return IdForType(); } -#if ABSL_ANY_DETAIL_HAS_RTTI +#ifdef ABSL_INTERNAL_HAS_RTTI const std::type_info& Type() const noexcept final { return typeid(T); } -#endif // ABSL_ANY_DETAIL_HAS_RTTI +#endif // ABSL_INTERNAL_HAS_RTTI T value; }; @@ -521,8 +512,6 @@ T* any_cast(any* operand) noexcept { ABSL_NAMESPACE_END } // namespace absl -#undef ABSL_ANY_DETAIL_HAS_RTTI - #endif // ABSL_USES_STD_ANY #endif // ABSL_TYPES_ANY_H_ -- cgit v1.2.3 From e11e039e02ef30a98a7928ce6c59cebe096dd753 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 1 Dec 2021 15:08:07 -0800 Subject: Export of internal Abseil changes -- 08d99ee216b7bfac1c5182db952d4e053e5ebc31 by Abseil Team : Fix race condition reported by tsan on `inline_element_size` in hashtablez. PiperOrigin-RevId: 413520771 GitOrigin-RevId: 08d99ee216b7bfac1c5182db952d4e053e5ebc31 Change-Id: Ibd396803f04a659cfbdb8dc7ec37511643657694 --- absl/container/internal/hashtablez_sampler.cc | 6 ++++-- absl/container/internal/hashtablez_sampler.h | 19 +++++++++++++++---- absl/container/internal/hashtablez_sampler_test.cc | 14 +++++++------- absl/container/internal/raw_hash_set_test.cc | 3 ++- absl/container/sample_element_size_test.cc | 3 ++- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 1a3ca7cc..a3e15a70 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -111,7 +111,8 @@ HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; HashtablezInfo* result = GlobalHashtablezSampler().Register(); - result->inline_element_size = inline_element_size; + result->inline_element_size.store(inline_element_size, + std::memory_order_relaxed); return result; } @@ -138,7 +139,8 @@ HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { } HashtablezInfo* result = GlobalHashtablezSampler().Register(); - result->inline_element_size = inline_element_size; + result->inline_element_size.store(inline_element_size, + std::memory_order_relaxed); return result; #endif } diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index ee4d293e..0fd9349f 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -82,16 +82,27 @@ struct HashtablezInfo : public profiling_internal::Sample { std::atomic hashes_bitwise_xor; std::atomic max_reserve; + // One could imagine that inline_element_size could be non-atomic, since it + // *almost* follows the rules for the fields that are set by + // `PrepareForSampling`. However, TSAN reports a race (see b/207323922) in + // which + // A: Thread 1: Register() returns, unlocking init_mu. + // B: Thread 2: Iterate() is called, locking init_mu. + // C: Thread 1: inlined_element_size is stored. + // D: Thread 2: inlined_element_size is accessed (a race). + // A simple solution is to make inline_element_size atomic so that we treat it + // at as we do the other atomic fields. + std::atomic inline_element_size; + // All of the fields below are set by `PrepareForSampling`, they must not be // mutated in `Record*` functions. They are logically `const` in that sense. - // These are guarded by init_mu, but that is not externalized to clients, who - // can only read them during `HashtablezSampler::Iterate` which will hold the - // lock. + // These are guarded by init_mu, but that is not externalized to clients, + // which can read them only during `SampleRecorder::Iterate` which will hold + // the lock. static constexpr int kMaxStackDepth = 64; absl::Time create_time; int32_t depth; void* stack[kMaxStackDepth]; - size_t inline_element_size; }; inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index 449619a3..a7307a20 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -83,7 +83,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); - info.inline_element_size = test_element_size; + info.inline_element_size.store(test_element_size, std::memory_order_relaxed); EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 0); @@ -95,7 +95,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); EXPECT_GE(info.create_time, test_start); - EXPECT_EQ(info.inline_element_size, test_element_size); + EXPECT_EQ(info.inline_element_size.load(), test_element_size); info.capacity.store(1, std::memory_order_relaxed); info.size.store(1, std::memory_order_relaxed); @@ -119,7 +119,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); - EXPECT_EQ(info.inline_element_size, test_element_size); + EXPECT_EQ(info.inline_element_size.load(), test_element_size); EXPECT_GE(info.create_time, test_start); } @@ -162,7 +162,7 @@ TEST(HashtablezInfoTest, RecordErase) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); - info.inline_element_size = test_element_size; + info.inline_element_size.store(test_element_size); EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.size.load(), 0); RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); @@ -170,7 +170,7 @@ TEST(HashtablezInfoTest, RecordErase) { RecordEraseSlow(&info); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 1); - EXPECT_EQ(info.inline_element_size, test_element_size); + EXPECT_EQ(info.inline_element_size.load(), test_element_size); } TEST(HashtablezInfoTest, RecordRehash) { @@ -178,7 +178,7 @@ TEST(HashtablezInfoTest, RecordRehash) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); - info.inline_element_size = test_element_size; + info.inline_element_size.store(test_element_size); RecordInsertSlow(&info, 0x1, 0); RecordInsertSlow(&info, 0x2, kProbeLength); RecordInsertSlow(&info, 0x4, kProbeLength); @@ -197,7 +197,7 @@ TEST(HashtablezInfoTest, RecordRehash) { EXPECT_EQ(info.total_probe_length.load(), 3); EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.num_rehashes.load(), 1); - EXPECT_EQ(info.inline_element_size, test_element_size); + EXPECT_EQ(info.inline_element_size.load(), test_element_size); } TEST(HashtablezInfoTest, RecordReservation) { diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 362b3cae..4b9f6cc0 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -2075,7 +2075,8 @@ TEST(RawHashSamplerTest, Sample) { std::memory_order_relaxed)]++; reservations[info.max_reserve.load(std::memory_order_relaxed)]++; } - EXPECT_EQ(info.inline_element_size, sizeof(int64_t)); + EXPECT_EQ(info.inline_element_size.load(std::memory_order_relaxed), + sizeof(int64_t)); ++end_size; }); diff --git a/absl/container/sample_element_size_test.cc b/absl/container/sample_element_size_test.cc index b23626b4..4bbef210 100644 --- a/absl/container/sample_element_size_test.cc +++ b/absl/container/sample_element_size_test.cc @@ -51,7 +51,8 @@ void TestInlineElementSize( size_t new_count = 0; sampler.Iterate([&](const HashtablezInfo& info) { if (preexisting_info.insert(&info).second) { - EXPECT_EQ(info.inline_element_size, expected_element_size); + EXPECT_EQ(info.inline_element_size.load(std::memory_order_relaxed), + expected_element_size); ++new_count; } }); -- cgit v1.2.3 From 9336be04a242237cd41a525bedfcf3be1bb55377 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 3 Dec 2021 10:01:02 -0800 Subject: Export of internal Abseil changes -- e7f53dfbf809812e84770217777f81b6308a3084 by Abseil Team : Add a parameter pack to absl profile to allow profiles to separate dynamic data from static data that is available at constructor-time. Background: `inline_element_size` is effectively constant, but there is a data race between its initialization and its access. We had fixed that race by making inline_element_size atomic. This CL changes `inline_element_size` back to a non-atomic integer, and provides a way for all profiles to provide Register()-time values. PiperOrigin-RevId: 413960559 -- 70234c5943f8e37e17c1d9c54d8ed61d39880abf by Chris Kennelly : Document that absl::FunctionRef does not allocate. PiperOrigin-RevId: 413946831 -- 3308ae571412c4be3cc32d088c6edac98ff2d1ed by Samuel Benzaquen : Internal change PiperOrigin-RevId: 413933619 -- 1617093a730d055edcf7bc04fdd6509783f5f75d by Martijn Vels : Internal Change PiperOrigin-RevId: 413778735 -- 03ad683f059c806a6c8b04f5b79b2662c3df8c73 by Evan Brown : Unify btree erase_if definitions and optimize them so that we only do rebalancing once per leaf node. PiperOrigin-RevId: 413757280 -- 5ba402f70801938178e486617063f01c7862525d by Martijn Vels : Cleanup up cord sampling internals PiperOrigin-RevId: 413755011 -- 522da8f9d3e0f11630d89fb41952004742bc335a by Evan Brown : Add b-tree benchmark for erase_if. Since this benchmark doesn't work for std:: containers before C++20, disable it for them. PiperOrigin-RevId: 413740844 -- a690ea42de8ed4a761d00235d8b2fb7548ba9732 by Andy Getzendanner : Import of CCTZ from GitHub. PiperOrigin-RevId: 413735737 GitOrigin-RevId: e7f53dfbf809812e84770217777f81b6308a3084 Change-Id: I4f9f9039ba92831bc48971964aa063244c9fed72 --- absl/container/BUILD.bazel | 3 + absl/container/CMakeLists.txt | 3 + absl/container/btree_benchmark.cc | 64 +++++++++++++++++----- absl/container/btree_map.h | 20 +------ absl/container/btree_set.h | 20 +------ absl/container/btree_test.cc | 14 +++++ absl/container/flat_hash_map.h | 1 + absl/container/internal/btree.h | 46 ++++++++++++++++ absl/container/internal/btree_container.h | 1 + absl/container/internal/hashtablez_sampler.cc | 18 ++---- absl/container/internal/hashtablez_sampler.h | 16 +----- absl/container/internal/hashtablez_sampler_test.cc | 41 +++++++------- absl/container/internal/raw_hash_set_test.cc | 3 +- absl/container/node_hash_map.h | 1 + absl/container/node_hash_set.h | 1 + absl/container/sample_element_size_test.cc | 3 +- absl/functional/function_ref.h | 3 +- absl/functional/function_ref_test.cc | 1 + absl/profiling/internal/sample_recorder.h | 20 +++++-- absl/strings/cord.cc | 40 -------------- absl/strings/internal/cordz_update_tracker.h | 5 +- absl/strings/internal/cordz_update_tracker_test.cc | 5 +- .../internal/cctz/include/cctz/civil_time_detail.h | 18 +++--- 23 files changed, 192 insertions(+), 155 deletions(-) diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index ea2c98b8..728c4be1 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -241,6 +241,7 @@ cc_library( ":hash_function_defaults", ":raw_hash_map", "//absl/algorithm:container", + "//absl/base:core_headers", "//absl/memory", ], ) @@ -310,6 +311,7 @@ cc_library( ":node_slot_policy", ":raw_hash_map", "//absl/algorithm:container", + "//absl/base:core_headers", "//absl/memory", ], ) @@ -342,6 +344,7 @@ cc_library( ":node_slot_policy", ":raw_hash_set", "//absl/algorithm:container", + "//absl/base:core_headers", "//absl/memory", ], ) diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index bb718cf8..b819deeb 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -274,6 +274,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::container_memory + absl::core_headers absl::hash_function_defaults absl::raw_hash_map absl::algorithm_container @@ -347,6 +348,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::container_memory + absl::core_headers absl::hash_function_defaults absl::node_slot_policy absl::raw_hash_map @@ -381,6 +383,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::core_headers absl::hash_function_defaults absl::node_slot_policy absl::raw_hash_set diff --git a/absl/container/btree_benchmark.cc b/absl/container/btree_benchmark.cc index 65b6790b..0ca497c8 100644 --- a/absl/container/btree_benchmark.cc +++ b/absl/container/btree_benchmark.cc @@ -153,9 +153,9 @@ void BM_FullLookup(benchmark::State& state) { BM_LookupImpl(state, true); } -// Benchmark deletion of values from a container. +// Benchmark erasing values from a container. template -void BM_Delete(benchmark::State& state) { +void BM_Erase(benchmark::State& state) { using V = typename remove_pair_const::type; typename KeyOfValue::type key_of_value; std::vector values = GenerateValues(kBenchmarkValues); @@ -180,9 +180,9 @@ void BM_Delete(benchmark::State& state) { } } -// Benchmark deletion of multiple values from a container. +// Benchmark erasing multiple values from a container. template -void BM_DeleteRange(benchmark::State& state) { +void BM_EraseRange(benchmark::State& state) { using V = typename remove_pair_const::type; typename KeyOfValue::type key_of_value; std::vector values = GenerateValues(kBenchmarkValues); @@ -222,6 +222,40 @@ void BM_DeleteRange(benchmark::State& state) { } } +// Predicate that erases every other element. We can't use a lambda because +// C++11 doesn't support generic lambdas. +// TODO(b/207389011): consider adding benchmarks that remove different fractions +// of keys (e.g. 10%, 90%). +struct EraseIfPred { + uint64_t i = 0; + template + bool operator()(const T&) { + return ++i % 2; + } +}; + +// Benchmark erasing multiple values from a container with a predicate. +template +void BM_EraseIf(benchmark::State& state) { + using V = typename remove_pair_const::type; + std::vector values = GenerateValues(kBenchmarkValues); + + // Removes half of the keys per batch. + const int batch_size = (kBenchmarkValues + 1) / 2; + EraseIfPred pred; + while (state.KeepRunningBatch(batch_size)) { + state.PauseTiming(); + { + T container(values.begin(), values.end()); + state.ResumeTiming(); + erase_if(container, pred); + benchmark::DoNotOptimize(container); + state.PauseTiming(); + } + state.ResumeTiming(); + } +} + // Benchmark steady-state insert (into first half of range) and remove (from // second half of range), treating the container approximately like a queue with // log-time access for all elements. This benchmark does not test the case where @@ -477,14 +511,14 @@ BTREE_TYPES(Time); void BM_##type##_##func(benchmark::State& state) { BM_##func(state); } \ BENCHMARK(BM_##type##_##func) -#define MY_BENCHMARK3(type) \ +#define MY_BENCHMARK3_STL(type) \ MY_BENCHMARK4(type, Insert); \ MY_BENCHMARK4(type, InsertSorted); \ MY_BENCHMARK4(type, InsertSmall); \ MY_BENCHMARK4(type, Lookup); \ MY_BENCHMARK4(type, FullLookup); \ - MY_BENCHMARK4(type, Delete); \ - MY_BENCHMARK4(type, DeleteRange); \ + MY_BENCHMARK4(type, Erase); \ + MY_BENCHMARK4(type, EraseRange); \ MY_BENCHMARK4(type, QueueAddRem); \ MY_BENCHMARK4(type, MixedAddRem); \ MY_BENCHMARK4(type, Fifo); \ @@ -492,9 +526,13 @@ BTREE_TYPES(Time); MY_BENCHMARK4(type, InsertRangeRandom); \ MY_BENCHMARK4(type, InsertRangeSorted) +#define MY_BENCHMARK3(type) \ + MY_BENCHMARK4(type, EraseIf); \ + MY_BENCHMARK3_STL(type) + #define MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type) \ - MY_BENCHMARK3(stl_##type); \ - MY_BENCHMARK3(stl_unordered_##type); \ + MY_BENCHMARK3_STL(stl_##type); \ + MY_BENCHMARK3_STL(stl_unordered_##type); \ MY_BENCHMARK3(btree_256_##type) #define MY_BENCHMARK2(type) \ @@ -684,12 +722,12 @@ double ContainerInfo(const btree_map>& b) { btree_set>; \ using btree_256_map_size##SIZE##copies##SIZE##ptr = \ btree_map>; \ - MY_BENCHMARK3(stl_set_size##SIZE##copies##SIZE##ptr); \ - MY_BENCHMARK3(stl_unordered_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3_STL(stl_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3_STL(stl_unordered_set_size##SIZE##copies##SIZE##ptr); \ MY_BENCHMARK3(flat_hash_set_size##SIZE##copies##SIZE##ptr); \ MY_BENCHMARK3(btree_256_set_size##SIZE##copies##SIZE##ptr); \ - MY_BENCHMARK3(stl_map_size##SIZE##copies##SIZE##ptr); \ - MY_BENCHMARK3(stl_unordered_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3_STL(stl_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3_STL(stl_unordered_map_size##SIZE##copies##SIZE##ptr); \ MY_BENCHMARK3(flat_hash_map_size##SIZE##copies##SIZE##ptr); \ MY_BENCHMARK3(btree_256_map_size##SIZE##copies##SIZE##ptr) diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index 797b949f..ad484ce0 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -482,15 +482,7 @@ void swap(btree_map &x, btree_map &y) { template typename btree_map::size_type erase_if( btree_map &map, Pred pred) { - const auto initial_size = map.size(); - for (auto it = map.begin(); it != map.end();) { - if (pred(*it)) { - it = map.erase(it); - } else { - ++it; - } - } - return initial_size - map.size(); + return container_internal::btree_access::erase_if(map, std::move(pred)); } // absl::btree_multimap @@ -817,15 +809,7 @@ void swap(btree_multimap &x, btree_multimap &y) { template typename btree_multimap::size_type erase_if( btree_multimap &map, Pred pred) { - const auto initial_size = map.size(); - for (auto it = map.begin(); it != map.end();) { - if (pred(*it)) { - it = map.erase(it); - } else { - ++it; - } - } - return initial_size - map.size(); + return container_internal::btree_access::erase_if(map, std::move(pred)); } namespace container_internal { diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 2c217d8f..78826830 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -402,15 +402,7 @@ void swap(btree_set &x, btree_set &y) { template typename btree_set::size_type erase_if(btree_set &set, Pred pred) { - const auto initial_size = set.size(); - for (auto it = set.begin(); it != set.end();) { - if (pred(*it)) { - it = set.erase(it); - } else { - ++it; - } - } - return initial_size - set.size(); + return container_internal::btree_access::erase_if(set, std::move(pred)); } // absl::btree_multiset<> @@ -732,15 +724,7 @@ void swap(btree_multiset &x, btree_multiset &y) { template typename btree_multiset::size_type erase_if( btree_multiset & set, Pred pred) { - const auto initial_size = set.size(); - for (auto it = set.begin(); it != set.end();) { - if (pred(*it)) { - it = set.erase(it); - } else { - ++it; - } - } - return initial_size - set.size(); + return container_internal::btree_access::erase_if(set, std::move(pred)); } namespace container_internal { diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index 650c51db..13cb8186 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -2490,6 +2490,20 @@ TEST(Btree, EraseIf) { EXPECT_EQ(erase_if(s, &IsEven), 2); EXPECT_THAT(s, ElementsAre(1, 3, 5)); } + // Test that erase_if invokes the predicate once per element. + { + absl::btree_set s; + for (int i = 0; i < 1000; ++i) s.insert(i); + int pred_calls = 0; + EXPECT_EQ(erase_if(s, + [&pred_calls](int k) { + ++pred_calls; + return k % 2; + }), + 500); + EXPECT_THAT(s, SizeIs(500)); + EXPECT_EQ(pred_calls, 1000); + } } TEST(Btree, InsertOrAssign) { diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index 69fbf239..83c71029 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -36,6 +36,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/base/macros.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index bf65a03d..29603379 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -830,6 +830,7 @@ class btree_node { template friend struct btree_iterator; friend class BtreeNodePeer; + friend struct btree_access; }; template @@ -964,6 +965,7 @@ struct btree_iterator { friend class btree_multiset_container; template friend class base_checker; + friend struct btree_access; const key_type &key() const { return node->key(position); } slot_type *slot() { return node->slot(position); } @@ -1336,6 +1338,8 @@ class btree { allocator_type get_allocator() const { return allocator(); } private: + friend struct btree_access; + // Internal accessor routines. node_type *root() { return root_.template get<2>(); } const node_type *root() const { return root_.template get<2>(); } @@ -2530,6 +2534,48 @@ int btree

::internal_verify(const node_type *node, const key_type *lo, return count; } +struct btree_access { + template + static auto erase_if(BtreeContainer &container, Pred pred) + -> typename BtreeContainer::size_type { + const auto initial_size = container.size(); + auto &tree = container.tree_; + auto *alloc = tree.mutable_allocator(); + for (auto it = container.begin(); it != container.end();) { + if (!pred(*it)) { + ++it; + continue; + } + auto *node = it.node; + if (!node->leaf()) { + // Handle internal nodes normally. + it = container.erase(it); + continue; + } + // If this is a leaf node, then we do all the erases from this node + // at once before doing rebalancing. + + // The current position to transfer slots to. + int to_pos = it.position; + node->value_destroy(it.position, alloc); + while (++it.position < node->finish()) { + if (pred(*it)) { + node->value_destroy(it.position, alloc); + } else { + node->transfer(node->slot(to_pos++), node->slot(it.position), + alloc); + } + } + const int num_deleted = node->finish() - to_pos; + tree.size_ -= num_deleted; + node->set_finish(to_pos); + it.position = to_pos; + it = tree.rebalance_after_delete(it); + } + return initial_size - container.size(); + } +}; + } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index a99668c7..d28b2446 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -228,6 +228,7 @@ class btree_container { } protected: + friend struct btree_access; Tree tree_; }; diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index a3e15a70..1d24db5f 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -61,13 +61,10 @@ HashtablezSampler& GlobalHashtablezSampler() { return *sampler; } -// TODO(bradleybear): The comments at this constructors declaration say that the -// fields are not initialized, but this definition does initialize the fields. -// Something needs to be cleaned up. -HashtablezInfo::HashtablezInfo() { PrepareForSampling(); } +HashtablezInfo::HashtablezInfo() = default; HashtablezInfo::~HashtablezInfo() = default; -void HashtablezInfo::PrepareForSampling() { +void HashtablezInfo::PrepareForSampling(size_t inline_element_size_value) { capacity.store(0, std::memory_order_relaxed); size.store(0, std::memory_order_relaxed); num_erases.store(0, std::memory_order_relaxed); @@ -85,6 +82,7 @@ void HashtablezInfo::PrepareForSampling() { // instead. depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, /* skip_count= */ 0); + inline_element_size = inline_element_size_value; } static bool ShouldForceSampling() { @@ -110,9 +108,8 @@ static bool ShouldForceSampling() { HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; - HashtablezInfo* result = GlobalHashtablezSampler().Register(); - result->inline_element_size.store(inline_element_size, - std::memory_order_relaxed); + HashtablezInfo* result = + GlobalHashtablezSampler().Register(inline_element_size); return result; } @@ -138,10 +135,7 @@ HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { return SampleSlow(next_sample, inline_element_size); } - HashtablezInfo* result = GlobalHashtablezSampler().Register(); - result->inline_element_size.store(inline_element_size, - std::memory_order_relaxed); - return result; + return GlobalHashtablezSampler().Register(inline_element_size); #endif } diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 0fd9349f..6738786c 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -67,7 +67,8 @@ struct HashtablezInfo : public profiling_internal::Sample { // Puts the object into a clean state, fills in the logically `const` members, // blocking for any readers that are currently sampling the object. - void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); + void PrepareForSampling(size_t inline_element_size_value) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); // These fields are mutated by the various Record* APIs and need to be // thread-safe. @@ -82,18 +83,6 @@ struct HashtablezInfo : public profiling_internal::Sample { std::atomic hashes_bitwise_xor; std::atomic max_reserve; - // One could imagine that inline_element_size could be non-atomic, since it - // *almost* follows the rules for the fields that are set by - // `PrepareForSampling`. However, TSAN reports a race (see b/207323922) in - // which - // A: Thread 1: Register() returns, unlocking init_mu. - // B: Thread 2: Iterate() is called, locking init_mu. - // C: Thread 1: inlined_element_size is stored. - // D: Thread 2: inlined_element_size is accessed (a race). - // A simple solution is to make inline_element_size atomic so that we treat it - // at as we do the other atomic fields. - std::atomic inline_element_size; - // All of the fields below are set by `PrepareForSampling`, they must not be // mutated in `Record*` functions. They are logically `const` in that sense. // These are guarded by init_mu, but that is not externalized to clients, @@ -103,6 +92,7 @@ struct HashtablezInfo : public profiling_internal::Sample { absl::Time create_time; int32_t depth; void* stack[kMaxStackDepth]; + size_t inline_element_size; // How big is the slot? }; inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index a7307a20..ac6ed9b1 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -70,7 +70,8 @@ std::vector GetSizes(HashtablezSampler* s) { } HashtablezInfo* Register(HashtablezSampler* s, size_t size) { - auto* info = s->Register(); + const size_t test_element_size = 17; + auto* info = s->Register(test_element_size); assert(info != nullptr); info->size.store(size); return info; @@ -81,9 +82,8 @@ TEST(HashtablezInfoTest, PrepareForSampling) { const size_t test_element_size = 17; HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(); + info.PrepareForSampling(test_element_size); - info.inline_element_size.store(test_element_size, std::memory_order_relaxed); EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 0); @@ -95,7 +95,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); EXPECT_GE(info.create_time, test_start); - EXPECT_EQ(info.inline_element_size.load(), test_element_size); + EXPECT_EQ(info.inline_element_size, test_element_size); info.capacity.store(1, std::memory_order_relaxed); info.size.store(1, std::memory_order_relaxed); @@ -108,7 +108,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { info.max_reserve.store(1, std::memory_order_relaxed); info.create_time = test_start - absl::Hours(20); - info.PrepareForSampling(); + info.PrepareForSampling(test_element_size); EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 0); @@ -119,14 +119,15 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); - EXPECT_EQ(info.inline_element_size.load(), test_element_size); + EXPECT_EQ(info.inline_element_size, test_element_size); EXPECT_GE(info.create_time, test_start); } TEST(HashtablezInfoTest, RecordStorageChanged) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(); + const size_t test_element_size = 19; + info.PrepareForSampling(test_element_size); RecordStorageChangedSlow(&info, 17, 47); EXPECT_EQ(info.size.load(), 17); EXPECT_EQ(info.capacity.load(), 47); @@ -138,7 +139,8 @@ TEST(HashtablezInfoTest, RecordStorageChanged) { TEST(HashtablezInfoTest, RecordInsert) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(); + const size_t test_element_size = 23; + info.PrepareForSampling(test_element_size); EXPECT_EQ(info.max_probe_length.load(), 0); RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); EXPECT_EQ(info.max_probe_length.load(), 6); @@ -161,8 +163,7 @@ TEST(HashtablezInfoTest, RecordErase) { const size_t test_element_size = 29; HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(); - info.inline_element_size.store(test_element_size); + info.PrepareForSampling(test_element_size); EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.size.load(), 0); RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); @@ -170,15 +171,14 @@ TEST(HashtablezInfoTest, RecordErase) { RecordEraseSlow(&info); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 1); - EXPECT_EQ(info.inline_element_size.load(), test_element_size); + EXPECT_EQ(info.inline_element_size, test_element_size); } TEST(HashtablezInfoTest, RecordRehash) { const size_t test_element_size = 31; HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(); - info.inline_element_size.store(test_element_size); + info.PrepareForSampling(test_element_size); RecordInsertSlow(&info, 0x1, 0); RecordInsertSlow(&info, 0x2, kProbeLength); RecordInsertSlow(&info, 0x4, kProbeLength); @@ -197,13 +197,14 @@ TEST(HashtablezInfoTest, RecordRehash) { EXPECT_EQ(info.total_probe_length.load(), 3); EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.num_rehashes.load(), 1); - EXPECT_EQ(info.inline_element_size.load(), test_element_size); + EXPECT_EQ(info.inline_element_size, test_element_size); } TEST(HashtablezInfoTest, RecordReservation) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(); + const size_t test_element_size = 33; + info.PrepareForSampling(test_element_size); RecordReservationSlow(&info, 3); EXPECT_EQ(info.max_reserve.load(), 3); @@ -266,7 +267,8 @@ TEST(HashtablezSamplerTest, Sample) { TEST(HashtablezSamplerTest, Handle) { auto& sampler = GlobalHashtablezSampler(); - HashtablezInfoHandle h(sampler.Register()); + const size_t test_element_size = 39; + HashtablezInfoHandle h(sampler.Register(test_element_size)); auto* info = HashtablezInfoHandlePeer::GetInfo(&h); info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed); @@ -338,18 +340,19 @@ TEST(HashtablezSamplerTest, MultiThreaded) { ThreadPool pool(10); for (int i = 0; i < 10; ++i) { - pool.Schedule([&sampler, &stop]() { + const size_t elt_size = 10 + i % 2; + pool.Schedule([&sampler, &stop, elt_size]() { std::random_device rd; std::mt19937 gen(rd()); std::vector infoz; while (!stop.HasBeenNotified()) { if (infoz.empty()) { - infoz.push_back(sampler.Register()); + infoz.push_back(sampler.Register(elt_size)); } switch (std::uniform_int_distribution<>(0, 2)(gen)) { case 0: { - infoz.push_back(sampler.Register()); + infoz.push_back(sampler.Register(elt_size)); break; } case 1: { diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 4b9f6cc0..362b3cae 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -2075,8 +2075,7 @@ TEST(RawHashSamplerTest, Sample) { std::memory_order_relaxed)]++; reservations[info.max_reserve.load(std::memory_order_relaxed)]++; } - EXPECT_EQ(info.inline_element_size.load(std::memory_order_relaxed), - sizeof(int64_t)); + EXPECT_EQ(info.inline_element_size, sizeof(int64_t)); ++end_size; }); diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index 0d4ebeda..ca1ed408 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -41,6 +41,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/base/macros.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/node_slot_policy.h" diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 3bc5fe6b..9421e11e 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -38,6 +38,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/base/macros.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/node_slot_policy.h" #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export diff --git a/absl/container/sample_element_size_test.cc b/absl/container/sample_element_size_test.cc index 4bbef210..b23626b4 100644 --- a/absl/container/sample_element_size_test.cc +++ b/absl/container/sample_element_size_test.cc @@ -51,8 +51,7 @@ void TestInlineElementSize( size_t new_count = 0; sampler.Iterate([&](const HashtablezInfo& info) { if (preexisting_info.insert(&info).second) { - EXPECT_EQ(info.inline_element_size.load(std::memory_order_relaxed), - expected_element_size); + EXPECT_EQ(info.inline_element_size, expected_element_size); ++new_count; } }); diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h index 824e3cea..f9779607 100644 --- a/absl/functional/function_ref.h +++ b/absl/functional/function_ref.h @@ -69,7 +69,8 @@ class FunctionRef; // An `absl::FunctionRef` is a lightweight wrapper to any invokable object with // a compatible signature. Generally, an `absl::FunctionRef` should only be used // as an argument type and should be preferred as an argument over a const -// reference to a `std::function`. +// reference to a `std::function`. `absl::FunctionRef` itself does not allocate, +// although the wrapped invokable may. // // Example: // diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc index 3aa59745..412027cd 100644 --- a/absl/functional/function_ref_test.cc +++ b/absl/functional/function_ref_test.cc @@ -14,6 +14,7 @@ #include "absl/functional/function_ref.h" +#include #include #include "gmock/gmock.h" diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h index 6c146210..fc269bdd 100644 --- a/absl/profiling/internal/sample_recorder.h +++ b/absl/profiling/internal/sample_recorder.h @@ -59,7 +59,8 @@ class SampleRecorder { ~SampleRecorder(); // Registers for sampling. Returns an opaque registration info. - T* Register(); + template + T* Register(Targs&&... args); // Unregisters the sample. void Unregister(T* sample); @@ -81,7 +82,8 @@ class SampleRecorder { private: void PushNew(T* sample); void PushDead(T* sample); - T* PopDead(); + template + T* PopDead(Targs... args); std::atomic dropped_samples_; std::atomic size_estimate_; @@ -163,7 +165,8 @@ void SampleRecorder::PushDead(T* sample) { } template -T* SampleRecorder::PopDead() { +template +T* SampleRecorder::PopDead(Targs... args) { absl::MutexLock graveyard_lock(&graveyard_.init_mu); // The list is circular, so eventually it collapses down to @@ -175,12 +178,13 @@ T* SampleRecorder::PopDead() { absl::MutexLock sample_lock(&sample->init_mu); graveyard_.dead = sample->dead; sample->dead = nullptr; - sample->PrepareForSampling(); + sample->PrepareForSampling(std::forward(args)...); return sample; } template -T* SampleRecorder::Register() { +template +T* SampleRecorder::Register(Targs&&... args) { int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); if (size > max_samples_.load(std::memory_order_relaxed)) { size_estimate_.fetch_sub(1, std::memory_order_relaxed); @@ -188,10 +192,14 @@ T* SampleRecorder::Register() { return nullptr; } - T* sample = PopDead(); + T* sample = PopDead(args...); if (sample == nullptr) { // Resurrection failed. Hire a new warlock. sample = new T(); + { + absl::MutexLock sample_lock(&sample->init_mu); + sample->PrepareForSampling(std::forward(args)...); + } PushNew(sample); } diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 0015bb9f..5905bac0 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -468,46 +468,6 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, return true; } -template -void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, - size_t length) { - auto constexpr method = CordzUpdateTracker::kGetAppendRegion; - - CordRep* root = tree(); - size_t sz = root ? root->length : inline_size(); - if (root == nullptr) { - size_t available = kMaxInline - sz; - if (available >= (has_length ? length : 1)) { - *region = data_.as_chars() + sz; - *size = has_length ? length : available; - set_inline_size(has_length ? sz + length : kMaxInline); - return; - } - } - - size_t extra = has_length ? length : (std::max)(sz, kMinFlatLength); - CordzUpdateScope scope(root ? data_.cordz_info() : nullptr, method); - CordRep* rep = root ? cord_internal::RemoveCrcNode(root) - : MakeFlatWithExtraCapacity(extra); - if (PrepareAppendRegion(rep, region, size, length)) { - CommitTree(root, rep, scope, method); - return; - } - - // Allocate new node. - CordRepFlat* new_node = CordRepFlat::New(extra); - new_node->length = std::min(new_node->Capacity(), length); - *region = new_node->Data(); - *size = new_node->length; - - if (btree_enabled()) { - rep = CordRepBtree::Append(ForceBtree(rep), new_node); - } else { - rep = Concat(rep, new_node); - } - CommitTree(root, rep, scope, method); -} - // Computes the memory side of the provided edge which must be a valid data edge // for a btrtee, i.e., a FLAT, EXTERNAL or SUBSTRING of a FLAT or EXTERNAL node. static bool RepMemoryUsageDataEdge(const CordRep* rep, diff --git a/absl/strings/internal/cordz_update_tracker.h b/absl/strings/internal/cordz_update_tracker.h index 7a41c180..c5170662 100644 --- a/absl/strings/internal/cordz_update_tracker.h +++ b/absl/strings/internal/cordz_update_tracker.h @@ -39,8 +39,8 @@ class CordzUpdateTracker { // Tracked update methods. enum MethodIdentifier { kUnknown, - kAppendBuffer, kAppendCord, + kAppendCordBuffer, kAppendExternalMemory, kAppendString, kAssignCord, @@ -50,13 +50,14 @@ class CordzUpdateTracker { kConstructorString, kCordReader, kFlatten, + kGetAppendBuffer, kGetAppendRegion, kMakeCordFromExternal, kMoveAppendCord, kMoveAssignCord, kMovePrependCord, - kPrependBuffer, kPrependCord, + kPrependCordBuffer, kPrependString, kRemovePrefix, kRemoveSuffix, diff --git a/absl/strings/internal/cordz_update_tracker_test.cc b/absl/strings/internal/cordz_update_tracker_test.cc index 8eda529e..9b1f7986 100644 --- a/absl/strings/internal/cordz_update_tracker_test.cc +++ b/absl/strings/internal/cordz_update_tracker_test.cc @@ -37,8 +37,8 @@ using Methods = std::array; // Returns an array of all methods defined in `MethodIdentifier` Methods AllMethods() { return Methods{Method::kUnknown, - Method::kAppendBuffer, Method::kAppendCord, + Method::kAppendCordBuffer, Method::kAppendExternalMemory, Method::kAppendString, Method::kAssignCord, @@ -48,13 +48,14 @@ Methods AllMethods() { Method::kConstructorString, Method::kCordReader, Method::kFlatten, + Method::kGetAppendBuffer, Method::kGetAppendRegion, Method::kMakeCordFromExternal, Method::kMoveAppendCord, Method::kMoveAssignCord, Method::kMovePrependCord, - Method::kPrependBuffer, Method::kPrependCord, + Method::kPrependCordBuffer, Method::kPrependString, Method::kRemovePrefix, Method::kRemoveSuffix, diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h index 8aadde57..a5b084e6 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h +++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h @@ -84,14 +84,13 @@ CONSTEXPR_F bool is_leap_year(year_t y) noexcept { return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); } CONSTEXPR_F int year_index(year_t y, month_t m) noexcept { - return (static_cast((y + (m > 2)) % 400) + 400) % 400; + const int yi = static_cast((y + (m > 2)) % 400); + return yi < 0 ? yi + 400 : yi; } -CONSTEXPR_F int days_per_century(year_t y, month_t m) noexcept { - const int yi = year_index(y, m); +CONSTEXPR_F int days_per_century(int yi) noexcept { return 36524 + (yi == 0 || yi > 300); } -CONSTEXPR_F int days_per_4years(year_t y, month_t m) noexcept { - const int yi = year_index(y, m); +CONSTEXPR_F int days_per_4years(int yi) noexcept { return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96); } CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept { @@ -133,17 +132,22 @@ CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh, } } if (d > 365) { + int yi = year_index(ey, m); // Index into Gregorian 400 year cycle. for (;;) { - int n = days_per_century(ey, m); + int n = days_per_century(yi); if (d <= n) break; d -= n; ey += 100; + yi += 100; + if (yi >= 400) yi -= 400; } for (;;) { - int n = days_per_4years(ey, m); + int n = days_per_4years(yi); if (d <= n) break; d -= n; ey += 4; + yi += 4; + if (yi >= 400) yi -= 400; } for (;;) { int n = days_per_year(ey, m); -- cgit v1.2.3 From fb7dd24b18e82893e5922be5d1c8ae0f3fe3c9fa Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Fri, 10 Dec 2021 10:50:53 -0800 Subject: cmake: add ABSL_BUILD_TESTING option (#1057) Abseil's own tests now are disabled if either BUILD_TESTING or a new option called ABSL_BUILD_TESTING is false. Additionally, Abseil's CMakeLists.txt no longer re-declares the BUILD_TESTING option with a value of false. Abseil had been using just the BUILD_TESTING option, since the fix for #901. Because setting BUILD_TESTING false still works to disable Abseil's tests, this change preserves the behavior asked for in that issue. Previous to that, Abseil had a project specific flag for this, as is the typical idiom used in other projects. The issue with BUILD_TESTING is that it is an all-or-nothing policy. When Abseil is incorporated as a subproject, the encompasing project has no convenient way to enable its own tests while disabling Abseil's. Fixes #1056 --- CMake/AbseilHelpers.cmake | 7 ++++--- CMake/README.md | 13 ++++++++----- CMake/install_test_project/test.sh | 2 +- CMakeLists.txt | 9 ++++----- ci/linux_gcc-latest_libstdcxx_cmake.sh | 2 +- ci/linux_gcc_alpine_cmake.sh | 2 +- ci/macos_xcode_cmake.sh | 2 +- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 17c4f449..9ad2627d 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -40,7 +40,8 @@ endif() # LINKOPTS: List of link options # PUBLIC: Add this so that this library will be exported under absl:: # Also in IDE, target will appear in Abseil folder while non PUBLIC will be in Abseil/internal. -# TESTONLY: When added, this target will only be built if BUILD_TESTING=ON. +# TESTONLY: When added, this target will only be built if both +# BUILD_TESTING=ON and ABSL_BUILD_TESTING=ON. # # Note: # By default, absl_cc_library will always create a library named absl_${NAME}, @@ -82,7 +83,7 @@ function(absl_cc_library) ${ARGN} ) - if(ABSL_CC_LIB_TESTONLY AND NOT BUILD_TESTING) + if(ABSL_CC_LIB_TESTONLY AND NOT (BUILD_TESTING AND ABSL_BUILD_TESTING)) return() endif() @@ -364,7 +365,7 @@ endfunction() # GTest::gtest_main # ) function(absl_cc_test) - if(NOT BUILD_TESTING) + if(NOT (BUILD_TESTING AND ABSL_BUILD_TESTING)) return() endif() diff --git a/CMake/README.md b/CMake/README.md index f8b27e63..8134615e 100644 --- a/CMake/README.md +++ b/CMake/README.md @@ -20,8 +20,10 @@ googletest framework ### Step-by-Step Instructions 1. If you want to build the Abseil tests, integrate the Abseil dependency -[Google Test](https://github.com/google/googletest) into your CMake project. To disable Abseil tests, you have to pass -`-DBUILD_TESTING=OFF` when configuring your project with CMake. +[Google Test](https://github.com/google/googletest) into your CMake +project. To disable Abseil tests, you have to pass either +`-DBUILD_TESTING=OFF` or `-DABSL_BUILD_TESTING=OFF` when configuring your +project with CMake. 2. Download Abseil and copy it into a subdirectory in your CMake project or add Abseil as a [git submodule](https://git-scm.com/docs/git-submodule) in your @@ -91,7 +93,8 @@ setting a consistent `CMAKE_CXX_STANDARD` that is sufficiently high. ### Running Abseil Tests with CMake -Use the `-DBUILD_TESTING=ON` flag to run Abseil tests. +Use the `-DABSL_BUILD_TESTING=ON` flag to run Abseil tests. Note that +BUILD_TESTING must also be on (the default). You will need to provide Abseil with a Googletest dependency. There are two options for how to do this: @@ -109,7 +112,7 @@ For example, to run just the Abseil tests, you could use this script: cd path/to/abseil-cpp mkdir build cd build -cmake -DBUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=ON .. +cmake -DABSL_BUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=ON .. make -j ctest ``` @@ -175,7 +178,7 @@ cmake --build /temporary/build/abseil-cpp --target install ## Google Test Options -`-DBUILD_TESTING=ON` must be set to enable testing +`-DABSL_BUILD_TESTING=ON` must be set to enable testing - Have Abseil download and build Google Test for you: `-DABSL_USE_EXTERNAL_GOOGLETEST=OFF` (default) - Download and build latest Google Test: `-DABSL_USE_GOOGLETEST_HEAD=ON` diff --git a/CMake/install_test_project/test.sh b/CMake/install_test_project/test.sh index aecbb8fe..cc028bac 100755 --- a/CMake/install_test_project/test.sh +++ b/CMake/install_test_project/test.sh @@ -55,7 +55,7 @@ cmake "${absl_dir}" \ -DABSL_USE_EXTERNAL_GOOGLETEST=ON \ -DABSL_FIND_GOOGLETEST=ON \ -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_TESTING=ON \ + -DABSL_BUILD_TESTING=ON \ -DBUILD_SHARED_LIBS="${build_shared_libs}" make -j $(nproc) ctest -j $(nproc) --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index a1400b74..ff49ac1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,10 +46,6 @@ if (POLICY CMP0091) cmake_policy(SET CMP0091 NEW) endif (POLICY CMP0091) -# Set BUILD_TESTING to OFF by default. -# This must come before the project() and include(CTest) lines. -OPTION(BUILD_TESTING "Build tests" OFF) - project(absl LANGUAGES CXX) include(CTest) @@ -111,6 +107,9 @@ find_package(Threads REQUIRED) include(CMakeDependentOption) +option(ABSL_BUILD_TESTING + "If ON, Abseil will build all of Abseil's own tests." OFF) + option(ABSL_USE_EXTERNAL_GOOGLETEST "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subproject." OFF) @@ -130,7 +129,7 @@ set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH "If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout." ) -if(BUILD_TESTING) +if(BUILD_TESTING AND ABSL_BUILD_TESTING) ## check targets if (ABSL_USE_EXTERNAL_GOOGLETEST) if (ABSL_FIND_GOOGLETEST) diff --git a/ci/linux_gcc-latest_libstdcxx_cmake.sh b/ci/linux_gcc-latest_libstdcxx_cmake.sh index ab06aa05..eccb3818 100755 --- a/ci/linux_gcc-latest_libstdcxx_cmake.sh +++ b/ci/linux_gcc-latest_libstdcxx_cmake.sh @@ -54,7 +54,7 @@ for std in ${ABSL_CMAKE_CXX_STANDARDS}; do cmake /abseil-cpp \ -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \ -DBUILD_SHARED_LIBS=${build_shared} \ - -DBUILD_TESTING=ON \ + -DABSL_BUILD_TESTING=ON \ -DCMAKE_BUILD_TYPE=${compilation_mode} \ -DCMAKE_CXX_STANDARD=${std} \ -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \ diff --git a/ci/linux_gcc_alpine_cmake.sh b/ci/linux_gcc_alpine_cmake.sh index bce27d29..bf2e1239 100755 --- a/ci/linux_gcc_alpine_cmake.sh +++ b/ci/linux_gcc_alpine_cmake.sh @@ -53,7 +53,7 @@ for std in ${ABSL_CMAKE_CXX_STANDARDS}; do /bin/sh -c " cmake /abseil-cpp \ -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \ - -DBUILD_TESTING=ON \ + -DABSL_BUILD_TESTING=ON \ -DCMAKE_BUILD_TYPE=${compilation_mode} \ -DCMAKE_CXX_STANDARD=${std} \ -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \ diff --git a/ci/macos_xcode_cmake.sh b/ci/macos_xcode_cmake.sh index 2a870cf4..71ea2534 100755 --- a/ci/macos_xcode_cmake.sh +++ b/ci/macos_xcode_cmake.sh @@ -45,7 +45,7 @@ for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do time cmake ${ABSEIL_ROOT} \ -GXcode \ -DBUILD_SHARED_LIBS=${build_shared} \ - -DBUILD_TESTING=ON \ + -DABSL_BUILD_TESTING=ON \ -DCMAKE_BUILD_TYPE=${compilation_mode} \ -DCMAKE_CXX_STANDARD=11 \ -DCMAKE_MODULE_LINKER_FLAGS="-Wl,--no-undefined" \ -- cgit v1.2.3 From 1065514ef332d036f3437e950e78d35ce6b7c740 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 13 Dec 2021 14:47:02 -0800 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- 07240ca7822d007cdcc79f2c40bd58b2c2010348 by Abseil Team : Correct the comment from "AlphaNum" to "Arg". PiperOrigin-RevId: 416139192 -- adcba4a6b3763626e1db7b1e8c108b3114903557 by Martijn Vels : Fix NewExternalRep() to require data being non-empty, and remove nullptr return. PiperOrigin-RevId: 416135865 -- c0d14cd918fb16f15d1d84de9284b5c5ecc1f8f2 by Abseil Team : Fix doc comment for absl::ascii_isprint(). The comment was incorrectly saying that it includes all whitespace. It doesn't; the only whitespace char it includes is ' '. PiperOrigin-RevId: 416112524 -- d83327800159c07002b6865e21232a12463e02dd by Abseil Team : Internal change PiperOrigin-RevId: 416099978 -- baf11e9ca42ca9140cdbf8075f971db8d65b1195 by Ilya Tokar : Prevent compiler from optimizing Group_Match* benchmarks away. Currently we benchmark single store of precomputed value. Not all affected benchmarks show performance changes: BM_Group_Match 0.53ns ± 1% 0.53ns ± 0% -0.42% (p=0.038 n=10+10) BM_Group_MatchEmpty 0.26ns ± 1% 0.26ns ± 1% ~ (p=1.000 n=10+10) BM_Group_MatchEmptyOrDeleted 0.26ns ± 1% 0.26ns ± 1% ~ (p=0.121 n=10+10) BM_Group_CountLeadingEmptyOrDeleted 0.26ns ± 1% 0.45ns ± 0% +70.05% (p=0.000 n=10+8) BM_Group_MatchFirstEmptyOrDeleted 0.26ns ± 0% 0.44ns ± 1% +65.91% (p=0.000 n=8+9) But inspecting the generated code shows the difference, e. g. BM_Group_MatchFirstEmptyOrDeleted Before: add $0xffffffffffffffff,%rbx jne 30 After: pcmpeqd %xmm0,%xmm0 pcmpgtb -0x30(%rbp),%xmm0 pmovmskb %xmm0,%eax add: 0x23$0xffffffffffffffff,%rbx jne 40 PiperOrigin-RevId: 416083515 -- 122fbff893dc4571b3e75e4b241eb4495b925610 by Abseil Team : Put namespace guard in ABSL_DECLARE_FLAG to make declaring a flag in a namespace a compiler error instead of a linker error. PiperOrigin-RevId: 416036072 -- 020fd8a20f5fa319e948846e003391fcb9e03868 by Ilya Tokar : Make Cord::InlineRep::set_data unconditionally zero out memory. Currently there is a single case where we don't zero out memory as an optimization. Unconditional zeroing doesn't show any changes in benchmarks, except for the unrelated improvement: BM_CordPartialCopyToCord/1M/1 12.6ns ± 4% 12.6ns ± 4% ~ (p=0.857 n=16+19) BM_CordPartialCopyToCord/1M/128 44.9ns ± 7% 45.0ns ± 3% ~ (p=0.468 n=18+17) BM_CordPartialCopyToCord/1M/1k 64.5ns ± 4% 61.4ns ± 4% -4.82% (p=0.000 n=19+17) BM_CordPartialCopyToCord/1M/8k 139ns ± 3% 128ns ±15% -7.76% (p=0.009 n=17+20) BM_CordPartialCopyToCord/1M/16k 193ns ± 6% 168ns ± 6% -13.17% (p=0.000 n=17+17) BM_CordPartialCopyToCord/4M/16k 199ns ± 4% 177ns ± 4% -11.36% (p=0.000 n=17+18) BM_CordPartialCopyToCord/4M/32k 275ns ± 3% 250ns ± 4% -9.00% (p=0.000 n=18+18) BM_CordPartialCopyToCord/4M/64k 291ns ± 4% 266ns ± 5% -8.53% (p=0.000 n=18+16) BM_CordPartialCopyToCord/4M/128k 322ns ± 5% 291ns ± 4% -9.43% (p=0.000 n=20+18) BM_CordPartialCopyToCord/8M/32k 281ns ± 5% 251ns ± 4% -10.38% (p=0.000 n=20+16) BM_CordPartialCopyToCord/8M/64k 293ns ± 6% 267ns ± 4% -8.87% (p=0.000 n=16+19) BM_CordPartialCopyToCord/8M/128k 334ns ± 3% 305ns ± 2% -8.56% (p=0.000 n=17+16) This is clearly an alignmnet effect since number of the executed instructions is the same: M_CordPartialCopyToCord/1M/1 155 ± 0% 155 ± 0% ~ (all samples are equal) BM_CordPartialCopyToCord/1M/128 446 ± 0% 446 ± 0% ~ (p=0.332 n=36+39) BM_CordPartialCopyToCord/1M/1k 473 ± 0% 473 ± 0% ~ (p=0.969 n=40+40) BM_CordPartialCopyToCord/1M/8k 808 ± 0% 808 ± 0% ~ (p=0.127 n=40+39) BM_CordPartialCopyToCord/1M/16k 957 ± 0% 957 ± 0% ~ (p=0.532 n=40+40) BM_CordPartialCopyToCord/4M/16k 952 ± 0% 952 ± 0% ~ (p=0.686 n=39+39) BM_CordPartialCopyToCord/4M/32k 1.12k ± 0% 1.12k ± 0% ~ (p=0.690 n=40+40) BM_CordPartialCopyToCord/4M/64k 1.23k ± 0% 1.23k ± 0% ~ (p=0.182 n=40+39) BM_CordPartialCopyToCord/4M/128k 1.44k ± 0% 1.44k ± 0% ~ (p=0.711 n=40+40) BM_CordPartialCopyToCord/8M/32k 1.12k ± 0% 1.12k ± 0% ~ (p=0.697 n=40+40) BM_CordPartialCopyToCord/8M/64k 1.23k ± 0% 1.23k ± 0% +0.00% (p=0.049 n=40+40) BM_CordPartialCopyToCord/8M/128k 1.44k ± 0% 1.44k ± 0% ~ (p=0.507 n=40+40) This makes code simpler and doesn't regress performance. PiperOrigin-RevId: 415560574 -- 37305b2690b31682088749e4d62f40d7095bdc54 by Derek Mauro : Internal change PiperOrigin-RevId: 415558737 -- 86aaed569b9e743c1eb813a5f48def978a793db3 by Martijn Vels : Internal change PiperOrigin-RevId: 415515201 -- 6cdb8786cdcb4fa0b8a4b72fc98940877d1fdeff by Abseil Team : Update SubmitMutexProfileData to accept wait_cycles instead of wait_timestamp PiperOrigin-RevId: 415360871 -- 9f979d307aa16ad09f214e04876cbe84395c0901 by Abseil Team : absl::flat_hash_set compiles with -Wconversion -Wsign-compare PiperOrigin-RevId: 415357498 -- 9eceb14174708f15e61259d449b214a8a4c7f9e7 by Abseil Team : Fix AddressIsReadable for the corner case of (aligned) addr == NULL. PiperOrigin-RevId: 415307792 -- 1a39ffe55898375e2d7f88c17c99db5a1b95b313 by Martijn Vels : Internal change PiperOrigin-RevId: 415162872 -- 64378549b110d5f5762185a5906c520fba70f0e7 by Abseil Team : Fix a typo in the comments PiperOrigin-RevId: 415088461 -- 41aae8322e913b82710153c22b97c611fdb6e1fb by Abseil Team : Switch from `connect` to `rt_sigreturn` -- the latter is much less problematic for system call sandboxes. PiperOrigin-RevId: 415073965 -- 870c5e3388b6a35611bff538626fe7a1c8c87171 by Abseil Team : Add ABSL_HAVE_HWADDRESS_SANITIZER and ABSL_HAVE_LEAK_SANITIZER PiperOrigin-RevId: 414871189 -- f213ed60a66b58da7ac40555adfb1d529ff0a4db by Derek Mauro : Remove reference to __SANITIZE_MEMORY__, which does not exist It appears to have been copied by pattern matching from the ASAN/TSAN code blocks. https://github.com/gcc-mirror/gcc/blob/f47662204de27f7685699eeef89aa173ccf32d85/gcc/cppbuiltin.c#L79-L126 PiperOrigin-RevId: 414806587 -- b152891e73ab515f397ceb53f66c8ee2f33863ea by Abseil Team : Rollback previous commit: SYS_open is not defined in certain environments. PiperOrigin-RevId: 414521820 -- 5a1cbb282331023902e1374dd0d920c4effbe47f by Abseil Team : Use syscall(SYS_open, ...) instead of open() to avoid possible symbol interposition. Also add some warning notes. PiperOrigin-RevId: 414508186 -- 1824d6593612710aafdc599a89b0adced7d787f6 by Abseil Team : Correct aarch64 macro check The macro is __aarch64__, not __arch64__. PiperOrigin-RevId: 414446225 -- a1536a57b64dfd53945d33a01cfc08b18c99c97b by Abseil Team : Fix backwards comment in the last commit. PiperOrigin-RevId: 414281214 -- 11ac021ba779513667a31cf2563ddafc57d6d913 by Abseil Team : AddressIsReadable() didn't work correctly on ARM when the given pointer was misaligned at the end of the page. Fix that by aligning the pointer on an 8-byte boundary before checking it. PiperOrigin-RevId: 414203863 GitOrigin-RevId: 07240ca7822d007cdcc79f2c40bd58b2c2010348 Change-Id: If5f129194d59f5c9e5d84efd8cd9e17a70e072ab --- absl/base/config.h | 24 ++++- absl/base/internal/direct_mmap.h | 2 +- absl/container/internal/raw_hash_set.h | 27 +++--- absl/container/internal/raw_hash_set_benchmark.cc | 20 ++++- absl/debugging/internal/address_is_readable.cc | 102 ++++++++++------------ absl/flags/declare.h | 6 +- absl/random/internal/randen_detect.cc | 1 - absl/strings/ascii.h | 2 +- absl/strings/cord.cc | 19 ++-- absl/strings/cord.h | 34 ++++---- absl/strings/cord_test.cc | 77 +++++++++++----- absl/strings/substitute.h | 4 +- absl/synchronization/mutex.cc | 2 +- absl/synchronization/notification.h | 2 +- 14 files changed, 189 insertions(+), 133 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index c29b206a..373aa0cc 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -751,8 +751,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // a compiler instrumentation module and a run-time library. #ifdef ABSL_HAVE_MEMORY_SANITIZER #error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set." -#elif defined(__SANITIZE_MEMORY__) -#define ABSL_HAVE_MEMORY_SANITIZER 1 #elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer) #define ABSL_HAVE_MEMORY_SANITIZER 1 #endif @@ -779,6 +777,28 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_ADDRESS_SANITIZER 1 #endif +// ABSL_HAVE_HWADDRESS_SANITIZER +// +// Hardware-Assisted AddressSanitizer (or HWASAN) is even faster than asan +// memory error detector which can use CPU features like ARM TBI, Intel LAM or +// AMD UAI. +#ifdef ABSL_HAVE_HWADDRESS_SANITIZER +#error "ABSL_HAVE_HWADDRESS_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_HWADDRESS__) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(hwaddress_sanitizer) +#define ABSL_HAVE_HWADDRESS_SANITIZER 1 +#endif + +// ABSL_HAVE_LEAK_SANITIZER +// +// LeakSanitizer (or lsan) is a detector of memory leaks. +#ifdef ABSL_HAVE_LEAK_SANITIZER +#error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set." +#elif ABSL_HAVE_FEATURE(leak_sanitizer) +#define ABSL_HAVE_LEAK_SANITIZER 1 +#endif + // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION // // Class template argument deduction is a language feature added in C++17. diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h index 274054cd..7037094b 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -80,7 +80,7 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, (defined(__PPC__) && !defined(__PPC64__)) || \ (defined(__riscv) && __riscv_xlen == 32) || \ (defined(__s390__) && !defined(__s390x__)) || \ - (defined(__sparc__) && !defined(__arch64__)) + (defined(__sparc__) && !defined(__aarch64__)) // On these architectures, implement mmap with mmap2. static int pagesize = 0; if (pagesize == 0) { diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index d4b72ab1..ad12f41c 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -201,7 +201,7 @@ constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) { template uint32_t TrailingZeros(T x) { ABSL_INTERNAL_ASSUME(x != 0); - return countr_zero(x); + return static_cast(countr_zero(x)); } // An abstraction over a bitmask. It provides an easy way to iterate through the @@ -230,7 +230,7 @@ class BitMask { return *this; } explicit operator bool() const { return mask_ != 0; } - int operator*() const { return LowestBitSet(); } + uint32_t operator*() const { return LowestBitSet(); } uint32_t LowestBitSet() const { return container_internal::TrailingZeros(mask_) >> Shift; } @@ -248,7 +248,7 @@ class BitMask { uint32_t LeadingZeros() const { constexpr int total_significant_bits = SignificantBits << Shift; constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; - return countl_zero(mask_ << extra_bits) >> Shift; + return static_cast(countl_zero(mask_ << extra_bits)) >> Shift; } private: @@ -360,7 +360,7 @@ struct GroupSse2Impl { BitMask Match(h2_t hash) const { auto match = _mm_set1_epi8(hash); return BitMask( - _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))); + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); } // Returns a bitmask representing the positions of empty slots. @@ -368,7 +368,7 @@ struct GroupSse2Impl { #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 // This only works because ctrl_t::kEmpty is -128. return BitMask( - _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); + static_cast(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)))); #else return Match(static_cast(ctrl_t::kEmpty)); #endif @@ -376,14 +376,15 @@ struct GroupSse2Impl { // Returns a bitmask representing the positions of empty or deleted slots. BitMask MatchEmptyOrDeleted() const { - auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); + auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); return BitMask( - _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))); + static_cast( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)))); } // Returns the number of trailing empty or deleted elements in the group. uint32_t CountLeadingEmptyOrDeleted() const { - auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); + auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); return TrailingZeros(static_cast( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1)); } @@ -1465,7 +1466,7 @@ class raw_hash_set { auto seq = probe(ctrl_, hash, capacity_); while (true) { Group g{ctrl_ + seq.offset()}; - for (int i : g.Match(H2(hash))) { + for (uint32_t i : g.Match(H2(hash))) { if (ABSL_PREDICT_TRUE(PolicyTraits::apply( EqualElement{key, eq_ref()}, PolicyTraits::element(slots_ + seq.offset(i))))) @@ -1610,7 +1611,7 @@ class raw_hash_set { void erase_meta_only(const_iterator it) { assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator"); --size_; - const size_t index = it.inner_.ctrl_ - ctrl_; + const size_t index = static_cast(it.inner_.ctrl_ - ctrl_); const size_t index_before = (index - Group::kWidth) & capacity_; const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty(); const auto empty_before = Group(ctrl_ + index_before).MatchEmpty(); @@ -1832,7 +1833,7 @@ class raw_hash_set { auto seq = probe(ctrl_, hash, capacity_); while (true) { Group g{ctrl_ + seq.offset()}; - for (int i : g.Match(H2(hash))) { + for (uint32_t i : g.Match(H2(hash))) { if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) == elem)) return true; @@ -1864,7 +1865,7 @@ class raw_hash_set { auto seq = probe(ctrl_, hash, capacity_); while (true) { Group g{ctrl_ + seq.offset()}; - for (int i : g.Match(H2(hash))) { + for (uint32_t i : g.Match(H2(hash))) { if (ABSL_PREDICT_TRUE(PolicyTraits::apply( EqualElement{key, eq_ref()}, PolicyTraits::element(slots_ + seq.offset(i))))) @@ -1984,7 +1985,7 @@ struct HashtableDebugAccess> { auto seq = probe(set.ctrl_, hash, set.capacity_); while (true) { container_internal::Group g{set.ctrl_ + seq.offset()}; - for (int i : g.Match(container_internal::H2(hash))) { + for (uint32_t i : g.Match(container_internal::H2(hash))) { if (Traits::apply( typename Set::template EqualElement{ key, set.eq_ref()}, diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc index c886d3ad..146ef433 100644 --- a/absl/container/internal/raw_hash_set_benchmark.cc +++ b/absl/container/internal/raw_hash_set_benchmark.cc @@ -330,6 +330,7 @@ void BM_Group_Match(benchmark::State& state) { h2_t h = 1; for (auto _ : state) { ::benchmark::DoNotOptimize(h); + ::benchmark::DoNotOptimize(g); ::benchmark::DoNotOptimize(g.Match(h)); } } @@ -339,7 +340,10 @@ void BM_Group_MatchEmpty(benchmark::State& state) { std::array group; Iota(group.begin(), group.end(), -4); Group g{group.data()}; - for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmpty()); + for (auto _ : state) { + ::benchmark::DoNotOptimize(g); + ::benchmark::DoNotOptimize(g.MatchEmpty()); + } } BENCHMARK(BM_Group_MatchEmpty); @@ -347,7 +351,10 @@ void BM_Group_MatchEmptyOrDeleted(benchmark::State& state) { std::array group; Iota(group.begin(), group.end(), -4); Group g{group.data()}; - for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted()); + for (auto _ : state) { + ::benchmark::DoNotOptimize(g); + ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted()); + } } BENCHMARK(BM_Group_MatchEmptyOrDeleted); @@ -355,8 +362,10 @@ void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) { std::array group; Iota(group.begin(), group.end(), -2); Group g{group.data()}; - for (auto _ : state) + for (auto _ : state) { + ::benchmark::DoNotOptimize(g); ::benchmark::DoNotOptimize(g.CountLeadingEmptyOrDeleted()); + } } BENCHMARK(BM_Group_CountLeadingEmptyOrDeleted); @@ -364,7 +373,10 @@ void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) { std::array group; Iota(group.begin(), group.end(), -2); Group g{group.data()}; - for (auto _ : state) ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted()); + for (auto _ : state) { + ::benchmark::DoNotOptimize(g); + ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted()); + } } BENCHMARK(BM_Group_MatchFirstEmptyOrDeleted); diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 26df3289..4be6256b 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -30,16 +30,12 @@ bool AddressIsReadable(const void* /* addr */) { return true; } ABSL_NAMESPACE_END } // namespace absl -#else +#else // __linux__ && !__ANDROID__ -#include -#include -#include -#include +#include +#include #include -#include - #include "absl/base/internal/errno_saver.h" #include "absl/base/internal/raw_logging.h" @@ -47,60 +43,54 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { +// NOTE: be extra careful about adding any interposable function calls here +// (such as open(), read(), etc.). These symbols may be interposed and will get +// invoked in contexts they don't expect. +// +// NOTE: any new system calls here may also require sandbox reconfiguration. +// bool AddressIsReadable(const void *addr) { - int fd = 0; + // Align address on 8-byte boundary. On aarch64, checking last + // byte before inaccessible page returned unexpected EFAULT. + const uintptr_t u_addr = reinterpret_cast(addr) & ~7; + addr = reinterpret_cast(u_addr); + + // rt_sigprocmask below will succeed for this input. + if (addr == nullptr) return false; + absl::base_internal::ErrnoSaver errno_saver; - for (int j = 0; j < 2; j++) { - // Here we probe with some syscall which - // - accepts a one-byte region of user memory as input - // - tests for EFAULT before other validation - // - has no problematic side-effects - // - // connect(2) works for this. It copies the address into kernel - // memory before any validation beyond requiring an open fd. - // But a one byte address is never valid (sa_family is two bytes), - // so the call cannot succeed and change any state. - // - // This strategy depends on Linux implementation details, - // so we rely on the test to alert us if it stops working. - // - // Some discarded past approaches: - // - msync() doesn't reject PROT_NONE regions - // - write() on /dev/null doesn't return EFAULT - // - write() on a pipe requires creating it and draining the writes - // - // Use syscall(SYS_connect, ...) instead of connect() to prevent ASAN - // and other checkers from complaining about accesses to arbitrary memory. - do { - ABSL_RAW_CHECK(syscall(SYS_connect, fd, addr, 1) == -1, - "should never succeed"); - } while (errno == EINTR); - if (errno == EFAULT) return false; - if (errno == EBADF) { - if (j != 0) { - // Unclear what happened. - ABSL_RAW_LOG(ERROR, "unexpected EBADF on fd %d", fd); - return false; - } - // fd 0 must have been closed. Try opening it again. - // Note: we shouldn't leak too many file descriptors here, since we expect - // to get fd==0 reopened. - fd = open("/dev/null", O_RDONLY); - if (fd == -1) { - ABSL_RAW_LOG(ERROR, "can't open /dev/null"); - return false; - } - } else { - // probably EINVAL or ENOTSOCK; we got past EFAULT validation. - return true; - } - } - ABSL_RAW_CHECK(false, "unreachable"); - return false; + + // Here we probe with some syscall which + // - accepts an 8-byte region of user memory as input + // - tests for EFAULT before other validation + // - has no problematic side-effects + // + // rt_sigprocmask(2) works for this. It copies sizeof(kernel_sigset_t)==8 + // bytes from the address into the kernel memory before any validation. + // + // The call can never succeed, since the `how` parameter is not one of + // SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK. + // + // This strategy depends on Linux implementation details, + // so we rely on the test to alert us if it stops working. + // + // Some discarded past approaches: + // - msync() doesn't reject PROT_NONE regions + // - write() on /dev/null doesn't return EFAULT + // - write() on a pipe requires creating it and draining the writes + // - connect() works but is problematic for sandboxes and needs a valid + // file descriptor + // + // This can never succeed (invalid first argument to sigprocmask). + ABSL_RAW_CHECK(syscall(SYS_rt_sigprocmask, ~0, addr, nullptr, + /*sizeof(kernel_sigset_t)*/ 8) == -1, + "unexpected success"); + ABSL_RAW_CHECK(errno == EFAULT || errno == EINVAL, "unexpected errno"); + return errno != EFAULT; } } // namespace debugging_internal ABSL_NAMESPACE_END } // namespace absl -#endif +#endif // __linux__ && !__ANDROID__ diff --git a/absl/flags/declare.h b/absl/flags/declare.h index b9794d8b..a791b667 100644 --- a/absl/flags/declare.h +++ b/absl/flags/declare.h @@ -60,6 +60,10 @@ ABSL_NAMESPACE_END // The ABSL_DECLARE_FLAG(type, name) macro expands to: // // extern absl::Flag FLAGS_name; -#define ABSL_DECLARE_FLAG(type, name) extern ::absl::Flag FLAGS_##name +#define ABSL_DECLARE_FLAG(type, name) \ + extern absl::Flag FLAGS_##name; \ + namespace absl /* block flags in namespaces */ {} \ + /* second redeclaration is to allow applying attributes */ \ + extern absl::Flag FLAGS_##name #endif // ABSL_FLAGS_DECLARE_H_ diff --git a/absl/random/internal/randen_detect.cc b/absl/random/internal/randen_detect.cc index bbe7b965..9bb58fc6 100644 --- a/absl/random/internal/randen_detect.cc +++ b/absl/random/internal/randen_detect.cc @@ -40,7 +40,6 @@ #if defined(ABSL_INTERNAL_USE_X86_CPUID) #if defined(_WIN32) || defined(_WIN64) #include // NOLINT(build/include_order) -#pragma intrinsic(__cpuid) #else // MSVC-equivalent __cpuid intrinsic function. static void __cpuid(int cpu_info[4], int info_type) { diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h index b46bc71f..9b8e5d1a 100644 --- a/absl/strings/ascii.h +++ b/absl/strings/ascii.h @@ -133,7 +133,7 @@ inline bool ascii_isdigit(unsigned char c) { return c >= '0' && c <= '9'; } // ascii_isprint() // -// Determines whether the given character is printable, including whitespace. +// Determines whether the given character is printable, including spaces. inline bool ascii_isprint(unsigned char c) { return c >= 32 && c < 127; } // ascii_isgraph() diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 5905bac0..425b4bee 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -311,11 +311,10 @@ static CordRep* CordRepFromString(std::string&& src) { constexpr unsigned char Cord::InlineRep::kMaxInline; -inline void Cord::InlineRep::set_data(const char* data, size_t n, - bool nullify_tail) { +inline void Cord::InlineRep::set_data(const char* data, size_t n) { static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15"); - cord_internal::SmallMemmove(data_.as_chars(), data, n, nullify_tail); + cord_internal::SmallMemmove(data_.as_chars(), data, n); set_inline_size(n); } @@ -375,7 +374,8 @@ void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) { } void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) { - if (tree == nullptr) return; + assert(tree != nullptr); + assert(tree->length != 0); assert(!tree->IsCrc()); if (data_.is_tree()) { AppendTreeToTree(tree, method); @@ -412,6 +412,7 @@ void Cord::InlineRep::PrependTreeToTree(CordRep* tree, void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { assert(tree != nullptr); + assert(tree->length != 0); assert(!tree->IsCrc()); if (data_.is_tree()) { PrependTreeToTree(tree, method); @@ -549,7 +550,7 @@ Cord::Cord(absl::string_view src, MethodIdentifier method) : contents_(InlineData::kDefaultInit) { const size_t n = src.size(); if (n <= InlineRep::kMaxInline) { - contents_.set_data(src.data(), n, true); + contents_.set_data(src.data(), n); } else { CordRep* rep = NewTree(src.data(), n, 0); contents_.EmplaceTree(rep, method); @@ -559,7 +560,7 @@ Cord::Cord(absl::string_view src, MethodIdentifier method) template > Cord::Cord(T&& src) : contents_(InlineData::kDefaultInit) { if (src.size() <= InlineRep::kMaxInline) { - contents_.set_data(src.data(), src.size(), true); + contents_.set_data(src.data(), src.size()); } else { CordRep* rep = CordRepFromString(std::forward(src)); contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString); @@ -610,7 +611,7 @@ Cord& Cord::operator=(absl::string_view src) { // - MaybeUntrackCord must be called before set_data() clobbers cordz_info. // - set_data() must be called before Unref(tree) as it may reference tree. if (tree != nullptr) CordzInfo::MaybeUntrackCord(contents_.cordz_info()); - contents_.set_data(data, length, true); + contents_.set_data(data, length); if (tree != nullptr) CordRep::Unref(tree); return *this; } @@ -1014,9 +1015,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { CordRep* tree = contents_.tree(); if (tree == nullptr) { - // sub_cord is newly constructed, no need to re-zero-out the tail of - // contents_ memory. - sub_cord.contents_.set_data(contents_.data() + pos, new_size, false); + sub_cord.contents_.set_data(contents_.data() + pos, new_size); return sub_cord; } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 27d3475f..3bbd763e 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -763,9 +763,8 @@ class Cord { bool empty() const; size_t size() const; const char* data() const; // Returns nullptr if holding pointer - void set_data(const char* data, size_t n, - bool nullify_tail); // Discards pointer, if any - char* set_data(size_t n); // Write data to the result + void set_data(const char* data, size_t n); // Discards pointer, if any + char* set_data(size_t n); // Write data to the result // Returns nullptr if holding bytes absl::cord_internal::CordRep* tree() const; absl::cord_internal::CordRep* as_tree() const; @@ -857,7 +856,7 @@ class Cord { bool is_profiled() const { return data_.is_tree() && data_.is_profiled(); } // Returns the available inlined capacity, or 0 if is_tree() == true. - size_t inline_capacity() const { + size_t remaining_inline_capacity() const { return data_.is_tree() ? 0 : kMaxInline - data_.inline_size(); } @@ -968,8 +967,8 @@ namespace cord_internal { // Fast implementation of memmove for up to 15 bytes. This implementation is // safe for overlapping regions. If nullify_tail is true, the destination is // padded with '\0' up to 16 bytes. -inline void SmallMemmove(char* dst, const char* src, size_t n, - bool nullify_tail = false) { +template +inline void SmallMemmove(char* dst, const char* src, size_t n) { if (n >= 8) { assert(n <= 16); uint64_t buf1; @@ -1006,22 +1005,16 @@ inline void SmallMemmove(char* dst, const char* src, size_t n, } // Does non-template-specific `CordRepExternal` initialization. -// Expects `data` to be non-empty. +// Requires `data` to be non-empty. void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep); // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer -// to it, or `nullptr` if `data` was empty. +// to it. Requires `data` to be non-empty. template // NOLINTNEXTLINE - suppress clang-tidy raw pointer return. CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { + assert(!data.empty()); using ReleaserType = absl::decay_t; - if (data.empty()) { - // Never create empty external nodes. - InvokeReleaser(Rank0{}, ReleaserType(std::forward(releaser)), - data); - return nullptr; - } - CordRepExternal* rep = new CordRepExternalImpl( std::forward(releaser), 0); InitializeCordRepExternal(data, rep); @@ -1041,10 +1034,15 @@ inline CordRep* NewExternalRep(absl::string_view data, template Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) { Cord cord; - if (auto* rep = ::absl::cord_internal::NewExternalRep( - data, std::forward(releaser))) { - cord.contents_.EmplaceTree(rep, + if (ABSL_PREDICT_TRUE(!data.empty())) { + cord.contents_.EmplaceTree(::absl::cord_internal::NewExternalRep( + data, std::forward(releaser)), Cord::MethodIdentifier::kMakeCordFromExternal); + } else { + using ReleaserType = absl::decay_t; + cord_internal::InvokeReleaser( + cord_internal::Rank0{}, ReleaserType(std::forward(releaser)), + data); } return cord; } diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index ea865cca..a22db0bb 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -1370,31 +1370,64 @@ TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) { } TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) { - struct Releaser { - void operator()(absl::string_view) & { *lvalue_invoked = true; } - void operator()(absl::string_view) && { *rvalue_invoked = true; } + enum InvokedAs { kMissing, kLValue, kRValue }; + enum CopiedAs { kNone, kMove, kCopy }; + struct Tracker { + CopiedAs copied_as = kNone; + InvokedAs invoked_as = kMissing; + + void Record(InvokedAs rhs) { + ASSERT_EQ(invoked_as, kMissing); + invoked_as = rhs; + } - bool* lvalue_invoked; - bool* rvalue_invoked; - }; + void Record(CopiedAs rhs) { + if (copied_as == kNone || rhs == kCopy) copied_as = rhs; + } + } tracker; - bool lvalue_invoked = false; - bool rvalue_invoked = false; - Releaser releaser = {&lvalue_invoked, &rvalue_invoked}; - (void)MaybeHardened(absl::MakeCordFromExternal("", releaser)); - EXPECT_FALSE(lvalue_invoked); - EXPECT_TRUE(rvalue_invoked); - rvalue_invoked = false; + class Releaser { + public: + explicit Releaser(Tracker* tracker) : tr_(tracker) { *tracker = Tracker(); } + Releaser(Releaser&& rhs) : tr_(rhs.tr_) { tr_->Record(kMove); } + Releaser(const Releaser& rhs) : tr_(rhs.tr_) { tr_->Record(kCopy); } - (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser)); - EXPECT_FALSE(lvalue_invoked); - EXPECT_TRUE(rvalue_invoked); - rvalue_invoked = false; - - // NOLINTNEXTLINE: suppress clang-tidy std::move on trivially copyable type. - (void)MaybeHardened(absl::MakeCordFromExternal("dummy", std::move(releaser))); - EXPECT_FALSE(lvalue_invoked); - EXPECT_TRUE(rvalue_invoked); + void operator()(absl::string_view) & { tr_->Record(kLValue); } + void operator()(absl::string_view) && { tr_->Record(kRValue); } + + private: + Tracker* tr_; + }; + + const Releaser releaser1(&tracker); + (void)MaybeHardened(absl::MakeCordFromExternal("", releaser1)); + EXPECT_EQ(tracker.copied_as, kCopy); + EXPECT_EQ(tracker.invoked_as, kRValue); + + const Releaser releaser2(&tracker); + (void)MaybeHardened(absl::MakeCordFromExternal("", releaser2)); + EXPECT_EQ(tracker.copied_as, kCopy); + EXPECT_EQ(tracker.invoked_as, kRValue); + + Releaser releaser3(&tracker); + (void)MaybeHardened(absl::MakeCordFromExternal("", std::move(releaser3))); + EXPECT_EQ(tracker.copied_as, kMove); + EXPECT_EQ(tracker.invoked_as, kRValue); + + Releaser releaser4(&tracker); + (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser4)); + EXPECT_EQ(tracker.copied_as, kCopy); + EXPECT_EQ(tracker.invoked_as, kRValue); + + const Releaser releaser5(&tracker); + (void)MaybeHardened(absl::MakeCordFromExternal("dummy", releaser5)); + EXPECT_EQ(tracker.copied_as, kCopy); + EXPECT_EQ(tracker.invoked_as, kRValue); + + Releaser releaser6(&tracker); + (void)MaybeHardened(absl::MakeCordFromExternal("foo", std::move(releaser6))); + EXPECT_EQ(tracker.copied_as, kMove); + EXPECT_EQ(tracker.invoked_as, kRValue); } TEST_P(CordTest, ExternalMemoryBasicUsage) { diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index 151c56f5..dae4e63f 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -159,8 +159,8 @@ class Arg { Arg(Hex hex); // NOLINT(runtime/explicit) Arg(Dec dec); // NOLINT(runtime/explicit) - // vector::reference and const_reference require special help to - // convert to `AlphaNum` because it requires two user defined conversions. + // vector::reference and const_reference require special help to convert + // to `Arg` because it requires two user defined conversions. template ::value && diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 76ad41fe..3af4cda9 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -2327,7 +2327,7 @@ ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams *waitp) { base_internal::CycleClock::Now() - enqueue_timestamp; mutex_tracer("slow release", this, wait_cycles); ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); - submit_profile_data(enqueue_timestamp); + submit_profile_data(wait_cycles); ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); } } diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h index 9a354ca2..429968da 100644 --- a/absl/synchronization/notification.h +++ b/absl/synchronization/notification.h @@ -22,7 +22,7 @@ // The `Notification` object maintains a private boolean "notified" state that // transitions to `true` at most once. The `Notification` class provides the // following primary member functions: -// * `HasBeenNotified() `to query its state +// * `HasBeenNotified()` to query its state // * `WaitForNotification*()` to have threads wait until the "notified" state // is `true`. // * `Notify()` to set the notification's "notified" state to `true` and -- cgit v1.2.3 From 52d41a9ec23e39db7e2cbce5c9449506cf2d3a5c Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 14 Dec 2021 12:42:15 -0800 Subject: Export of internal Abseil changes -- 81f95fcf85b75b84f9892c73123501472b9cff33 by Martijn Vels : Introduce GetEstimatedMemoryUsage(CordMemoryAccounting::kFairShare) Memory usage analysis is moved into a separate cord_analysis.cc source. PiperOrigin-RevId: 416370158 -- 6bc7b1348fd27fe53f100c9eabd47f4f2cb9c19c by Abseil Team : Support scoped enum in absl::Substitute. PiperOrigin-RevId: 416345422 -- 6399f4f6ae05ebcd67664ebd844902f699ab8ec7 by Abseil Team : Correct the computation of contention cycles Currently, we record contention cycles from the first time a thread started waiting on a mutex. Consider a situation in which two threads, T1 and T2, run a loop at the top of which they acquire a common mutex and release it at the end of the loop body. Further assume that T2 is never able to acquire the mutex as T1 repeatedly acquires and then releases the mutex. In this case, we would expect that the reported contention cycles would be increase linearly over time. But currently we observe a quadratic behavior in the reported waiting time as mentioned in b/14684244#comment10. To fix the issue, this CL records the contention cycles experienced by all the threads woken up when the mutex is released. Further, contention_start_cycles is set to the current time since the contention cycles for the time already passed has been taken into account. With this CL, we get a linear increase in the waiting time, the expected behavior. PiperOrigin-RevId: 416322593 -- 149c1637c8a0f1a38e5a8f9f27e5803a2015a554 by Jorg Brown : Make Status::EmptyString more efficient by constructing it in global space, rather than on the heap. See https://godbolt.org/z/8M9n7YqcY for reduced code size. PiperOrigin-RevId: 416307833 -- 3b4562a8be5a3c80077cb67b0a32c97419058380 by Abseil Team : Clarify the usage of RegisterMutexProfiler PiperOrigin-RevId: 416146130 GitOrigin-RevId: 81f95fcf85b75b84f9892c73123501472b9cff33 Change-Id: Iccb72d7ee617e6ebe226a38170d62e0849b43480 --- CMake/AbseilDll.cmake | 2 + absl/status/status.cc | 7 +- absl/strings/BUILD.bazel | 2 + absl/strings/CMakeLists.txt | 2 + absl/strings/cord.cc | 114 ------------------- absl/strings/cord.h | 36 ++++-- absl/strings/cord_analysis.cc | 237 ++++++++++++++++++++++++++++++++++++++++ absl/strings/cord_analysis.h | 44 ++++++++ absl/strings/cord_test.cc | 236 +++++++++++++++++++++++++++++---------- absl/strings/substitute.h | 8 ++ absl/strings/substitute_test.cc | 48 ++++++++ absl/synchronization/mutex.cc | 18 +-- absl/synchronization/mutex.h | 7 +- 13 files changed, 564 insertions(+), 197 deletions(-) create mode 100644 absl/strings/cord_analysis.cc create mode 100644 absl/strings/cord_analysis.h diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 07a4576e..781388b9 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -195,6 +195,8 @@ set(ABSL_INTERNAL_DLL_FILES "strings/charconv.cc" "strings/charconv.h" "strings/cord.cc" + "strings/cord_analysis.cc" + "strings/cord_analysis.h" "strings/cord.h" "strings/escaping.cc" "strings/escaping.h" diff --git a/absl/status/status.cc b/absl/status/status.cc index bcf3413e..6b316ac6 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -185,8 +185,11 @@ void Status::ForEachPayload( } const std::string* Status::EmptyString() { - static std::string* empty_string = new std::string(); - return empty_string; + static union EmptyString { + std::string str; + ~EmptyString() {} + } empty = {{}}; + return &empty.str; } constexpr const char Status::kMovedFromString[]; diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 5d433cce..0dc667e0 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -412,6 +412,8 @@ cc_library( name = "cord", srcs = [ "cord.cc", + "cord_analysis.cc", + "cord_analysis.h", ], hdrs = [ "cord.h", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 0eef91df..4dd605ec 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -834,6 +834,8 @@ absl_cc_library( "cord.h" SRCS "cord.cc" + "cord_analysis.cc" + "cord_analysis.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 425b4bee..ddd14ef4 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -469,51 +469,6 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, return true; } -// Computes the memory side of the provided edge which must be a valid data edge -// for a btrtee, i.e., a FLAT, EXTERNAL or SUBSTRING of a FLAT or EXTERNAL node. -static bool RepMemoryUsageDataEdge(const CordRep* rep, - size_t* total_mem_usage) { - size_t maybe_sub_size = 0; - if (ABSL_PREDICT_FALSE(rep->IsSubstring())) { - maybe_sub_size = sizeof(cord_internal::CordRepSubstring); - rep = rep->substring()->child; - } - if (rep->IsFlat()) { - *total_mem_usage += maybe_sub_size + rep->flat()->AllocatedSize(); - return true; - } - if (rep->IsExternal()) { - // We don't know anything about the embedded / bound data, but we can safely - // assume it is 'at least' a word / pointer to data. In the future we may - // choose to use the 'data' byte as a tag to identify the types of some - // well-known externals, such as a std::string instance. - *total_mem_usage += maybe_sub_size + - sizeof(cord_internal::CordRepExternalImpl) + - rep->length; - return true; - } - return false; -} - -// If the rep is a leaf, this will increment the value at total_mem_usage and -// will return true. -static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { - if (rep->IsFlat()) { - *total_mem_usage += rep->flat()->AllocatedSize(); - return true; - } - if (rep->IsExternal()) { - // We don't know anything about the embedded / bound data, but we can safely - // assume it is 'at least' a word / pointer to data. In the future we may - // choose to use the 'data' byte as a tag to identify the types of some - // well-known externals, such as a std::string instance. - *total_mem_usage += - sizeof(cord_internal::CordRepExternalImpl) + rep->length; - return true; - } - return false; -} - void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { assert(&src != this); assert(is_tree() || src.is_tree()); @@ -1968,75 +1923,6 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, return true; } -// Traverses the tree and computes the total memory allocated. -/* static */ size_t Cord::MemoryUsageAux(const CordRep* rep) { - size_t total_mem_usage = 0; - - if (rep->IsCrc()) { - total_mem_usage += sizeof(CordRepCrc); - rep = rep->crc()->child; - } - - // Allow a quick exit for the common case that the root is a leaf. - if (RepMemoryUsageLeaf(rep, &total_mem_usage)) { - return total_mem_usage; - } - - // Iterate over the tree. cur_node is never a leaf node and leaf nodes will - // never be appended to tree_stack. This reduces overhead from manipulating - // tree_stack. - absl::InlinedVector tree_stack; - const CordRep* cur_node = rep; - while (true) { - const CordRep* next_node = nullptr; - - if (cur_node->IsConcat()) { - total_mem_usage += sizeof(CordRepConcat); - const CordRep* left = cur_node->concat()->left; - if (!RepMemoryUsageLeaf(left, &total_mem_usage)) { - next_node = left; - } - - const CordRep* right = cur_node->concat()->right; - if (!RepMemoryUsageLeaf(right, &total_mem_usage)) { - if (next_node) { - tree_stack.push_back(next_node); - } - next_node = right; - } - } else if (cur_node->IsBtree()) { - total_mem_usage += sizeof(CordRepBtree); - const CordRepBtree* node = cur_node->btree(); - if (node->height() == 0) { - for (const CordRep* edge : node->Edges()) { - RepMemoryUsageDataEdge(edge, &total_mem_usage); - } - } else { - for (const CordRep* edge : node->Edges()) { - tree_stack.push_back(edge); - } - } - } else { - // Since cur_node is not a leaf or a concat node it must be a substring. - assert(cur_node->IsSubstring()); - total_mem_usage += sizeof(CordRepSubstring); - next_node = cur_node->substring()->child; - if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) { - next_node = nullptr; - } - } - - if (!next_node) { - if (tree_stack.empty()) { - return total_mem_usage; - } - next_node = tree_stack.back(); - tree_stack.pop_back(); - } - cur_node = next_node; - } -} - std::ostream& operator<<(std::ostream& out, const Cord& cord) { for (absl::string_view chunk : cord.Chunks()) { out.write(chunk.data(), chunk.size()); diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 3bbd763e..49d51da2 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -79,6 +79,7 @@ #include "absl/container/inlined_vector.h" #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" +#include "absl/strings/cord_analysis.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_btree_reader.h" @@ -102,6 +103,20 @@ template Cord MakeCordFromExternal(absl::string_view, Releaser&&); void CopyCordToString(const Cord& src, std::string* dst); +// Cord memory accounting modes +enum class CordMemoryAccounting { + // Counts the *approximate* number of bytes held in full or in part by this + // Cord (which may not remain the same between invocations). Cords that share + // memory could each be "charged" independently for the same shared memory. + kTotal, + + // Counts the *approximate* number of bytes held in full or in part by this + // Cord weighted by the sharing ratio of that data. For example, if some data + // edge is shared by 4 different Cords, then each cord is attributed 1/4th of + // the total memory usage as a 'fair share' of the total memory usage. + kFairShare, +}; + // Cord // // A Cord is a sequence of characters, designed to be more efficient than a @@ -272,11 +287,10 @@ class Cord { // Cord::EstimatedMemoryUsage() // - // Returns the *approximate* number of bytes held in full or in part by this - // Cord (which may not remain the same between invocations). Note that Cords - // that share memory could each be "charged" independently for the same shared - // memory. - size_t EstimatedMemoryUsage() const; + // Returns the *approximate* number of bytes held by this cord. + // See CordMemoryAccounting for more information on accounting method used. + size_t EstimatedMemoryUsage(CordMemoryAccounting accounting_method = + CordMemoryAccounting::kTotal) const; // Cord::Compare() // @@ -890,9 +904,6 @@ class Cord { }; InlineRep contents_; - // Helper for MemoryUsage(). - static size_t MemoryUsageAux(const absl::cord_internal::CordRep* rep); - // Helper for GetFlat() and TryFlat(). static bool GetFlatAux(absl::cord_internal::CordRep* rep, absl::string_view* fragment); @@ -1235,10 +1246,15 @@ inline size_t Cord::size() const { inline bool Cord::empty() const { return contents_.empty(); } -inline size_t Cord::EstimatedMemoryUsage() const { +inline size_t Cord::EstimatedMemoryUsage( + CordMemoryAccounting accounting_method) const { size_t result = sizeof(Cord); if (const absl::cord_internal::CordRep* rep = contents_.tree()) { - result += MemoryUsageAux(rep); + if (accounting_method == CordMemoryAccounting::kFairShare) { + result += cord_internal::GetEstimatedFairShareMemoryUsage(rep); + } else { + result += cord_internal::GetEstimatedMemoryUsage(rep); + } } return result; } diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc new file mode 100644 index 00000000..435b4c94 --- /dev/null +++ b/absl/strings/cord_analysis.cc @@ -0,0 +1,237 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/cord_analysis.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_crc.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cord_rep_ring.h" +// +#include "absl/base/macros.h" +#include "absl/base/port.h" +#include "absl/functional/function_ref.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +// Accounting mode for analyzing memory usage. +enum class Mode { kTotal, kFairShare }; + +// CordRepRef holds a `const CordRep*` reference in rep, and depending on mode, +// holds a 'fraction' representing a cumulative inverse refcount weight. +template +struct CordRepRef { + // Instantiates a CordRepRef instance. + explicit CordRepRef(const CordRep* r) : rep(r) {} + + // Creates a child reference holding the provided child. + // Overloaded to add cumulative reference count for kFairShare. + CordRepRef Child(const CordRep* child) const { return CordRepRef(child); } + + const CordRep* rep; +}; + +// RawUsage holds the computed total number of bytes. +template +struct RawUsage { + size_t total = 0; + + // Add 'size' to total, ignoring the CordRepRef argument. + void Add(size_t size, CordRepRef) { total += size; } +}; + +// Returns n / refcount avoiding a div for the common refcount == 1. +template +double MaybeDiv(double d, refcount_t refcount) { + return refcount == 1 ? d : d / refcount; +} + +// Overloaded 'kFairShare' specialization for CordRepRef. This class holds a +// `fraction` value which represents a cumulative inverse refcount weight. +// For example, a top node with a reference count of 2 will have a fraction +// value of 1/2 = 0.5, representing the 'fair share' of memory it references. +// A node below such a node with a reference count of 5 then has a fraction of +// 0.5 / 5 = 0.1 representing the fair share of memory below that node, etc. +template <> +struct CordRepRef { + // Creates a CordRepRef with the provided rep and top (parent) fraction. + explicit CordRepRef(const CordRep* r, double frac = 1.0) + : rep(r), fraction(MaybeDiv(frac, r->refcount.Get())) {} + + // Returns a CordRepRef with a fraction of `this->fraction / child.refcount` + CordRepRef Child(const CordRep* child) const { + return CordRepRef(child, fraction); + } + + const CordRep* rep; + double fraction; +}; + +// Overloaded 'kFairShare' specialization for RawUsage +template <> +struct RawUsage { + double total = 0; + + // Adds `size` multiplied by `rep.fraction` to the total size. + void Add(size_t size, CordRepRef rep) { + total += static_cast(size) * rep.fraction; + } +}; + +// Returns true if the provided rep is a valid data edge. +bool IsDataEdge(const CordRep* rep) { + // The fast path is that `rep` is an EXTERNAL or FLAT node, making the below + // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL + // check in the slow path the SUBSTRING check to optimize for the hot path. + if (rep->tag == EXTERNAL || rep->tag >= FLAT) return true; + if (rep->tag == SUBSTRING) rep = rep->substring()->child; + return rep->tag == EXTERNAL || rep->tag >= FLAT; +} + +// Computes the estimated memory size of the provided data edge. +// External reps are assumed 'heap allocated at their exact size'. +template +void AnalyzeDataEdge(CordRepRef rep, RawUsage& raw_usage) { + assert(IsDataEdge(rep.rep)); + + // Consume all substrings + if (rep.rep->tag == SUBSTRING) { + raw_usage.Add(sizeof(CordRepSubstring), rep); + rep = rep.Child(rep.rep->substring()->child); + } + + // Consume FLAT / EXTERNAL + const size_t size = + rep.rep->tag >= FLAT + ? rep.rep->flat()->AllocatedSize() + : rep.rep->length + sizeof(CordRepExternalImpl); + raw_usage.Add(size, rep); +} + +// Computes the memory size of the provided Concat tree. +template +void AnalyzeConcat(CordRepRef rep, RawUsage& raw_usage) { + absl::InlinedVector, 47> pending; + + while (rep.rep != nullptr) { + const CordRepConcat* concat = rep.rep->concat(); + CordRepRef left = rep.Child(concat->left); + CordRepRef right = rep.Child(concat->right); + + raw_usage.Add(sizeof(CordRepConcat), rep); + + switch ((IsDataEdge(left.rep) ? 1 : 0) | (IsDataEdge(right.rep) ? 2 : 0)) { + case 0: // neither left or right are data edges + rep = left; + pending.push_back(right); + break; + case 1: // only left is a data edge + AnalyzeDataEdge(left, raw_usage); + rep = right; + break; + case 2: // only right is a data edge + AnalyzeDataEdge(right, raw_usage); + rep = left; + break; + case 3: // left and right are data edges + AnalyzeDataEdge(right, raw_usage); + AnalyzeDataEdge(left, raw_usage); + if (!pending.empty()) { + rep = pending.back(); + pending.pop_back(); + } else { + rep.rep = nullptr; + } + break; + } + } +} + +// Computes the memory size of the provided Ring tree. +template +void AnalyzeRing(CordRepRef rep, RawUsage& raw_usage) { + const CordRepRing* ring = rep.rep->ring(); + raw_usage.Add(CordRepRing::AllocSize(ring->capacity()), rep); + ring->ForEach([&](CordRepRing::index_type pos) { + AnalyzeDataEdge(rep.Child(ring->entry_child(pos)), raw_usage); + }); +} + +// Computes the memory size of the provided Btree tree. +template +void AnalyzeBtree(CordRepRef rep, RawUsage& raw_usage) { + raw_usage.Add(sizeof(CordRepBtree), rep); + const CordRepBtree* tree = rep.rep->btree(); + if (tree->height() > 0) { + for (CordRep* edge : tree->Edges()) { + AnalyzeBtree(rep.Child(edge), raw_usage); + } + } else { + for (CordRep* edge : tree->Edges()) { + AnalyzeDataEdge(rep.Child(edge), raw_usage); + } + } +} + +template +size_t GetEstimatedUsage(const CordRep* rep) { + // Zero initialized memory usage totals. + RawUsage raw_usage; + + // Capture top level node and refcount into a CordRepRef. + CordRepRef repref(rep); + + // Consume the top level CRC node if present. + if (repref.rep->tag == CRC) { + raw_usage.Add(sizeof(CordRepCrc), repref); + repref = repref.Child(repref.rep->crc()->child); + } + + if (IsDataEdge(repref.rep)) { + AnalyzeDataEdge(repref, raw_usage); + } else if (repref.rep->tag == BTREE) { + AnalyzeBtree(repref, raw_usage); + } else if (repref.rep->tag == CONCAT) { + AnalyzeConcat(repref, raw_usage); + } else if (repref.rep->tag == RING) { + AnalyzeRing(repref, raw_usage); + } else { + assert(false); + } + + return static_cast(raw_usage.total); +} + +} // namespace + +size_t GetEstimatedMemoryUsage(const CordRep* rep) { + return GetEstimatedUsage(rep); +} + +size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep) { + return GetEstimatedUsage(rep); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/cord_analysis.h b/absl/strings/cord_analysis.h new file mode 100644 index 00000000..7041ad1a --- /dev/null +++ b/absl/strings/cord_analysis.h @@ -0,0 +1,44 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CORD_ANALYSIS_H_ +#define ABSL_STRINGS_CORD_ANALYSIS_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Returns the *approximate* number of bytes held in full or in part by this +// Cord (which may not remain the same between invocations). Cords that share +// memory could each be "charged" independently for the same shared memory. +size_t GetEstimatedMemoryUsage(const CordRep* rep); + +// Returns the *approximate* number of bytes held in full or in part by this +// CordRep weighted by the sharing ratio of that data. For example, if some data +// edge is shared by 4 different Cords, then each cord is attribute 1/4th of +// the total memory usage as a 'fair share' of the total memory usage. +size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + + +#endif // ABSL_STRINGS_CORD_ANALYSIS_H_ diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index a22db0bb..e499b55f 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -50,7 +50,12 @@ static constexpr auto MAX_FLAT_TAG = absl::cord_internal::MAX_FLAT_TAG; typedef std::mt19937_64 RandomEngine; using absl::cord_internal::CordRep; +using absl::cord_internal::CordRepBtree; +using absl::cord_internal::CordRepConcat; +using absl::cord_internal::CordRepCrc; +using absl::cord_internal::CordRepExternal; using absl::cord_internal::CordRepFlat; +using absl::cord_internal::CordRepSubstring; using absl::cord_internal::kFlatOverhead; using absl::cord_internal::kMaxFlatLength; @@ -196,6 +201,7 @@ class CordTestPeer { } static bool IsTree(const Cord& c) { return c.contents_.is_tree(); } + static CordRep* Tree(const Cord& c) { return c.contents_.tree(); } static cord_internal::CordzInfo* GetCordzInfo(const Cord& c) { return c.contents_.cordz_info(); @@ -1474,76 +1480,186 @@ TEST_P(CordTest, ExternalMemoryGet) { } // CordMemoryUsage tests verify the correctness of the EstimatedMemoryUsage() -// These tests take into account that the reported memory usage is approximate -// and non-deterministic. For all tests, We verify that the reported memory -// usage is larger than `size()`, and less than `size() * 1.5` as a cord should -// never reserve more 'extra' capacity than half of its size as it grows. -// Additionally we have some whiteboxed expectations based on our knowledge of -// the layout and size of empty and inlined cords, and flat nodes. +// We use whiteboxed expectations based on our knowledge of the layout and size +// of empty and inlined cords, and flat nodes. -TEST_P(CordTest, CordMemoryUsageEmpty) { - EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage()); -} - -TEST_P(CordTest, CordMemoryUsageEmbedded) { - absl::Cord a("hello"); - EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); -} +constexpr auto kFairShare = absl::CordMemoryAccounting::kFairShare; -TEST_P(CordTest, CordMemoryUsageEmbeddedAppend) { - absl::Cord a("a"); - absl::Cord b("bcd"); - EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord)); - a.Append(b); - EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); +// Creates a cord of `n` `c` values, making sure no string stealing occurs. +absl::Cord MakeCord(size_t n, char c) { + const std::string s(n, c); + return absl::Cord(s); } -TEST_P(CordTest, CordMemoryUsageExternalMemory) { - static const int kLength = 1000; +TEST(CordTest, CordMemoryUsageEmpty) { absl::Cord cord; - AddExternalMemory(std::string(kLength, 'x'), &cord); - EXPECT_GT(cord.EstimatedMemoryUsage(), kLength); - EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5); -} - -TEST_P(CordTest, CordMemoryUsageFlat) { - static const int kLength = 125; - absl::Cord a(std::string(kLength, 'a')); - EXPECT_GT(a.EstimatedMemoryUsage(), kLength); - EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5); + EXPECT_EQ(sizeof(absl::Cord), cord.EstimatedMemoryUsage()); + EXPECT_EQ(sizeof(absl::Cord), cord.EstimatedMemoryUsage(kFairShare)); } -TEST_P(CordTest, CordMemoryUsageAppendFlat) { - using absl::strings_internal::CordTestAccess; - absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); - size_t length = a.EstimatedMemoryUsage(); - a.Append(std::string(CordTestAccess::MaxFlatLength(), 'b')); - size_t delta = a.EstimatedMemoryUsage() - length; - EXPECT_GT(delta, CordTestAccess::MaxFlatLength()); - EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5); +TEST(CordTest, CordMemoryUsageInlined) { + absl::Cord a("hello"); + EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); + EXPECT_EQ(a.EstimatedMemoryUsage(kFairShare), sizeof(absl::Cord)); } -TEST_P(CordTest, CordMemoryUsageAppendExternal) { - static const int kLength = 1000; - using absl::strings_internal::CordTestAccess; - absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); - size_t length = a.EstimatedMemoryUsage(); - AddExternalMemory(std::string(kLength, 'b'), &a); - size_t delta = a.EstimatedMemoryUsage() - length; - EXPECT_GT(delta, kLength); - EXPECT_LE(delta, kLength * 1.5); -} +TEST(CordTest, CordMemoryUsageExternalMemory) { + absl::Cord cord; + AddExternalMemory(std::string(1000, 'x'), &cord); + const size_t expected = + sizeof(absl::Cord) + 1000 + sizeof(CordRepExternal) + sizeof(intptr_t); + EXPECT_EQ(cord.EstimatedMemoryUsage(), expected); + EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), expected); +} + +TEST(CordTest, CordMemoryUsageFlat) { + absl::Cord cord = MakeCord(1000, 'a'); + const size_t flat_size = + absl::CordTestPeer::Tree(cord)->flat()->AllocatedSize(); + EXPECT_EQ(cord.EstimatedMemoryUsage(), sizeof(absl::Cord) + flat_size); + EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + flat_size); +} + +TEST(CordTest, CordMemoryUsageSubStringSharedFlat) { + absl::Cord flat = MakeCord(2000, 'a'); + const size_t flat_size = + absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize(); + absl::Cord cord = flat.Subcord(500, 1000); + EXPECT_EQ(cord.EstimatedMemoryUsage(), + sizeof(absl::Cord) + sizeof(CordRepSubstring) + flat_size); + EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + sizeof(CordRepSubstring) + flat_size / 2); +} + +TEST(CordTest, CordMemoryUsageFlatShared) { + absl::Cord shared = MakeCord(1000, 'a'); + absl::Cord cord(shared); + const size_t flat_size = + absl::CordTestPeer::Tree(cord)->flat()->AllocatedSize(); + EXPECT_EQ(cord.EstimatedMemoryUsage(), sizeof(absl::Cord) + flat_size); + EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + flat_size / 2); +} + +TEST(CordTest, CordMemoryUsageFlatHardenedAndShared) { + absl::Cord shared = MakeCord(1000, 'a'); + absl::Cord cord(shared); + const size_t flat_size = + absl::CordTestPeer::Tree(cord)->flat()->AllocatedSize(); + cord.SetExpectedChecksum(1); + EXPECT_EQ(cord.EstimatedMemoryUsage(), + sizeof(absl::Cord) + sizeof(CordRepCrc) + flat_size); + EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + sizeof(CordRepCrc) + flat_size / 2); + + absl::Cord cord2(cord); + EXPECT_EQ(cord2.EstimatedMemoryUsage(), + sizeof(absl::Cord) + sizeof(CordRepCrc) + flat_size); + EXPECT_EQ(cord2.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + (sizeof(CordRepCrc) + flat_size / 2) / 2); +} + +TEST(CordTest, CordMemoryUsageBTree) { + absl::cord_internal::enable_cord_btree(true); + + absl::Cord cord1; + size_t flats1_size = 0; + absl::Cord flats1[4] = {MakeCord(1000, 'a'), MakeCord(1100, 'a'), + MakeCord(1200, 'a'), MakeCord(1300, 'a')}; + for (absl::Cord flat : flats1) { + flats1_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize(); + cord1.Append(std::move(flat)); + } + + // Make sure the created cord is a BTREE tree. Under some builds such as + // windows DLL, we may have ODR like effects on the flag, meaning the DLL + // code will run with the picked up default. + if (!absl::CordTestPeer::Tree(cord1)->IsBtree()) { + ABSL_RAW_LOG(WARNING, "Cord library code not respecting btree flag"); + return; + } + + size_t rep1_size = sizeof(CordRepBtree) + flats1_size; + size_t rep1_shared_size = sizeof(CordRepBtree) + flats1_size / 2; + + EXPECT_EQ(cord1.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep1_size); + EXPECT_EQ(cord1.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + rep1_shared_size); + + absl::Cord cord2; + size_t flats2_size = 0; + absl::Cord flats2[4] = {MakeCord(600, 'a'), MakeCord(700, 'a'), + MakeCord(800, 'a'), MakeCord(900, 'a')}; + for (absl::Cord& flat : flats2) { + flats2_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize(); + cord2.Append(std::move(flat)); + } + size_t rep2_size = sizeof(CordRepBtree) + flats2_size; + + EXPECT_EQ(cord2.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep2_size); + EXPECT_EQ(cord2.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + rep2_size); + + absl::Cord cord(cord1); + cord.Append(std::move(cord2)); + + EXPECT_EQ(cord.EstimatedMemoryUsage(), + sizeof(absl::Cord) + sizeof(CordRepBtree) + rep1_size + rep2_size); + EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + sizeof(CordRepBtree) + rep1_shared_size / 2 + + rep2_size); +} + +TEST(CordTest, CordMemoryUsageConcat) { + absl::cord_internal::enable_cord_btree(false); + + absl::Cord cord1; + size_t flats1_size = 0; + absl::Cord flats1[4] = {MakeCord(1000, 'a'), MakeCord(1100, 'a'), + MakeCord(1200, 'a'), MakeCord(1300, 'a')}; + for (absl::Cord flat : flats1) { + flats1_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize(); + cord1.Append(std::move(flat)); + } + + // Make sure the created cord is a CONCAT tree. Under some builds such as + // windows DLL, we may have ODR like effects on the flag, meaning the DLL + // code will run with the picked up default. + if (!absl::CordTestPeer::Tree(cord1)->IsConcat()) { + ABSL_RAW_LOG(WARNING, "Cord library code not respecting btree flag"); + return; + } + + size_t rep1_size = sizeof(CordRepConcat) * 3 + flats1_size; + size_t rep1_shared_size = sizeof(CordRepConcat) * 3 + flats1_size / 2; + + EXPECT_EQ(cord1.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep1_size); + EXPECT_EQ(cord1.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + rep1_shared_size); + + absl::Cord cord2; + size_t flats2_size = 0; + absl::Cord flats2[4] = {MakeCord(600, 'a'), MakeCord(700, 'a'), + MakeCord(800, 'a'), MakeCord(900, 'a')}; + for (absl::Cord& flat : flats2) { + flats2_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize(); + cord2.Append(std::move(flat)); + } + + size_t rep2_size = sizeof(CordRepConcat) * 3 + flats2_size; + + EXPECT_EQ(cord2.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep2_size); + EXPECT_EQ(cord2.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + rep2_size); -TEST_P(CordTest, CordMemoryUsageSubString) { - static const int kLength = 2000; - using absl::strings_internal::CordTestAccess; - absl::Cord a(std::string(kLength, 'a')); - size_t length = a.EstimatedMemoryUsage(); - AddExternalMemory(std::string(kLength, 'b'), &a); - absl::Cord b = a.Subcord(0, kLength + kLength / 2); - size_t delta = b.EstimatedMemoryUsage() - length; - EXPECT_GT(delta, kLength); - EXPECT_LE(delta, kLength * 1.5); + absl::Cord cord(cord1); + cord.Append(std::move(cord2)); + EXPECT_EQ(cord.EstimatedMemoryUsage(), + sizeof(absl::Cord) + sizeof(CordRepConcat) + rep1_size + rep2_size); + EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), + sizeof(absl::Cord) + sizeof(CordRepConcat) + rep1_shared_size / 2 + + rep2_size); } // Regtest for a change that had to be rolled back because it expanded out diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index dae4e63f..6d2b08ab 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -174,6 +174,14 @@ class Arg { // "0x". However, in the case of `nullptr`, "NULL" is printed. Arg(const void* value); // NOLINT(runtime/explicit) + // Normal enums are already handled by the integer formatters. + // This overload matches only scoped enums. + template {} && !std::is_convertible{}>::type> + Arg(T value) // NOLINT(google-explicit-constructor) + : Arg(static_cast::type>(value)) {} + Arg(const Arg&) = delete; Arg& operator=(const Arg&) = delete; diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc index 442c9215..9e6b9403 100644 --- a/absl/strings/substitute_test.cc +++ b/absl/strings/substitute_test.cc @@ -184,6 +184,54 @@ TEST(SubstituteTest, VectorBoolRef) { EXPECT_EQ("Logic be like: true false true false", str); } +TEST(SubstituteTest, Enums) { + enum UnscopedEnum { kEnum0 = 0, kEnum1 = 1 }; + EXPECT_EQ("0 1", absl::Substitute("$0 $1", UnscopedEnum::kEnum0, + UnscopedEnum::kEnum1)); + + enum class ScopedEnum { kEnum0 = 0, kEnum1 = 1 }; + EXPECT_EQ("0 1", + absl::Substitute("$0 $1", ScopedEnum::kEnum0, ScopedEnum::kEnum1)); + + enum class ScopedEnumInt32 : int32_t { kEnum0 = 989, kEnum1 = INT32_MIN }; + EXPECT_EQ("989 -2147483648", + absl::Substitute("$0 $1", ScopedEnumInt32::kEnum0, + ScopedEnumInt32::kEnum1)); + + enum class ScopedEnumUInt32 : uint32_t { kEnum0 = 1, kEnum1 = UINT32_MAX }; + EXPECT_EQ("1 4294967295", absl::Substitute("$0 $1", ScopedEnumUInt32::kEnum0, + ScopedEnumUInt32::kEnum1)); + + enum class ScopedEnumInt64 : int64_t { kEnum0 = -1, kEnum1 = 42949672950 }; + EXPECT_EQ("-1 42949672950", absl::Substitute("$0 $1", ScopedEnumInt64::kEnum0, + ScopedEnumInt64::kEnum1)); + + enum class ScopedEnumUInt64 : uint64_t { kEnum0 = 1, kEnum1 = 42949672950 }; + EXPECT_EQ("1 42949672950", absl::Substitute("$0 $1", ScopedEnumUInt64::kEnum0, + ScopedEnumUInt64::kEnum1)); + + enum class ScopedEnumChar : signed char { kEnum0 = -1, kEnum1 = 1 }; + EXPECT_EQ("-1 1", absl::Substitute("$0 $1", ScopedEnumChar::kEnum0, + ScopedEnumChar::kEnum1)); + + enum class ScopedEnumUChar : unsigned char { + kEnum0 = 0, + kEnum1 = 1, + kEnumMax = 255 + }; + EXPECT_EQ("0 1 255", absl::Substitute("$0 $1 $2", ScopedEnumUChar::kEnum0, + ScopedEnumUChar::kEnum1, + ScopedEnumUChar::kEnumMax)); + + enum class ScopedEnumInt16 : int16_t { kEnum0 = -100, kEnum1 = 10000 }; + EXPECT_EQ("-100 10000", absl::Substitute("$0 $1", ScopedEnumInt16::kEnum0, + ScopedEnumInt16::kEnum1)); + + enum class ScopedEnumUInt16 : uint16_t { kEnum0 = 0, kEnum1 = 10000 }; + EXPECT_EQ("0 10000", absl::Substitute("$0 $1", ScopedEnumUInt16::kEnum0, + ScopedEnumUInt16::kEnum1)); +} + #ifdef GTEST_HAS_DEATH_TEST TEST(SubstituteDeathTest, SubstituteDeath) { diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 3af4cda9..376ea794 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -109,7 +109,7 @@ static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu, bool locking, bool trylock, bool read_lock); -void RegisterMutexProfiler(void (*fn)(int64_t wait_timestamp)) { +void RegisterMutexProfiler(void (*fn)(int64_t wait_cycles)) { submit_profile_data.Store(fn); } @@ -2315,16 +2315,18 @@ ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams *waitp) { } // end of for(;;)-loop if (wake_list != kPerThreadSynchNull) { - int64_t enqueue_timestamp = wake_list->waitp->contention_start_cycles; - bool cond_waiter = wake_list->cond_waiter; + int64_t wait_cycles = 0; + int64_t now = base_internal::CycleClock::Now(); do { + // Sample lock contention events only if the waiter was trying to acquire + // the lock, not waiting on a condition variable or Condition. + if (!wake_list->cond_waiter) { + wait_cycles += (now - wake_list->waitp->contention_start_cycles); + wake_list->waitp->contention_start_cycles = now; + } wake_list = Wakeup(wake_list); // wake waiters } while (wake_list != kPerThreadSynchNull); - if (!cond_waiter) { - // Sample lock contention events only if the (first) waiter was trying to - // acquire the lock, not waiting on a condition variable or Condition. - int64_t wait_cycles = - base_internal::CycleClock::Now() - enqueue_timestamp; + if (wait_cycles > 0) { mutex_tracer("slow release", this, wait_cycles); ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); submit_profile_data(wait_cycles); diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index 38338f24..9a3e438f 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -984,14 +984,15 @@ inline Condition::Condition(const T *object, // Register a hook for profiling support. // // The function pointer registered here will be called whenever a mutex is -// contended. The callback is given the absl/base/cycleclock.h timestamp when -// waiting began. +// contended. The callback is given the cycles for which waiting happened (as +// measured by //absl/base/internal/cycleclock.h, and which may not +// be real "cycle" counts.) // // Calls to this function do not race or block, but there is no ordering // guaranteed between calls to this function and call to the provided hook. // In particular, the previously registered hook may still be called for some // time after this function returns. -void RegisterMutexProfiler(void (*fn)(int64_t wait_timestamp)); +void RegisterMutexProfiler(void (*fn)(int64_t wait_cycles)); // Register a hook for Mutex tracing. // -- cgit v1.2.3 From f523d0dd69806d8057a898bb2595910558156aaa Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 20 Dec 2021 08:44:32 -0800 Subject: Export of internal Abseil changes -- 1e25fb5996193524bf2f75781f1e6bdcdf972a3a by Gennadiy Rozental : Introduce enums to represent lower and upper thresholds for LogSeverity. PiperOrigin-RevId: 417414673 -- abfddb3a6ad8ce4849fbb6dadb34d7d6295def0f by Abseil Team : Restore __arch64__ macro check for SPARC PiperOrigin-RevId: 416798208 PiperOrigin-RevId: 416688543 GitOrigin-RevId: 1e25fb5996193524bf2f75781f1e6bdcdf972a3a Change-Id: I424dd98e5e07420d5f9dfd191f2bfc06036e2bd1 --- absl/base/internal/direct_mmap.h | 2 +- absl/base/log_severity.cc | 26 ++++++++++++++++++++ absl/base/log_severity.h | 51 ++++++++++++++++++++++++++++++++++++++++ absl/base/log_severity_test.cc | 43 ++++++++++++++++++++++++++++++++- 4 files changed, 120 insertions(+), 2 deletions(-) diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h index 7037094b..274054cd 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -80,7 +80,7 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, (defined(__PPC__) && !defined(__PPC64__)) || \ (defined(__riscv) && __riscv_xlen == 32) || \ (defined(__s390__) && !defined(__s390x__)) || \ - (defined(__sparc__) && !defined(__aarch64__)) + (defined(__sparc__) && !defined(__arch64__)) // On these architectures, implement mmap with mmap2. static int pagesize = 0; if (pagesize == 0) { diff --git a/absl/base/log_severity.cc b/absl/base/log_severity.cc index 72312afd..de26b06e 100644 --- a/absl/base/log_severity.cc +++ b/absl/base/log_severity.cc @@ -23,5 +23,31 @@ std::ostream& operator<<(std::ostream& os, absl::LogSeverity s) { if (s == absl::NormalizeLogSeverity(s)) return os << absl::LogSeverityName(s); return os << "absl::LogSeverity(" << static_cast(s) << ")"; } + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s) { + switch (s) { + case absl::LogSeverityAtLeast::kInfo: + case absl::LogSeverityAtLeast::kWarning: + case absl::LogSeverityAtLeast::kError: + case absl::LogSeverityAtLeast::kFatal: + return os << ">=" << static_cast(s); + case absl::LogSeverityAtLeast::kInfinity: + return os << "INFINITY"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s) { + switch (s) { + case absl::LogSeverityAtMost::kInfo: + case absl::LogSeverityAtMost::kWarning: + case absl::LogSeverityAtMost::kError: + case absl::LogSeverityAtMost::kFatal: + return os << "<=" << static_cast(s); + case absl::LogSeverityAtMost::kNegativeInfinity: + return os << "NEGATIVE_INFINITY"; + } + return os; +} ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h index 22364224..8bdca38b 100644 --- a/absl/base/log_severity.h +++ b/absl/base/log_severity.h @@ -115,6 +115,57 @@ constexpr absl::LogSeverity NormalizeLogSeverity(int s) { // unspecified; do not rely on it. std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); +// Enums representing a lower bound for LogSeverity. APIs that only operate on +// messages of at least a certain level (for example, `SetMinLogLevel()`) use +// this type to specify that level. absl::LogSeverityAtLeast::kInfinity is +// a level above all threshold levels and therefore no log message will +// ever meet this threshold. +enum class LogSeverityAtLeast : int { + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), + kInfinity = 1000, +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s); + +// Enums representing an upper bound for LogSeverity. APIs that only operate on +// messages of at most a certain level (for example, buffer all messages at or +// below a certain level) use this type to specify that level. +// absl::LogSeverityAtMost::kNegativeInfinity is a level below all threshold +// levels and therefore will exclude all log messages. +enum class LogSeverityAtMost : int { + kNegativeInfinity = -1000, + kInfo = static_cast(absl::LogSeverity::kInfo), + kWarning = static_cast(absl::LogSeverity::kWarning), + kError = static_cast(absl::LogSeverity::kError), + kFatal = static_cast(absl::LogSeverity::kFatal), +}; + +std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s); + +#define COMPOP(op1, op2, T) \ + constexpr bool operator op1(absl::T lhs, absl::LogSeverity rhs) { \ + return static_cast(lhs) op1 rhs; \ + } \ + constexpr bool operator op2(absl::LogSeverity lhs, absl::T rhs) { \ + return lhs op2 static_cast(rhs); \ + } + +// Comparisons between `LogSeverity` and `LogSeverityAtLeast`/ +// `LogSeverityAtMost` are only supported in one direction. +// Valid checks are: +// LogSeverity >= LogSeverityAtLeast +// LogSeverity < LogSeverityAtLeast +// LogSeverity <= LogSeverityAtMost +// LogSeverity > LogSeverityAtMost +COMPOP(>, <, LogSeverityAtLeast) +COMPOP(<=, >=, LogSeverityAtLeast) +COMPOP(<, >, LogSeverityAtMost) +COMPOP(>=, <=, LogSeverityAtMost) +#undef COMPOP + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc index 55b26d17..16091a5b 100644 --- a/absl/base/log_severity_test.cc +++ b/absl/base/log_severity_test.cc @@ -35,7 +35,8 @@ using ::testing::IsTrue; using ::testing::TestWithParam; using ::testing::Values; -std::string StreamHelper(absl::LogSeverity value) { +template +std::string StreamHelper(T value) { std::ostringstream stream; stream << value; return stream.str(); @@ -201,4 +202,44 @@ TEST_P(UnparseFlagToOtherIntegerTest, ReturnsExpectedValueAndRoundTrips) { IsTrue()); EXPECT_THAT(reparsed_value, Eq(to_unparse)); } + +TEST(LogThresholdTest, LogSeverityAtLeastTest) { + EXPECT_LT(absl::LogSeverity::kError, absl::LogSeverityAtLeast::kFatal); + EXPECT_GT(absl::LogSeverityAtLeast::kError, absl::LogSeverity::kInfo); + + EXPECT_LE(absl::LogSeverityAtLeast::kInfo, absl::LogSeverity::kError); + EXPECT_GE(absl::LogSeverity::kError, absl::LogSeverityAtLeast::kInfo); +} + +TEST(LogThresholdTest, LogSeverityAtMostTest) { + EXPECT_GT(absl::LogSeverity::kError, absl::LogSeverityAtMost::kWarning); + EXPECT_LT(absl::LogSeverityAtMost::kError, absl::LogSeverity::kFatal); + + EXPECT_GE(absl::LogSeverityAtMost::kFatal, absl::LogSeverity::kError); + EXPECT_LE(absl::LogSeverity::kWarning, absl::LogSeverityAtMost::kError); +} + +TEST(LogThresholdTest, Extremes) { + EXPECT_LT(absl::LogSeverity::kFatal, absl::LogSeverityAtLeast::kInfinity); + EXPECT_GT(absl::LogSeverity::kInfo, + absl::LogSeverityAtMost::kNegativeInfinity); +} + +TEST(LogThresholdTest, Output) { + EXPECT_THAT(StreamHelper(absl::LogSeverityAtLeast::kInfo), Eq(">=INFO")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtLeast::kWarning), + Eq(">=WARNING")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtLeast::kError), Eq(">=ERROR")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtLeast::kFatal), Eq(">=FATAL")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtLeast::kInfinity), + Eq("INFINITY")); + + EXPECT_THAT(StreamHelper(absl::LogSeverityAtMost::kInfo), Eq("<=INFO")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtMost::kWarning), Eq("<=WARNING")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtMost::kError), Eq("<=ERROR")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtMost::kFatal), Eq("<=FATAL")); + EXPECT_THAT(StreamHelper(absl::LogSeverityAtMost::kNegativeInfinity), + Eq("NEGATIVE_INFINITY")); +} + } // namespace -- cgit v1.2.3 From d758735198573d04e3b01b8495a4bbdb1a0d5f90 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 23 Dec 2021 10:59:35 -0800 Subject: Export of internal Abseil changes -- cca8a0c0d709803ce3413861ccbdb1b47e8fdcef by Abseil Team : Add AssertHeld() to SpinLock. PiperOrigin-RevId: 418034654 -- 9e95a0e614b4cd7929d8db4324ca69d7be5351b2 by Evan Brown : Make btree use an unsigned size_type (size_t). This brings btree in line with std::set, et al., which are required to use an unsigned size type - see https://en.cppreference.com/w/cpp/container/set. Also avoid some warnings about comparing integers of different signs. PiperOrigin-RevId: 418014466 GitOrigin-RevId: cca8a0c0d709803ce3413861ccbdb1b47e8fdcef Change-Id: I2b951cf1d69a3bb9c8dc236c69ab4a06b45047ea --- absl/base/internal/spinlock.h | 8 ++++++++ absl/container/internal/btree.h | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index ac40daff..6d8d8ddd 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h @@ -120,6 +120,14 @@ class ABSL_LOCKABLE SpinLock { return (lockword_.load(std::memory_order_relaxed) & kSpinLockHeld) != 0; } + // Return immediately if this thread holds the SpinLock exclusively. + // Otherwise, report an error by crashing with a diagnostic. + inline void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK() { + if (!IsHeld()) { + ABSL_RAW_LOG(FATAL, "thread should hold the lock on SpinLock"); + } + } + protected: // These should not be exported except for testing. diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index 29603379..26bd5e14 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -238,7 +238,7 @@ struct common_params { using allocator_type = Alloc; using key_type = Key; - using size_type = std::make_signed::type; + using size_type = size_t; using difference_type = ptrdiff_t; using slot_policy = SlotPolicy; @@ -1497,7 +1497,7 @@ inline void btree_node

::emplace_value(const size_type i, set_finish(finish() + 1); if (!leaf() && finish() > i + 1) { - for (int j = finish(); j > i + 1; --j) { + for (field_type j = finish(); j > i + 1; --j) { set_child(j, child(j - 1)); } clear_child(i + 1); @@ -2124,7 +2124,7 @@ auto btree

::erase_range(iterator begin, iterator end) return {0, begin}; } - if (count == size_) { + if (static_cast(count) == size_) { clear(); return {count, this->end()}; } -- cgit v1.2.3 From 04610889a913d29037205ca72e9d7fd7acc925fe Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 28 Dec 2021 11:28:18 -0800 Subject: Export of internal Abseil changes -- 3fad46c668edd864a62511f2a6875b8b79e38f34 by Evan Brown : Use switches instead of lookup tables for zap_desig_waker and ignore_waiting_writers so that we can avoid dTLB misses. See, e.g., https://godbolt.org/z/a7Gb9vzzj. Also, now that these are functions, follow function style in naming and comments. PiperOrigin-RevId: 418654693 -- ba5107744023a4e9163a44d706fbe8e4a1bc0fd9 by Abseil Team : Check for Clang before attempting to expand __clang_major__. This avoids a warning about an undefined macro on Windows when compiling with MinGW-GCC. PiperOrigin-RevId: 418287329 GitOrigin-RevId: 3fad46c668edd864a62511f2a6875b8b79e38f34 Change-Id: I28104980c4d3b204537b248447a6bd1022c9ef5d --- absl/base/attributes.h | 7 ++-- absl/synchronization/mutex.cc | 74 ++++++++++++++++++++++++++----------------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index e3907827..f180cd77 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -136,9 +136,10 @@ // 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(_WIN32) || __clang_major__ < 9) && !defined(__MINGW32__) +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + (!defined(_WIN32) || (defined(__clang__) && __clang_major__ < 9)) && \ + !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 376ea794..8f25e3dd 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -1744,23 +1744,33 @@ ABSL_XRAY_LOG_ARGS(1) void Mutex::ReaderUnlock() { ABSL_TSAN_MUTEX_POST_UNLOCK(this, __tsan_mutex_read_lock); } -// The zap_desig_waker bitmask is used to clear the designated waker flag in -// the mutex if this thread has blocked, and therefore may be the designated -// waker. -static const intptr_t zap_desig_waker[] = { - ~static_cast(0), // not blocked - ~static_cast( - kMuDesig) // blocked; turn off the designated waker bit -}; - -// The ignore_waiting_writers bitmask is used to ignore the existence -// of waiting writers if a reader that has already blocked once -// wakes up. -static const intptr_t ignore_waiting_writers[] = { - ~static_cast(0), // not blocked - ~static_cast( - kMuWrWait) // blocked; pretend there are no waiting writers -}; +// Clears the designated waker flag in the mutex if this thread has blocked, and +// therefore may be the designated waker. +static intptr_t ClearDesignatedWakerMask(int flag) { + assert(flag >= 0); + assert(flag <= 1); + switch (flag) { + case 0: // not blocked + return ~static_cast(0); + case 1: // blocked; turn off the designated waker bit + return ~static_cast(kMuDesig); + } + ABSL_INTERNAL_UNREACHABLE; +} + +// Conditionally ignores the existence of waiting writers if a reader that has +// already blocked once wakes up. +static intptr_t IgnoreWaitingWritersMask(int flag) { + assert(flag >= 0); + assert(flag <= 1); + switch (flag) { + case 0: // not blocked + return ~static_cast(0); + case 1: // blocked; pretend there are no waiting writers + return ~static_cast(kMuWrWait); + } + ABSL_INTERNAL_UNREACHABLE; +} // Internal version of LockWhen(). See LockSlowWithDeadline() ABSL_ATTRIBUTE_NOINLINE void Mutex::LockSlow(MuHow how, const Condition *cond, @@ -1852,8 +1862,10 @@ bool Mutex::LockSlowWithDeadline(MuHow how, const Condition *cond, bool unlock = false; if ((v & how->fast_need_zero) == 0 && // try fast acquire mu_.compare_exchange_strong( - v, (how->fast_or | (v & zap_desig_waker[flags & kMuHasBlocked])) + - how->fast_add, + v, + (how->fast_or | + (v & ClearDesignatedWakerMask(flags & kMuHasBlocked))) + + how->fast_add, std::memory_order_acquire, std::memory_order_relaxed)) { if (cond == nullptr || EvalConditionAnnotated(cond, this, true, false, how == kShared)) { @@ -1927,9 +1939,10 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { CheckForMutexCorruption(v, "Lock"); if ((v & waitp->how->slow_need_zero) == 0) { if (mu_.compare_exchange_strong( - v, (waitp->how->fast_or | - (v & zap_desig_waker[flags & kMuHasBlocked])) + - waitp->how->fast_add, + v, + (waitp->how->fast_or | + (v & ClearDesignatedWakerMask(flags & kMuHasBlocked))) + + waitp->how->fast_add, std::memory_order_acquire, std::memory_order_relaxed)) { if (waitp->cond == nullptr || EvalConditionAnnotated(waitp->cond, this, true, false, @@ -1946,8 +1959,9 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { if ((v & (kMuSpin|kMuWait)) == 0) { // no waiters // This thread tries to become the one and only waiter. PerThreadSynch *new_h = Enqueue(nullptr, waitp, v, flags); - intptr_t nv = (v & zap_desig_waker[flags & kMuHasBlocked] & kMuLow) | - kMuWait; + intptr_t nv = + (v & ClearDesignatedWakerMask(flags & kMuHasBlocked) & kMuLow) | + kMuWait; ABSL_RAW_CHECK(new_h != nullptr, "Enqueue to empty list failed"); if (waitp->how == kExclusive && (v & kMuReader) != 0) { nv |= kMuWrWait; @@ -1961,12 +1975,13 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { waitp->thread->waitp = nullptr; } } else if ((v & waitp->how->slow_inc_need_zero & - ignore_waiting_writers[flags & kMuHasBlocked]) == 0) { + IgnoreWaitingWritersMask(flags & kMuHasBlocked)) == 0) { // This is a reader that needs to increment the reader count, // but the count is currently held in the last waiter. if (mu_.compare_exchange_strong( - v, (v & zap_desig_waker[flags & kMuHasBlocked]) | kMuSpin | - kMuReader, + v, + (v & ClearDesignatedWakerMask(flags & kMuHasBlocked)) | + kMuSpin | kMuReader, std::memory_order_acquire, std::memory_order_relaxed)) { PerThreadSynch *h = GetPerThreadSynch(v); h->readers += kMuOne; // inc reader count in waiter @@ -1987,8 +2002,9 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { } } else if ((v & kMuSpin) == 0 && // attempt to queue ourselves mu_.compare_exchange_strong( - v, (v & zap_desig_waker[flags & kMuHasBlocked]) | kMuSpin | - kMuWait, + v, + (v & ClearDesignatedWakerMask(flags & kMuHasBlocked)) | + kMuSpin | kMuWait, std::memory_order_acquire, std::memory_order_relaxed)) { PerThreadSynch *h = GetPerThreadSynch(v); PerThreadSynch *new_h = Enqueue(h, waitp, v, flags); -- cgit v1.2.3 From c498947f8cf6dd4eb7bf4d589ca0f3816fd77d36 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 4 Jan 2022 07:59:44 -0800 Subject: Export of internal Abseil changes -- 620668e56950d7cfc39db2cd321adf265199ad77 by Abseil Team : absl::random compiles (at least for some cases) with -Wconversion -Wsign-compare PiperOrigin-RevId: 419595521 -- 746651e5fbcab6080d25c4eef8617fc289a448f6 by Abseil Team : Annotate FlagImpl storage buffer Flag type can contain legit uninitialized bits, e.g. padding. When the code calls bit_cast as int64_t, it will contain those bits as well. Then when we pass the int into the store it's UB for C++ and will be reported by the new msan. PiperOrigin-RevId: 418666492 GitOrigin-RevId: 620668e56950d7cfc39db2cd321adf265199ad77 Change-Id: Idd1190f5c98a0a13c4019f3d92cec0313822084c --- absl/flags/BUILD.bazel | 1 + absl/flags/CMakeLists.txt | 1 + absl/flags/internal/flag.cc | 3 +++ absl/random/bernoulli_distribution.h | 8 ++++---- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index d20deab4..020b7911 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -204,6 +204,7 @@ cc_library( "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:dynamic_annotations", "//absl/memory", "//absl/meta:type_traits", "//absl/strings", diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 7f3298e9..29c85ad3 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -105,6 +105,7 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::dynamic_annotations absl::fast_type_id ) diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 7102559e..55892d77 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -30,6 +30,7 @@ #include "absl/base/call_once.h" #include "absl/base/casts.h" #include "absl/base/config.h" +#include "absl/base/dynamic_annotations.h" #include "absl/base/optimization.h" #include "absl/flags/config.h" #include "absl/flags/internal/commandlineflag.h" @@ -160,6 +161,8 @@ void FlagImpl::Init() { std::memcpy(buf.data() + Sizeof(op_), &initialized, sizeof(initialized)); } + // Type can contain valid uninitialized bits, e.g. padding. + ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buf.data(), buf.size()); OneWordValue().store(absl::bit_cast(buf), std::memory_order_release); break; diff --git a/absl/random/bernoulli_distribution.h b/absl/random/bernoulli_distribution.h index 25bd0d5c..d81b6ae6 100644 --- a/absl/random/bernoulli_distribution.h +++ b/absl/random/bernoulli_distribution.h @@ -138,16 +138,16 @@ bool bernoulli_distribution::Generate(double p, // 64 bits. // // Second, `c` is constructed by first casting explicitly to a signed - // integer and then converting implicitly to an unsigned integer of the same + // integer and then casting explicitly to an unsigned integer of the same // size. This is done because the hardware conversion instructions produce // signed integers from double; if taken as a uint64_t the conversion would // be wrong for doubles greater than 2^63 (not relevant in this use-case). // If converted directly to an unsigned integer, the compiler would end up // emitting code to handle such large values that are not relevant due to // the known bounds on `c`. To avoid these extra instructions this - // implementation converts first to the signed type and then use the - // implicit conversion to unsigned (which is a no-op). - const uint64_t c = static_cast(p * kP32); + // implementation converts first to the signed type and then convert to + // unsigned (which is a no-op). + const uint64_t c = static_cast(static_cast(p * kP32)); const uint32_t v = fast_u32(g); // FAST PATH: this path fails with probability 1/2^32. Note that simply // returning v <= c would approximate P very well (up to an absolute error -- cgit v1.2.3 From 628a2825f8dc0219964886e7cc3f7f519e3bd950 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 4 Jan 2022 18:04:19 -0800 Subject: Export of internal Abseil changes -- 04e8fdf6a37d31f9364ca0f70bf663ee972481c6 by Abseil Team : Another implicit sign conversion found and removed. PiperOrigin-RevId: 419718762 -- dbb6bca7d3cfa9ce79e70d0ed3d0354a4e3a0983 by Abseil Team : Fix sign conversions so that absl/status/status.h can compile with -Wconversion -Wsign-compare PiperOrigin-RevId: 419658075 GitOrigin-RevId: 04e8fdf6a37d31f9364ca0f70bf663ee972481c6 Change-Id: I18441488cc84f573c2818ee241c387e1953d5105 --- absl/container/inlined_vector.h | 4 ++-- absl/container/internal/inlined_vector.h | 4 ++-- absl/container/internal/raw_hash_set.h | 2 +- absl/numeric/bits.h | 3 ++- absl/random/internal/pcg_engine.h | 2 +- absl/strings/ascii.h | 6 +++--- absl/strings/internal/cord_internal.h | 9 +++++---- absl/strings/numbers.h | 2 +- absl/strings/str_cat.h | 30 ++++++++++++++++++------------ absl/types/span.h | 3 ++- 10 files changed, 37 insertions(+), 28 deletions(-) diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 5597d43e..711b29c1 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -151,7 +151,7 @@ class InlinedVector { const allocator_type& allocator = allocator_type()) : storage_(allocator) { storage_.Initialize(IteratorValueAdapter(first), - std::distance(first, last)); + static_cast(std::distance(first, last))); } // Creates an inlined vector with elements constructed from the provided input @@ -522,7 +522,7 @@ class InlinedVector { EnableIfAtLeastForwardIterator = 0> void assign(ForwardIterator first, ForwardIterator last) { storage_.Assign(IteratorValueAdapter(first), - std::distance(first, last)); + static_cast(std::distance(first, last))); } // Overload of `InlinedVector::assign(...)` to replace the contents of the diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 98c26afc..cd34a413 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -311,10 +311,10 @@ class Storage { // Storage Constructors and Destructor // --------------------------------------------------------------------------- - Storage() : metadata_(A(), /* size and is_allocated */ 0) {} + Storage() : metadata_(A(), /* size and is_allocated */ 0u) {} explicit Storage(const A& allocator) - : metadata_(allocator, /* size and is_allocated */ 0) {} + : metadata_(allocator, /* size and is_allocated */ 0u) {} ~Storage() { if (GetSizeAndIsAllocated() == 0) { diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index ad12f41c..1157d25a 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1953,7 +1953,7 @@ class raw_hash_set { absl::container_internal::CompressedTuple - settings_{0, HashtablezInfoHandle{}, hasher{}, key_equal{}, + settings_{0u, HashtablezInfoHandle{}, hasher{}, key_equal{}, allocator_type{}}; }; diff --git a/absl/numeric/bits.h b/absl/numeric/bits.h index 52013ad4..628cdf50 100644 --- a/absl/numeric/bits.h +++ b/absl/numeric/bits.h @@ -133,7 +133,8 @@ template ABSL_INTERNAL_CONSTEXPR_CLZ inline typename std::enable_if::value, T>::type bit_width(T x) noexcept { - return std::numeric_limits::digits - countl_zero(x); + return std::numeric_limits::digits - + static_cast(countl_zero(x)); } // Returns: If x == 0, 0; otherwise the maximal value y such that diff --git a/absl/random/internal/pcg_engine.h b/absl/random/internal/pcg_engine.h index 8efaf2e0..4ab44c94 100644 --- a/absl/random/internal/pcg_engine.h +++ b/absl/random/internal/pcg_engine.h @@ -262,7 +262,7 @@ struct pcg_xsl_rr_128_64 { uint64_t rotate = h >> 58u; uint64_t s = Uint128Low64(state) ^ h; #endif - return rotr(s, rotate); + return rotr(s, static_cast(rotate)); } }; diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h index 9b8e5d1a..42eadaea 100644 --- a/absl/strings/ascii.h +++ b/absl/strings/ascii.h @@ -197,7 +197,7 @@ ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) { ABSL_MUST_USE_RESULT inline absl::string_view StripLeadingAsciiWhitespace( absl::string_view str) { auto it = std::find_if_not(str.begin(), str.end(), absl::ascii_isspace); - return str.substr(it - str.begin()); + return str.substr(static_cast(it - str.begin())); } // Strips in place whitespace from the beginning of the given string. @@ -211,13 +211,13 @@ inline void StripLeadingAsciiWhitespace(std::string* str) { ABSL_MUST_USE_RESULT inline absl::string_view StripTrailingAsciiWhitespace( absl::string_view str) { auto it = std::find_if_not(str.rbegin(), str.rend(), absl::ascii_isspace); - return str.substr(0, str.rend() - it); + return str.substr(0, static_cast(str.rend() - it)); } // Strips in place whitespace from the end of the given string inline void StripTrailingAsciiWhitespace(std::string* str) { auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace); - str->erase(str->rend() - it); + str->erase(static_cast(str->rend() - it)); } // Returns absl::string_view with whitespace stripped from both ends of the diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index b16c8fa5..776d1c40 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -462,8 +462,8 @@ class InlineData { // Requires the current instance to hold a tree value. CordzInfo* cordz_info() const { assert(is_tree()); - intptr_t info = - static_cast(absl::big_endian::ToHost64(as_tree_.cordz_info)); + intptr_t info = static_cast( + absl::big_endian::ToHost64(static_cast(as_tree_.cordz_info))); assert(info & 1); return reinterpret_cast(info - 1); } @@ -473,8 +473,9 @@ class InlineData { // Requires the current instance to hold a tree value. void set_cordz_info(CordzInfo* cordz_info) { assert(is_tree()); - intptr_t info = reinterpret_cast(cordz_info) | 1; - as_tree_.cordz_info = absl::big_endian::FromHost64(info); + uintptr_t info = reinterpret_cast(cordz_info) | 1; + as_tree_.cordz_info = + static_cast(absl::big_endian::FromHost64(info)); } // Resets the current cordz_info to null / empty. diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index e977fc67..3ed24669 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -265,7 +265,7 @@ inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { } #endif // | 0x1 so that even 0 has 1 digit. - return 16 - countl_zero(val | 0x1) / 4; + return 16 - static_cast(countl_zero(val | 0x1) / 4); } } // namespace numbers_internal diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index a8a85c73..4d228b09 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -214,23 +214,29 @@ class AlphaNum { // A bool ctor would also convert incoming pointers (bletch). AlphaNum(int x) // NOLINT(runtime/explicit) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(unsigned int x) // NOLINT(runtime/explicit) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(unsigned long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(long long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(unsigned long long x) // NOLINT(*) - : piece_(digits_, - numbers_internal::FastIntToBuffer(x, digits_) - &digits_[0]) {} + : piece_(digits_, static_cast( + numbers_internal::FastIntToBuffer(x, digits_) - + &digits_[0])) {} AlphaNum(float f) // NOLINT(runtime/explicit) : piece_(digits_, numbers_internal::SixDigitsToBuffer(f, digits_)) {} diff --git a/absl/types/span.h b/absl/types/span.h index 6272bb7a..fdfbd77c 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -664,7 +664,8 @@ constexpr Span MakeSpan(T* ptr, size_t size) noexcept { template Span MakeSpan(T* begin, T* end) noexcept { - return ABSL_HARDENING_ASSERT(begin <= end), Span(begin, end - begin); + return ABSL_HARDENING_ASSERT(begin <= end), + Span(begin, static_cast(end - begin)); } template -- cgit v1.2.3 From 294166fcbeae8c19b8817330a0874c186d147266 Mon Sep 17 00:00:00 2001 From: Andrei Polushin Date: Fri, 7 Jan 2022 21:42:54 +0700 Subject: cmake: use target aliases from local Google Test checkout. (#1083) * cmake: use target aliases from local Google Test checkout. `ABSL_FIND_GOOGLETEST` is a dependent option, it is always `OFF` when `ABSL_USE_EXTERNAL_GOOGLETEST` is `OFF` (i.e. when Google Test is checked out locally). `GTest::` target aliases are to be added only if `find_package(GTest)` has been actually used. * cmake: clarify adding GTest:: aliases. --- CMakeLists.txt | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff49ac1e..5bd3415a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,8 +134,14 @@ if(BUILD_TESTING AND ABSL_BUILD_TESTING) if (ABSL_USE_EXTERNAL_GOOGLETEST) if (ABSL_FIND_GOOGLETEST) find_package(GTest REQUIRED) - else() - if (NOT TARGET gtest AND NOT TARGET GTest::gtest) + elseif(NOT TARGET GTest::gtest) + if(TARGET gtest) + # When Google Test is included directly rather than through find_package, the aliases are missing. + add_library(GTest::gtest ALIAS gtest) + add_library(GTest::gtest_main ALIAS gtest_main) + add_library(GTest::gmock ALIAS gmock) + add_library(GTest::gmock_main ALIAS gmock_main) + else() message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.") endif() endif() @@ -157,14 +163,6 @@ if(BUILD_TESTING AND ABSL_BUILD_TESTING) include(CMake/Googletest/DownloadGTest.cmake) endif() - if (NOT ABSL_FIND_GOOGLETEST) - # When Google Test is included directly rather than through find_package, the aliases are missing. - add_library(GTest::gtest ALIAS gtest) - add_library(GTest::gtest_main ALIAS gtest_main) - add_library(GTest::gmock ALIAS gmock) - add_library(GTest::gmock_main ALIAS gmock_main) - endif() - check_target(GTest::gtest) check_target(GTest::gtest_main) check_target(GTest::gmock) -- cgit v1.2.3 From 4799f6225a4fcf1fa2383d2eff25830c343bc9e9 Mon Sep 17 00:00:00 2001 From: Andrei Polushin Date: Fri, 7 Jan 2022 21:44:10 +0700 Subject: cmake: make `random_mocking_bit_gen` library public. (#1084) An external project may depend on an Abseil `TESTONLY` library like `absl::random_mocking_bit_gen`. Such a library should not be excluded from build when Abseil's own tests are not built. fixes #997 --- CMake/AbseilHelpers.cmake | 3 ++- absl/random/CMakeLists.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 9ad2627d..ed87dde0 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -83,7 +83,8 @@ function(absl_cc_library) ${ARGN} ) - if(ABSL_CC_LIB_TESTONLY AND NOT (BUILD_TESTING AND ABSL_BUILD_TESTING)) + if(NOT ABSL_CC_LIB_PUBLIC AND ABSL_CC_LIB_TESTONLY AND + NOT (BUILD_TESTING AND ABSL_BUILD_TESTING)) return() endif() diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 7fbd460d..b4ada69b 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -121,6 +121,7 @@ absl_cc_library( absl::variant GTest::gmock GTest::gtest + PUBLIC TESTONLY ) -- cgit v1.2.3 From 78f9680225b9792c26dfdd99d0bd26c96de53dd4 Mon Sep 17 00:00:00 2001 From: Andrei Polushin Date: Fri, 7 Jan 2022 21:45:06 +0700 Subject: macos: support Apple Universal 2 builds (#1086) fixes #970 --- absl/copts/AbseilConfigureCopts.cmake | 44 ++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake index 942ce90a..15d6c895 100644 --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -12,7 +12,49 @@ else() set(ABSL_BUILD_DLL FALSE) endif() -if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64") +if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES [[Clang]]) + # Some CMake targets (not known at the moment of processing) could be set to + # compile for multiple architectures as specified by the OSX_ARCHITECTURES + # property, which is target-specific. We should neither inspect nor rely on + # any CMake property or variable to detect an architecture, in particular: + # + # - CMAKE_OSX_ARCHITECTURES + # is just an initial value for OSX_ARCHITECTURES; set too early. + # + # - OSX_ARCHITECTURES + # is a per-target property; targets could be defined later, and their + # properties could be modified any time later. + # + # - CMAKE_SYSTEM_PROCESSOR + # does not reflect multiple architectures at all. + # + # When compiling for multiple architectures, a build system can invoke a + # compiler either + # + # - once: a single command line for multiple architectures (Ninja build) + # - twice: two command lines per each architecture (Xcode build system) + # + # If case of Xcode, it would be possible to set an Xcode-specific attributes + # like XCODE_ATTRIBUTE_OTHER_CPLUSPLUSFLAGS[arch=arm64] or similar. + # + # In both cases, the viable strategy is to pass all arguments at once, allowing + # the compiler to dispatch arch-specific arguments to a designated backend. + set(ABSL_RANDOM_RANDEN_COPTS "") + foreach(_arch IN ITEMS "x86_64" "arm64") + string(TOUPPER "${_arch}" _arch_uppercase) + string(REPLACE "X86_64" "X64" _arch_uppercase ${_arch_uppercase}) + foreach(_flag IN LISTS ABSL_RANDOM_HWAES_${_arch_uppercase}_FLAGS) + list(APPEND ABSL_RANDOM_RANDEN_COPTS "-Xarch_${_arch}" "${_flag}") + endforeach() + endforeach() + # If a compiler happens to deal with an argument for a currently unused + # architecture, it will warn about an unused command line argument. + option(ABSL_RANDOM_RANDEN_COPTS_WARNING OFF + "Warn if one of ABSL_RANDOM_RANDEN_COPTS is unused") + if(ABSL_RANDOM_RANDEN_COPTS AND NOT ABSL_RANDOM_RANDEN_COPTS_WARNING) + list(APPEND ABSL_RANDOM_RANDEN_COPTS "-Wno-unused-command-line-argument") + endif() +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64") if (MSVC) set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}") else() -- cgit v1.2.3 From 2a042b082ca6fc8592ec98d800012fc03c965c15 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 10 Jan 2022 10:41:45 -0800 Subject: Export of internal Abseil changes -- 389189dbb322df0d0468ab13edf7dc185dc63833 by Abseil Team : absl str_format.h can compile with -Wconversion and -Wsign-compare PiperOrigin-RevId: 420799960 -- 762e5adc429fc143756c42fe92fe8073c87c075f by Abseil Team : GetStackTraceWithContext: Fix min_dropped_frames when no frames are recorded Previously, if there were still frames to skip, they would be included in min_dropped_frames. PiperOrigin-RevId: 420766341 -- 7d4374b8eaa410f4f98ec03d6a8997dccadfb271 by Abseil Team : absl::flags compiles with -Wconversion and -Wsign-compare PiperOrigin-RevId: 420476807 -- 5f00f7805419d725fa1ff57b388e4c0750d1d6b0 by Derek Mauro : Internal change PiperOrigin-RevId: 420282152 -- bd5471fc34956acf3888bf90287b2aee4415c96d by Derek Mauro : Internal change PiperOrigin-RevId: 420282033 -- 61c78020804e4290e9b2fe151aeaf99b198716ee by Derek Mauro : Internal change PiperOrigin-RevId: 420281867 -- dbe3aad24b65ea11664401a307ea3d2f28e8a7b9 by Derek Mauro : Remove the incorrect test for the availability of [[nodiscard]] It should have been ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) instead of ABSL_HAVE_ATTRIBUTE(nodiscard). As a result, some code is not compliant with [[nodiscard]], so we cannot simply correct the availability test. Recommend that C++17-only code use the standard [[nodiscard] directly. PiperOrigin-RevId: 420150702 GitOrigin-RevId: 389189dbb322df0d0468ab13edf7dc185dc63833 Change-Id: Idf6ebae3c4edd945c9032c7db3d0ab32d16e8078 --- absl/base/attributes.h | 10 +++++++--- absl/debugging/internal/stacktrace_aarch64-inl.inc | 11 ++++++++--- absl/debugging/internal/stacktrace_arm-inl.inc | 11 ++++++++--- absl/debugging/internal/stacktrace_powerpc-inl.inc | 11 ++++++++--- absl/debugging/internal/stacktrace_riscv-inl.inc | 11 ++++++++--- absl/debugging/internal/stacktrace_x86-inl.inc | 11 ++++++++--- absl/flags/internal/flag.h | 8 +++++--- absl/strings/internal/str_format/arg.h | 2 +- absl/strings/internal/str_format/extension.h | 6 ++++-- absl/strings/internal/str_format/output.h | 3 ++- absl/strings/internal/str_format/parser.h | 6 ++++-- 11 files changed, 63 insertions(+), 27 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index f180cd77..00aad489 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -401,6 +401,9 @@ // // Tells the compiler to warn about unused results. // +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard `[[nodiscard]]` directly over this macro. +// // When annotating a function, it must appear as the first part of the // declaration or definition. The compiler will warn if the return value from // such a function is unused: @@ -427,9 +430,10 @@ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 // // Note: past advice was to place the macro after the argument list. -#if ABSL_HAVE_ATTRIBUTE(nodiscard) -#define ABSL_MUST_USE_RESULT [[nodiscard]] -#elif defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) +// +// TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is +// compliant with the stricter [[nodiscard]]. +#if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) #define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) #else #define ABSL_MUST_USE_RESULT diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc index f4859d7c..4f9db9d6 100644 --- a/absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -176,12 +176,17 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, // Implementation detail: we clamp the max of frames we are willing to // count, so as not to spend too much time in the loop below. const int kMaxUnwind = 200; - int j = 0; - for (; frame_pointer != nullptr && j < kMaxUnwind; j++) { + int num_dropped_frames = 0; + for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } frame_pointer = NextStackFrame(frame_pointer, ucp); } - *min_dropped_frames = j; + *min_dropped_frames = num_dropped_frames; } return n; } diff --git a/absl/debugging/internal/stacktrace_arm-inl.inc b/absl/debugging/internal/stacktrace_arm-inl.inc index 2a1bf2e8..102a2a12 100644 --- a/absl/debugging/internal/stacktrace_arm-inl.inc +++ b/absl/debugging/internal/stacktrace_arm-inl.inc @@ -112,11 +112,16 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, // Implementation detail: we clamp the max of frames we are willing to // count, so as not to spend too much time in the loop below. const int kMaxUnwind = 200; - int j = 0; - for (; sp != nullptr && j < kMaxUnwind; j++) { + int num_dropped_frames = 0; + for (int j = 0; sp != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } sp = NextStackFrame(sp); } - *min_dropped_frames = j; + *min_dropped_frames = num_dropped_frames; } return n; } diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc index cf8c0516..085cef67 100644 --- a/absl/debugging/internal/stacktrace_powerpc-inl.inc +++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -231,11 +231,16 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, // Implementation detail: we clamp the max of frames we are willing to // count, so as not to spend too much time in the loop below. const int kMaxUnwind = 1000; - int j = 0; - for (; next_sp != nullptr && j < kMaxUnwind; j++) { + int num_dropped_frames = 0; + for (int j = 0; next_sp != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } next_sp = NextStackFrame(next_sp, ucp); } - *min_dropped_frames = j; + *min_dropped_frames = num_dropped_frames; } return n; } diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc index 8cbc7854..b4bdb5f1 100644 --- a/absl/debugging/internal/stacktrace_riscv-inl.inc +++ b/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -213,12 +213,17 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, // Implementation detail: we clamp the max of frames we are willing to // count, so as not to spend too much time in the loop below. const int kMaxUnwind = 200; - int j = 0; - for (; frame_pointer != nullptr && j < kMaxUnwind; j++) { + int num_dropped_frames = 0; + for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } frame_pointer = NextStackFrame(frame_pointer, ucp); } - *min_dropped_frames = j; + *min_dropped_frames = num_dropped_frames; } return n; } diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc index 847a5473..1b5d8235 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -341,12 +341,17 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, // Implementation detail: we clamp the max of frames we are willing to // count, so as not to spend too much time in the loop below. const int kMaxUnwind = 1000; - int j = 0; - for (; fp != nullptr && j < kMaxUnwind; j++) { + int num_dropped_frames = 0; + for (int j = 0; fp != nullptr && j < kMaxUnwind; j++) { + if (skip_count > 0) { + skip_count--; + } else { + num_dropped_frames++; + } fp = NextStackFrame(fp, ucp, stack_low, stack_high); } - *min_dropped_frames = j; + *min_dropped_frames = num_dropped_frames; } return n; } diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 2d0a7e9c..6154638c 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -303,7 +303,9 @@ constexpr FlagDefaultArg DefaultArg(char) { /////////////////////////////////////////////////////////////////////////////// // Flag current value auxiliary structs. -constexpr int64_t UninitializedFlagValue() { return 0xababababababababll; } +constexpr int64_t UninitializedFlagValue() { + return static_cast(0xababababababababll); +} template using FlagUseValueAndInitBitStorage = std::integral_constant< @@ -755,8 +757,8 @@ void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { case FlagOp::kValueOffset: { // Round sizeof(FlagImp) to a multiple of alignof(FlagValue) to get the // offset of the data. - ptrdiff_t round_to = alignof(FlagValue); - ptrdiff_t offset = + size_t round_to = alignof(FlagValue); + size_t offset = (sizeof(FlagImpl) + round_to - 1) / round_to * round_to; return reinterpret_cast(offset); } diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 3c91be70..b9dda909 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -144,7 +144,7 @@ StringConvertResult FormatConvertImpl(const AbslCord& value, size_t space_remaining = 0; int width = conv.width(); - if (width >= 0) space_remaining = width; + if (width >= 0) space_remaining = static_cast(width); size_t to_write = value.size(); diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index 55cbb56d..c47536d6 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h @@ -70,7 +70,7 @@ class FormatSinkImpl { ~FormatSinkImpl() { Flush(); } void Flush() { - raw_.Write(string_view(buf_, pos_ - buf_)); + raw_.Write(string_view(buf_, static_cast(pos_ - buf_))); pos_ = buf_; } @@ -120,7 +120,9 @@ class FormatSinkImpl { } private: - size_t Avail() const { return buf_ + sizeof(buf_) - pos_; } + size_t Avail() const { + return static_cast(buf_ + sizeof(buf_) - pos_); + } FormatRawSinkImpl raw_; size_t size_ = 0; diff --git a/absl/strings/internal/str_format/output.h b/absl/strings/internal/str_format/output.h index 8030dae0..15e751ab 100644 --- a/absl/strings/internal/str_format/output.h +++ b/absl/strings/internal/str_format/output.h @@ -22,6 +22,7 @@ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_ #include +#include #include #include @@ -71,7 +72,7 @@ inline void AbslFormatFlush(std::string* out, string_view s) { out->append(s.data(), s.size()); } inline void AbslFormatFlush(std::ostream* out, string_view s) { - out->write(s.data(), s.size()); + out->write(s.data(), static_cast(s.size())); } inline void AbslFormatFlush(FILERawSink* sink, string_view v) { diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index ad8646ed..32b91d03 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h @@ -151,7 +151,8 @@ bool ParseFormatString(string_view src, Consumer consumer) { const char* p = src.data(); const char* const end = p + src.size(); while (p != end) { - const char* percent = static_cast(memchr(p, '%', end - p)); + const char* percent = + static_cast(memchr(p, '%', static_cast(end - p))); if (!percent) { // We found the last substring. return consumer.Append(string_view(p, end - p)); @@ -242,7 +243,8 @@ class ParsedFormatBase { string_view text(base, 0); for (const auto& item : items_) { const char* const end = text.data() + text.size(); - text = string_view(end, (base + item.text_end) - end); + text = + string_view(end, static_cast((base + item.text_end) - end)); if (item.is_conversion) { if (!consumer.ConvertOne(item.conv, text)) return false; } else { -- cgit v1.2.3 From c59e7e59f5d29619ddc07fcb59be3dcba9585814 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 14 Jan 2022 04:25:20 -0800 Subject: Export of internal Abseil changes -- 0db7f4046f9b59c0f8c3df2f0eb7fd88fc328439 by Abseil Team : Revise documentation of bit_cast: * Removes inappropriate examples (round-tripping pointers, serialization), for which reinterpret_cast is more appropriate. * Removes mention of "bit representation", which is not an explicit notion in C++. The best we get is "byte representation". * Removes a circular defition of "bitcast" as itself, and instead explains what it does. * Removes the mathism "for some values of", which is probably not totally accessible to a general audience, and in any case needless verbiage. * Fixes comments in the example. * Replaces some colloquialisms with simpler, more direct language. PiperOrigin-RevId: 421791786 -- e04e64df55d93c1b9a09c0483b97cc4d8763260d by Derek Mauro : Update Docker image to use GCC 11.2, Clang 14 (prerelease), CMake 3.22.1, and Bazel 4.2.2 PiperOrigin-RevId: 421658559 -- d002bb3dc5cd1fc5b4cbd79a450efc894caa567c by Chris Kennelly : Add a small microbenchmark for absl::bit_width. PiperOrigin-RevId: 421604852 -- 131b057d1b76ecd7170421b48d661bb958ff676b by Evan Brown : Adds a disabled test for EBO in nested `CompressedTuple`s. PiperOrigin-RevId: 421413134 -- e34c7876d3a1212d90c73c030ccae6169b682d43 by Jorg Brown : Show users a better error message if they pass a pointer to absl::Uniform. PiperOrigin-RevId: 421090472 GitOrigin-RevId: 0db7f4046f9b59c0f8c3df2f0eb7fd88fc328439 Change-Id: I5a004e8d17e974fa4897a09d1466ae8fc65dfdbb --- absl/base/casts.h | 57 +++++++++--------- absl/container/internal/compressed_tuple_test.cc | 10 ++++ absl/numeric/BUILD.bazel | 14 +++++ absl/numeric/bits_benchmark.cc | 73 ++++++++++++++++++++++++ absl/random/internal/distribution_caller.h | 2 + ci/linux_clang-latest_libcxx_asan_bazel.sh | 4 +- ci/linux_clang-latest_libcxx_bazel.sh | 4 +- ci/linux_clang-latest_libcxx_tsan_bazel.sh | 4 +- ci/linux_docker_containers.sh | 4 +- 9 files changed, 137 insertions(+), 35 deletions(-) create mode 100644 absl/numeric/bits_benchmark.cc diff --git a/absl/base/casts.h b/absl/base/casts.h index 83c69126..b16af233 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -105,47 +105,50 @@ constexpr To implicit_cast(typename absl::internal::identity_t to) { // bit_cast() // -// Performs a bitwise cast on a type without changing the underlying bit -// representation of that type's value. The two types must be of the same size -// and both types must be trivially copyable. As with most casts, use with -// caution. A `bit_cast()` might be needed when you need to temporarily treat a -// type as some other type, such as in the following cases: -// -// * Serialization (casting temporarily to `char *` for those purposes is -// always allowed by the C++ standard) -// * Managing the individual bits of a type within mathematical operations -// that are not normally accessible through that type -// * Casting non-pointer types to pointer types (casting the other way is -// allowed by `reinterpret_cast()` but round-trips cannot occur the other -// way). -// -// Example: +// Creates a value of the new type `Dest` whose representation is the same as +// that of the argument, which is of (deduced) type `Source` (a "bitwise cast"; +// every bit in the value representation of the result is equal to the +// corresponding bit in the object representation of the source). Source and +// destination types must be of the same size, and both types must be trivially +// copyable. +// +// As with most casts, use with caution. A `bit_cast()` might be needed when you +// need to treat a value as the value of some other type, for example, to access +// the individual bits of an object which are not normally accessible through +// the object's type, such as for working with the binary representation of a +// floating point value: // // float f = 3.14159265358979; // int i = bit_cast(f); // // i = 0x40490fdb // -// Casting non-pointer types to pointer types and then dereferencing them -// traditionally produces undefined behavior. +// Reinterpreting and accessing a value directly as a different type (as shown +// below) usually results in undefined behavior. // // Example: // // // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast(&f); // WRONG +// float f = 3.14159265358979; +// int i = reinterpret_cast(f); // Wrong +// int j = *reinterpret_cast(&f); // Equally wrong +// int k = *bit_cast(&f); // Equally wrong +// +// Reinterpret-casting results in undefined behavior according to the ISO C++ +// specification, section [basic.lval]. Roughly, this section says: if an object +// in memory has one type, and a program accesses it with a different type, the +// result is undefined behavior for most "different type". // -// The address-casting method produces undefined behavior according to the ISO -// C++ specification section [basic.lval]. Roughly, this section says: if an -// object in memory has one type, and a program accesses it with a different -// type, the result is undefined behavior for most values of "different type". +// Using bit_cast on a pointer and then dereferencing it is no better than using +// reinterpret_cast. You should only use bit_cast on the value itself. // // Such casting results in type punning: holding an object in memory of one type // and reading its bits back using a different type. A `bit_cast()` avoids this -// issue by implementing its casts using `memcpy()`, which avoids introducing -// this undefined behavior. +// issue by copying the object representation to a new value, which avoids +// introducing this undefined behavior (since the original value is never +// accessed in the wrong way). // -// NOTE: The requirements here are more strict than the bit_cast of standard -// proposal p0476 due to the need for workarounds and lack of intrinsics. +// NOTE: The requirements here are stricter than the bit_cast of standard +// proposal P0476 due to the need for workarounds and lack of intrinsics. // Specifically, this implementation also requires `Dest` to be // default-constructible. template < diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc index 62a7483e..74111f97 100644 --- a/absl/container/internal/compressed_tuple_test.cc +++ b/absl/container/internal/compressed_tuple_test.cc @@ -403,6 +403,16 @@ TEST(CompressedTupleTest, EmptyFinalClass) { } #endif +// TODO(b/214288561): enable this test. +TEST(CompressedTupleTest, DISABLED_NestedEbo) { + struct Empty1 {}; + struct Empty2 {}; + CompressedTuple, int> x; + CompressedTuple y; + // Currently fails with sizeof(x) == 8, sizeof(y) == 4. + EXPECT_EQ(sizeof(x), sizeof(y)); +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index 1f9e0f20..eaa27dfd 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel @@ -37,6 +37,20 @@ cc_library( ], ) +cc_binary( + name = "bits_benchmark", + testonly = 1, + srcs = ["bits_benchmark.cc"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bits", + "//absl/base:core_headers", + "//absl/random", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_test( name = "bits_test", size = "small", diff --git a/absl/numeric/bits_benchmark.cc b/absl/numeric/bits_benchmark.cc new file mode 100644 index 00000000..3de1dbfa --- /dev/null +++ b/absl/numeric/bits_benchmark.cc @@ -0,0 +1,73 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "benchmark/benchmark.h" +#include "absl/base/optimization.h" +#include "absl/numeric/bits.h" +#include "absl/random/random.h" + +namespace absl { +namespace { + +template +static void BM_bitwidth(benchmark::State& state) { + const int count = state.range(0); + + absl::BitGen rng; + std::vector values; + values.reserve(count); + for (int i = 0; i < count; ++i) { + values.push_back(absl::Uniform(rng, 0, std::numeric_limits::max())); + } + + while (state.KeepRunningBatch(count)) { + for (int i = 0; i < count; ++i) { + benchmark::DoNotOptimize(values[i]); + } + } +} +BENCHMARK_TEMPLATE(BM_bitwidth, uint8_t)->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_bitwidth, uint16_t)->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_bitwidth, uint32_t)->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_bitwidth, uint64_t)->Range(1, 1 << 20); + +template +static void BM_bitwidth_nonzero(benchmark::State& state) { + const int count = state.range(0); + + absl::BitGen rng; + std::vector values; + values.reserve(count); + for (int i = 0; i < count; ++i) { + values.push_back(absl::Uniform(rng, 1, std::numeric_limits::max())); + } + + while (state.KeepRunningBatch(count)) { + for (int i = 0; i < count; ++i) { + const T value = values[i]; + ABSL_INTERNAL_ASSUME(value > 0); + benchmark::DoNotOptimize(value); + } + } +} +BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint8_t)->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint16_t)->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint32_t)->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_bitwidth_nonzero, uint64_t)->Range(1, 1 << 20); + +} // namespace +} // namespace absl diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h index fc81b787..f1ad5ccd 100644 --- a/absl/random/internal/distribution_caller.h +++ b/absl/random/internal/distribution_caller.h @@ -32,6 +32,8 @@ namespace random_internal { // to intercept such calls. template struct DistributionCaller { + static_assert(!std::is_pointer::value, + "You must pass a reference, not a pointer."); // SFINAE to detect whether the URBG type includes a member matching // bool InvokeMock(base_internal::FastTypeIdType, void*, void*). // diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh index 5245933a..0605e2b3 100755 --- a/ci/linux_clang-latest_libcxx_asan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh @@ -70,8 +70,8 @@ for std in ${STD}; do --rm \ -e CC="/opt/llvm/clang/bin/clang" \ -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \ - -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \ - -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \ + -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu" \ + -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx/include/c++/v1" \ ${DOCKER_EXTRA_ARGS:-} \ ${DOCKER_CONTAINER} \ /usr/local/bin/bazel test ... \ diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh index e0fe653d..00517749 100755 --- a/ci/linux_clang-latest_libcxx_bazel.sh +++ b/ci/linux_clang-latest_libcxx_bazel.sh @@ -71,8 +71,8 @@ for std in ${STD}; do --rm \ -e CC="/opt/llvm/clang/bin/clang" \ -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \ - -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \ - -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \ + -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu" \ + -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx/include/c++/v1" \ ${DOCKER_EXTRA_ARGS:-} \ ${DOCKER_CONTAINER} \ /bin/sh -c " diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh index 555f6b1c..da4fcd0a 100755 --- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh @@ -70,8 +70,8 @@ for std in ${STD}; do --rm \ -e CC="/opt/llvm/clang/bin/clang" \ -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \ - -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx-tsan/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib" \ - -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx-tsan/include/c++/v1" \ + -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx-tsan/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib/x86_64-unknown-linux-gnu" \ + -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx-tsan/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx-tsan/include/c++/v1" \ ${DOCKER_EXTRA_ARGS:-} \ ${DOCKER_CONTAINER} \ /usr/local/bin/bazel test ... \ diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh index 32865b83..37be5310 100644 --- a/ci/linux_docker_containers.sh +++ b/ci/linux_docker_containers.sh @@ -16,6 +16,6 @@ # Test scripts should source this file to get the identifiers. readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026" -readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617" -readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617" +readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220113" +readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220113" readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20210617" -- cgit v1.2.3 From 9bb42857112ad13b23de4333fbb75eb47db9de95 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 18 Jan 2022 13:02:48 -0800 Subject: Export of internal Abseil changes -- 7f5caec21a1a88db6486b8bc2e5f6d5baba076ed by Derek Mauro : Tag absl::StatusOr with [[nodiscard]] when it is available [[nodiscard]] provides better diagnostics on classes than the current ABSL_MUST_USE_RESULT, which expands to __attribute__((warn_unused_result)) Ideally we would make ABSL_MUST_USE_RESULT expand to [[nodiscard]], but we would need to fix all code to build with [[nodiscard]] first. PiperOrigin-RevId: 422628161 -- 236be69f0f34ccaa3adc831075613847797e6557 by Jorg Brown : Make sure all of absl/strings compiles even with -Wsign-compare and -Wconversion warnings. PiperOrigin-RevId: 422573904 -- 005669883a8794e6d19dc4bbb4f0e18032e2fbc9 by Chris Kennelly : Include sampling stride in hashtable sampling data. This allows us to weight each sample to control for our sampling rate. PiperOrigin-RevId: 421886935 -- 78046ce6b429b9f5b6c97b05e8448a791db93bbe by Abseil Team : Use __builtin_bit_cast for absl::bit_cast when possible This makes absl::bit_cast match the requirements of the standard when compiler support exists. PiperOrigin-RevId: 421883999 -- f397461f4bbeabd32437df0f2275663aeb51adb2 by Derek Mauro : Tag absl::Status with [[nodiscard]] when it is available [[nodiscard]] provides better diagnostics on classes than the current ABSL_MUST_USE_RESULT, which expands to __attribute__((warn_unused_result)) Ideally we would make ABSL_MUST_USE_RESULT expand to [[nodiscard]], but we would need to fix all code to build with [[nodiscard]] first. PiperOrigin-RevId: 421825565 GitOrigin-RevId: 7f5caec21a1a88db6486b8bc2e5f6d5baba076ed Change-Id: I760b45b68f6012809c70c2a584e4144a99f98733 --- absl/base/casts.h | 18 ++++++-- absl/container/internal/hashtablez_sampler.cc | 34 ++++++++++----- absl/container/internal/hashtablez_sampler.h | 18 +++++--- absl/container/internal/hashtablez_sampler_test.cc | 51 ++++++++++++++-------- absl/profiling/internal/sample_recorder.h | 1 + absl/profiling/internal/sample_recorder_test.cc | 39 +++++++++++------ absl/status/internal/status_internal.h | 8 ++++ absl/status/statusor.h | 6 +++ absl/strings/internal/escaping.cc | 9 ++-- absl/strings/internal/ostringstream.cc | 2 +- absl/strings/internal/utf8.cc | 18 ++++---- 11 files changed, 139 insertions(+), 65 deletions(-) diff --git a/absl/base/casts.h b/absl/base/casts.h index b16af233..bf100efa 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -44,8 +44,12 @@ struct is_bitcastable bool, sizeof(Dest) == sizeof(Source) && type_traits_internal::is_trivially_copyable::value && - type_traits_internal::is_trivially_copyable::value && - std::is_default_constructible::value> {}; + type_traits_internal::is_trivially_copyable::value +#if !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + && std::is_default_constructible::value +#endif + > { +}; } // namespace internal_casts @@ -147,20 +151,26 @@ constexpr To implicit_cast(typename absl::internal::identity_t to) { // introducing this undefined behavior (since the original value is never // accessed in the wrong way). // -// NOTE: The requirements here are stricter than the bit_cast of standard -// proposal P0476 due to the need for workarounds and lack of intrinsics. +// NOTE: The requirements here are more strict than the bit_cast of standard +// proposal P0476 when __builtin_bit_cast is not available. // Specifically, this implementation also requires `Dest` to be // default-constructible. template < typename Dest, typename Source, typename std::enable_if::value, int>::type = 0> +#if ABSL_HAVE_BUILTIN(__builtin_bit_cast) +inline constexpr Dest bit_cast(const Source& source) { + return __builtin_bit_cast(Dest, source); +} +#else inline Dest bit_cast(const Source& source) { Dest dest; memcpy(static_cast(std::addressof(dest)), static_cast(std::addressof(source)), sizeof(dest)); return dest; } +#endif // NOTE: This overload is only picked if the requirements of bit_cast are // not met. It is therefore UB, but is provided temporarily as previous diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 1d24db5f..322e0547 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -27,6 +27,7 @@ #include "absl/profiling/internal/exponential_biased.h" #include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/mutex.h" +#include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -53,7 +54,7 @@ void TriggerHashtablezConfigListener() { } // namespace #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; +ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample = {0, 0}; #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) HashtablezSampler& GlobalHashtablezSampler() { @@ -64,7 +65,8 @@ HashtablezSampler& GlobalHashtablezSampler() { HashtablezInfo::HashtablezInfo() = default; HashtablezInfo::~HashtablezInfo() = default; -void HashtablezInfo::PrepareForSampling(size_t inline_element_size_value) { +void HashtablezInfo::PrepareForSampling(int64_t stride, + size_t inline_element_size_value) { capacity.store(0, std::memory_order_relaxed); size.store(0, std::memory_order_relaxed); num_erases.store(0, std::memory_order_relaxed); @@ -77,6 +79,7 @@ void HashtablezInfo::PrepareForSampling(size_t inline_element_size_value) { max_reserve.store(0, std::memory_order_relaxed); create_time = absl::Now(); + weight = stride; // The inliner makes hardcoded skip_count difficult (especially when combined // with LTO). We use the ability to exclude stacks by regex when encoding // instead. @@ -105,23 +108,32 @@ static bool ShouldForceSampling() { return state == kForce; } -HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { +HashtablezInfo* SampleSlow(SamplingState& next_sample, + size_t inline_element_size) { if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { - *next_sample = 1; + next_sample.next_sample = 1; + const int64_t old_stride = exchange(next_sample.sample_stride, 1); HashtablezInfo* result = - GlobalHashtablezSampler().Register(inline_element_size); + GlobalHashtablezSampler().Register(old_stride, inline_element_size); return result; } #if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) - *next_sample = std::numeric_limits::max(); + next_sample = { + std::numeric_limits::max(), + std::numeric_limits::max(), + }; return nullptr; #else - bool first = *next_sample < 0; - *next_sample = g_exponential_biased_generator.GetStride( + bool first = next_sample.next_sample < 0; + + const int64_t next_stride = g_exponential_biased_generator.GetStride( g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + + next_sample.next_sample = next_stride; + const int64_t old_stride = exchange(next_sample.sample_stride, next_stride); // Small values of interval are equivalent to just sampling next time. - ABSL_ASSERT(*next_sample >= 1); + ABSL_ASSERT(next_stride >= 1); // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold // low enough that we will start sampling in a reasonable time, so we just use @@ -131,11 +143,11 @@ HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { // We will only be negative on our first count, so we should just retry in // that case. if (first) { - if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr; + if (ABSL_PREDICT_TRUE(--next_sample.next_sample > 0)) return nullptr; return SampleSlow(next_sample, inline_element_size); } - return GlobalHashtablezSampler().Register(inline_element_size); + return GlobalHashtablezSampler().Register(old_stride, inline_element_size); #endif } diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 6738786c..e7c204ee 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -67,7 +67,7 @@ struct HashtablezInfo : public profiling_internal::Sample { // Puts the object into a clean state, fills in the logically `const` members, // blocking for any readers that are currently sampling the object. - void PrepareForSampling(size_t inline_element_size_value) + void PrepareForSampling(int64_t stride, size_t inline_element_size_value) ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); // These fields are mutated by the various Record* APIs and need to be @@ -145,7 +145,15 @@ inline void RecordEraseSlow(HashtablezInfo* info) { std::memory_order_relaxed); } -HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size); +struct SamplingState { + int64_t next_sample; + // When we make a sampling decision, we record that distance so we can weight + // each sample. + int64_t sample_stride; +}; + +HashtablezInfo* SampleSlow(SamplingState& next_sample, + size_t inline_element_size); void UnsampleSlow(HashtablezInfo* info); #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) @@ -235,7 +243,7 @@ class HashtablezInfoHandle { #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample; +extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample; #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) // Returns an RAII sampling handle that manages registration and unregistation @@ -243,11 +251,11 @@ extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample; inline HashtablezInfoHandle Sample( size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) { #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) - if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { + if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0)) { return HashtablezInfoHandle(nullptr); } return HashtablezInfoHandle( - SampleSlow(&global_next_sample, inline_element_size)); + SampleSlow(global_next_sample, inline_element_size)); #else return HashtablezInfoHandle(nullptr); #endif // !ABSL_PER_THREAD_TLS diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index ac6ed9b1..77cdf2fd 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -70,8 +70,9 @@ std::vector GetSizes(HashtablezSampler* s) { } HashtablezInfo* Register(HashtablezSampler* s, size_t size) { + const int64_t test_stride = 123; const size_t test_element_size = 17; - auto* info = s->Register(test_element_size); + auto* info = s->Register(test_stride, test_element_size); assert(info != nullptr); info->size.store(size); return info; @@ -79,10 +80,11 @@ HashtablezInfo* Register(HashtablezSampler* s, size_t size) { TEST(HashtablezInfoTest, PrepareForSampling) { absl::Time test_start = absl::Now(); + const int64_t test_stride = 123; const size_t test_element_size = 17; HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(test_element_size); + info.PrepareForSampling(test_stride, test_element_size); EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); @@ -95,6 +97,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); EXPECT_GE(info.create_time, test_start); + EXPECT_EQ(info.weight, test_stride); EXPECT_EQ(info.inline_element_size, test_element_size); info.capacity.store(1, std::memory_order_relaxed); @@ -108,7 +111,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { info.max_reserve.store(1, std::memory_order_relaxed); info.create_time = test_start - absl::Hours(20); - info.PrepareForSampling(test_element_size); + info.PrepareForSampling(test_stride * 2, test_element_size); EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 0); @@ -119,6 +122,7 @@ TEST(HashtablezInfoTest, PrepareForSampling) { EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); EXPECT_EQ(info.max_reserve.load(), 0); + EXPECT_EQ(info.weight, 2 * test_stride); EXPECT_EQ(info.inline_element_size, test_element_size); EXPECT_GE(info.create_time, test_start); } @@ -126,8 +130,9 @@ TEST(HashtablezInfoTest, PrepareForSampling) { TEST(HashtablezInfoTest, RecordStorageChanged) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); + const int64_t test_stride = 21; const size_t test_element_size = 19; - info.PrepareForSampling(test_element_size); + info.PrepareForSampling(test_stride, test_element_size); RecordStorageChangedSlow(&info, 17, 47); EXPECT_EQ(info.size.load(), 17); EXPECT_EQ(info.capacity.load(), 47); @@ -139,8 +144,9 @@ TEST(HashtablezInfoTest, RecordStorageChanged) { TEST(HashtablezInfoTest, RecordInsert) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); + const int64_t test_stride = 25; const size_t test_element_size = 23; - info.PrepareForSampling(test_element_size); + info.PrepareForSampling(test_stride, test_element_size); EXPECT_EQ(info.max_probe_length.load(), 0); RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); EXPECT_EQ(info.max_probe_length.load(), 6); @@ -160,10 +166,11 @@ TEST(HashtablezInfoTest, RecordInsert) { } TEST(HashtablezInfoTest, RecordErase) { + const int64_t test_stride = 31; const size_t test_element_size = 29; HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(test_element_size); + info.PrepareForSampling(test_stride, test_element_size); EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.size.load(), 0); RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); @@ -175,10 +182,11 @@ TEST(HashtablezInfoTest, RecordErase) { } TEST(HashtablezInfoTest, RecordRehash) { + const int64_t test_stride = 33; const size_t test_element_size = 31; HashtablezInfo info; absl::MutexLock l(&info.init_mu); - info.PrepareForSampling(test_element_size); + info.PrepareForSampling(test_stride, test_element_size); RecordInsertSlow(&info, 0x1, 0); RecordInsertSlow(&info, 0x2, kProbeLength); RecordInsertSlow(&info, 0x4, kProbeLength); @@ -203,8 +211,9 @@ TEST(HashtablezInfoTest, RecordRehash) { TEST(HashtablezInfoTest, RecordReservation) { HashtablezInfo info; absl::MutexLock l(&info.init_mu); + const int64_t test_stride = 35; const size_t test_element_size = 33; - info.PrepareForSampling(test_element_size); + info.PrepareForSampling(test_stride, test_element_size); RecordReservationSlow(&info, 3); EXPECT_EQ(info.max_reserve.load(), 3); @@ -224,9 +233,10 @@ TEST(HashtablezSamplerTest, SmallSampleParameter) { SetHashtablezSampleParameter(100); for (int i = 0; i < 1000; ++i) { - int64_t next_sample = 0; - HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size); - EXPECT_GT(next_sample, 0); + SamplingState next_sample = {0, 0}; + HashtablezInfo* sample = SampleSlow(next_sample, test_element_size); + EXPECT_GT(next_sample.next_sample, 0); + EXPECT_EQ(next_sample.next_sample, next_sample.sample_stride); EXPECT_NE(sample, nullptr); UnsampleSlow(sample); } @@ -238,9 +248,10 @@ TEST(HashtablezSamplerTest, LargeSampleParameter) { SetHashtablezSampleParameter(std::numeric_limits::max()); for (int i = 0; i < 1000; ++i) { - int64_t next_sample = 0; - HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size); - EXPECT_GT(next_sample, 0); + SamplingState next_sample = {0, 0}; + HashtablezInfo* sample = SampleSlow(next_sample, test_element_size); + EXPECT_GT(next_sample.next_sample, 0); + EXPECT_EQ(next_sample.next_sample, next_sample.sample_stride); EXPECT_NE(sample, nullptr); UnsampleSlow(sample); } @@ -267,14 +278,16 @@ TEST(HashtablezSamplerTest, Sample) { TEST(HashtablezSamplerTest, Handle) { auto& sampler = GlobalHashtablezSampler(); + const int64_t test_stride = 41; const size_t test_element_size = 39; - HashtablezInfoHandle h(sampler.Register(test_element_size)); + HashtablezInfoHandle h(sampler.Register(test_stride, test_element_size)); auto* info = HashtablezInfoHandlePeer::GetInfo(&h); info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed); bool found = false; sampler.Iterate([&](const HashtablezInfo& h) { if (&h == info) { + EXPECT_EQ(h.weight, test_stride); EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678); found = true; } @@ -340,19 +353,20 @@ TEST(HashtablezSamplerTest, MultiThreaded) { ThreadPool pool(10); for (int i = 0; i < 10; ++i) { + const int64_t sampling_stride = 11 + i % 3; const size_t elt_size = 10 + i % 2; - pool.Schedule([&sampler, &stop, elt_size]() { + pool.Schedule([&sampler, &stop, sampling_stride, elt_size]() { std::random_device rd; std::mt19937 gen(rd()); std::vector infoz; while (!stop.HasBeenNotified()) { if (infoz.empty()) { - infoz.push_back(sampler.Register(elt_size)); + infoz.push_back(sampler.Register(sampling_stride, elt_size)); } switch (std::uniform_int_distribution<>(0, 2)(gen)) { case 0: { - infoz.push_back(sampler.Register(elt_size)); + infoz.push_back(sampler.Register(sampling_stride, elt_size)); break; } case 1: { @@ -361,6 +375,7 @@ TEST(HashtablezSamplerTest, MultiThreaded) { HashtablezInfo* info = infoz[p]; infoz[p] = infoz.back(); infoz.pop_back(); + EXPECT_EQ(info->weight, sampling_stride); sampler.Unregister(info); break; } diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h index fc269bdd..5f65983b 100644 --- a/absl/profiling/internal/sample_recorder.h +++ b/absl/profiling/internal/sample_recorder.h @@ -46,6 +46,7 @@ struct Sample { absl::Mutex init_mu; T* next = nullptr; T* dead ABSL_GUARDED_BY(init_mu) = nullptr; + int64_t weight; // How many sampling events were required to sample this one. }; // Holds samples and their associated stack traces with a soft limit of diff --git a/absl/profiling/internal/sample_recorder_test.cc b/absl/profiling/internal/sample_recorder_test.cc index ec6e0fa2..3373329a 100644 --- a/absl/profiling/internal/sample_recorder_test.cc +++ b/absl/profiling/internal/sample_recorder_test.cc @@ -36,7 +36,7 @@ using ::testing::UnorderedElementsAre; struct Info : public Sample { public: - void PrepareForSampling() {} + void PrepareForSampling(int64_t w) { weight = w; } std::atomic size; absl::Time create_time; }; @@ -49,8 +49,14 @@ std::vector GetSizes(SampleRecorder* s) { return res; } -Info* Register(SampleRecorder* s, size_t size) { - auto* info = s->Register(); +std::vector GetWeights(SampleRecorder* s) { + std::vector res; + s->Iterate([&](const Info& info) { res.push_back(info.weight); }); + return res; +} + +Info* Register(SampleRecorder* s, int64_t weight, size_t size) { + auto* info = s->Register(weight); assert(info != nullptr); info->size.store(size); return info; @@ -58,13 +64,15 @@ Info* Register(SampleRecorder* s, size_t size) { TEST(SampleRecorderTest, Registration) { SampleRecorder sampler; - auto* info1 = Register(&sampler, 1); + auto* info1 = Register(&sampler, 31, 1); EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1)); + EXPECT_THAT(GetWeights(&sampler), UnorderedElementsAre(31)); - auto* info2 = Register(&sampler, 2); + auto* info2 = Register(&sampler, 32, 2); EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2)); info1->size.store(3); EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2)); + EXPECT_THAT(GetWeights(&sampler), UnorderedElementsAre(31, 32)); sampler.Unregister(info1); sampler.Unregister(info2); @@ -74,18 +82,22 @@ TEST(SampleRecorderTest, Unregistration) { SampleRecorder sampler; std::vector infos; for (size_t i = 0; i < 3; ++i) { - infos.push_back(Register(&sampler, i)); + infos.push_back(Register(&sampler, 33 + i, i)); } EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2)); + EXPECT_THAT(GetWeights(&sampler), UnorderedElementsAre(33, 34, 35)); sampler.Unregister(infos[1]); EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2)); + EXPECT_THAT(GetWeights(&sampler), UnorderedElementsAre(33, 35)); - infos.push_back(Register(&sampler, 3)); - infos.push_back(Register(&sampler, 4)); + infos.push_back(Register(&sampler, 36, 3)); + infos.push_back(Register(&sampler, 37, 4)); EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4)); + EXPECT_THAT(GetWeights(&sampler), UnorderedElementsAre(33, 35, 36, 37)); sampler.Unregister(infos[3]); EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4)); + EXPECT_THAT(GetWeights(&sampler), UnorderedElementsAre(33, 35, 37)); sampler.Unregister(infos[0]); sampler.Unregister(infos[2]); @@ -99,18 +111,18 @@ TEST(SampleRecorderTest, MultiThreaded) { ThreadPool pool(10); for (int i = 0; i < 10; ++i) { - pool.Schedule([&sampler, &stop]() { + pool.Schedule([&sampler, &stop, i]() { std::random_device rd; std::mt19937 gen(rd()); std::vector infoz; while (!stop.HasBeenNotified()) { if (infoz.empty()) { - infoz.push_back(sampler.Register()); + infoz.push_back(sampler.Register(i)); } switch (std::uniform_int_distribution<>(0, 2)(gen)) { case 0: { - infoz.push_back(sampler.Register()); + infoz.push_back(sampler.Register(i)); break; } case 1: { @@ -119,6 +131,7 @@ TEST(SampleRecorderTest, MultiThreaded) { Info* info = infoz[p]; infoz[p] = infoz.back(); infoz.pop_back(); + EXPECT_EQ(info->weight, i); sampler.Unregister(info); break; } @@ -143,8 +156,8 @@ TEST(SampleRecorderTest, MultiThreaded) { TEST(SampleRecorderTest, Callback) { SampleRecorder sampler; - auto* info1 = Register(&sampler, 1); - auto* info2 = Register(&sampler, 2); + auto* info1 = Register(&sampler, 39, 1); + auto* info2 = Register(&sampler, 40, 2); static const Info* expected; diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index ac12940a..34914d2e 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h @@ -16,6 +16,7 @@ #include +#include "absl/base/attributes.h" #include "absl/container/inlined_vector.h" #include "absl/strings/cord.h" @@ -25,7 +26,14 @@ namespace absl { ABSL_NAMESPACE_BEGIN // Returned Status objects may not be ignored. Codesearch doesn't handle ifdefs // as part of a class definitions (b/6995610), so we use a forward declaration. +// +// TODO(b/176172494): ABSL_MUST_USE_RESULT should expand to the more strict +// [[nodiscard]]. For now, just use [[nodiscard]] directly when it is available. +#if ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) +class [[nodiscard]] Status; +#else class ABSL_MUST_USE_RESULT Status; +#endif ABSL_NAMESPACE_END } // namespace absl #endif // !SWIG diff --git a/absl/status/statusor.h b/absl/status/statusor.h index c051fbb3..d6ebdc2b 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -106,7 +106,13 @@ class BadStatusOrAccess : public std::exception { // Returned StatusOr objects may not be ignored. template +#if ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) +// TODO(b/176172494): ABSL_MUST_USE_RESULT should expand to the more strict +// [[nodiscard]]. For now, just use [[nodiscard]] directly when it is available. +class [[nodiscard]] StatusOr; +#else class ABSL_MUST_USE_RESULT StatusOr; +#endif // ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) // absl::StatusOr // diff --git a/absl/strings/internal/escaping.cc b/absl/strings/internal/escaping.cc index c5271286..7f87e124 100644 --- a/absl/strings/internal/escaping.cc +++ b/absl/strings/internal/escaping.cc @@ -102,8 +102,8 @@ size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, } } // To save time, we didn't update szdest or szsrc in the loop. So do it now. - szdest = limit_dest - cur_dest; - szsrc = limit_src - cur_src; + szdest = static_cast(limit_dest - cur_dest); + szsrc = static_cast(limit_src - cur_src); /* now deal with the tail (<=3 bytes) */ switch (szsrc) { @@ -154,7 +154,8 @@ size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, // the loop because the loop above always reads 4 bytes, and the fourth // byte is past the end of the input. if (szdest < 4) return 0; - uint32_t in = (cur_src[0] << 16) + absl::big_endian::Load16(cur_src + 1); + uint32_t in = + (uint32_t{cur_src[0]} << 16) + absl::big_endian::Load16(cur_src + 1); cur_dest[0] = base64[in >> 18]; in &= 0x3FFFF; cur_dest[1] = base64[in >> 12]; @@ -172,7 +173,7 @@ size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, ABSL_RAW_LOG(FATAL, "Logic problem? szsrc = %zu", szsrc); break; } - return (cur_dest - dest); + return static_cast(cur_dest - dest); } } // namespace strings_internal diff --git a/absl/strings/internal/ostringstream.cc b/absl/strings/internal/ostringstream.cc index 05324c78..dc6cfe16 100644 --- a/absl/strings/internal/ostringstream.cc +++ b/absl/strings/internal/ostringstream.cc @@ -27,7 +27,7 @@ OStringStream::Buf::int_type OStringStream::overflow(int c) { std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) { assert(s_); - s_->append(s, n); + s_->append(s, static_cast(n)); return n; } diff --git a/absl/strings/internal/utf8.cc b/absl/strings/internal/utf8.cc index 8fd8edc1..7ecb93df 100644 --- a/absl/strings/internal/utf8.cc +++ b/absl/strings/internal/utf8.cc @@ -25,25 +25,25 @@ size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) { *buffer = static_cast(utf8_char); return 1; } else if (utf8_char <= 0x7FF) { - buffer[1] = 0x80 | (utf8_char & 0x3F); + buffer[1] = static_cast(0x80 | (utf8_char & 0x3F)); utf8_char >>= 6; - buffer[0] = 0xC0 | utf8_char; + buffer[0] = static_cast(0xC0 | utf8_char); return 2; } else if (utf8_char <= 0xFFFF) { - buffer[2] = 0x80 | (utf8_char & 0x3F); + buffer[2] = static_cast(0x80 | (utf8_char & 0x3F)); utf8_char >>= 6; - buffer[1] = 0x80 | (utf8_char & 0x3F); + buffer[1] = static_cast(0x80 | (utf8_char & 0x3F)); utf8_char >>= 6; - buffer[0] = 0xE0 | utf8_char; + buffer[0] = static_cast(0xE0 | utf8_char); return 3; } else { - buffer[3] = 0x80 | (utf8_char & 0x3F); + buffer[3] = static_cast(0x80 | (utf8_char & 0x3F)); utf8_char >>= 6; - buffer[2] = 0x80 | (utf8_char & 0x3F); + buffer[2] = static_cast(0x80 | (utf8_char & 0x3F)); utf8_char >>= 6; - buffer[1] = 0x80 | (utf8_char & 0x3F); + buffer[1] = static_cast(0x80 | (utf8_char & 0x3F)); utf8_char >>= 6; - buffer[0] = 0xF0 | utf8_char; + buffer[0] = static_cast(0xF0 | utf8_char); return 4; } } -- cgit v1.2.3 From fbbb5865a562c9a9167d71c1cf56b82025a8f065 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 19 Jan 2022 11:04:50 -0800 Subject: Export of internal Abseil changes -- 487c7a754a3b93bc0f9de14bdced48007a96ae55 by Greg Falcon : Add support for absl::Hash to hash unordered containers. These can now be hashed directly, as well as combined in AbslHashValue implementations. This also adds a new method, `H::combine_unordered()`, to the public AbslHashValue hash state API. This allows users to implement hash specializations for their own unordered collection types. A traits class, `H::is_hashable`, is also added to the hash state API. H::is_hashable::value reflects whether type T is considered hashable by the AbslHashValue framework. This allows users to properly SFINAE templated versions of AbslHashValue. (The AbslHashValue implementation added to raw_hash_set shows an example of its use.) PiperOrigin-RevId: 422856706 GitOrigin-RevId: 487c7a754a3b93bc0f9de14bdced48007a96ae55 Change-Id: Id31fd4ccba282f8c9ae6fcee6ae0ad0f7879f456 --- absl/container/internal/raw_hash_set.h | 8 ++ absl/hash/BUILD.bazel | 6 ++ absl/hash/CMakeLists.txt | 5 + absl/hash/hash.h | 85 ++++++++++++++-- absl/hash/hash_benchmark.cc | 77 +++++++++++++- absl/hash/hash_test.cc | 94 +++++++++++++++-- absl/hash/internal/hash.h | 180 ++++++++++++++++++++++++++++++--- absl/hash/internal/spy_hash_state.h | 35 +++++++ 8 files changed, 453 insertions(+), 37 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 1157d25a..93a3fa85 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -1539,6 +1539,14 @@ class raw_hash_set { return !(a == b); } + template + friend typename std::enable_if::value, + H>::type + AbslHashValue(H h, const raw_hash_set& s) { + return H::combine(H::combine_unordered(std::move(h), s.begin(), s.end()), + s.size()); + } + friend void swap(raw_hash_set& a, raw_hash_set& b) noexcept(noexcept(a.swap(b))) { a.swap(b); diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index f0640d34..bcc316f9 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -41,6 +41,7 @@ cc_library( "//absl/base:core_headers", "//absl/base:endian", "//absl/container:fixed_array", + "//absl/functional:function_ref", "//absl/meta:type_traits", "//absl/numeric:int128", "//absl/strings", @@ -74,7 +75,11 @@ cc_test( ":hash_testing", ":spy_hash_state", "//absl/base:core_headers", + "//absl/container:btree", + "//absl/container:flat_hash_map", "//absl/container:flat_hash_set", + "//absl/container:node_hash_map", + "//absl/container:node_hash_set", "//absl/meta:type_traits", "//absl/numeric:int128", "//absl/strings:cord_test_helpers", @@ -93,6 +98,7 @@ cc_binary( deps = [ ":hash", "//absl/base:core_headers", + "//absl/container:flat_hash_set", "//absl/random", "//absl/strings", "//absl/strings:cord", diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index 5916ae3c..34434fa0 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -30,6 +30,7 @@ absl_cc_library( absl::core_headers absl::endian absl::fixed_array + absl::function_ref absl::meta absl::int128 absl::strings @@ -68,7 +69,11 @@ absl_cc_test( absl::hash absl::hash_testing absl::core_headers + absl::btree + absl::flat_hash_map absl::flat_hash_set + absl::node_hash_map + absl::node_hash_set absl::spy_hash_state absl::meta absl::int128 diff --git a/absl/hash/hash.h b/absl/hash/hash.h index 8282ea53..f31fde40 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h @@ -26,9 +26,9 @@ // support Abseil hashing without requiring you to define a hashing // algorithm. // * `HashState`, a type-erased class which implements the manipulation of the -// hash state (H) itself, contains member functions `combine()` and -// `combine_contiguous()`, which you can use to contribute to an existing -// hash state when hashing your types. +// hash state (H) itself; contains member functions `combine()`, +// `combine_contiguous()`, and `combine_unordered()`; and which you can use +// to contribute to an existing hash state when hashing your types. // // Unlike `std::hash` or other hashing frameworks, the Abseil hashing framework // provides most of its utility by abstracting away the hash algorithm (and its @@ -74,7 +74,9 @@ #define ABSL_HASH_HASH_H_ #include +#include +#include "absl/functional/function_ref.h" #include "absl/hash/internal/hash.h" namespace absl { @@ -107,14 +109,27 @@ ABSL_NAMESPACE_BEGIN // * std::string_view (as well as any instance of std::basic_string that // uses char and std::char_traits) // * All the standard sequence containers (provided the elements are hashable) -// * All the standard ordered associative containers (provided the elements are +// * All the standard associative containers (provided the elements are // hashable) // * absl types such as the following: // * absl::string_view -// * absl::InlinedVector -// * absl::FixedArray // * absl::uint128 // * absl::Time, absl::Duration, and absl::TimeZone +// * absl containers (provided the elements are hashable) such as the +// following: +// * absl::flat_hash_set, absl::node_hash_set, absl::btree_set +// * absl::flat_hash_map, absl::node_hash_map, absl::btree_map +// * absl::btree_multiset, absl::btree_multimap +// * absl::InlinedVector +// * absl::FixedArray +// +// When absl::Hash is used to hash an unordered container with a custom hash +// functor, the elements are hashed using default absl::Hash semantics, not +// the custom hash functor. This is consistent with the behavior of +// operator==() on unordered containers, which compares elements pairwise with +// operator==() rather than the custom equality functor. It is usually a +// mistake to use either operator==() or absl::Hash on unordered collections +// that use functors incompatible with operator==() equality. // // Note: the list above is not meant to be exhaustive. Additional type support // may be added, in which case the above list will be updated. @@ -153,7 +168,8 @@ ABSL_NAMESPACE_BEGIN // that are otherwise difficult to extend using `AbslHashValue()`. (See the // `HashState` class below.) // -// The "hash state" concept contains two member functions for mixing hash state: +// The "hash state" concept contains three member functions for mixing hash +// state: // // * `H::combine(state, values...)` // @@ -187,6 +203,15 @@ ABSL_NAMESPACE_BEGIN // (it may perform internal optimizations). If you need this guarantee, use a // loop instead. // +// * `H::combine_unordered(state, begin, end)` +// +// Combines a set of elements denoted by an iterator pair into a hash +// state, returning the updated state. Note that the existing hash +// state is move-only and must be passed by value. +// +// Unlike the other two methods, the hashing is order-independent. +// This can be used to hash unordered collections. +// // ----------------------------------------------------------------------------- // Adding Type Support to `absl::Hash` // ----------------------------------------------------------------------------- @@ -243,8 +268,9 @@ size_t HashOf(const Types&... values) { // classes, virtual functions, etc.). The type erasure adds overhead so it // should be avoided unless necessary. // -// Note: This wrapper will only erase calls to: +// Note: This wrapper will only erase calls to // combine_contiguous(H, const unsigned char*, size_t) +// RunCombineUnordered(H, CombinerF) // // All other calls will be handled internally and will not invoke overloads // provided by the wrapped class. @@ -318,6 +344,8 @@ class HashState : public hash_internal::HashStateBase { private: HashState() = default; + friend class HashState::HashStateBase; + template static void CombineContiguousImpl(void* p, const unsigned char* first, size_t size) { @@ -329,16 +357,57 @@ class HashState : public hash_internal::HashStateBase { void Init(T* state) { state_ = state; combine_contiguous_ = &CombineContiguousImpl; + run_combine_unordered_ = &RunCombineUnorderedImpl; + } + + template + struct CombineUnorderedInvoker { + template + void operator()(T inner_state, ConsumerT inner_cb) { + f(HashState::Create(&inner_state), + [&](HashState& inner_erased) { inner_cb(inner_erased.Real()); }); + } + + absl::FunctionRef)> f; + }; + + template + static HashState RunCombineUnorderedImpl( + HashState state, + absl::FunctionRef)> + f) { + // Note that this implementation assumes that inner_state and outer_state + // are the same type. This isn't true in the SpyHash case, but SpyHash + // types are move-convertible to each other, so this still works. + T& real_state = state.Real(); + real_state = T::RunCombineUnordered( + std::move(real_state), CombineUnorderedInvoker{f}); + return state; + } + + template + static HashState RunCombineUnordered(HashState state, CombinerT combiner) { + auto* run = state.run_combine_unordered_; + return run(std::move(state), std::ref(combiner)); } // Do not erase an already erased state. void Init(HashState* state) { state_ = state->state_; combine_contiguous_ = state->combine_contiguous_; + run_combine_unordered_ = state->run_combine_unordered_; + } + + template + T& Real() { + return *static_cast(state_); } void* state_; void (*combine_contiguous_)(void*, const unsigned char*, size_t); + HashState (*run_combine_unordered_)( + HashState state, + absl::FunctionRef)>); }; ABSL_NAMESPACE_END diff --git a/absl/hash/hash_benchmark.cc b/absl/hash/hash_benchmark.cc index d498ac29..8712a01c 100644 --- a/absl/hash/hash_benchmark.cc +++ b/absl/hash/hash_benchmark.cc @@ -19,6 +19,7 @@ #include #include "absl/base/attributes.h" +#include "absl/container/flat_hash_set.h" #include "absl/hash/hash.h" #include "absl/random/random.h" #include "absl/strings/cord.h" @@ -107,6 +108,44 @@ absl::Cord FragmentedCord(size_t size) { return result; } +template +std::vector Vector(size_t count) { + std::vector result; + for (size_t v = 0; v < count; ++v) { + result.push_back(v); + } + return result; +} + +// Bogus type that replicates an unorderd_set's bit mixing, but with +// vector-speed iteration. This is intended to measure the overhead of unordered +// hashing without counting the speed of unordered_set iteration. +template +struct FastUnorderedSet { + explicit FastUnorderedSet(size_t count) { + for (size_t v = 0; v < count; ++v) { + values.push_back(v); + } + } + std::vector values; + + template + friend H AbslHashValue(H h, const FastUnorderedSet& fus) { + return H::combine(H::combine_unordered(std::move(h), fus.values.begin(), + fus.values.end()), + fus.values.size()); + } +}; + +template +absl::flat_hash_set FlatHashSet(size_t count) { + absl::flat_hash_set result; + for (size_t v = 0; v < count; ++v) { + result.insert(v); + } + return result; +} + // Generates a benchmark and a codegen method for the provided types. The // codegen method provides a well known entrypoint for dumping assembly. #define MAKE_BENCHMARK(hash, name, ...) \ @@ -145,10 +184,22 @@ MAKE_BENCHMARK(AbslHash, Cord_Flat_200, FlatCord(200)); MAKE_BENCHMARK(AbslHash, Cord_Flat_5000, FlatCord(5000)); MAKE_BENCHMARK(AbslHash, Cord_Fragmented_200, FragmentedCord(200)); MAKE_BENCHMARK(AbslHash, Cord_Fragmented_5000, FragmentedCord(5000)); -MAKE_BENCHMARK(AbslHash, VectorInt64_10, std::vector(10)); -MAKE_BENCHMARK(AbslHash, VectorInt64_100, std::vector(100)); -MAKE_BENCHMARK(AbslHash, VectorDouble_10, std::vector(10, 1.1)); -MAKE_BENCHMARK(AbslHash, VectorDouble_100, std::vector(100, 1.1)); +MAKE_BENCHMARK(AbslHash, VectorInt64_10, Vector(10)); +MAKE_BENCHMARK(AbslHash, VectorInt64_100, Vector(100)); +MAKE_BENCHMARK(AbslHash, VectorInt64_1000, Vector(1000)); +MAKE_BENCHMARK(AbslHash, VectorDouble_10, Vector(10)); +MAKE_BENCHMARK(AbslHash, VectorDouble_100, Vector(100)); +MAKE_BENCHMARK(AbslHash, VectorDouble_1000, Vector(1000)); +MAKE_BENCHMARK(AbslHash, FlatHashSetInt64_10, FlatHashSet(10)); +MAKE_BENCHMARK(AbslHash, FlatHashSetInt64_100, FlatHashSet(100)); +MAKE_BENCHMARK(AbslHash, FlatHashSetInt64_1000, FlatHashSet(1000)); +MAKE_BENCHMARK(AbslHash, FlatHashSetDouble_10, FlatHashSet(10)); +MAKE_BENCHMARK(AbslHash, FlatHashSetDouble_100, FlatHashSet(100)); +MAKE_BENCHMARK(AbslHash, FlatHashSetDouble_1000, FlatHashSet(1000)); +MAKE_BENCHMARK(AbslHash, FastUnorderedSetInt64_1000, + FastUnorderedSet(1000)); +MAKE_BENCHMARK(AbslHash, FastUnorderedSetDouble_1000, + FastUnorderedSet(1000)); MAKE_BENCHMARK(AbslHash, PairStringString_0, std::make_pair(std::string(), std::string())); MAKE_BENCHMARK(AbslHash, PairStringString_10, @@ -180,6 +231,24 @@ MAKE_BENCHMARK(TypeErasedAbslHash, VectorDouble_10, std::vector(10, 1.1)); MAKE_BENCHMARK(TypeErasedAbslHash, VectorDouble_100, std::vector(100, 1.1)); +MAKE_BENCHMARK(TypeErasedAbslHash, VectorDouble_1000, + std::vector(1000, 1.1)); +MAKE_BENCHMARK(TypeErasedAbslHash, FlatHashSetInt64_10, + FlatHashSet(10)); +MAKE_BENCHMARK(TypeErasedAbslHash, FlatHashSetInt64_100, + FlatHashSet(100)); +MAKE_BENCHMARK(TypeErasedAbslHash, FlatHashSetInt64_1000, + FlatHashSet(1000)); +MAKE_BENCHMARK(TypeErasedAbslHash, FlatHashSetDouble_10, + FlatHashSet(10)); +MAKE_BENCHMARK(TypeErasedAbslHash, FlatHashSetDouble_100, + FlatHashSet(100)); +MAKE_BENCHMARK(TypeErasedAbslHash, FlatHashSetDouble_1000, + FlatHashSet(1000)); +MAKE_BENCHMARK(TypeErasedAbslHash, FastUnorderedSetInt64_1000, + FastUnorderedSet(1000)); +MAKE_BENCHMARK(TypeErasedAbslHash, FastUnorderedSetDouble_1000, + FastUnorderedSet(1000)); // The latency benchmark attempts to model the speed of the hash function in // production. When a hash function is used for hashtable lookups it is rarely diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index dbfacb2d..cea54dc0 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -34,12 +34,18 @@ #include #include #include +#include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/container/node_hash_map.h" +#include "absl/container/node_hash_set.h" #include "absl/hash/hash_testing.h" #include "absl/hash/internal/spy_hash_state.h" #include "absl/meta/type_traits.h" @@ -433,6 +439,52 @@ TEST(HashValueTest, StdBitset) { std::bitset(bit_strings[5].c_str())})); } // namespace +// Dummy type with unordered equality and hashing semantics. This preserves +// input order internally, and is used below to ensure we get test coverage +// for equal sequences with different iteraton orders. +template +class UnorderedSequence { + public: + UnorderedSequence() = default; + template + UnorderedSequence(std::initializer_list l) + : values_(l.begin(), l.end()) {} + template ::value, + bool>::type = true> + UnorderedSequence(ForwardIterator begin, ForwardIterator end) + : values_(begin, end) {} + // one-argument constructor of value type T, to appease older toolchains that + // get confused by one-element initializer lists in some contexts + explicit UnorderedSequence(const T& v) : values_(&v, &v + 1) {} + + using value_type = T; + + size_t size() const { return values_.size(); } + typename std::vector::const_iterator begin() const { + return values_.begin(); + } + typename std::vector::const_iterator end() const { return values_.end(); } + + friend bool operator==(const UnorderedSequence& lhs, + const UnorderedSequence& rhs) { + return lhs.size() == rhs.size() && + std::is_permutation(lhs.begin(), lhs.end(), rhs.begin()); + } + friend bool operator!=(const UnorderedSequence& lhs, + const UnorderedSequence& rhs) { + return !(lhs == rhs); + } + template + friend H AbslHashValue(H h, const UnorderedSequence& u) { + return H::combine(H::combine_unordered(std::move(h), u.begin(), u.end()), + u.size()); + } + + private: + std::vector values_; +}; + template class HashValueSequenceTest : public testing::Test { }; @@ -456,10 +508,13 @@ TYPED_TEST_P(HashValueSequenceTest, BasicUsage) { } REGISTER_TYPED_TEST_CASE_P(HashValueSequenceTest, BasicUsage); -using IntSequenceTypes = - testing::Types, std::forward_list, std::list, - std::vector, std::vector, TypeErasedVector, - TypeErasedVector, std::set, std::multiset>; +using IntSequenceTypes = testing::Types< + std::deque, std::forward_list, std::list, std::vector, + std::vector, TypeErasedContainer>, std::set, + std::multiset, UnorderedSequence, + TypeErasedContainer>, std::unordered_set, + std::unordered_multiset, absl::flat_hash_set, + absl::node_hash_set, absl::btree_set>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueSequenceTest, IntSequenceTypes); template @@ -487,11 +542,15 @@ TYPED_TEST_P(HashValueNestedSequenceTest, BasicUsage) { } REGISTER_TYPED_TEST_CASE_P(HashValueNestedSequenceTest, BasicUsage); -using NestedIntSequenceTypes = - testing::Types>, - std::vector>, - TypeErasedVector>, - TypeErasedVector>>; +template +using TypeErasedSet = TypeErasedContainer>; + +using NestedIntSequenceTypes = testing::Types< + std::vector>, std::vector>, + std::vector>, UnorderedSequence>, + UnorderedSequence>, + UnorderedSequence>, TypeErasedSet>, + TypeErasedSet>, TypeErasedSet>>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueNestedSequenceTest, NestedIntSequenceTypes); @@ -674,7 +733,11 @@ TYPED_TEST_P(HashValueAssociativeMapTest, BasicUsage) { } REGISTER_TYPED_TEST_CASE_P(HashValueAssociativeMapTest, BasicUsage); -using AssociativeMapTypes = testing::Types>; +using AssociativeMapTypes = testing::Types< + std::map, std::unordered_map, + absl::flat_hash_map, + absl::node_hash_map, absl::btree_map, + UnorderedSequence>>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueAssociativeMapTest, AssociativeMapTypes); @@ -702,7 +765,8 @@ TYPED_TEST_P(HashValueAssociativeMultimapTest, BasicUsage) { REGISTER_TYPED_TEST_CASE_P(HashValueAssociativeMultimapTest, BasicUsage); using AssociativeMultimapTypes = - testing::Types>; + testing::Types, + std::unordered_multimap>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueAssociativeMultimapTest, AssociativeMultimapTypes); @@ -1062,6 +1126,14 @@ TEST(HashTest, TypeErased) { EXPECT_EQ(SpyHash(std::make_pair(TypeErasedValue(7), 17)), SpyHash(std::make_pair(size_t{7}, 17))); + + absl::flat_hash_set> ss = {{1, 2}, {3, 4}}; + TypeErasedContainer>> es = { + absl::flat_hash_set{1, 2}, {3, 4}}; + absl::flat_hash_set>> se = { + {1, 2}, {3, 4}}; + EXPECT_EQ(SpyHash(ss), SpyHash(es)); + EXPECT_EQ(SpyHash(ss), SpyHash(se)); } struct ValueWithBoolConversion { diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index 97b68bad..a424e014 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,8 @@ #include #include #include +#include +#include #include #include @@ -54,6 +57,9 @@ namespace absl { ABSL_NAMESPACE_BEGIN + +class HashState; + namespace hash_internal { // Internal detail: Large buffers are hashed in smaller chunks. This function @@ -115,24 +121,66 @@ class PiecewiseCombiner { size_t position_; }; +// is_hashable() +// +// Trait class which returns true if T is hashable by the absl::Hash framework. +// Used for the AbslHashValue implementations for composite types below. +template +struct is_hashable; + // HashStateBase // -// A hash state object represents an intermediate state in the computation -// of an unspecified hash algorithm. `HashStateBase` provides a CRTP style -// base class for hash state implementations. Developers adding type support -// for `absl::Hash` should not rely on any parts of the state object other than -// the following member functions: +// An internal implementation detail that contains common implementation details +// for all of the "hash state objects" objects generated by Abseil. This is not +// a public API; users should not create classes that inherit from this. +// +// A hash state object is the template argument `H` passed to `AbslHashValue`. +// It represents an intermediate state in the computation of an unspecified hash +// algorithm. `HashStateBase` provides a CRTP style base class for hash state +// implementations. Developers adding type support for `absl::Hash` should not +// rely on any parts of the state object other than the following member +// functions: // // * HashStateBase::combine() // * HashStateBase::combine_contiguous() +// * HashStateBase::combine_unordered() // -// A derived hash state class of type `H` must provide a static member function +// A derived hash state class of type `H` must provide a public member function // with a signature similar to the following: // // `static H combine_contiguous(H state, const unsigned char*, size_t)`. // +// It must also provide a private template method named RunCombineUnordered. +// +// A "consumer" is a 1-arg functor returning void. Its argument is a reference +// to an inner hash state object, and it may be called multiple times. When +// called, the functor consumes the entropy from the provided state object, +// and resets that object to its empty state. +// +// A "combiner" is a stateless 2-arg functor returning void. Its arguments are +// an inner hash state object and an ElementStateConsumer functor. A combiner +// uses the provided inner hash state object to hash each element of the +// container, passing the inner hash state object to the consumer after hashing +// each element. +// +// Given these definitions, a derived hash state class of type H +// must provide a private template method with a signature similar to the +// following: +// +// `template ` +// `static H RunCombineUnordered(H outer_state, CombinerT combiner)` +// +// This function is responsible for constructing the inner state object and +// providing a consumer to the combiner. It uses side effects of the consumer +// and combiner to mix the state of each element in an order-independent manner, +// and uses this to return an updated value of `outer_state`. +// +// This inside-out approach generates efficient object code in the normal case, +// but allows us to use stack storage to implement the absl::HashState type +// erasure mechanism (avoiding heap allocations while hashing). +// // `HashStateBase` will provide a complete implementation for a hash state -// object in terms of this method. +// object in terms of these two methods. // // Example: // @@ -141,6 +189,10 @@ class PiecewiseCombiner { // static H combine_contiguous(H state, const unsigned char*, size_t); // using MyHashState::HashStateBase::combine; // using MyHashState::HashStateBase::combine_contiguous; +// using MyHashState::HashStateBase::combine_unordered; +// private: +// template +// static H RunCombineUnordered(H state, CombinerT combiner); // }; template class HashStateBase { @@ -181,7 +233,30 @@ class HashStateBase { template static H combine_contiguous(H state, const T* data, size_t size); + template + static H combine_unordered(H state, I begin, I end); + using AbslInternalPiecewiseCombiner = PiecewiseCombiner; + + template + using is_hashable = absl::hash_internal::is_hashable; + + private: + // Common implementation of the iteration step of a "combiner", as described + // above. + template + struct CombineUnorderedCallback { + I begin; + I end; + + template + void operator()(InnerH inner_state, ElementStateConsumer cb) { + for (; begin != end; ++begin) { + inner_state = H::combine(std::move(inner_state), *begin); + cb(inner_state); + } + } + }; }; // is_uniquely_represented @@ -350,13 +425,6 @@ H AbslHashValue(H hash_state, std::nullptr_t) { // AbslHashValue for Composite Types // ----------------------------------------------------------------------------- -// is_hashable() -// -// Trait class which returns true if T is hashable by the absl::Hash framework. -// Used for the AbslHashValue implementations for composite types below. -template -struct is_hashable; - // AbslHashValue() for hashing pairs template typename std::enable_if::value && is_hashable::value, @@ -503,8 +571,10 @@ AbslHashValue(H hash_state, const std::vector& vector) { } // AbslHashValue special cases for hashing std::vector + #if defined(ABSL_IS_BIG_ENDIAN) && \ (defined(__GLIBCXX__) || defined(__GLIBCPP__)) + // std::hash in libstdc++ does not work correctly with vector on Big // Endian platforms therefore we need to implement a custom AbslHashValue for // it. More details on the bug: @@ -587,6 +657,55 @@ typename std::enable_if::value, H>::type AbslHashValue( return H::combine(std::move(hash_state), set.size()); } +// ----------------------------------------------------------------------------- +// AbslHashValue for Unordered Associative Containers +// ----------------------------------------------------------------------------- + +// AbslHashValue for hashing std::unordered_set +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, const std::unordered_set& s) { + return H::combine( + H::combine_unordered(std::move(hash_state), s.begin(), s.end()), + s.size()); +} + +// AbslHashValue for hashing std::unordered_multiset +template +typename std::enable_if::value, H>::type AbslHashValue( + H hash_state, + const std::unordered_multiset& s) { + return H::combine( + H::combine_unordered(std::move(hash_state), s.begin(), s.end()), + s.size()); +} + +// AbslHashValue for hashing std::unordered_set +template +typename std::enable_if::value && is_hashable::value, + H>::type +AbslHashValue(H hash_state, + const std::unordered_map& s) { + return H::combine( + H::combine_unordered(std::move(hash_state), s.begin(), s.end()), + s.size()); +} + +// AbslHashValue for hashing std::unordered_multiset +template +typename std::enable_if::value && is_hashable::value, + H>::type +AbslHashValue(H hash_state, + const std::unordered_multimap& s) { + return H::combine( + H::combine_unordered(std::move(hash_state), s.begin(), s.end()), + s.size()); +} + // ----------------------------------------------------------------------------- // AbslHashValue for Wrapper Types // ----------------------------------------------------------------------------- @@ -830,6 +949,31 @@ class ABSL_DLL MixingHashState : public HashStateBase { // move-only ensures that there is only one non-moved-from object. MixingHashState() : state_(Seed()) {} + friend class MixingHashState::HashStateBase; + + template + static MixingHashState RunCombineUnordered(MixingHashState state, + CombinerT combiner) { + uint64_t unordered_state = 0; + combiner(MixingHashState{}, [&](MixingHashState& inner_state) { + // Add the hash state of the element to the running total, but mix the + // carry bit back into the low bit. This in intended to avoid losing + // entropy to overflow, especially when unordered_multisets contain + // multiple copies of the same value. + auto element_state = inner_state.state_; + unordered_state += element_state; + if (unordered_state < element_state) { + ++unordered_state; + } + inner_state = MixingHashState{}; + }); + return MixingHashState::combine(std::move(state), unordered_state); + } + + // Allow the HashState type-erasure implementation to invoke + // RunCombinedUnordered() directly. + friend class absl::HashState; + // Workaround for MSVC bug. // We make the type copyable to fix the calling convention, even though we // never actually copy it. Keep it private to not affect the public API of the @@ -1064,6 +1208,14 @@ H HashStateBase::combine_contiguous(H state, const T* data, size_t size) { return hash_internal::hash_range_or_bytes(std::move(state), data, size); } +// HashStateBase::combine_unordered() +template +template +H HashStateBase::combine_unordered(H state, I begin, I end) { + return H::RunCombineUnordered(std::move(state), + CombineUnorderedCallback{begin, end}); +} + // HashStateBase::PiecewiseCombiner::add_buffer() template H PiecewiseCombiner::add_buffer(H state, const unsigned char* data, diff --git a/absl/hash/internal/spy_hash_state.h b/absl/hash/internal/spy_hash_state.h index c0831208..09728266 100644 --- a/absl/hash/internal/spy_hash_state.h +++ b/absl/hash/internal/spy_hash_state.h @@ -15,6 +15,7 @@ #ifndef ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ #define ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ +#include #include #include #include @@ -167,6 +168,24 @@ class SpyHashStateImpl : public HashStateBase> { using SpyHashStateImpl::HashStateBase::combine_contiguous; + template + static SpyHashStateImpl RunCombineUnordered(SpyHashStateImpl state, + CombinerT combiner) { + UnorderedCombinerCallback cb; + + combiner(SpyHashStateImpl{}, std::ref(cb)); + + std::sort(cb.element_hash_representations.begin(), + cb.element_hash_representations.end()); + state.hash_representation_.insert(state.hash_representation_.end(), + cb.element_hash_representations.begin(), + cb.element_hash_representations.end()); + if (cb.error && cb.error->has_value()) { + state.error_ = std::move(cb.error); + } + return state; + } + absl::optional error() const { if (moved_from_) { return "Returned a moved-from instance of the hash state object."; @@ -178,6 +197,22 @@ class SpyHashStateImpl : public HashStateBase> { template friend class SpyHashStateImpl; + struct UnorderedCombinerCallback { + std::vector element_hash_representations; + std::shared_ptr> error; + + // The inner spy can have a different type. + template + void operator()(SpyHashStateImpl& inner) { + element_hash_representations.push_back( + absl::StrJoin(inner.hash_representation_, "")); + if (inner.error_->has_value()) { + error = std::move(inner.error_); + } + inner = SpyHashStateImpl{}; + } + }; + // This is true if SpyHashStateImpl has been passed to a call of // AbslHashValue with the wrong type. This detects that the user called // AbslHashValue directly (because the hash state type does not match). -- cgit v1.2.3 From b2c96417bd5c2b0a550611e503002a4594a932b2 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 21 Jan 2022 12:05:00 -0800 Subject: Export of internal Abseil changes -- 75504b9d2eb7560359b98b69136d071f980e04f2 by Abseil Team : Fix typos in documentation. PiperOrigin-RevId: 423376798 -- bf87e5de48a868f49a57d516be027e6e3a3cc3bd by Gennadiy Rozental : Correct WEAK attribute enabling condition. ABSL_ATTRIBUTE_WEAK is present if: compiler has built-in attribute weak OR we are using gcc (and not clang) AND we are not on windows OR we use windows clang version >= 9.0.0 AND we are not on MinGW PiperOrigin-RevId: 423357629 -- a01a8f1b7ea3da4ec349db452162a3333953dd9d by Abseil Team : There are magic numbers in the expected load factors and probe lengths, and they seem to be wrong especially under msvc. Even under the linux tool chain, these tests fail occasionally. Fix the magic numbers to make the tests succeed. PiperOrigin-RevId: 423320829 -- fd109295a1425ca1cb2b69fe34a294b6189542c0 by Laramie Leavitt : Manually align buffers in randen_engine. In C++ it's implementation defined whether types with extended alignment are supported. randen_engine uses vector intrinsics with 16-byte alignment requirements in some instances, so internally we allocate an extra 8 bytes to manually align to 16. No detectable performance impact. PiperOrigin-RevId: 423109265 GitOrigin-RevId: 75504b9d2eb7560359b98b69136d071f980e04f2 Change-Id: I8c5ab2269ff6d9e89d3b4d0e95d36ddb6ce8096e --- absl/base/attributes.h | 13 +++-- absl/container/flat_hash_set.h | 14 +++--- absl/container/internal/raw_hash_set_test.cc | 16 +++---- absl/random/internal/randen.h | 16 ++----- absl/random/internal/randen_engine.h | 71 +++++++++++++++++++--------- 5 files changed, 74 insertions(+), 56 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 00aad489..4ab6fa27 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -136,9 +136,9 @@ // 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(_WIN32) || (defined(__clang__) && __clang_major__ < 9)) && \ +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + (!defined(_WIN32) || (defined(__clang__) && __clang_major__ >= 9)) && \ !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) @@ -312,7 +312,6 @@ __attribute__((section(#name))) __attribute__((noinline)) #endif - // ABSL_ATTRIBUTE_SECTION_VARIABLE // // Tells the compiler/linker to put a given variable into a section and define @@ -339,8 +338,8 @@ // a no-op on ELF but not on Mach-O. // #ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS -#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ - extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ +#define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ + extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK #endif #ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS @@ -503,7 +502,7 @@ #define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) #define ABSL_XRAY_LOG_ARGS(N) \ - [[clang::xray_always_instrument, clang::xray_log_args(N)]] + [[clang::xray_always_instrument, clang::xray_log_args(N)]] #else #define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] #endif diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index f1c650cc..0fb2ae6f 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -67,7 +67,7 @@ struct FlatHashSetPolicy; // // By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All // fundamental and Abseil types that support the `absl::Hash` framework have a -// compatible equality operator for comparing insertions into `flat_hash_map`. +// compatible equality operator for comparing insertions into `flat_hash_set`. // If your type is not yet supported by the `absl::Hash` framework, see // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. @@ -106,7 +106,7 @@ class flat_hash_set public: // Constructors and Assignment Operators // - // A flat_hash_set supports the same overload set as `std::unordered_map` + // A flat_hash_set supports the same overload set as `std::unordered_set` // for construction and assignment: // // * Default constructor @@ -173,7 +173,7 @@ class flat_hash_set // available within the `flat_hash_set`. // // NOTE: this member function is particular to `absl::flat_hash_set` and is - // not provided in the `std::unordered_map` API. + // not provided in the `std::unordered_set` API. using Base::capacity; // flat_hash_set::empty() @@ -332,7 +332,7 @@ class flat_hash_set // flat_hash_set::swap(flat_hash_set& other) // // Exchanges the contents of this `flat_hash_set` with those of the `other` - // flat hash map, avoiding invocation of any move, copy, or swap operations on + // flat hash set, avoiding invocation of any move, copy, or swap operations on // individual elements. // // All iterators and references on the `flat_hash_set` remain valid, excepting @@ -340,7 +340,7 @@ class flat_hash_set // // `swap()` requires that the flat hash set's hashing and key equivalence // functions be Swappable, and are exchaged using unqualified calls to - // non-member `swap()`. If the map's allocator has + // non-member `swap()`. If the set's allocator has // `std::allocator_traits::propagate_on_container_swap::value` // set to `true`, the allocators are also exchanged using an unqualified call // to non-member `swap()`; otherwise, the allocators are not swapped. @@ -395,14 +395,14 @@ class flat_hash_set // flat_hash_set::bucket_count() // // Returns the number of "buckets" within the `flat_hash_set`. Note that - // because a flat hash map contains all elements within its internal storage, + // because a flat hash set contains all elements within its internal storage, // this value simply equals the current capacity of the `flat_hash_set`. using Base::bucket_count; // flat_hash_set::load_factor() // // Returns the current load factor of the `flat_hash_set` (the average number - // of slots occupied with a value within the hash map). + // of slots occupied with a value within the hash set). using Base::load_factor; // flat_hash_set::max_load_factor() diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 362b3cae..47015bcf 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -1244,7 +1244,7 @@ ExpectedStats XorSeedExpectedStats() { case 16: if (kRandomizesInserts) { return {0.1, - 1.0, + 2.0, {{0.95, 0.1}}, {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}}; } else { @@ -1258,7 +1258,7 @@ ExpectedStats XorSeedExpectedStats() { return {}; } -TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) { +TEST(Table, EnsureNonQuadraticTopNXorSeedByProbeSeqLength) { ProbeStatsPerSize stats; std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; for (size_t size : sizes) { @@ -1330,17 +1330,17 @@ ExpectedStats LinearTransformExpectedStats() { {{0.95, 0.3}}, {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}}; } else { - return {0.15, - 0.5, - {{0.95, 0.3}}, - {{0.95, 0}, {0.99, 3}, {0.999, 15}, {0.9999, 25}}}; + return {0.4, + 0.6, + {{0.95, 0.5}}, + {{0.95, 1}, {0.99, 14}, {0.999, 23}, {0.9999, 26}}}; } case 16: if (kRandomizesInserts) { return {0.1, 0.4, {{0.95, 0.3}}, - {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}}; + {{0.95, 1}, {0.99, 2}, {0.999, 9}, {0.9999, 15}}}; } else { return {0.05, 0.2, @@ -1352,7 +1352,7 @@ ExpectedStats LinearTransformExpectedStats() { return {}; } -TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) { +TEST(Table, EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) { ProbeStatsPerSize stats; std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; for (size_t size : sizes) { diff --git a/absl/random/internal/randen.h b/absl/random/internal/randen.h index 9a3840b8..9ff4a7a5 100644 --- a/absl/random/internal/randen.h +++ b/absl/random/internal/randen.h @@ -43,10 +43,8 @@ class Randen { // Generate updates the randen sponge. The outer portion of the sponge // (kCapacityBytes .. kStateBytes) may be consumed as PRNG state. - template - void Generate(T (&state)[N]) const { - static_assert(N * sizeof(T) == kStateBytes, - "Randen::Generate() requires kStateBytes of state"); + // REQUIRES: state points to kStateBytes of state. + inline void Generate(void* state) const { #if ABSL_RANDOM_INTERNAL_AES_DISPATCH // HW AES Dispatch. if (has_crypto_) { @@ -65,13 +63,9 @@ class Randen { // Absorb incorporates additional seed material into the randen sponge. After // absorb returns, Generate must be called before the state may be consumed. - template - void Absorb(const S (&seed)[M], T (&state)[N]) const { - static_assert(M * sizeof(S) == RandenTraits::kSeedBytes, - "Randen::Absorb() requires kSeedBytes of seed"); - - static_assert(N * sizeof(T) == RandenTraits::kStateBytes, - "Randen::Absorb() requires kStateBytes of state"); + // REQUIRES: seed points to kSeedBytes of seed. + // REQUIRES: state points to kStateBytes of state. + inline void Absorb(const void* seed, void* state) const { #if ABSL_RANDOM_INTERNAL_AES_DISPATCH // HW AES Dispatch. if (has_crypto_) { diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h index 372c3ac2..b4708664 100644 --- a/absl/random/internal/randen_engine.h +++ b/absl/random/internal/randen_engine.h @@ -42,7 +42,7 @@ namespace random_internal { // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. template -class alignas(16) randen_engine { +class alignas(8) randen_engine { public: // C++11 URBG interface: using result_type = T; @@ -58,7 +58,8 @@ class alignas(16) randen_engine { return (std::numeric_limits::max)(); } - explicit randen_engine(result_type seed_value = 0) { seed(seed_value); } + randen_engine() : randen_engine(0) {} + explicit randen_engine(result_type seed_value) { seed(seed_value); } template = kStateSizeT) { next_ = kCapacityT; - impl_.Generate(state_); + impl_.Generate(begin); } - - return little_endian::ToHost(state_[next_++]); + return little_endian::ToHost(begin[next_++]); } template @@ -92,9 +103,10 @@ class alignas(16) randen_engine { void seed(result_type seed_value = 0) { next_ = kStateSizeT; // Zeroes the inner state and fills the outer state with seed_value to - // mimics behaviour of reseed - std::fill(std::begin(state_), std::begin(state_) + kCapacityT, 0); - std::fill(std::begin(state_) + kCapacityT, std::end(state_), seed_value); + // mimic the behaviour of reseed + auto* begin = state(); + std::fill(begin, begin + kCapacityT, 0); + std::fill(begin + kCapacityT, begin + kStateSizeT, seed_value); } // Inserts entropy into (part of) the state. Calling this periodically with @@ -105,7 +117,6 @@ class alignas(16) randen_engine { using sequence_result_type = typename SeedSequence::result_type; static_assert(sizeof(sequence_result_type) == 4, "SeedSequence::result_type must be 32-bit"); - constexpr size_t kBufferSize = Randen::kSeedBytes / sizeof(sequence_result_type); alignas(16) sequence_result_type buffer[kBufferSize]; @@ -119,8 +130,8 @@ class alignas(16) randen_engine { if (entropy_size < kBufferSize) { // ... and only request that many values, or 256-bits, when unspecified. const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size; - std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0); - seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy); + std::fill(buffer + requested_entropy, buffer + kBufferSize, 0); + seq.generate(buffer, buffer + requested_entropy); #ifdef ABSL_IS_BIG_ENDIAN // Randen expects the seed buffer to be in Little Endian; reverse it on // Big Endian platforms. @@ -146,9 +157,9 @@ class alignas(16) randen_engine { std::swap(buffer[--dst], buffer[--src]); } } else { - seq.generate(std::begin(buffer), std::end(buffer)); + seq.generate(buffer, buffer + kBufferSize); } - impl_.Absorb(buffer, state_); + impl_.Absorb(buffer, state()); // Generate will be called when operator() is called next_ = kStateSizeT; @@ -159,9 +170,10 @@ class alignas(16) randen_engine { count -= step; constexpr uint64_t kRateT = kStateSizeT - kCapacityT; + auto* begin = state(); while (count > 0) { next_ = kCapacityT; - impl_.Generate(state_); + impl_.Generate(*reinterpret_cast(begin)); step = std::min(kRateT, count); count -= step; } @@ -169,9 +181,9 @@ class alignas(16) randen_engine { } bool operator==(const randen_engine& other) const { + const auto* begin = state(); return next_ == other.next_ && - std::equal(std::begin(state_), std::end(state_), - std::begin(other.state_)); + std::equal(begin, begin + kStateSizeT, other.state()); } bool operator!=(const randen_engine& other) const { @@ -185,11 +197,12 @@ class alignas(16) randen_engine { using numeric_type = typename random_internal::stream_format_type::type; auto saver = random_internal::make_ostream_state_saver(os); - for (const auto& elem : engine.state_) { + auto* it = engine.state(); + for (auto* end = it + kStateSizeT; it < end; ++it) { // In the case that `elem` is `uint8_t`, it must be cast to something // larger so that it prints as an integer rather than a character. For // simplicity, apply the cast all circumstances. - os << static_cast(little_endian::FromHost(elem)) + os << static_cast(little_endian::FromHost(*it)) << os.fill(); } os << engine.next_; @@ -215,7 +228,7 @@ class alignas(16) randen_engine { if (is.fail()) { return is; } - std::memcpy(engine.state_, state, sizeof(engine.state_)); + std::memcpy(engine.state(), state, sizeof(state)); engine.next_ = next; return is; } @@ -226,9 +239,21 @@ class alignas(16) randen_engine { static constexpr size_t kCapacityT = Randen::kCapacityBytes / sizeof(result_type); - // First kCapacityT are `inner', the others are accessible random bits. - alignas(16) result_type state_[kStateSizeT]; - size_t next_; // index within state_ + // Returns the state array pointer, which is aligned to 16 bytes. + // The first kCapacityT are the `inner' sponge; the remainder are available. + result_type* state() { + return reinterpret_cast( + (reinterpret_cast(&raw_state_) & 0xf) ? (raw_state_ + 8) + : raw_state_); + } + const result_type* state() const { + return const_cast(this)->state(); + } + + // raw state array, manually aligned in state(). This overallocates + // by 8 bytes since C++ does not guarantee extended heap alignment. + alignas(8) char raw_state_[Randen::kStateBytes + 8]; + size_t next_; // index within state() Randen impl_; }; -- cgit v1.2.3 From e3fdd9b16a2a90c9e01e00de46605ce59bebc661 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 24 Jan 2022 12:56:12 -0800 Subject: Export of internal Abseil changes -- 505dc83f11dbc14d6e493d83ed6451966629fe71 by Evan Brown : In debug mode, make b-tree adapt comparators to do checking to diagnose invalid non-strict-weak-ordering comparators. Reference: https://en.cppreference.com/w/cpp/named_req/Compare - Add an opt-out mechanism for tests that rely on counting the number of comparisons. - Use the unadapted comparator (original_key_compare) in making the use_linear_search decision so is_same still works. PiperOrigin-RevId: 423889350 GitOrigin-RevId: 505dc83f11dbc14d6e493d83ed6451966629fe71 Change-Id: I65b0ba489c69c8dcfb107684db84f3f7f4d72daa --- absl/container/btree_test.cc | 119 ++++++++++++++----- absl/container/internal/btree.h | 187 ++++++++++++++++++++++++------ absl/container/internal/btree_container.h | 4 +- 3 files changed, 241 insertions(+), 69 deletions(-) diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index 13cb8186..b2c3d73f 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -15,6 +15,7 @@ #include "absl/container/btree_test.h" #include +#include #include #include #include @@ -1344,38 +1345,34 @@ TEST(Btree, InitializerListInsert) { EXPECT_EQ(++it, range.second); } -template -void AssertKeyCompareToAdapted() { - using Adapted = typename key_compare_to_adapter::type; - static_assert(!std::is_same::value, - "key_compare_to_adapter should have adapted this comparator."); +template +void AssertKeyCompareStringAdapted() { + using Adapted = typename key_compare_adapter::type; static_assert( - std::is_same>::value, - "Adapted comparator should be a key-compare-to comparator."); + std::is_same::value || + std::is_same::value, + "key_compare_adapter should have string-adapted this comparator."); } -template -void AssertKeyCompareToNotAdapted() { - using Unadapted = typename key_compare_to_adapter::type; +template +void AssertKeyCompareNotStringAdapted() { + using Adapted = typename key_compare_adapter::type; static_assert( - std::is_same::value, - "key_compare_to_adapter shouldn't have adapted this comparator."); - static_assert( - std::is_same>::value, - "Un-adapted comparator should return bool."); + !std::is_same::value && + !std::is_same::value, + "key_compare_adapter shouldn't have string-adapted this comparator."); } -TEST(Btree, KeyCompareToAdapter) { - AssertKeyCompareToAdapted, std::string>(); - AssertKeyCompareToAdapted, std::string>(); - AssertKeyCompareToAdapted, absl::string_view>(); - AssertKeyCompareToAdapted, - absl::string_view>(); - AssertKeyCompareToAdapted, absl::Cord>(); - AssertKeyCompareToAdapted, absl::Cord>(); - AssertKeyCompareToNotAdapted, int>(); - AssertKeyCompareToNotAdapted, int>(); +TEST(Btree, KeyCompareAdapter) { + AssertKeyCompareStringAdapted, std::string>(); + AssertKeyCompareStringAdapted, std::string>(); + AssertKeyCompareStringAdapted, + absl::string_view>(); + AssertKeyCompareStringAdapted, + absl::string_view>(); + AssertKeyCompareStringAdapted, absl::Cord>(); + AssertKeyCompareStringAdapted, absl::Cord>(); + AssertKeyCompareNotStringAdapted, int>(); + AssertKeyCompareNotStringAdapted, int>(); } TEST(Btree, RValueInsert) { @@ -1425,11 +1422,19 @@ TEST(Btree, RValueInsert) { EXPECT_EQ(tracker.swaps(), 0); } -// A btree set with a specific number of values per node. +template +struct CheckedCompareOptedOutCmp : Cmp, BtreeTestOnlyCheckedCompareOptOutBase { + using Cmp::Cmp; + CheckedCompareOptedOutCmp() {} + CheckedCompareOptedOutCmp(Cmp cmp) : Cmp(std::move(cmp)) {} // NOLINT +}; + +// A btree set with a specific number of values per node. Opt out of +// checked_compare so that we can expect exact numbers of comparisons. template > class SizedBtreeSet : public btree_set_container, + set_params, std::allocator, BtreeNodePeer::GetTargetNodeSize(TargetValuesPerNode), /*Multi=*/false>>> { using Base = typename SizedBtreeSet::btree_set_container; @@ -2297,7 +2302,9 @@ TEST(Btree, TryEmplaceWithHintWorks) { }; using Cmp = decltype(cmp); - absl::btree_map m(cmp); + // Use a map that is opted out of key_compare being adapted so we can expect + // strict comparison call limits. + absl::btree_map> m(cmp); for (int i = 0; i < 128; ++i) { m.emplace(i, i); } @@ -2967,6 +2974,58 @@ TEST(Btree, ConstructImplicitlyWithUnadaptedComparator) { absl::btree_set set = {{}, MultiKeyComp{}}; } +#ifndef NDEBUG +TEST(Btree, InvalidComparatorsCaught) { + { + struct ZeroAlwaysLessCmp { + bool operator()(int lhs, int rhs) const { + if (lhs == 0) return true; + return lhs < rhs; + } + }; + absl::btree_set set; + EXPECT_DEATH(set.insert({0, 1, 2}), "is_self_equivalent"); + } + { + struct ThreeWayAlwaysLessCmp { + absl::weak_ordering operator()(int, int) const { + return absl::weak_ordering::less; + } + }; + absl::btree_set set; + EXPECT_DEATH(set.insert({0, 1, 2}), "is_self_equivalent"); + } + { + struct SumGreaterZeroCmp { + bool operator()(int lhs, int rhs) const { + // First, do equivalence correctly - so we can test later condition. + if (lhs == rhs) return false; + return lhs + rhs > 0; + } + }; + absl::btree_set set; + // Note: '!' only needs to be escaped when it's the first character. + EXPECT_DEATH(set.insert({0, 1, 2}), + R"regex(\!lhs_comp_rhs \|\| !comp\(\)\(rhs, lhs\))regex"); + } + { + struct ThreeWaySumGreaterZeroCmp { + absl::weak_ordering operator()(int lhs, int rhs) const { + // First, do equivalence correctly - so we can test later condition. + if (lhs == rhs) return absl::weak_ordering::equivalent; + + if (lhs + rhs > 0) return absl::weak_ordering::less; + if (lhs + rhs == 0) return absl::weak_ordering::equivalent; + return absl::weak_ordering::greater; + } + }; + absl::btree_set set; + EXPECT_DEATH(set.insert({0, 1, 2}), + R"regex(lhs_comp_rhs < 0 -> rhs_comp_lhs > 0)regex"); + } +} +#endif + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index 26bd5e14..bbc319c1 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -74,12 +74,14 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +template +using compare_result_t = absl::result_of_t; + // A helper class that indicates if the Compare parameter is a key-compare-to // comparator. template using btree_is_key_compare_to = - std::is_convertible, - absl::weak_ordering>; + std::is_convertible, absl::weak_ordering>; struct StringBtreeDefaultLess { using is_transparent = void; @@ -146,49 +148,140 @@ struct StringBtreeDefaultGreater { } }; -// A helper class to convert a boolean comparison into a three-way "compare-to" -// comparison that returns an `absl::weak_ordering`. This helper -// class is specialized for less, greater, -// less, greater, less, and -// greater. -// -// key_compare_to_adapter is provided so that btree users -// automatically get the more efficient compare-to code when using common -// Abseil string types with common comparison functors. -// These string-like specializations also turn on heterogeneous lookup by -// default. +// See below comments for checked_compare. +template ::value> +struct checked_compare_base : Compare { + using Compare::Compare; + explicit checked_compare_base(Compare c) : Compare(std::move(c)) {} + const Compare &comp() const { return *this; } +}; template -struct key_compare_to_adapter { - using type = Compare; +struct checked_compare_base { + explicit checked_compare_base(Compare c) : compare(std::move(c)) {} + const Compare &comp() const { return compare; } + Compare compare; +}; + +// A mechanism for opting out of checked_compare for use only in btree_test.cc. +struct BtreeTestOnlyCheckedCompareOptOutBase {}; + +// A helper class to adapt the specified comparator for two use cases: +// (1) When using common Abseil string types with common comparison functors, +// convert a boolean comparison into a three-way comparison that returns an +// `absl::weak_ordering`. This helper class is specialized for +// less, greater, less, +// greater, less, and greater. +// (2) Adapt the comparator to diagnose cases of non-strict-weak-ordering (see +// https://en.cppreference.com/w/cpp/named_req/Compare) in debug mode. Whenever +// a comparison is made, we will make assertions to verify that the comparator +// is valid. +template +struct key_compare_adapter { + // Inherit from checked_compare_base to support function pointers and also + // keep empty-base-optimization (EBO) support for classes. + // Note: we can't use CompressedTuple here because that would interfere + // with the EBO for `btree::root_`. `btree::root_` is itself a CompressedTuple + // and nested `CompressedTuple`s don't support EBO. + // TODO(b/214288561): use CompressedTuple instead once it supports EBO for + // nested `CompressedTuple`s. + struct checked_compare : checked_compare_base { + private: + using Base = typename checked_compare::checked_compare_base; + using Base::comp; + + // If possible, returns whether `t` is equivalent to itself. We can only do + // this for `Key`s because we can't be sure that it's safe to call + // `comp()(k, k)` otherwise. Even if SFINAE allows it, there could be a + // compilation failure inside the implementation of the comparison operator. + bool is_self_equivalent(const Key &k) const { + // Note: this works for both boolean and three-way comparators. + return comp()(k, k) == 0; + } + // If we can't compare `t` with itself, returns true unconditionally. + template + bool is_self_equivalent(const T &) const { + return true; + } + + public: + using Base::Base; + checked_compare(Compare comp) : Base(std::move(comp)) {} // NOLINT + + // Allow converting to Compare for use in key_comp()/value_comp(). + explicit operator Compare() const { return comp(); } + + template >::value, + int> = 0> + bool operator()(const T &lhs, const U &rhs) const { + // NOTE: if any of these assertions fail, then the comparator does not + // establish a strict-weak-ordering (see + // https://en.cppreference.com/w/cpp/named_req/Compare). + assert(is_self_equivalent(lhs)); + assert(is_self_equivalent(rhs)); + const bool lhs_comp_rhs = comp()(lhs, rhs); + assert(!lhs_comp_rhs || !comp()(rhs, lhs)); + return lhs_comp_rhs; + } + + template < + typename T, typename U, + absl::enable_if_t, + absl::weak_ordering>::value, + int> = 0> + absl::weak_ordering operator()(const T &lhs, const U &rhs) const { + // NOTE: if any of these assertions fail, then the comparator does not + // establish a strict-weak-ordering (see + // https://en.cppreference.com/w/cpp/named_req/Compare). + assert(is_self_equivalent(lhs)); + assert(is_self_equivalent(rhs)); + const absl::weak_ordering lhs_comp_rhs = comp()(lhs, rhs); +#ifndef NDEBUG + const absl::weak_ordering rhs_comp_lhs = comp()(rhs, lhs); + if (lhs_comp_rhs > 0) { + assert(rhs_comp_lhs < 0 && "lhs_comp_rhs > 0 -> rhs_comp_lhs < 0"); + } else if (lhs_comp_rhs == 0) { + assert(rhs_comp_lhs == 0 && "lhs_comp_rhs == 0 -> rhs_comp_lhs == 0"); + } else { + assert(rhs_comp_lhs > 0 && "lhs_comp_rhs < 0 -> rhs_comp_lhs > 0"); + } +#endif + return lhs_comp_rhs; + } + }; + using type = absl::conditional_t< + std::is_base_of::value, + Compare, checked_compare>; }; template <> -struct key_compare_to_adapter> { +struct key_compare_adapter, std::string> { using type = StringBtreeDefaultLess; }; template <> -struct key_compare_to_adapter> { +struct key_compare_adapter, std::string> { using type = StringBtreeDefaultGreater; }; template <> -struct key_compare_to_adapter> { +struct key_compare_adapter, absl::string_view> { using type = StringBtreeDefaultLess; }; template <> -struct key_compare_to_adapter> { +struct key_compare_adapter, absl::string_view> { using type = StringBtreeDefaultGreater; }; template <> -struct key_compare_to_adapter> { +struct key_compare_adapter, absl::Cord> { using type = StringBtreeDefaultLess; }; template <> -struct key_compare_to_adapter> { +struct key_compare_adapter, absl::Cord> { using type = StringBtreeDefaultGreater; }; @@ -224,6 +317,13 @@ struct prefers_linear_node_search< T, absl::void_t> : T::absl_btree_prefer_linear_node_search {}; +template +constexpr bool compare_has_valid_result_type() { + using compare_result_type = compare_result_t; + return std::is_same::value || + std::is_convertible::value; +} + template struct common_params { @@ -231,7 +331,24 @@ struct common_params { // If Compare is a common comparator for a string-like type, then we adapt it // to use heterogeneous lookup and to be a key-compare-to comparator. - using key_compare = typename key_compare_to_adapter::type; + // We also adapt the comparator to diagnose invalid comparators in debug mode. + // We disable this when `Compare` is invalid in a way that will cause + // adaptation to fail (having invalid return type) so that we can give a + // better compilation failure in static_assert_validation. If we don't do + // this, then there will be cascading compilation failures that are confusing + // for users. + using key_compare = + absl::conditional_t(), + Compare, + typename key_compare_adapter::type>; + + static constexpr bool kIsKeyCompareStringAdapted = + std::is_same::value || + std::is_same::value; + static constexpr bool kIsKeyCompareTransparent = + IsTransparent::value || + kIsKeyCompareStringAdapted; + // A type which indicates if we have a key-compare-to functor or a plain old // key-compare functor. using is_key_compare_to = btree_is_key_compare_to; @@ -260,11 +377,9 @@ struct common_params { // that we know has the same equivalence classes for all lookup types. template constexpr static bool can_have_multiple_equivalent_keys() { - return Multi || - (IsTransparent::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value); + return Multi || (IsTransparent::value && + !std::is_same::value && + !kIsKeyCompareStringAdapted); } enum { @@ -366,6 +481,7 @@ class btree_node { using field_type = typename Params::node_count_type; using allocator_type = typename Params::allocator_type; using slot_type = typename Params::slot_type; + using original_key_compare = typename Params::original_key_compare; public: using params_type = Params; @@ -387,15 +503,15 @@ class btree_node { // - Otherwise, choose binary. // TODO(ezb): Might make sense to add condition(s) based on node-size. using use_linear_search = std::integral_constant< - bool, - has_linear_node_search_preference::value - ? prefers_linear_node_search::value - : has_linear_node_search_preference::value + bool, has_linear_node_search_preference::value + ? prefers_linear_node_search::value + : has_linear_node_search_preference::value ? prefers_linear_node_search::value : std::is_arithmetic::value && - (std::is_same, key_compare>::value || + (std::is_same, + original_key_compare>::value || std::is_same, - key_compare>::value)>; + original_key_compare>::value)>; // This class is organized by absl::container_internal::Layout as if it had // the following structure: @@ -1821,11 +1937,8 @@ constexpr bool btree

::static_assert_validation() { "target node size too large"); // Verify that key_compare returns an absl::{weak,strong}_ordering or bool. - using compare_result_type = - absl::result_of_t; static_assert( - std::is_same::value || - std::is_convertible::value, + compare_has_valid_result_type(), "key comparison function must return absl::{weak,strong}_ordering or " "bool."); diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index d28b2446..bae5c6e2 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -44,8 +44,8 @@ class btree_container { // transparent case. template using key_arg = - typename KeyArg::value>:: - template type; + typename KeyArg::template type< + K, typename Tree::key_type>; public: using key_type = typename Tree::key_type; -- cgit v1.2.3 From 4862fe55506f4987a0b9d17cf768f9be84b5c1af Mon Sep 17 00:00:00 2001 From: Roman Gershman Date: Thu, 27 Jan 2022 19:57:20 +0200 Subject: Disable ABSL_INTERNAL_ENABLE_FORMAT_CHECKER under VsCode/Intellisense (#1097) --- absl/strings/internal/str_format/checker.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h index 2a2601ec..a0a13cec 100644 --- a/absl/strings/internal/str_format/checker.h +++ b/absl/strings/internal/str_format/checker.h @@ -22,9 +22,15 @@ // Compile time check support for entry points. #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER -#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) + +// We disable format checker under vscode intellisense compilation. +// See https://github.com/microsoft/vscode-cpptools/issues/3683 for +// more details. +#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && \ + !defined(__INTELLISENSE__) #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1 -#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) +#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) && + // !defined(__INTELLISENSE__) #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER namespace absl { -- cgit v1.2.3 From d2da09a4cfada6997914da65d83aac0bb558155c Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 27 Jan 2022 09:56:30 -0800 Subject: Export of internal Abseil changes -- 7fbe730c56c0890f27df47f9ad4f0cfa600c6ad9 by Derek Mauro : Internal change PiperOrigin-RevId: 424639048 -- 1825fcf704e45e881cdd1d2fe3eb2226f523f3cd by Derek Mauro : Disable tests Table.EnsureNonQuadraticTopNXorSeedByProbeSeqLength and Table.EnsureNonQuadraticTopNLinearTransformByProbeSeqLength again as they are (still) flaky PiperOrigin-RevId: 424616745 -- e624119171d9bceec2f6df023b220a6642070cfb by Abseil Team : Switch from *_TEST_CASE_P to *_TEST_SUITE_P gtest macros. PiperOrigin-RevId: 424404790 -- a110698c10554fb1f640412db8cd7c5a38b7b8e8 by Abseil Team : Remove extraneous newline in documentation. PiperOrigin-RevId: 424328502 GitOrigin-RevId: 7fbe730c56c0890f27df47f9ad4f0cfa600c6ad9 Change-Id: I56fd8c7cecf65fe37cb4cf2dde70842867d637d7 --- absl/container/internal/raw_hash_set_test.cc | 6 ++++-- absl/hash/hash_test.cc | 24 ++++++++++++------------ absl/strings/internal/str_format/checker.h | 1 - absl/strings/str_split.h | 3 +-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 47015bcf..e7732f67 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -1258,7 +1258,8 @@ ExpectedStats XorSeedExpectedStats() { return {}; } -TEST(Table, EnsureNonQuadraticTopNXorSeedByProbeSeqLength) { +// TODO(b/80415403): Figure out why this test is so flaky, esp. on MSVC +TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) { ProbeStatsPerSize stats; std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; for (size_t size : sizes) { @@ -1352,7 +1353,8 @@ ExpectedStats LinearTransformExpectedStats() { return {}; } -TEST(Table, EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) { +// TODO(b/80415403): Figure out why this test is so flaky. +TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) { ProbeStatsPerSize stats; std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; for (size_t size : sizes) { diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index cea54dc0..39ff8f52 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -139,10 +139,10 @@ TYPED_TEST_P(HashValueIntTest, FastPath) { absl::Hash>{}(std::tuple(n))); } -REGISTER_TYPED_TEST_CASE_P(HashValueIntTest, BasicUsage, FastPath); +REGISTER_TYPED_TEST_SUITE_P(HashValueIntTest, BasicUsage, FastPath); using IntTypes = testing::Types; -INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueIntTest, IntTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueIntTest, IntTypes); enum LegacyEnum { kValue1, kValue2, kValue3 }; @@ -507,7 +507,7 @@ TYPED_TEST_P(HashValueSequenceTest, BasicUsage) { EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); } -REGISTER_TYPED_TEST_CASE_P(HashValueSequenceTest, BasicUsage); +REGISTER_TYPED_TEST_SUITE_P(HashValueSequenceTest, BasicUsage); using IntSequenceTypes = testing::Types< std::deque, std::forward_list, std::list, std::vector, std::vector, TypeErasedContainer>, std::set, @@ -515,7 +515,7 @@ using IntSequenceTypes = testing::Types< TypeErasedContainer>, std::unordered_set, std::unordered_multiset, absl::flat_hash_set, absl::node_hash_set, absl::btree_set>; -INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueSequenceTest, IntSequenceTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueSequenceTest, IntSequenceTypes); template class HashValueNestedSequenceTest : public testing::Test {}; @@ -541,7 +541,7 @@ TYPED_TEST_P(HashValueNestedSequenceTest, BasicUsage) { EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); } -REGISTER_TYPED_TEST_CASE_P(HashValueNestedSequenceTest, BasicUsage); +REGISTER_TYPED_TEST_SUITE_P(HashValueNestedSequenceTest, BasicUsage); template using TypeErasedSet = TypeErasedContainer>; @@ -551,7 +551,7 @@ using NestedIntSequenceTypes = testing::Types< UnorderedSequence>, UnorderedSequence>, TypeErasedSet>, TypeErasedSet>, TypeErasedSet>>; -INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueNestedSequenceTest, +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueNestedSequenceTest, NestedIntSequenceTypes); // Private type that only supports AbslHashValue to make sure our chosen hash @@ -732,13 +732,13 @@ TYPED_TEST_P(HashValueAssociativeMapTest, BasicUsage) { EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); } -REGISTER_TYPED_TEST_CASE_P(HashValueAssociativeMapTest, BasicUsage); +REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMapTest, BasicUsage); using AssociativeMapTypes = testing::Types< std::map, std::unordered_map, absl::flat_hash_map, absl::node_hash_map, absl::btree_map, UnorderedSequence>>; -INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueAssociativeMapTest, +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMapTest, AssociativeMapTypes); template @@ -763,11 +763,11 @@ TYPED_TEST_P(HashValueAssociativeMultimapTest, BasicUsage) { EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars)); } -REGISTER_TYPED_TEST_CASE_P(HashValueAssociativeMultimapTest, BasicUsage); +REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest, BasicUsage); using AssociativeMultimapTypes = testing::Types, std::unordered_multimap>; -INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueAssociativeMultimapTest, +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMultimapTest, AssociativeMultimapTypes); TEST(HashValueTest, ReferenceWrapper) { @@ -1007,10 +1007,10 @@ TYPED_TEST_P(HashIntTest, BasicUsage) { Hash>()({})); } -REGISTER_TYPED_TEST_CASE_P(HashIntTest, BasicUsage); +REGISTER_TYPED_TEST_SUITE_P(HashIntTest, BasicUsage); using IntTypes = testing::Types; -INSTANTIATE_TYPED_TEST_CASE_P(My, HashIntTest, IntTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(My, HashIntTest, IntTypes); struct StructWithPadding { char c; diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h index a0a13cec..4fd19d13 100644 --- a/absl/strings/internal/str_format/checker.h +++ b/absl/strings/internal/str_format/checker.h @@ -22,7 +22,6 @@ // Compile time check support for entry points. #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - // We disable format checker under vscode intellisense compilation. // See https://github.com/microsoft/vscode-cpptools/issues/3683 for // more details. diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h index bfbca422..7bbb68a3 100644 --- a/absl/strings/str_split.h +++ b/absl/strings/str_split.h @@ -461,8 +461,7 @@ using EnableSplitIfString = // first two split strings become the `std::pair` `.first` and `.second` // members, respectively. The remaining split substrings are discarded. If there // are less than two split substrings, the empty string is used for the -// corresponding -// `std::pair` member. +// corresponding `std::pair` member. // // Example: // -- cgit v1.2.3 From a655ec4aff1cad3d4d076781ec302cdaaf97993f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 27 Jan 2022 11:43:45 -0800 Subject: Export of internal Abseil changes -- dab53ef01a1c8ceebd1347a4918a0def0d7d4ce5 by Derek Mauro : Migrate away from the remaining usages of GoogleTest's legacy TestCase API and use the new TestSuite API instead PiperOrigin-RevId: 424668744 GitOrigin-RevId: dab53ef01a1c8ceebd1347a4918a0def0d7d4ce5 Change-Id: I599de09f3349d18c2d74795b1f867fa6c99c3eed --- absl/container/inlined_vector_test.cc | 17 +++++++++-------- absl/container/internal/hash_function_defaults_test.cc | 2 +- .../container/internal/unordered_map_constructor_test.h | 2 +- absl/container/internal/unordered_map_lookup_test.h | 4 ++-- absl/container/internal/unordered_map_modifiers_test.h | 11 ++++++----- .../container/internal/unordered_set_constructor_test.h | 2 +- absl/container/internal/unordered_set_lookup_test.h | 2 +- absl/container/internal/unordered_set_modifiers_test.h | 8 ++++---- absl/random/beta_distribution_test.cc | 6 +++--- absl/random/exponential_distribution_test.cc | 6 +++--- absl/random/gaussian_distribution_test.cc | 2 +- absl/random/log_uniform_int_distribution_test.cc | 2 +- absl/random/poisson_distribution_test.cc | 2 +- absl/random/zipf_distribution_test.cc | 2 +- absl/strings/internal/str_format/convert_test.cc | 6 +++--- 15 files changed, 38 insertions(+), 36 deletions(-) diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 98aff334..4c1ba04a 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc @@ -1545,17 +1545,18 @@ TYPED_TEST_P(InstanceTest, InitializerListAssign) { } } -REGISTER_TYPED_TEST_CASE_P(InstanceTest, Swap, CountConstructorsDestructors, - CountConstructorsDestructorsOnCopyConstruction, - CountConstructorsDestructorsOnMoveConstruction, - CountConstructorsDestructorsOnAssignment, - CountConstructorsDestructorsOnMoveAssignment, - CountElemAssignInlineBacking, RangedConstructor, - RangedAssign, InitializerListAssign); +REGISTER_TYPED_TEST_SUITE_P(InstanceTest, Swap, CountConstructorsDestructors, + CountConstructorsDestructorsOnCopyConstruction, + CountConstructorsDestructorsOnMoveConstruction, + CountConstructorsDestructorsOnAssignment, + CountConstructorsDestructorsOnMoveAssignment, + CountElemAssignInlineBacking, RangedConstructor, + RangedAssign, InitializerListAssign); using InstanceTypes = ::testing::Types; -INSTANTIATE_TYPED_TEST_CASE_P(InstanceTestOnTypes, InstanceTest, InstanceTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(InstanceTestOnTypes, InstanceTest, + InstanceTypes); TEST(DynamicVec, DynamicVecCompiles) { DynamicVec v; diff --git a/absl/container/internal/hash_function_defaults_test.cc b/absl/container/internal/hash_function_defaults_test.cc index 59576b8e..9f0a4c72 100644 --- a/absl/container/internal/hash_function_defaults_test.cc +++ b/absl/container/internal/hash_function_defaults_test.cc @@ -310,7 +310,7 @@ struct StringLikeTest : public ::testing::Test { hash_default_hash hash; }; -TYPED_TEST_CASE_P(StringLikeTest); +TYPED_TEST_SUITE_P(StringLikeTest); TYPED_TEST_P(StringLikeTest, Eq) { EXPECT_TRUE(this->eq(this->a1, this->b1)); diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h index c1d20f3c..7e84dc25 100644 --- a/absl/container/internal/unordered_map_constructor_test.h +++ b/absl/container/internal/unordered_map_constructor_test.h @@ -476,7 +476,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { // containers in unspecified state (and in practice in causes memory-leak // according to heap-checker!). -REGISTER_TYPED_TEST_CASE_P( +REGISTER_TYPED_TEST_SUITE_P( ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, diff --git a/absl/container/internal/unordered_map_lookup_test.h b/absl/container/internal/unordered_map_lookup_test.h index e76421e5..3713cd9a 100644 --- a/absl/container/internal/unordered_map_lookup_test.h +++ b/absl/container/internal/unordered_map_lookup_test.h @@ -107,8 +107,8 @@ TYPED_TEST_P(LookupTest, EqualRange) { } } -REGISTER_TYPED_TEST_CASE_P(LookupTest, At, OperatorBracket, Count, Find, - EqualRange); +REGISTER_TYPED_TEST_SUITE_P(LookupTest, At, OperatorBracket, Count, Find, + EqualRange); } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/unordered_map_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h index d3543936..4d9ab30f 100644 --- a/absl/container/internal/unordered_map_modifiers_test.h +++ b/absl/container/internal/unordered_map_modifiers_test.h @@ -297,11 +297,12 @@ TYPED_TEST_P(ModifiersTest, Swap) { // TODO(alkis): Write tests for extract. // TODO(alkis): Write tests for merge. -REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, - InsertRange, InsertWithinCapacity, - InsertRangeWithinCapacity, InsertOrAssign, - InsertOrAssignHint, Emplace, EmplaceHint, TryEmplace, - TryEmplaceHint, Erase, EraseRange, EraseKey, Swap); +REGISTER_TYPED_TEST_SUITE_P(ModifiersTest, Clear, Insert, InsertHint, + InsertRange, InsertWithinCapacity, + InsertRangeWithinCapacity, InsertOrAssign, + InsertOrAssignHint, Emplace, EmplaceHint, + TryEmplace, TryEmplaceHint, Erase, EraseRange, + EraseKey, Swap); template struct is_unique_ptr : std::false_type {}; diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h index 41165b05..af1116e6 100644 --- a/absl/container/internal/unordered_set_constructor_test.h +++ b/absl/container/internal/unordered_set_constructor_test.h @@ -478,7 +478,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); } -REGISTER_TYPED_TEST_CASE_P( +REGISTER_TYPED_TEST_SUITE_P( ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, diff --git a/absl/container/internal/unordered_set_lookup_test.h b/absl/container/internal/unordered_set_lookup_test.h index 8f2f4b20..b35f766e 100644 --- a/absl/container/internal/unordered_set_lookup_test.h +++ b/absl/container/internal/unordered_set_lookup_test.h @@ -82,7 +82,7 @@ TYPED_TEST_P(LookupTest, EqualRange) { } } -REGISTER_TYPED_TEST_CASE_P(LookupTest, Count, Find, EqualRange); +REGISTER_TYPED_TEST_SUITE_P(LookupTest, Count, Find, EqualRange); } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h index 6e473e45..d8864bb2 100644 --- a/absl/container/internal/unordered_set_modifiers_test.h +++ b/absl/container/internal/unordered_set_modifiers_test.h @@ -209,10 +209,10 @@ TYPED_TEST_P(ModifiersTest, Swap) { // TODO(alkis): Write tests for extract. // TODO(alkis): Write tests for merge. -REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, - InsertRange, InsertWithinCapacity, - InsertRangeWithinCapacity, Emplace, EmplaceHint, - Erase, EraseRange, EraseKey, Swap); +REGISTER_TYPED_TEST_SUITE_P(ModifiersTest, Clear, Insert, InsertHint, + InsertRange, InsertWithinCapacity, + InsertRangeWithinCapacity, Emplace, EmplaceHint, + Erase, EraseRange, EraseKey, Swap); } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc index d980c969..9a3eb2b7 100644 --- a/absl/random/beta_distribution_test.cc +++ b/absl/random/beta_distribution_test.cc @@ -54,7 +54,7 @@ using RealTypes = std::conditional, ::testing::Types>::type; -TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes); +TYPED_TEST_SUITE(BetaDistributionInterfaceTest, RealTypes); TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { // The threshold for whether std::exp(1/a) is finite. @@ -431,13 +431,13 @@ std::string ParamName( return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); } -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( TestSampleStatisticsCombinations, BetaDistributionTest, ::testing::Combine(::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4), ::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4)), ParamName); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( TestSampleStatistics_SelectedPairs, BetaDistributionTest, ::testing::Values(std::make_pair(0.5, 1000), std::make_pair(1000, 0.5), std::make_pair(900, 1000), std::make_pair(10000, 20000), diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc index 81a5d17b..3c44d9ec 100644 --- a/absl/random/exponential_distribution_test.cc +++ b/absl/random/exponential_distribution_test.cc @@ -58,7 +58,7 @@ using RealTypes = std::conditional, ::testing::Types>::type; -TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes); +TYPED_TEST_SUITE(ExponentialDistributionTypedTest, RealTypes); TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { using param_type = @@ -343,8 +343,8 @@ std::string ParamName(const ::testing::TestParamInfo& info) { return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); } -INSTANTIATE_TEST_CASE_P(All, ExponentialDistributionTests, - ::testing::ValuesIn(GenParams()), ParamName); +INSTANTIATE_TEST_SUITE_P(All, ExponentialDistributionTests, + ::testing::ValuesIn(GenParams()), ParamName); // NOTE: absl::exponential_distribution is not guaranteed to be stable. TEST(ExponentialDistributionTest, StabilityTest) { diff --git a/absl/random/gaussian_distribution_test.cc b/absl/random/gaussian_distribution_test.cc index c0bac2b0..4584ac92 100644 --- a/absl/random/gaussian_distribution_test.cc +++ b/absl/random/gaussian_distribution_test.cc @@ -54,7 +54,7 @@ using RealTypes = std::conditional, ::testing::Types>::type; -TYPED_TEST_CASE(GaussianDistributionInterfaceTest, RealTypes); +TYPED_TEST_SUITE(GaussianDistributionInterfaceTest, RealTypes); TYPED_TEST(GaussianDistributionInterfaceTest, SerializeTest) { using param_type = diff --git a/absl/random/log_uniform_int_distribution_test.cc b/absl/random/log_uniform_int_distribution_test.cc index 5e780d96..0d0fcb95 100644 --- a/absl/random/log_uniform_int_distribution_test.cc +++ b/absl/random/log_uniform_int_distribution_test.cc @@ -42,7 +42,7 @@ class LogUniformIntDistributionTypeTest : public ::testing::Test {}; using IntTypes = ::testing::Types; -TYPED_TEST_CASE(LogUniformIntDistributionTypeTest, IntTypes); +TYPED_TEST_SUITE(LogUniformIntDistributionTypeTest, IntTypes); TYPED_TEST(LogUniformIntDistributionTypeTest, SerializeTest) { using param_type = diff --git a/absl/random/poisson_distribution_test.cc b/absl/random/poisson_distribution_test.cc index 8baabd11..4f585b9b 100644 --- a/absl/random/poisson_distribution_test.cc +++ b/absl/random/poisson_distribution_test.cc @@ -73,7 +73,7 @@ class PoissonDistributionInterfaceTest : public ::testing::Test {}; using IntTypes = ::testing::Types; -TYPED_TEST_CASE(PoissonDistributionInterfaceTest, IntTypes); +TYPED_TEST_SUITE(PoissonDistributionInterfaceTest, IntTypes); TYPED_TEST(PoissonDistributionInterfaceTest, SerializeTest) { using param_type = typename absl::poisson_distribution::param_type; diff --git a/absl/random/zipf_distribution_test.cc b/absl/random/zipf_distribution_test.cc index f8cf70e0..c8bb89db 100644 --- a/absl/random/zipf_distribution_test.cc +++ b/absl/random/zipf_distribution_test.cc @@ -44,7 +44,7 @@ class ZipfDistributionTypedTest : public ::testing::Test {}; using IntTypes = ::testing::Types; -TYPED_TEST_CASE(ZipfDistributionTypedTest, IntTypes); +TYPED_TEST_SUITE(ZipfDistributionTypedTest, IntTypes); TYPED_TEST(ZipfDistributionTypedTest, SerializeTest) { using param_type = typename absl::zipf_distribution::param_type; diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index 91e03609..d9fbf61c 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -473,7 +473,7 @@ TYPED_TEST_P(TypedFormatConvertTest, Char) { } } -REGISTER_TYPED_TEST_CASE_P(TypedFormatConvertTest, AllIntsWithFlags, Char); +REGISTER_TYPED_TEST_SUITE_P(TypedFormatConvertTest, AllIntsWithFlags, Char); typedef ::testing::Types< int, unsigned, volatile int, @@ -482,8 +482,8 @@ typedef ::testing::Types< long long, unsigned long long, signed char, unsigned char, char> AllIntTypes; -INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes, - TypedFormatConvertTest, AllIntTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(TypedFormatConvertTestWithAllIntTypes, + TypedFormatConvertTest, AllIntTypes); TEST_F(FormatConvertTest, VectorBool) { // Make sure vector's values behave as bools. std::vector v = {true, false}; -- cgit v1.2.3 From 940c06c25d2953f44310b68eb8aab6114dba11fb Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 28 Jan 2022 09:04:58 -0800 Subject: Export of internal Abseil changes -- ef2bdebfdff0a93d3af6248d709d42a47fe2e7b4 by Derek Mauro : Compile with -DGTEST_REMOVE_LEGACY_TEST_CASEAPI_ in Bazel CI builds to prevent uses of the legacy TestCase API from sneaking in PiperOrigin-RevId: 424882792 -- 4cf19d7b4dd855685deb0d4d3170e4db9f6f789b by Abseil Team : Fix typo in comment. PiperOrigin-RevId: 424745867 GitOrigin-RevId: ef2bdebfdff0a93d3af6248d709d42a47fe2e7b4 Change-Id: I50ad9b4cf3fa17831aa6521c58d81b0ce8ec0c47 --- WORKSPACE | 8 ++++---- absl/status/status.h | 2 +- ci/cmake_common.sh | 2 +- ci/linux_clang-latest_libcxx_asan_bazel.sh | 1 + ci/linux_clang-latest_libcxx_bazel.sh | 1 + ci/linux_clang-latest_libcxx_tsan_bazel.sh | 1 + ci/linux_clang-latest_libstdcxx_bazel.sh | 1 + ci/linux_gcc-floor_libstdcxx_bazel.sh | 1 + ci/linux_gcc-latest_libstdcxx_bazel.sh | 1 + ci/macos_xcode_bazel.sh | 1 + 10 files changed, 13 insertions(+), 6 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index c9aa8caf..8120cd17 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -20,11 +20,11 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( - name = "com_google_googletest", # 2021-07-09T13:28:13Z - sha256 = "12ef65654dc01ab40f6f33f9d02c04f2097d2cd9fbe48dc6001b29543583b0ad", - strip_prefix = "googletest-8d51ffdfab10b3fba636ae69bc03da4b54f8c235", + name = "com_google_googletest", # 2022-01-28T15:27:11Z + sha256 = "eb70a6d4520f940956a6b3e37d205d92736bb104c6a1b2b9f82bfc41bd7a2b34", + strip_prefix = "googletest-28e1da21d8d677bc98f12ccc7fc159ff19e8e817", # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. - urls = ["https://github.com/google/googletest/archive/8d51ffdfab10b3fba636ae69bc03da4b54f8c235.zip"], + urls = ["https://github.com/google/googletest/archive/28e1da21d8d677bc98f12ccc7fc159ff19e8e817.zip"], ) # Google benchmark. diff --git a/absl/status/status.h b/absl/status/status.h index 5a09faeb..db4b340a 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -533,7 +533,7 @@ class Status final { //---------------------------------------------------------------------------- // A payload may be attached to a status to provide additional context to an - // error that may not be satisifed by an existing `absl::StatusCode`. + // error that may not be satisfied by an existing `absl::StatusCode`. // Typically, this payload serves one of several purposes: // // * It may provide more fine-grained semantic information about the error diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index 51f31069..8a93389b 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="8d51ffdfab10b3fba636ae69bc03da4b54f8c235" +readonly ABSL_GOOGLETEST_COMMIT="28e1da21d8d677bc98f12ccc7fc159ff19e8e817" # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh index 0605e2b3..196e5c1d 100755 --- a/ci/linux_clang-latest_libcxx_asan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh @@ -77,6 +77,7 @@ for std in ${STD}; do /usr/local/bin/bazel test ... \ --compilation_mode="${compilation_mode}" \ --copt="${exceptions_mode}" \ + --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \ --copt="-fsanitize=address" \ --copt="-fsanitize=float-divide-by-zero" \ --copt="-fsanitize=nullability" \ diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh index 00517749..39aecf58 100755 --- a/ci/linux_clang-latest_libcxx_bazel.sh +++ b/ci/linux_clang-latest_libcxx_bazel.sh @@ -83,6 +83,7 @@ for std in ${STD}; do /usr/local/bin/bazel test ... \ --compilation_mode=\"${compilation_mode}\" \ --copt=\"${exceptions_mode}\" \ + --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \ --copt=-Werror \ --define=\"absl=1\" \ --distdir=\"/bazel-distdir\" \ diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh index da4fcd0a..b0a1abff 100755 --- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh @@ -78,6 +78,7 @@ for std in ${STD}; do --build_tag_filters="-notsan" \ --compilation_mode="${compilation_mode}" \ --copt="${exceptions_mode}" \ + --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \ --copt="-fsanitize=thread" \ --copt="-fno-sanitize-blacklist" \ --copt=-Werror \ diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh index 36fdf82c..8550481b 100755 --- a/ci/linux_clang-latest_libstdcxx_bazel.sh +++ b/ci/linux_clang-latest_libstdcxx_bazel.sh @@ -75,6 +75,7 @@ for std in ${STD}; do /usr/local/bin/bazel test ... \ --compilation_mode="${compilation_mode}" \ --copt="--gcc-toolchain=/usr/local" \ + --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \ --copt="${exceptions_mode}" \ --copt=-Werror \ --define="absl=1" \ diff --git a/ci/linux_gcc-floor_libstdcxx_bazel.sh b/ci/linux_gcc-floor_libstdcxx_bazel.sh index 54ab68a3..8b41b5d7 100755 --- a/ci/linux_gcc-floor_libstdcxx_bazel.sh +++ b/ci/linux_gcc-floor_libstdcxx_bazel.sh @@ -75,6 +75,7 @@ for std in ${STD}; do /usr/local/bin/bazel test ... \ --compilation_mode="${compilation_mode}" \ --copt="${exceptions_mode}" \ + --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \ --copt=-Werror \ --define="absl=1" \ --distdir="/bazel-distdir" \ diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh index 0555eced..3f7c71af 100755 --- a/ci/linux_gcc-latest_libstdcxx_bazel.sh +++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh @@ -81,6 +81,7 @@ for std in ${STD}; do /usr/local/bin/bazel test ... \ --compilation_mode=\"${compilation_mode}\" \ --copt=\"${exceptions_mode}\" \ + --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \ --copt=-Werror \ --define=\"absl=1\" \ --distdir=\"/bazel-distdir\" \ diff --git a/ci/macos_xcode_bazel.sh b/ci/macos_xcode_bazel.sh index 9e14e660..93fb03cd 100755 --- a/ci/macos_xcode_bazel.sh +++ b/ci/macos_xcode_bazel.sh @@ -46,6 +46,7 @@ if [[ -n "${ALTERNATE_OPTIONS:-}" ]]; then fi ${BAZEL_BIN} test ... \ + --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \ --copt=-Werror \ --keep_going \ --show_timestamps \ -- cgit v1.2.3 From 63d26fad49463561c2427a45aab283938ade10d9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 31 Jan 2022 09:50:11 -0800 Subject: Export of internal Abseil changes -- c475e8ac1ea163248415e7d4bdc05e3c47dd8f3d by Derek Mauro : Replace absl::bit_cast with std::bit_cast in C++20 Remove the deprecated implementation that is less-strict than the standard, and also relies on undefined behavior PiperOrigin-RevId: 425391711 -- dc97c996aed85161f0e490baafd3ed851e8bea9d by Abseil Team : Add enum example for ABSL_DEPRECATED() PiperOrigin-RevId: 425311634 GitOrigin-RevId: c475e8ac1ea163248415e7d4bdc05e3c47dd8f3d Change-Id: Id8f159b37fe3f3cace6ab7bce08daf9888c44bcf --- absl/base/attributes.h | 4 +++ absl/base/casts.h | 78 +++++++++++++++++++------------------------------- 2 files changed, 33 insertions(+), 49 deletions(-) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 4ab6fa27..91aabffe 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -656,6 +656,10 @@ // ABSL_DEPRECATED("Use DoThat() instead") // void DoThis(); // +// enum FooEnum { +// kBar ABSL_DEPRECATED("Use kBaz instead"), +// }; +// // Every usage of a deprecated entity will trigger a warning when compiled with // clang's `-Wdeprecated-declarations` option. This option is turned off by // default, but the warnings will be reported by clang-tidy. diff --git a/absl/base/casts.h b/absl/base/casts.h index bf100efa..b99adb06 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -29,6 +29,10 @@ #include #include +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +#include // For std::bit_cast. +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + #include "absl/base/internal/identity.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" @@ -36,23 +40,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN -namespace internal_casts { - -template -struct is_bitcastable - : std::integral_constant< - bool, - sizeof(Dest) == sizeof(Source) && - type_traits_internal::is_trivially_copyable::value && - type_traits_internal::is_trivially_copyable::value -#if !ABSL_HAVE_BUILTIN(__builtin_bit_cast) - && std::is_default_constructible::value -#endif - > { -}; - -} // namespace internal_casts - // implicit_cast() // // Performs an implicit conversion between types following the language @@ -123,7 +110,7 @@ constexpr To implicit_cast(typename absl::internal::identity_t to) { // floating point value: // // float f = 3.14159265358979; -// int i = bit_cast(f); +// int i = bit_cast(f); // // i = 0x40490fdb // // Reinterpreting and accessing a value directly as a different type (as shown @@ -151,48 +138,41 @@ constexpr To implicit_cast(typename absl::internal::identity_t to) { // introducing this undefined behavior (since the original value is never // accessed in the wrong way). // -// NOTE: The requirements here are more strict than the bit_cast of standard -// proposal P0476 when __builtin_bit_cast is not available. -// Specifically, this implementation also requires `Dest` to be -// default-constructible. -template < - typename Dest, typename Source, - typename std::enable_if::value, - int>::type = 0> +// The requirements of `absl::bit_cast` are more strict than that of +// `std::bit_cast` unless compiler support is available. Specifically, without +// compiler support, this implementation also requires `Dest` to be +// default-constructible. In C++20, `absl::bit_cast` is replaced by +// `std::bit_cast`. +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +using std::bit_cast; + +#else // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L + +template ::value && + type_traits_internal::is_trivially_copyable::value +#if !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + && std::is_default_constructible::value +#endif // !ABSL_HAVE_BUILTIN(__builtin_bit_cast) + , + int>::type = 0> #if ABSL_HAVE_BUILTIN(__builtin_bit_cast) inline constexpr Dest bit_cast(const Source& source) { return __builtin_bit_cast(Dest, source); } -#else +#else // ABSL_HAVE_BUILTIN(__builtin_bit_cast) inline Dest bit_cast(const Source& source) { Dest dest; memcpy(static_cast(std::addressof(dest)), static_cast(std::addressof(source)), sizeof(dest)); return dest; } -#endif - -// NOTE: This overload is only picked if the requirements of bit_cast are -// not met. It is therefore UB, but is provided temporarily as previous -// versions of this function template were unchecked. Do not use this in -// new code. -template < - typename Dest, typename Source, - typename std::enable_if< - !internal_casts::is_bitcastable::value, - int>::type = 0> -ABSL_DEPRECATED( - "absl::bit_cast type requirements were violated. Update the types " - "being used such that they are the same size and are both " - "TriviallyCopyable.") -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), - "Source and destination types should have equal sizes."); +#endif // ABSL_HAVE_BUILTIN(__builtin_bit_cast) - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} +#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L ABSL_NAMESPACE_END } // namespace absl -- cgit v1.2.3 From 5202173ca7671ebe241cb4d9447dc4b1f2d3ec10 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 1 Feb 2022 13:52:49 -0800 Subject: Export of internal Abseil changes -- 121db1a08321eaa3006726cc32b459cf17880e35 by Derek Mauro : Internal change PiperOrigin-RevId: 425707805 -- 0dec484389bbb75aae0a412146e3564bf833a739 by Derek Mauro : macOS CI: Avoid depending on external sites like GitHub by prepopulating dependencies and setting --distdir Our Linux CI does this, but for some reason was never enabled on macOS PiperOrigin-RevId: 425668638 GitOrigin-RevId: 121db1a08321eaa3006726cc32b459cf17880e35 Change-Id: Id51645df90b6a0808dd5b18eb636a10f798e24ea --- absl/container/internal/inlined_vector.h | 8 ++++---- ci/macos_xcode_bazel.sh | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index cd34a413..2baf26f3 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -925,8 +925,8 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { inlined_ptr->GetSize()); } ABSL_INTERNAL_CATCH_ANY { - allocated_ptr->SetAllocation( - {allocated_storage_view.data, allocated_storage_view.capacity}); + allocated_ptr->SetAllocation(Allocation{ + allocated_storage_view.data, allocated_storage_view.capacity}); ABSL_INTERNAL_RETHROW; } @@ -934,8 +934,8 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { inlined_ptr->GetInlinedData(), inlined_ptr->GetSize()); - inlined_ptr->SetAllocation( - {allocated_storage_view.data, allocated_storage_view.capacity}); + inlined_ptr->SetAllocation(Allocation{allocated_storage_view.data, + allocated_storage_view.capacity}); } swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); diff --git a/ci/macos_xcode_bazel.sh b/ci/macos_xcode_bazel.sh index 93fb03cd..1e29311f 100755 --- a/ci/macos_xcode_bazel.sh +++ b/ci/macos_xcode_bazel.sh @@ -32,6 +32,13 @@ else BAZEL_BIN="bazel" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then + BAZEL_EXTRA_ARGS="--distdir=${KOKORO_GFILE_DIR}/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + # Print the compiler and Bazel versions. echo "---------------" gcc -v @@ -52,4 +59,5 @@ ${BAZEL_BIN} test ... \ --show_timestamps \ --test_env="TZDIR=${ABSEIL_ROOT}/absl/time/internal/cctz/testdata/zoneinfo" \ --test_output=errors \ - --test_tag_filters=-benchmark + --test_tag_filters=-benchmark \ + ${BAZEL_EXTRA_ARGS:-} -- cgit v1.2.3 From 36db0e4b695756c948dbb45568b43a62ba5883ec Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 3 Feb 2022 08:46:25 -0800 Subject: Export of internal Abseil changes -- 4409b08e103d6e7041d18a4d431290cafe3650cf by Derek Mauro : Workaround NVCC compile error in StringConstant Based on a patch in TensorFlow: https://github.com/tensorflow/tensorflow/blob/da83132aba8d2b6a3c3a9b2e662868eb24f4dd1e/third_party/absl/com_google_absl_fix_mac_and_nvcc_build.patch#L262-L282 Fixes #1105 PiperOrigin-RevId: 426156316 -- 25db16567ffc5400dfaa30b567398ede84729687 by Abseil Team : Only look for Elf64_Auxinfo on 64-bit FreeBSD. PiperOrigin-RevId: 426132251 -- 2e73c3d9df59b2b769d2b8dca97f0ca5c512c72a by Abseil Team : Add a problem hint to the error message when dereferencing the end() iterator. PiperOrigin-RevId: 426120394 -- 6befbf89c47963656b9e8151166ab4c8446d4785 by Martijn Vels : Make Cord Btree the default and remove opt out machinery This change makes btree the default Cord format and removes the machinery to opt out. Subsequent changes will remove the 'true' constant evaluation and effectively cleanup all old code and references to CONCAT. PiperOrigin-RevId: 426119728 -- f6a0a664029d61811d90bd484f4eefa0400b5dd4 by Abseil Team : Mark Notification::HasBeenNotified as ABSL_MUST_USE_RESULT PiperOrigin-RevId: 425927033 GitOrigin-RevId: 4409b08e103d6e7041d18a4d431290cafe3650cf Change-Id: I86f1052c63c13c6486baf4108de2554f162f9c40 --- absl/container/internal/raw_hash_set.h | 16 +++++--- absl/debugging/internal/vdso_support.cc | 2 + absl/strings/cord.cc | 5 +-- absl/strings/cord_test.cc | 73 +-------------------------------- absl/strings/internal/cord_internal.cc | 1 - absl/strings/internal/cord_internal.h | 6 --- absl/strings/internal/string_constant.h | 8 +++- absl/synchronization/notification.h | 3 +- 8 files changed, 24 insertions(+), 90 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 93a3fa85..7409d5ec 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -535,15 +535,19 @@ size_t SelectBucketCountForIterRange(InputIter first, InputIter last, } inline void AssertIsFull(ctrl_t* ctrl) { - ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && - "Invalid operation on iterator. The element might have " - "been erased, or the table might have rehashed."); + ABSL_HARDENING_ASSERT( + (ctrl != nullptr && IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, the table might have rehashed, or this may " + "be an end() iterator."); } inline void AssertIsValid(ctrl_t* ctrl) { - ABSL_HARDENING_ASSERT((ctrl == nullptr || IsFull(*ctrl)) && - "Invalid operation on iterator. The element might have " - "been erased, or the table might have rehashed."); + ABSL_HARDENING_ASSERT( + (ctrl == nullptr || IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, the table might have rehashed, or this may " + "be an end() iterator."); } struct FindInfo { diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index 977a9f6b..8a015d55 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -51,7 +51,9 @@ #endif #if defined(__FreeBSD__) +#if defined(__ELF_WORD_SIZE) && __ELF_WORD_SIZE == 64 using Elf64_auxv_t = Elf64_Auxinfo; +#endif using Elf32_auxv_t = Elf32_Auxinfo; #endif diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index ddd14ef4..59722107 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -96,10 +96,7 @@ static constexpr uint64_t min_length[] = { static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length); -static inline bool btree_enabled() { - return cord_internal::cord_btree_enabled.load( - std::memory_order_relaxed); -} +static inline constexpr bool btree_enabled() { return true; } static inline bool IsRootBalanced(CordRep* node) { if (!node->IsConcat()) { diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index e499b55f..c26e506d 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -231,15 +231,7 @@ ABSL_NAMESPACE_END // and with our without expected CRCs being set on the subject Cords. class CordTest : public testing::TestWithParam { public: - CordTest() : was_btree_(absl::cord_internal::cord_btree_enabled.load()) { - absl::cord_internal::cord_btree_enabled.store(UseBtree()); - } - ~CordTest() override { - absl::cord_internal::cord_btree_enabled.store(was_btree_); - } - // Returns true if test is running with btree enabled. - bool UseBtree() const { return GetParam() == 1 || GetParam() == 3; } bool UseCrc() const { return GetParam() == 2 || GetParam() == 3; } void MaybeHarden(absl::Cord& c) { if (UseCrc()) { @@ -255,27 +247,19 @@ class CordTest : public testing::TestWithParam { static std::string ToString(testing::TestParamInfo param) { switch (param.param) { case 0: - return "Concat"; - case 1: return "Btree"; - case 2: - return "ConcatHardened"; - case 3: + case 1: return "BtreeHardened"; default: assert(false); return "???"; } } - - private: - const bool was_btree_; }; -INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1, 2, 3), +INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Values(0, 1), CordTest::ToString); - TEST(CordRepFlat, AllFlatCapacities) { // Explicitly and redundantly assert built-in min/max limits static_assert(absl::cord_internal::kFlatOverhead < 32, ""); @@ -1561,8 +1545,6 @@ TEST(CordTest, CordMemoryUsageFlatHardenedAndShared) { } TEST(CordTest, CordMemoryUsageBTree) { - absl::cord_internal::enable_cord_btree(true); - absl::Cord cord1; size_t flats1_size = 0; absl::Cord flats1[4] = {MakeCord(1000, 'a'), MakeCord(1100, 'a'), @@ -1611,57 +1593,6 @@ TEST(CordTest, CordMemoryUsageBTree) { rep2_size); } -TEST(CordTest, CordMemoryUsageConcat) { - absl::cord_internal::enable_cord_btree(false); - - absl::Cord cord1; - size_t flats1_size = 0; - absl::Cord flats1[4] = {MakeCord(1000, 'a'), MakeCord(1100, 'a'), - MakeCord(1200, 'a'), MakeCord(1300, 'a')}; - for (absl::Cord flat : flats1) { - flats1_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize(); - cord1.Append(std::move(flat)); - } - - // Make sure the created cord is a CONCAT tree. Under some builds such as - // windows DLL, we may have ODR like effects on the flag, meaning the DLL - // code will run with the picked up default. - if (!absl::CordTestPeer::Tree(cord1)->IsConcat()) { - ABSL_RAW_LOG(WARNING, "Cord library code not respecting btree flag"); - return; - } - - size_t rep1_size = sizeof(CordRepConcat) * 3 + flats1_size; - size_t rep1_shared_size = sizeof(CordRepConcat) * 3 + flats1_size / 2; - - EXPECT_EQ(cord1.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep1_size); - EXPECT_EQ(cord1.EstimatedMemoryUsage(kFairShare), - sizeof(absl::Cord) + rep1_shared_size); - - absl::Cord cord2; - size_t flats2_size = 0; - absl::Cord flats2[4] = {MakeCord(600, 'a'), MakeCord(700, 'a'), - MakeCord(800, 'a'), MakeCord(900, 'a')}; - for (absl::Cord& flat : flats2) { - flats2_size += absl::CordTestPeer::Tree(flat)->flat()->AllocatedSize(); - cord2.Append(std::move(flat)); - } - - size_t rep2_size = sizeof(CordRepConcat) * 3 + flats2_size; - - EXPECT_EQ(cord2.EstimatedMemoryUsage(), sizeof(absl::Cord) + rep2_size); - EXPECT_EQ(cord2.EstimatedMemoryUsage(kFairShare), - sizeof(absl::Cord) + rep2_size); - - absl::Cord cord(cord1); - cord.Append(std::move(cord2)); - EXPECT_EQ(cord.EstimatedMemoryUsage(), - sizeof(absl::Cord) + sizeof(CordRepConcat) + rep1_size + rep2_size); - EXPECT_EQ(cord.EstimatedMemoryUsage(kFairShare), - sizeof(absl::Cord) + sizeof(CordRepConcat) + rep1_shared_size / 2 + - rep2_size); -} - // Regtest for a change that had to be rolled back because it expanded out // of the InlineRep too soon, which was observable through MemoryUsage(). TEST_P(CordTest, CordMemoryUsageInlineRep) { diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index c9ceac90..feb3bec1 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -27,7 +27,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -ABSL_CONST_INIT std::atomic cord_btree_enabled(kCordEnableBtreeDefault); ABSL_CONST_INIT std::atomic cord_ring_buffer_enabled( kCordEnableRingBufferDefault); ABSL_CONST_INIT std::atomic shallow_subcords_enabled( diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 776d1c40..b9ecbba6 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -37,12 +37,10 @@ class CordzInfo; // Default feature enable states for cord ring buffers enum CordFeatureDefaults { - kCordEnableBtreeDefault = true, kCordEnableRingBufferDefault = false, kCordShallowSubcordsDefault = false }; -extern std::atomic cord_btree_enabled; extern std::atomic cord_ring_buffer_enabled; extern std::atomic shallow_subcords_enabled; @@ -52,10 +50,6 @@ extern std::atomic shallow_subcords_enabled; // O(n^2) complexity as recursive / full tree validation is O(n). extern std::atomic cord_btree_exhaustive_validation; -inline void enable_cord_btree(bool enable) { - cord_btree_enabled.store(enable, std::memory_order_relaxed); -} - inline void enable_cord_ring_buffer(bool enable) { cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed); } diff --git a/absl/strings/internal/string_constant.h b/absl/strings/internal/string_constant.h index a11336b7..b358efdd 100644 --- a/absl/strings/internal/string_constant.h +++ b/absl/strings/internal/string_constant.h @@ -35,12 +35,18 @@ namespace strings_internal { // below. template struct StringConstant { + private: + static constexpr bool TryConstexprEval(absl::string_view view) { + return view.empty() || 2 * view[0] != 1; + } + + public: static constexpr absl::string_view value = T{}(); constexpr absl::string_view operator()() const { return value; } // Check to be sure `view` points to constant data. // Otherwise, it can't be constant evaluated. - static_assert(value.empty() || 2 * value[0] != 1, + static_assert(TryConstexprEval(value), "The input string_view must point to constant data."); }; diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h index 429968da..4bec2689 100644 --- a/absl/synchronization/notification.h +++ b/absl/synchronization/notification.h @@ -52,6 +52,7 @@ #include +#include "absl/base/attributes.h" #include "absl/base/macros.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" @@ -74,7 +75,7 @@ class Notification { // Notification::HasBeenNotified() // // Returns the value of the notification's internal "notified" state. - bool HasBeenNotified() const { + ABSL_MUST_USE_RESULT bool HasBeenNotified() const { return HasBeenNotifiedInternal(&this->notified_yet_); } -- cgit v1.2.3 From 73316fc3c565e5998983b0fb502d938ccddcded2 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 7 Feb 2022 09:07:44 -0800 Subject: Export of internal Abseil changes -- 262f6992ee0553782531ad58dd7d8bcd6d0b7bb7 by Martijn Vels : Remove CordRepConcat uses in tests and CONCAT constants. This change removes the creation and uses of CordRepConcat in unit tests, and replaces some remaining CONCAT constants with IsConcat() to make subsequent teardown of CordrepConcat uses easier. PiperOrigin-RevId: 426932694 -- fd590f7e849ba8c5395c589ed3f762cd00affdc8 by Abseil Team : Replace NULL and 0 with nullptr. PiperOrigin-RevId: 426912932 -- 183e5babc478748023f732a1608745a2a544c996 by Abseil Team : Replace "span of time" with "amount of time" in the first comment line, to make it clearer that we're not talking about a duration that starts at a specific time. PiperOrigin-RevId: 426691794 -- e28ca760f21505f1be55c7af336eb61d2f1b7fea by Martijn Vels : Add extra assertions on CordRepSubstring child nodes PiperOrigin-RevId: 426369104 GitOrigin-RevId: 262f6992ee0553782531ad58dd7d8bcd6d0b7bb7 Change-Id: I7ffdaa2dc999a2117944c9abdb1565b6b6f48b59 --- absl/base/internal/low_level_alloc_test.cc | 11 +- absl/strings/BUILD.bazel | 30 ---- absl/strings/CMakeLists.txt | 34 ---- absl/strings/cord_analysis.cc | 2 +- absl/strings/cord_ring_test.cc | 41 ----- absl/strings/internal/cord_internal.cc | 2 +- absl/strings/internal/cord_rep_btree.cc | 1 + absl/strings/internal/cord_rep_btree_navigator.cc | 1 + absl/strings/internal/cord_rep_btree_test.cc | 50 ------ absl/strings/internal/cord_rep_concat_test.cc | 143 ----------------- absl/strings/internal/cord_rep_consume.cc | 2 +- absl/strings/internal/cord_rep_consume_test.cc | 173 --------------------- absl/strings/internal/cord_rep_test_util.h | 14 +- absl/strings/internal/cordz_info.cc | 6 +- .../strings/internal/cordz_info_statistics_test.cc | 102 +----------- absl/time/time.h | 2 +- 16 files changed, 22 insertions(+), 592 deletions(-) delete mode 100644 absl/strings/internal/cord_rep_concat_test.cc delete mode 100644 absl/strings/internal/cord_rep_consume_test.cc diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc index 31abb888..8fdec09e 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc @@ -86,7 +86,7 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) { AllocMap::iterator it; BlockDesc block_desc; int rnd; - LowLevelAlloc::Arena *arena = 0; + LowLevelAlloc::Arena *arena = nullptr; if (use_new_arena) { int32_t flags = call_malloc_hook ? LowLevelAlloc::kCallMallocHook : 0; arena = LowLevelAlloc::NewArena(flags); @@ -101,11 +101,10 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) { case 0: // coin came up heads: add a block using_low_level_alloc = true; block_desc.len = rand() & 0x3fff; - block_desc.ptr = - reinterpret_cast( - arena == 0 - ? LowLevelAlloc::Alloc(block_desc.len) - : LowLevelAlloc::AllocWithArena(block_desc.len, arena)); + block_desc.ptr = reinterpret_cast( + arena == nullptr + ? LowLevelAlloc::Alloc(block_desc.len) + : LowLevelAlloc::AllocWithArena(block_desc.len, arena)); using_low_level_alloc = false; RandomizeBlockDesc(&block_desc); rnd = rand(); diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 0dc667e0..617577cd 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -308,20 +308,6 @@ cc_library( ], ) -cc_test( - name = "cord_rep_concat_test", - size = "small", - srcs = ["internal/cord_rep_concat_test.cc"], - copts = ABSL_TEST_COPTS, - visibility = ["//visibility:private"], - deps = [ - ":cord_internal", - ":cord_rep_test_util", - "//absl/base:config", - "@com_google_googletest//:gtest_main", - ], -) - cc_test( name = "cord_rep_btree_test", size = "medium", @@ -758,22 +744,6 @@ cc_test( ], ) -cc_test( - name = "cord_rep_consume_test", - size = "medium", - srcs = ["internal/cord_rep_consume_test.cc"], - copts = ABSL_TEST_COPTS, - visibility = ["//visibility:private"], - deps = [ - ":cord_internal", - ":strings", - "//absl/base:config", - "//absl/base:core_headers", - "//absl/debugging:leak_check", - "@com_google_googletest//:gtest_main", - ], -) - cc_test( name = "cord_ring_test", size = "medium", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 4dd605ec..e73a1ea0 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -933,40 +933,6 @@ absl_cc_test( GTest::gmock_main ) -absl_cc_test( - NAME - cord_rep_consume_test - SRCS - "internal/cord_rep_consume_test.cc" - COPTS - ${ABSL_TEST_COPTS} - DEPS - absl::base - absl::config - absl::cord_internal - absl::core_headers - absl::function_ref - absl::raw_logging_internal - absl::strings - GTest::gmock_main -) - -absl_cc_test( - NAME - cord_rep_concat_test - SRCS - "internal/cord_rep_concat_test.cc" - COPTS - ${ABSL_TEST_COPTS} - DEPS - absl::base - absl::config - absl::cord_internal - absl::cord_rep_test_util - absl::core_headers - GTest::gmock_main -) - absl_cc_test( NAME cord_rep_btree_test diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc index 435b4c94..c0d9ea79 100644 --- a/absl/strings/cord_analysis.cc +++ b/absl/strings/cord_analysis.cc @@ -211,7 +211,7 @@ size_t GetEstimatedUsage(const CordRep* rep) { AnalyzeDataEdge(repref, raw_usage); } else if (repref.rep->tag == BTREE) { AnalyzeBtree(repref, raw_usage); - } else if (repref.rep->tag == CONCAT) { + } else if (repref.rep->IsConcat()) { AnalyzeConcat(repref, raw_usage); } else if (repref.rep->tag == RING) { AnalyzeRing(repref, raw_usage); diff --git a/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc index f1318595..f39a0a4f 100644 --- a/absl/strings/cord_ring_test.cc +++ b/absl/strings/cord_ring_test.cc @@ -44,7 +44,6 @@ using ::absl::cord_internal::CordRepFlat; using ::absl::cord_internal::CordRepRing; using ::absl::cord_internal::CordRepSubstring; -using ::absl::cord_internal::CONCAT; using ::absl::cord_internal::EXTERNAL; using ::absl::cord_internal::SUBSTRING; @@ -262,16 +261,6 @@ CordRepSubstring* RemoveSuffix(size_t length, CordRep* rep) { return MakeSubstring(0, rep->length - length, rep); } -CordRepConcat* MakeConcat(CordRep* left, CordRep* right, int depth = 0) { - auto* concat = new CordRepConcat; - concat->tag = CONCAT; - concat->length = left->length + right->length; - concat->left = left; - concat->right = right; - concat->set_depth(depth); - return concat; -} - enum Composition { kMix, kAppend, kPrepend }; Composition RandomComposition() { @@ -296,7 +285,6 @@ constexpr const char* kFox = "The quick brown fox jumps over the lazy dog"; constexpr const char* kFoxFlats[] = {"The ", "quick ", "brown ", "fox ", "jumps ", "over ", "the ", "lazy ", "dog"}; -constexpr const char* kAlphabet = "abcdefghijklmnopqrstuvwxyz"; CordRepRing* FromFlats(Span flats, Composition composition = kAppend) { @@ -594,35 +582,6 @@ TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) { EXPECT_THAT(ToRawFlats(result), ElementsAre(str)); } -TEST_P(CordRingBuildInputTest, CreateFromConcat) { - CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), - MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; - auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]); - auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3])); - auto* concat = RefIfInputShared(MakeConcat(left, right)); - CordRepRing* result = NeedsUnref(CordRepRing::Create(concat)); - ASSERT_THAT(result, IsValidRingBuffer()); - EXPECT_THAT(result->length, Eq(26)); - EXPECT_THAT(ToString(result), Eq(kAlphabet)); -} - -TEST_P(CordRingBuildInputTest, CreateFromSubstringConcat) { - for (size_t off = 0; off < 26; ++off) { - for (size_t len = 1; len < 26 - off; ++len) { - CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), - MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; - auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]); - auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3])); - auto* concat = MakeConcat(left, right); - auto* child = RefIfInputShared(MakeSubstring(off, len, concat)); - CordRepRing* result = NeedsUnref(CordRepRing::Create(child)); - ASSERT_THAT(result, IsValidRingBuffer()); - ASSERT_THAT(result->length, Eq(len)); - ASSERT_THAT(ToString(result), string_view(kAlphabet).substr(off, len)); - } - } -} - TEST_P(CordRingCreateTest, Properties) { absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1), 120)); diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index feb3bec1..75544191 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -39,7 +39,7 @@ void CordRep::Destroy(CordRep* rep) { absl::InlinedVector pending; while (true) { assert(!rep->refcount.IsImmortal()); - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { CordRepConcat* rep_concat = rep->concat(); CordRep* right = rep_concat->right; if (!right->refcount.Decrement()) { diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index f436bc17..3ebd9f6d 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -119,6 +119,7 @@ CordRepSubstring* CreateSubstring(CordRep* rep, size_t offset, size_t n) { rep = CordRep::Ref(substring->child); CordRep::Unref(substring); } + assert(rep->IsExternal() || rep->IsFlat()); CordRepSubstring* substring = new CordRepSubstring(); substring->length = n; substring->tag = SUBSTRING; diff --git a/absl/strings/internal/cord_rep_btree_navigator.cc b/absl/strings/internal/cord_rep_btree_navigator.cc index d1f9995d..19afbe90 100644 --- a/absl/strings/internal/cord_rep_btree_navigator.cc +++ b/absl/strings/internal/cord_rep_btree_navigator.cc @@ -49,6 +49,7 @@ inline CordRep* Substring(CordRep* rep, size_t offset, size_t n) { rep = rep->substring()->child; } + assert(rep->IsExternal() || rep->IsFlat()); CordRepSubstring* substring = new CordRepSubstring(); substring->length = n; substring->tag = SUBSTRING; diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc index 1d0c68b2..1d56b252 100644 --- a/absl/strings/internal/cord_rep_btree_test.cc +++ b/absl/strings/internal/cord_rep_btree_test.cc @@ -52,7 +52,6 @@ using ::absl::cordrep_testing::CordToString; using ::absl::cordrep_testing::CordVisitReps; using ::absl::cordrep_testing::CreateFlatsFromString; using ::absl::cordrep_testing::CreateRandomString; -using ::absl::cordrep_testing::MakeConcat; using ::absl::cordrep_testing::MakeExternal; using ::absl::cordrep_testing::MakeFlat; using ::absl::cordrep_testing::MakeSubstring; @@ -322,7 +321,6 @@ TEST(CordRepBtreeTest, EdgeData) { CordRepExternal* external = MakeExternal("Hello external"); CordRep* substr1 = MakeSubstring(1, 6, CordRep::Ref(flat)); CordRep* substr2 = MakeSubstring(1, 6, CordRep::Ref(external)); - CordRep* concat = MakeConcat(CordRep::Ref(flat), CordRep::Ref(external)); CordRep* bad_substr = MakeSubstring(1, 2, CordRep::Ref(substr1)); EXPECT_TRUE(CordRepBtree::IsDataEdge(flat)); @@ -345,17 +343,13 @@ TEST(CordRepBtreeTest, EdgeData) { TypedEq(external->base + 1)); EXPECT_THAT(CordRepBtree::EdgeData(substr2), Eq("ello e")); - EXPECT_FALSE(CordRepBtree::IsDataEdge(concat)); EXPECT_FALSE(CordRepBtree::IsDataEdge(bad_substr)); #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) - EXPECT_DEATH(CordRepBtree::EdgeData(concat), ".*"); - EXPECT_DEATH(CordRepBtree::EdgeDataPtr(concat), ".*"); EXPECT_DEATH(CordRepBtree::EdgeData(bad_substr), ".*"); EXPECT_DEATH(CordRepBtree::EdgeDataPtr(bad_substr), ".*"); #endif CordRep::Unref(bad_substr); - CordRep::Unref(concat); CordRep::Unref(substr2); CordRep::Unref(substr1); CordRep::Unref(external); @@ -1006,50 +1000,6 @@ TEST_P(CordRepBtreeTest, AddLargeDataToLeaf) { } } -TEST_P(CordRepBtreeDualTest, CreateFromConcat) { - AutoUnref refs; - CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), - MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; - auto* left = MakeConcat(flats[0], flats[1]); - auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); - auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); - CordRepBtree* result = CordRepBtree::Create(concat); - ASSERT_TRUE(CordRepBtree::IsValid(result)); - EXPECT_THAT(result->length, Eq(26)); - EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); - CordRep::Unref(result); -} - -TEST_P(CordRepBtreeDualTest, AppendConcat) { - AutoUnref refs; - CordRep* flats[] = {MakeFlat("defgh"), MakeFlat("ijklm"), - MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; - auto* left = MakeConcat(flats[0], flats[1]); - auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); - auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); - CordRepBtree* result = CordRepBtree::Create(MakeFlat("abc")); - result = CordRepBtree::Append(result, concat); - ASSERT_TRUE(CordRepBtree::IsValid(result)); - EXPECT_THAT(result->length, Eq(26)); - EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); - CordRep::Unref(result); -} - -TEST_P(CordRepBtreeDualTest, PrependConcat) { - AutoUnref refs; - CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), - MakeFlat("nopqrstuv"), MakeFlat("wx")}; - auto* left = MakeConcat(flats[0], flats[1]); - auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); - auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); - CordRepBtree* result = CordRepBtree::Create(MakeFlat("yz")); - result = CordRepBtree::Prepend(result, concat); - ASSERT_TRUE(CordRepBtree::IsValid(result)); - EXPECT_THAT(result->length, Eq(26)); - EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); - CordRep::Unref(result); -} - TEST_P(CordRepBtreeTest, CreateFromTreeReturnsTree) { AutoUnref refs; CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("Hello world")); diff --git a/absl/strings/internal/cord_rep_concat_test.cc b/absl/strings/internal/cord_rep_concat_test.cc deleted file mode 100644 index 32bab975..00000000 --- a/absl/strings/internal/cord_rep_concat_test.cc +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2021 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/config.h" -#include "absl/strings/internal/cord_internal.h" -#include "absl/strings/internal/cord_rep_test_util.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { -namespace { - -using ::absl::cordrep_testing::MakeFlat; -using ::absl::cordrep_testing::MakeSubstring; -using ::testing::Eq; - -MATCHER_P2(EqExtractResult, tree, rep, "Equals ExtractResult") { - if (arg.tree != tree || arg.extracted != rep) { - *result_listener << "Expected {" << static_cast(tree) << ", " - << static_cast(rep) << "}, got {" << arg.tree - << ", " << arg.extracted << "}"; - return false; - } - return true; -} - -CordRepConcat* MakeConcat(CordRep* left, CordRep* right, int depth) { - CordRepConcat* concat = new CordRepConcat; - concat->tag = CONCAT; - concat->set_depth(depth); - concat->length = left->length + right->length; - concat->left = left; - concat->right = right; - return concat; -} - -CordRepConcat::ExtractResult ExtractLast(CordRepConcat* concat, - size_t extra_capacity = 1) { - return CordRepConcat::ExtractAppendBuffer(concat, extra_capacity); -} - -TEST(CordRepConcatTest, ExtractAppendBufferTwoFlats) { - CordRepFlat* flat1 = MakeFlat("abc"); - CordRepFlat* flat2 = MakeFlat("defg"); - CordRepConcat* concat = MakeConcat(flat1, flat2, 0); - EXPECT_THAT(ExtractLast(concat), EqExtractResult(flat1, flat2)); - CordRep::Unref(flat1); - CordRep::Unref(flat2); -} - -TEST(CordRepConcatTest, ExtractAppendBufferThreeFlatsOne) { - CordRepFlat* flat1 = MakeFlat("abc"); - CordRepFlat* flat2 = MakeFlat("defg"); - CordRepFlat* flat3 = MakeFlat("hijkl"); - CordRepConcat* lconcat = MakeConcat(flat1, flat2, 0); - CordRepConcat* concat = MakeConcat(lconcat, flat3, 1); - EXPECT_THAT(ExtractLast(concat), EqExtractResult(lconcat, flat3)); - ASSERT_THAT(lconcat->length, Eq(7)); - CordRep::Unref(lconcat); - CordRep::Unref(flat3); -} - -TEST(CordRepConcatTest, ExtractAppendBufferThreeFlatsTwo) { - CordRepFlat* flat1 = MakeFlat("hijkl"); - CordRepFlat* flat2 = MakeFlat("abc"); - CordRepFlat* flat3 = MakeFlat("defg"); - CordRepConcat* rconcat = MakeConcat(flat2, flat3, 0); - CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); - EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, flat3)); - ASSERT_THAT(concat->length, Eq(8)); - CordRep::Unref(concat); - CordRep::Unref(flat3); -} - -TEST(CordRepConcatTest, ExtractAppendBufferShared) { - CordRepFlat* flat1 = MakeFlat("hijkl"); - CordRepFlat* flat2 = MakeFlat("abc"); - CordRepFlat* flat3 = MakeFlat("defg"); - CordRepConcat* rconcat = MakeConcat(flat2, flat3, 0); - CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); - - CordRep::Ref(concat); - EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); - CordRep::Unref(concat); - - CordRep::Ref(rconcat); - EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); - CordRep::Unref(rconcat); - - CordRep::Ref(flat3); - EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); - CordRep::Unref(flat3); - - CordRep::Unref(concat); -} - -TEST(CordRepConcatTest, ExtractAppendBufferNotFlat) { - CordRepFlat* flat1 = MakeFlat("hijkl"); - CordRepFlat* flat2 = MakeFlat("abc"); - CordRepFlat* flat3 = MakeFlat("defg"); - auto substr = MakeSubstring(1, 2, flat3); - CordRepConcat* rconcat = MakeConcat(flat2, substr, 0); - CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); - EXPECT_THAT(ExtractLast(concat), EqExtractResult(concat, nullptr)); - CordRep::Unref(concat); -} - -TEST(CordRepConcatTest, ExtractAppendBufferNoCapacity) { - CordRepFlat* flat1 = MakeFlat("hijkl"); - CordRepFlat* flat2 = MakeFlat("abc"); - CordRepFlat* flat3 = MakeFlat("defg"); - size_t avail = flat3->Capacity() - flat3->length; - CordRepConcat* rconcat = MakeConcat(flat2, flat3, 0); - CordRepConcat* concat = MakeConcat(flat1, rconcat, 1); - - // Should fail if 1 byte over, success if exactly matching - EXPECT_THAT(ExtractLast(concat, avail + 1), EqExtractResult(concat, nullptr)); - EXPECT_THAT(ExtractLast(concat, avail), EqExtractResult(concat, flat3)); - - CordRep::Unref(concat); - CordRep::Unref(flat3); -} - -} // namespace -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/strings/internal/cord_rep_consume.cc b/absl/strings/internal/cord_rep_consume.cc index 81514543..253339be 100644 --- a/absl/strings/internal/cord_rep_consume.cc +++ b/absl/strings/internal/cord_rep_consume.cc @@ -66,7 +66,7 @@ void Consume(bool forward, CordRep* rep, ConsumeFn consume_fn) { absl::InlinedVector stack; for (;;) { - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { std::array res = ClipConcat(rep->concat()); CordRep* left = res[0]; CordRep* right = res[1]; diff --git a/absl/strings/internal/cord_rep_consume_test.cc b/absl/strings/internal/cord_rep_consume_test.cc deleted file mode 100644 index e507824b..00000000 --- a/absl/strings/internal/cord_rep_consume_test.cc +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2021 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/strings/internal/cord_rep_consume.h" - -#include -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/internal/cord_internal.h" -#include "absl/strings/internal/cord_rep_flat.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { -namespace { - -using testing::InSequence; -using testing::MockFunction; - -// Returns the depth of a node -int Depth(const CordRep* rep) { - return (rep->tag == CONCAT) ? rep->concat()->depth() : 0; -} - -// Creates a concatenation of the specified nodes. -CordRepConcat* CreateConcat(CordRep* left, CordRep* right) { - auto* concat = new CordRepConcat(); - concat->tag = CONCAT; - concat->left = left; - concat->right = right; - concat->length = left->length + right->length; - concat->set_depth(1 + (std::max)(Depth(left), Depth(right))); - return concat; -} - -// Creates a flat with the length set to `length` -CordRepFlat* CreateFlatWithLength(size_t length) { - auto* flat = CordRepFlat::New(length); - flat->length = length; - return flat; -} - -// Creates a substring node on the specified child. -CordRepSubstring* CreateSubstring(CordRep* child, size_t start, size_t length) { - auto* rep = new CordRepSubstring(); - rep->length = length; - rep->tag = SUBSTRING; - rep->start = start; - rep->child = child; - return rep; -} - -// Flats we use in the tests -CordRep* flat[6]; - -// Creates a test tree -CordRep* CreateTestTree() { - flat[0] = CreateFlatWithLength(1); - flat[1] = CreateFlatWithLength(7); - CordRepConcat* left = CreateConcat(flat[0], CreateSubstring(flat[1], 2, 4)); - - flat[2] = CreateFlatWithLength(9); - flat[3] = CreateFlatWithLength(13); - CordRepConcat* right1 = CreateConcat(flat[2], flat[3]); - - flat[4] = CreateFlatWithLength(15); - flat[5] = CreateFlatWithLength(19); - CordRepConcat* right2 = CreateConcat(flat[4], flat[5]); - - CordRepConcat* right = CreateConcat(right1, CreateSubstring(right2, 5, 17)); - return CreateConcat(left, right); -} - -TEST(CordRepConsumeTest, Consume) { - InSequence in_sequence; - CordRep* tree = CreateTestTree(); - MockFunction consume; - EXPECT_CALL(consume, Call(flat[0], 0, 1)); - EXPECT_CALL(consume, Call(flat[1], 2, 4)); - EXPECT_CALL(consume, Call(flat[2], 0, 9)); - EXPECT_CALL(consume, Call(flat[3], 0, 13)); - EXPECT_CALL(consume, Call(flat[4], 5, 10)); - EXPECT_CALL(consume, Call(flat[5], 0, 7)); - Consume(tree, consume.AsStdFunction()); - for (CordRep* rep : flat) { - EXPECT_TRUE(rep->refcount.IsOne()); - CordRep::Unref(rep); - } -} - -TEST(CordRepConsumeTest, ConsumeShared) { - InSequence in_sequence; - CordRep* tree = CreateTestTree(); - MockFunction consume; - EXPECT_CALL(consume, Call(flat[0], 0, 1)); - EXPECT_CALL(consume, Call(flat[1], 2, 4)); - EXPECT_CALL(consume, Call(flat[2], 0, 9)); - EXPECT_CALL(consume, Call(flat[3], 0, 13)); - EXPECT_CALL(consume, Call(flat[4], 5, 10)); - EXPECT_CALL(consume, Call(flat[5], 0, 7)); - Consume(CordRep::Ref(tree), consume.AsStdFunction()); - for (CordRep* rep : flat) { - EXPECT_FALSE(rep->refcount.IsOne()); - CordRep::Unref(rep); - } - CordRep::Unref(tree); -} - -TEST(CordRepConsumeTest, Reverse) { - InSequence in_sequence; - CordRep* tree = CreateTestTree(); - MockFunction consume; - EXPECT_CALL(consume, Call(flat[5], 0, 7)); - EXPECT_CALL(consume, Call(flat[4], 5, 10)); - EXPECT_CALL(consume, Call(flat[3], 0, 13)); - EXPECT_CALL(consume, Call(flat[2], 0, 9)); - EXPECT_CALL(consume, Call(flat[1], 2, 4)); - EXPECT_CALL(consume, Call(flat[0], 0, 1)); - ReverseConsume(tree, consume.AsStdFunction()); - for (CordRep* rep : flat) { - EXPECT_TRUE(rep->refcount.IsOne()); - CordRep::Unref(rep); - } -} - -TEST(CordRepConsumeTest, ReverseShared) { - InSequence in_sequence; - CordRep* tree = CreateTestTree(); - MockFunction consume; - EXPECT_CALL(consume, Call(flat[5], 0, 7)); - EXPECT_CALL(consume, Call(flat[4], 5, 10)); - EXPECT_CALL(consume, Call(flat[3], 0, 13)); - EXPECT_CALL(consume, Call(flat[2], 0, 9)); - EXPECT_CALL(consume, Call(flat[1], 2, 4)); - EXPECT_CALL(consume, Call(flat[0], 0, 1)); - ReverseConsume(CordRep::Ref(tree), consume.AsStdFunction()); - for (CordRep* rep : flat) { - EXPECT_FALSE(rep->refcount.IsOne()); - CordRep::Unref(rep); - } - CordRep::Unref(tree); -} - -TEST(CordRepConsumeTest, UnreachableFlat) { - InSequence in_sequence; - CordRepFlat* flat1 = CreateFlatWithLength(10); - CordRepFlat* flat2 = CreateFlatWithLength(20); - CordRepConcat* concat = CreateConcat(flat1, flat2); - CordRepSubstring* tree = CreateSubstring(concat, 15, 10); - MockFunction consume; - EXPECT_CALL(consume, Call(flat2, 5, 10)); - Consume(tree, consume.AsStdFunction()); - EXPECT_TRUE(flat2->refcount.IsOne()); - CordRep::Unref(flat2); -} - -} // namespace -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/strings/internal/cord_rep_test_util.h b/absl/strings/internal/cord_rep_test_util.h index ad828af2..692d7db5 100644 --- a/absl/strings/internal/cord_rep_test_util.h +++ b/absl/strings/internal/cord_rep_test_util.h @@ -42,18 +42,6 @@ inline cord_internal::CordRepSubstring* MakeSubstring( return sub; } -inline cord_internal::CordRepConcat* MakeConcat(cord_internal::CordRep* left, - cord_internal::CordRep* right, - int depth = 0) { - auto* concat = new cord_internal::CordRepConcat; - concat->tag = cord_internal::CONCAT; - concat->length = left->length + right->length; - concat->left = left; - concat->right = right; - concat->set_depth(depth); - return concat; -} - inline cord_internal::CordRepFlat* MakeFlat(absl::string_view value) { assert(value.length() <= cord_internal::kMaxFlatLength); auto* flat = cord_internal::CordRepFlat::New(value.length()); @@ -126,7 +114,7 @@ inline void CordVisitReps(cord_internal::CordRep* rep, Fn&& fn) { for (cord_internal::CordRep* edge : rep->btree()->Edges()) { CordVisitReps(edge, fn); } - } else if (rep->tag == cord_internal::CONCAT) { + } else if (rep->IsConcat()) { CordVisitReps(rep->concat()->left, fn); CordVisitReps(rep->concat()->right, fn); } diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 4d52a802..24605260 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -98,7 +98,7 @@ class CordRepAnalyzer { AnalyzeRing(repref); } else if (repref.rep->tag == BTREE) { AnalyzeBtree(repref); - } else if (repref.rep->tag == CONCAT) { + } else if (repref.rep->IsConcat()) { AnalyzeConcat(repref); } else { // We should have either a concat, btree, or ring node if not null. @@ -145,8 +145,8 @@ class CordRepAnalyzer { // Asserts that `rr.rep` is a concat node or null. static RepRef AssertConcat(RepRef repref) { const CordRep* rep = repref.rep; - assert(rep == nullptr || rep->tag == CONCAT); - return (rep != nullptr && rep->tag == CONCAT) ? repref : RepRef{nullptr, 0}; + assert(rep == nullptr || rep->IsConcat()); + return (rep != nullptr && rep->IsConcat()) ? repref : RepRef{nullptr, 0}; } // Counts a flat of the provide allocated size diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc index 5277c3c1..40f93dd3 100644 --- a/absl/strings/internal/cordz_info_statistics_test.cc +++ b/absl/strings/internal/cordz_info_statistics_test.cc @@ -76,16 +76,6 @@ CordRepSubstring* Substring(CordRep* rep) { return substring; } -// Creates a concat on the provided reps -CordRepConcat* Concat(CordRep* left, CordRep* right) { - auto* concat = new CordRepConcat; - concat->length = left->length + right->length; - concat->tag = CONCAT; - concat->left = left; - concat->right = right; - return concat; -} - // Reference count helper struct RefHelper { std::vector refs; @@ -158,7 +148,7 @@ double FairShareImpl(CordRep* rep, size_t ref) { rep->ring()->ForEach([&](CordRepRing::index_type i) { self += FairShareImpl(rep->ring()->entry_child(i), 1); }); - } else if (rep->tag == CONCAT) { + } else if (rep->IsConcat()) { self = SizeOf(rep->concat()); children = FairShareImpl(rep->concat()->left, ref) + FairShareImpl(rep->concat()->right, ref); @@ -307,80 +297,6 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) { EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); } -TEST(CordzInfoStatisticsTest, Concat) { - RefHelper ref; - auto* flat1 = Flat(300); - auto* flat2 = Flat(2000); - auto* concat = ref.NeedsUnref(Concat(flat1, flat2)); - - CordzStatistics expected; - expected.size = concat->length; - expected.estimated_memory_usage = - SizeOf(concat) + SizeOf(flat1) + SizeOf(flat2); - expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; - expected.node_count = 3; - expected.node_counts.flat = 2; - expected.node_counts.flat_512 = 1; - expected.node_counts.concat = 1; - - EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); -} - -TEST(CordzInfoStatisticsTest, DeepConcat) { - RefHelper ref; - auto* flat1 = Flat(300); - auto* flat2 = Flat(2000); - auto* flat3 = Flat(400); - auto* external = External(3000); - auto* substring = Substring(external); - auto* concat1 = Concat(flat1, flat2); - auto* concat2 = Concat(flat3, substring); - auto* concat = ref.NeedsUnref(Concat(concat1, concat2)); - - CordzStatistics expected; - expected.size = concat->length; - expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) + - SizeOf(flat2) + SizeOf(flat3) + - SizeOf(external) + SizeOf(substring); - expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; - - expected.node_count = 8; - expected.node_counts.flat = 3; - expected.node_counts.flat_512 = 2; - expected.node_counts.external = 1; - expected.node_counts.concat = 3; - expected.node_counts.substring = 1; - - EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); -} - -TEST(CordzInfoStatisticsTest, DeepSharedConcat) { - RefHelper ref; - auto* flat1 = Flat(40); - auto* flat2 = ref.Ref(Flat(2000), 4); - auto* flat3 = Flat(70); - auto* external = ref.Ref(External(3000)); - auto* substring = ref.Ref(Substring(external), 3); - auto* concat1 = Concat(flat1, flat2); - auto* concat2 = Concat(flat3, substring); - auto* concat = ref.Ref(ref.NeedsUnref(Concat(concat1, concat2))); - - CordzStatistics expected; - expected.size = concat->length; - expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) + - SizeOf(flat2) + SizeOf(flat3) + - SizeOf(external) + SizeOf(substring); - expected.estimated_fair_share_memory_usage = FairShare(concat); - expected.node_count = 8; - expected.node_counts.flat = 3; - expected.node_counts.flat_64 = 1; - expected.node_counts.flat_128 = 1; - expected.node_counts.external = 1; - expected.node_counts.concat = 3; - expected.node_counts.substring = 1; - - EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); -} TEST(CordzInfoStatisticsTest, Ring) { RefHelper ref; @@ -539,19 +455,15 @@ TEST(CordzInfoStatisticsTest, BtreeNodeShared) { TEST(CordzInfoStatisticsTest, Crc) { RefHelper ref; auto* left = Flat(1000); - auto* right = Flat(1000); - auto* concat = Concat(left, right); - auto* crc = ref.NeedsUnref(CordRepCrc::New(concat, 12345)); + auto* crc = ref.NeedsUnref(CordRepCrc::New(left, 12345)); CordzStatistics expected; - expected.size = concat->length; - expected.estimated_memory_usage = - SizeOf(crc) + SizeOf(concat) + SizeOf(left) + SizeOf(right); + expected.size = left->length; + expected.estimated_memory_usage = SizeOf(crc) + SizeOf(left); expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; - expected.node_count = 4; - expected.node_counts.flat = 2; - expected.node_counts.flat_1k = 2; - expected.node_counts.concat = 1; + expected.node_count = 2; + expected.node_counts.flat = 1; + expected.node_counts.flat_1k = 1; expected.node_counts.crc = 1; EXPECT_THAT(SampleCord(crc), EqStatistics(expected)); diff --git a/absl/time/time.h b/absl/time/time.h index 5abd815a..61fa159b 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -120,7 +120,7 @@ using EnableIfFloat = // Duration // -// The `absl::Duration` class represents a signed, fixed-length span of time. +// The `absl::Duration` class represents a signed, fixed-length amount of time. // A `Duration` is generated using a unit-specific factory function, or is // the result of subtracting one `absl::Time` from another. Durations behave // like unit-safe integers and they support all the natural integer-like -- cgit v1.2.3 From c2ef7033380a3d8661fee76465097422170fb653 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 14 Feb 2022 01:26:08 -0800 Subject: Export of internal Abseil changes -- ceee18732f9499d3a53d46d5974f12ea0774b900 by Abseil Team : Remove division from the profile guided optimization PiperOrigin-RevId: 428444108 -- fc27059f1b0c0b4cb8ddd9a7a88220af52c0c755 by Evan Brown : Rename btree_node::leaf to is_leaf and also add is_internal for readability improvements. PiperOrigin-RevId: 428076422 -- 6a90d18477cc3a6de84282b6e38d6f294aa72748 by Evan Brown : In sanitizer mode, add generation integers to b-tree nodes and iterators and validate that iterators have not been invalidated already when they're used. Even though generation integers are stored in all nodes, we only use the one stored in the root node for validation. The reason we keep one in all the nodes is that nodes can become a root node after they are allocated. Also change the order of args in init_leaf to not violate the style guide. PiperOrigin-RevId: 428054226 -- ede4a0f676f43e7003fd2599c263d55222e760ba by Martijn Vels : Physically remove CordRepConcat This CL removes all uses of CordRepConcat. This change is executed by removing all the dead 'btree_enabled()' and 'IsConcat' branches, and all subsequent dead code. This change explicitly does not optimize any of the remaining code other than the most trivial ones such as removing 'stack' loop vars and loops. PiperOrigin-RevId: 428002308 -- 7cc83d96118149cf1aa1258a066b8fd4517df5f6 by Evan Brown : Change btree_iterator from a struct to a class. Motivation: btree_iterator has private members and invariants so it should be a class. Also merge two private sections. PiperOrigin-RevId: 427768836 -- 524d478b0af422e1a867a8823d9fbad149030360 by Martijn Vels : Physically block the creation of new CordRepConcat nodes. This change removes CordRepConcat creation, issuing a FATAL errors on the (practically impossible) call path on broken invariants. This change is deliberately limited in impact, subsequent changes will be more voluminous ripping out the (now dead) CordRepConcat code. PiperOrigin-RevId: 427741022 -- e21eb354c1bb358ea8b64d0e3fbb378e87b8b8c4 by Derek Mauro : Update the implementation of ABSL_DEPRECATED to work with GCC, and recommend using the standard attribute [[deprecated]] for C++14 and newer GCC users that are experiencing new warnings can silence them with -Wno-deprecated-declatations. GCC users that want to see the warnings but not error on them can use -Wno-error=deprecated-declarations. PiperOrigin-RevId: 427228952 -- 0ab4ee5660f3a072054dc4ab5056925c26977c7a by Laramie Leavitt : Change comment to avoid overflow. PiperOrigin-RevId: 427090218 GitOrigin-RevId: ceee18732f9499d3a53d46d5974f12ea0774b900 Change-Id: Ida00477b6a3d02a8b7bb467be7621b618385d1e9 --- CMake/AbseilDll.cmake | 1 - absl/base/attributes.h | 14 +- absl/container/BUILD.bazel | 1 + absl/container/CMakeLists.txt | 1 + absl/container/btree_test.cc | 51 +- absl/container/internal/btree.h | 562 +++++++++++++-------- absl/container/internal/btree_container.h | 2 + absl/random/mocking_bit_gen.h | 2 +- absl/strings/BUILD.bazel | 1 - absl/strings/CMakeLists.txt | 1 - absl/strings/cord.cc | 472 +---------------- absl/strings/cord.h | 1 - absl/strings/cord_analysis.cc | 41 -- absl/strings/internal/cord_internal.cc | 42 +- absl/strings/internal/cord_internal.h | 30 +- absl/strings/internal/cord_rep_concat.cc | 63 --- absl/strings/internal/cord_rep_consume.cc | 81 +-- absl/strings/internal/cord_rep_flat.h | 8 +- absl/strings/internal/cord_rep_test_util.h | 3 - absl/strings/internal/cordz_info.cc | 38 -- .../strings/internal/cordz_info_statistics_test.cc | 4 - 21 files changed, 450 insertions(+), 969 deletions(-) delete mode 100644 absl/strings/internal/cord_rep_concat.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 781388b9..14ce9d18 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -212,7 +212,6 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/cord_rep_btree_navigator.h" "strings/internal/cord_rep_btree_reader.cc" "strings/internal/cord_rep_btree_reader.h" - "strings/internal/cord_rep_concat.cc" "strings/internal/cord_rep_crc.cc" "strings/internal/cord_rep_crc.h" "strings/internal/cord_rep_consume.h" diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 91aabffe..5721356d 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -646,6 +646,9 @@ // declarations. The macro argument is used as a custom diagnostic message (e.g. // suggestion of a better alternative). // +// For code or headers that are assured to only build with C++14 and up, prefer +// just using the standard `[[deprecated("message")]]` directly over this macro. +// // Examples: // // class ABSL_DEPRECATED("Use Bar instead") Foo {...}; @@ -661,13 +664,12 @@ // }; // // Every usage of a deprecated entity will trigger a warning when compiled with -// clang's `-Wdeprecated-declarations` option. This option is turned off by -// default, but the warnings will be reported by clang-tidy. -#if defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L +// GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain +// turns this warning off by default, instead relying on clang-tidy to report +// new uses of deprecated code. +#if ABSL_HAVE_ATTRIBUTE(deprecated) #define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) -#endif - -#ifndef ABSL_DEPRECATED +#else #define ABSL_DEPRECATED(message) #endif diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 728c4be1..bc25bac9 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -914,6 +914,7 @@ cc_library( ":container_memory", ":layout", "//absl/base:core_headers", + "//absl/base:raw_logging_internal", "//absl/base:throw_delegate", "//absl/memory", "//absl/meta:type_traits", diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index b819deeb..648a3dba 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -35,6 +35,7 @@ absl_cc_library( absl::core_headers absl::layout absl::memory + absl::raw_logging_internal absl::strings absl::throw_delegate absl::type_traits diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index b2c3d73f..e829e0ba 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -1213,6 +1213,11 @@ class BtreeNodePeer { constexpr static bool UsesLinearNodeSearch() { return btree_node::use_linear_search::value; } + + template + constexpr static bool UsesGenerations() { + return Btree::params_type::kEnableGenerations; + } }; namespace { @@ -1478,8 +1483,10 @@ TEST(Btree, MovesComparisonsCopiesSwapsTracking) { EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode(), 61); EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode(), 100); if (sizeof(void *) == 8) { - EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode>(), - BtreeNodePeer::GetNumSlotsPerNode()); + EXPECT_EQ( + BtreeNodePeer::GetNumSlotsPerNode>(), + // When we have generations, there is one fewer slot. + BtreeNodePeer::UsesGenerations>() ? 60 : 61); } // Test key insertion/deletion in random order. @@ -1533,8 +1540,10 @@ TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) { EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode(), 61); EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode(), 100); if (sizeof(void *) == 8) { - EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode>(), - BtreeNodePeer::GetNumSlotsPerNode()); + EXPECT_EQ( + BtreeNodePeer::GetNumSlotsPerNode>(), + // When we have generations, there is one fewer slot. + BtreeNodePeer::UsesGenerations>() ? 60 : 61); } // Test key insertion/deletion in random order. @@ -3020,8 +3029,38 @@ TEST(Btree, InvalidComparatorsCaught) { } }; absl::btree_set set; - EXPECT_DEATH(set.insert({0, 1, 2}), - R"regex(lhs_comp_rhs < 0 -> rhs_comp_lhs > 0)regex"); + EXPECT_DEATH(set.insert({0, 1, 2}), "lhs_comp_rhs < 0 -> rhs_comp_lhs > 0"); + } +} +#endif + +#ifndef _MSC_VER +// This test crashes on MSVC. +TEST(Btree, InvalidIteratorUse) { + if (!BtreeNodePeer::UsesGenerations>()) + GTEST_SKIP() << "Generation validation for iterators is disabled."; + + { + absl::btree_set set; + for (int i = 0; i < 10; ++i) set.insert(i); + auto it = set.begin(); + set.erase(it++); + EXPECT_DEATH(set.erase(it++), "invalidated iterator"); + } + { + absl::btree_set set; + for (int i = 0; i < 10; ++i) set.insert(i); + auto it = set.insert(20).first; + set.insert(30); + EXPECT_DEATH(*it, "invalidated iterator"); + } + { + absl::btree_set set; + for (int i = 0; i < 10000; ++i) set.insert(i); + auto it = set.find(5000); + ASSERT_NE(it, set.end()); + set.erase(1); + EXPECT_DEATH(*it, "invalidated iterator"); } } #endif diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index bbc319c1..6c10b00f 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -58,6 +58,7 @@ #include #include +#include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/container/internal/common.h" #include "absl/container/internal/compressed_tuple.h" @@ -74,6 +75,16 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS +#error ABSL_BTREE_ENABLE_GENERATIONS cannot be directly set +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER) +// When compiled in sanitizer mode, we add generation integers to the nodes and +// iterators. When iterators are used, we validate that the container has not +// been mutated since the iterator was constructed. +#define ABSL_BTREE_ENABLE_GENERATIONS +#endif + template using compare_result_t = absl::result_of_t; @@ -348,6 +359,12 @@ struct common_params { static constexpr bool kIsKeyCompareTransparent = IsTransparent::value || kIsKeyCompareStringAdapted; + static constexpr bool kEnableGenerations = +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + true; +#else + false; +#endif // A type which indicates if we have a key-compare-to functor or a plain old // key-compare functor. @@ -518,6 +535,13 @@ class btree_node { // // A pointer to the node's parent. // btree_node *parent; // + // // When ABSL_BTREE_ENABLE_GENERATIONS is defined, we also have a + // // generation integer in order to check that when iterators are + // // used, they haven't been invalidated already. Only the generation on + // // the root is used, but we have one on each node because whether a node + // // is root or not can change. + // uint32_t generation; + // // // The position of the node in the node's parent. // field_type position; // // The index of the first populated value in `values`. @@ -564,13 +588,16 @@ class btree_node { btree_node() = default; private: - using layout_type = absl::container_internal::Layout; + using layout_type = + absl::container_internal::Layout; constexpr static size_type SizeWithNSlots(size_type n) { - return layout_type(/*parent*/ 1, - /*position, start, finish, max_count*/ 4, - /*slots*/ n, - /*children*/ 0) + return layout_type( + /*parent*/ 1, + /*generation*/ params_type::kEnableGenerations ? 1 : 0, + /*position, start, finish, max_count*/ 4, + /*slots*/ n, + /*children*/ 0) .AllocSize(); } // A lower bound for the overhead of fields other than slots in a leaf node. @@ -609,16 +636,20 @@ class btree_node { // Leaves can have less than kNodeSlots values. constexpr static layout_type LeafLayout(const int slot_count = kNodeSlots) { - return layout_type(/*parent*/ 1, - /*position, start, finish, max_count*/ 4, - /*slots*/ slot_count, - /*children*/ 0); + return layout_type( + /*parent*/ 1, + /*generation*/ params_type::kEnableGenerations ? 1 : 0, + /*position, start, finish, max_count*/ 4, + /*slots*/ slot_count, + /*children*/ 0); } constexpr static layout_type InternalLayout() { - return layout_type(/*parent*/ 1, - /*position, start, finish, max_count*/ 4, - /*slots*/ kNodeSlots, - /*children*/ kNodeSlots + 1); + return layout_type( + /*parent*/ 1, + /*generation*/ params_type::kEnableGenerations ? 1 : 0, + /*position, start, finish, max_count*/ 4, + /*slots*/ kNodeSlots, + /*children*/ kNodeSlots + 1); } constexpr static size_type LeafSize(const int slot_count = kNodeSlots) { return LeafLayout(slot_count).AllocSize(); @@ -632,44 +663,47 @@ class btree_node { template inline typename layout_type::template ElementType *GetField() { // We assert that we don't read from values that aren't there. - assert(N < 3 || !leaf()); + assert(N < 4 || is_internal()); return InternalLayout().template Pointer(reinterpret_cast(this)); } template inline const typename layout_type::template ElementType *GetField() const { - assert(N < 3 || !leaf()); + assert(N < 4 || is_internal()); return InternalLayout().template Pointer( reinterpret_cast(this)); } void set_parent(btree_node *p) { *GetField<0>() = p; } - field_type &mutable_finish() { return GetField<1>()[2]; } - slot_type *slot(int i) { return &GetField<2>()[i]; } + field_type &mutable_finish() { return GetField<2>()[2]; } + slot_type *slot(int i) { return &GetField<3>()[i]; } slot_type *start_slot() { return slot(start()); } slot_type *finish_slot() { return slot(finish()); } - const slot_type *slot(int i) const { return &GetField<2>()[i]; } - void set_position(field_type v) { GetField<1>()[0] = v; } - void set_start(field_type v) { GetField<1>()[1] = v; } - void set_finish(field_type v) { GetField<1>()[2] = v; } + const slot_type *slot(int i) const { return &GetField<3>()[i]; } + void set_position(field_type v) { GetField<2>()[0] = v; } + void set_start(field_type v) { GetField<2>()[1] = v; } + void set_finish(field_type v) { GetField<2>()[2] = v; } // This method is only called by the node init methods. - void set_max_count(field_type v) { GetField<1>()[3] = v; } + void set_max_count(field_type v) { GetField<2>()[3] = v; } public: // Whether this is a leaf node or not. This value doesn't change after the // node is created. - bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; } + bool is_leaf() const { return GetField<2>()[3] != kInternalNodeMaxCount; } + // Whether this is an internal node or not. This value doesn't change after + // the node is created. + bool is_internal() const { return !is_leaf(); } // Getter for the position of this node in its parent. - field_type position() const { return GetField<1>()[0]; } + field_type position() const { return GetField<2>()[0]; } // Getter for the offset of the first value in the `values` array. field_type start() const { - // TODO(ezb): when floating storage is implemented, return GetField<1>()[1]; - assert(GetField<1>()[1] == 0); + // TODO(ezb): when floating storage is implemented, return GetField<2>()[1]; + assert(GetField<2>()[1] == 0); return 0; } // Getter for the offset after the last value in the `values` array. - field_type finish() const { return GetField<1>()[2]; } + field_type finish() const { return GetField<2>()[2]; } // Getters for the number of values stored in this node. field_type count() const { @@ -679,7 +713,7 @@ class btree_node { field_type max_count() const { // Internal nodes have max_count==kInternalNodeMaxCount. // Leaf nodes have max_count in [1, kNodeSlots]. - const field_type max_count = GetField<1>()[3]; + const field_type max_count = GetField<2>()[3]; return max_count == field_type{kInternalNodeMaxCount} ? field_type{kNodeSlots} : max_count; @@ -690,21 +724,44 @@ class btree_node { // Getter for whether the node is the root of the tree. The parent of the // root of the tree is the leftmost node in the tree which is guaranteed to // be a leaf. - bool is_root() const { return parent()->leaf(); } + bool is_root() const { return parent()->is_leaf(); } void make_root() { assert(parent()->is_root()); + set_generation(parent()->generation()); set_parent(parent()->parent()); } + // Gets the root node's generation integer, which is the one used by the tree. + uint32_t *get_root_generation() const { + assert(params_type::kEnableGenerations); + const btree_node *curr = this; + for (; !curr->is_root(); curr = curr->parent()) continue; + return const_cast(&curr->GetField<1>()[0]); + } + + // Returns the generation for iterator validation. + uint32_t generation() const { + return params_type::kEnableGenerations ? *get_root_generation() : 0; + } + // Updates generation. Should only be called on a root node or during node + // initialization. + void set_generation(uint32_t generation) { + if (params_type::kEnableGenerations) GetField<1>()[0] = generation; + } + // Updates the generation. We do this whenever the node is mutated. + void next_generation() { + if (params_type::kEnableGenerations) ++*get_root_generation(); + } + // Getters for the key/value at position i in the node. const key_type &key(int i) const { return params_type::key(slot(i)); } reference value(int i) { return params_type::element(slot(i)); } const_reference value(int i) const { return params_type::element(slot(i)); } // Getters/setter for the child at position i in the node. - btree_node *child(int i) const { return GetField<3>()[i]; } + btree_node *child(int i) const { return GetField<4>()[i]; } btree_node *start_child() const { return child(start()); } - btree_node *&mutable_child(int i) { return GetField<3>()[i]; } + btree_node *&mutable_child(int i) { return GetField<4>()[i]; } void clear_child(int i) { absl::container_internal::SanitizerPoisonObject(&mutable_child(i)); } @@ -861,7 +918,8 @@ class btree_node { void merge(btree_node *src, allocator_type *alloc); // Node allocation/deletion routines. - void init_leaf(btree_node *parent, int max_count) { + void init_leaf(int max_count, btree_node *parent) { + set_generation(0); set_parent(parent); set_position(0); set_start(0); @@ -871,7 +929,7 @@ class btree_node { start_slot(), max_count * sizeof(slot_type)); } void init_internal(btree_node *parent) { - init_leaf(parent, kNodeSlots); + init_leaf(kNodeSlots, parent); // Set `max_count` to a sentinel value to indicate that this node is // internal. set_max_count(kInternalNodeMaxCount); @@ -890,15 +948,18 @@ class btree_node { private: template void value_init(const field_type i, allocator_type *alloc, Args &&... args) { + next_generation(); absl::container_internal::SanitizerUnpoisonObject(slot(i)); params_type::construct(alloc, slot(i), std::forward(args)...); } void value_destroy(const field_type i, allocator_type *alloc) { + next_generation(); params_type::destroy(alloc, slot(i)); absl::container_internal::SanitizerPoisonObject(slot(i)); } void value_destroy_n(const field_type i, const field_type n, allocator_type *alloc) { + next_generation(); for (slot_type *s = slot(i), *end = slot(i + n); s != end; ++s) { params_type::destroy(alloc, s); absl::container_internal::SanitizerPoisonObject(s); @@ -914,6 +975,7 @@ class btree_node { // Transfers value from slot `src_i` in `src_node` to slot `dest_i` in `this`. void transfer(const size_type dest_i, const size_type src_i, btree_node *src_node, allocator_type *alloc) { + next_generation(); transfer(slot(dest_i), src_node->slot(src_i), alloc); } @@ -922,6 +984,7 @@ class btree_node { void transfer_n(const size_type n, const size_type dest_i, const size_type src_i, btree_node *src_node, allocator_type *alloc) { + next_generation(); for (slot_type *src = src_node->slot(src_i), *end = src + n, *dest = slot(dest_i); src != end; ++src, ++dest) { @@ -934,6 +997,7 @@ class btree_node { void transfer_n_backward(const size_type n, const size_type dest_i, const size_type src_i, btree_node *src_node, allocator_type *alloc) { + next_generation(); for (slot_type *src = src_node->slot(src_i + n - 1), *end = src - n, *dest = slot(dest_i + n - 1); src != end; --src, --dest) { @@ -944,14 +1008,13 @@ class btree_node { template friend class btree; template - friend struct btree_iterator; + friend class btree_iterator; friend class BtreeNodePeer; friend struct btree_access; }; template -struct btree_iterator { - private: +class btree_iterator { using key_type = typename Node::key_type; using size_type = typename Node::size_type; using params_type = typename Node::params_type; @@ -979,9 +1042,15 @@ struct btree_iterator { using reference = Reference; using iterator_category = std::bidirectional_iterator_tag; - btree_iterator() : node(nullptr), position(-1) {} - explicit btree_iterator(Node *n) : node(n), position(n->start()) {} - btree_iterator(Node *n, int p) : node(n), position(p) {} + btree_iterator() : btree_iterator(nullptr, -1) {} + explicit btree_iterator(Node *n) : btree_iterator(n, n->start()) {} + btree_iterator(Node *n, int p) : node_(n), position_(p) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + // Use `~uint32_t{}` as a sentinel value for iterator generations so it + // doesn't match the initial value for the actual generation. + generation_ = n != nullptr ? n->generation() : ~uint32_t{}; +#endif + } // NOTE: this SFINAE allows for implicit conversions from iterator to // const_iterator, but it specifically avoids hiding the copy constructor so @@ -992,58 +1061,32 @@ struct btree_iterator { std::is_same::value, int> = 0> btree_iterator(const btree_iterator other) // NOLINT - : node(other.node), position(other.position) {} - - private: - // This SFINAE allows explicit conversions from const_iterator to - // iterator, but also avoids hiding the copy constructor. - // NOTE: the const_cast is safe because this constructor is only called by - // non-const methods and the container owns the nodes. - template , const_iterator>::value && - std::is_same::value, - int> = 0> - explicit btree_iterator(const btree_iterator other) - : node(const_cast(other.node)), position(other.position) {} - - // Increment/decrement the iterator. - void increment() { - if (node->leaf() && ++position < node->finish()) { - return; - } - increment_slow(); - } - void increment_slow(); - - void decrement() { - if (node->leaf() && --position >= node->start()) { - return; - } - decrement_slow(); + : node_(other.node_), position_(other.position_) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + generation_ = other.generation_; +#endif } - void decrement_slow(); - public: bool operator==(const iterator &other) const { - return node == other.node && position == other.position; + return node_ == other.node_ && position_ == other.position_; } bool operator==(const const_iterator &other) const { - return node == other.node && position == other.position; + return node_ == other.node_ && position_ == other.position_; } bool operator!=(const iterator &other) const { - return node != other.node || position != other.position; + return node_ != other.node_ || position_ != other.position_; } bool operator!=(const const_iterator &other) const { - return node != other.node || position != other.position; + return node_ != other.node_ || position_ != other.position_; } // Accessors for the key/value the iterator is pointing at. reference operator*() const { - ABSL_HARDENING_ASSERT(node != nullptr); - ABSL_HARDENING_ASSERT(node->start() <= position); - ABSL_HARDENING_ASSERT(node->finish() > position); - return node->value(position); + ABSL_HARDENING_ASSERT(node_ != nullptr); + ABSL_HARDENING_ASSERT(node_->start() <= position_); + ABSL_HARDENING_ASSERT(node_->finish() > position_); + assert_valid_generation(); + return node_->value(position_); } pointer operator->() const { return &operator*(); } @@ -1083,15 +1126,74 @@ struct btree_iterator { friend class base_checker; friend struct btree_access; - const key_type &key() const { return node->key(position); } - slot_type *slot() { return node->slot(position); } + // This SFINAE allows explicit conversions from const_iterator to + // iterator, but also avoids hiding the copy constructor. + // NOTE: the const_cast is safe because this constructor is only called by + // non-const methods and the container owns the nodes. + template , const_iterator>::value && + std::is_same::value, + int> = 0> + explicit btree_iterator(const btree_iterator other) + : node_(const_cast(other.node_)), + position_(other.position_) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + generation_ = other.generation_; +#endif + } + + // Increment/decrement the iterator. + void increment() { + assert_valid_generation(); + if (node_->is_leaf() && ++position_ < node_->finish()) { + return; + } + increment_slow(); + } + void increment_slow(); + + void decrement() { + assert_valid_generation(); + if (node_->is_leaf() && --position_ >= node_->start()) { + return; + } + decrement_slow(); + } + void decrement_slow(); + + // Updates the generation. For use internally right before we return an + // iterator to the user. + void update_generation() { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + if (node_ != nullptr) generation_ = node_->generation(); +#endif + } + + const key_type &key() const { return node_->key(position_); } + slot_type *slot() { return node_->slot(position_); } + + void assert_valid_generation() const { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + if (node_ != nullptr && node_->generation() != generation_) { + ABSL_INTERNAL_LOG( + FATAL, + "Attempting to use an invalidated iterator. The corresponding b-tree " + "container has been mutated since this iterator was constructed."); + } +#endif + } // The node in the tree the iterator is pointing at. - Node *node; + Node *node_; // The position within the node of the tree the iterator is pointing at. // NOTE: this is an int rather than a field_type because iterators can point // to invalid positions (such as -1) in certain circumstances. - int position; + int position_; +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + // Used to check that the iterator hasn't been invalidated. + uint32_t generation_; +#endif }; template @@ -1106,6 +1208,9 @@ class btree { struct alignas(node_type::Alignment()) EmptyNodeType : node_type { using field_type = typename node_type::field_type; node_type *parent; +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + uint32_t generation = 0; +#endif field_type position = 0; field_type start = 0; field_type finish = 0; @@ -1490,12 +1595,12 @@ class btree { } node_type *new_leaf_node(node_type *parent) { node_type *n = allocate(node_type::LeafSize()); - n->init_leaf(parent, kNodeSlots); + n->init_leaf(kNodeSlots, parent); return n; } node_type *new_leaf_root_node(const int max_count) { node_type *n = allocate(node_type::LeafSize(max_count)); - n->init_leaf(/*parent=*/n, max_count); + n->init_leaf(max_count, /*parent=*/n); return n; } @@ -1519,10 +1624,10 @@ class btree { void try_shrink(); iterator internal_end(iterator iter) { - return iter.node != nullptr ? iter : end(); + return iter.node_ != nullptr ? iter : end(); } const_iterator internal_end(const_iterator iter) const { - return iter.node != nullptr ? iter : end(); + return iter.node_ != nullptr ? iter : end(); } // Emplaces a value into the btree immediately before iter. Requires that @@ -1532,9 +1637,8 @@ class btree { // Returns an iterator pointing to the first value >= the value "iter" is // pointing at. Note that "iter" might be pointing to an invalid location such - // as iter.position == iter.node->finish(). This routine simply moves iter up - // in the tree to a valid location. - // Requires: iter.node is non-null. + // as iter.position_ == iter.node_->finish(). This routine simply moves iter + // up in the tree to a valid location. Requires: iter.node_ is non-null. template static IterType internal_last(IterType iter); @@ -1570,7 +1674,7 @@ class btree { if (node == nullptr || (node == root() && empty())) { return node_stats(0, 0); } - if (node->leaf()) { + if (node->is_leaf()) { return node_stats(1, 0); } node_stats res(0, 1); @@ -1612,7 +1716,7 @@ inline void btree_node

::emplace_value(const size_type i, value_init(i, alloc, std::forward(args)...); set_finish(finish() + 1); - if (!leaf() && finish() > i + 1) { + if (is_internal() && finish() > i + 1) { for (field_type j = finish(); j > i + 1; --j) { set_child(j, child(j - 1)); } @@ -1630,7 +1734,7 @@ inline void btree_node

::remove_values(const field_type i, const field_type src_i = i + to_erase; transfer_n(orig_finish - src_i, i, src_i, this, alloc); - if (!leaf()) { + if (is_internal()) { // Delete all children between begin and end. for (int j = 0; j < to_erase; ++j) { clear_and_delete(child(i + j + 1), alloc); @@ -1667,7 +1771,7 @@ void btree_node

::rebalance_right_to_left(const int to_move, right->transfer_n(right->count() - to_move, right->start(), right->start() + to_move, right, alloc); - if (!leaf()) { + if (is_internal()) { // Move the child pointers from the right to the left node. for (int i = 0; i < to_move; ++i) { init_child(finish() + i + 1, right->child(i)); @@ -1714,7 +1818,7 @@ void btree_node

::rebalance_left_to_right(const int to_move, // 4) Move the new delimiting value to the parent from the left node. parent()->transfer(position(), finish() - to_move, this, alloc); - if (!leaf()) { + if (is_internal()) { // Move the child pointers from the left to the right node. for (int i = right->finish(); i >= right->start(); --i) { right->init_child(i + to_move, right->child(i)); @@ -1760,7 +1864,7 @@ void btree_node

::split(const int insert_position, btree_node *dest, value_destroy(finish(), alloc); parent()->init_child(position() + 1, dest); - if (!leaf()) { + if (is_internal()) { for (int i = dest->start(), j = finish() + 1; i <= dest->finish(); ++i, ++j) { assert(child(j) != nullptr); @@ -1781,7 +1885,7 @@ void btree_node

::merge(btree_node *src, allocator_type *alloc) { // Move the values from the right to the left node. transfer_n(src->count(), finish() + 1, src->start(), src, alloc); - if (!leaf()) { + if (is_internal()) { // Move the child pointers from the right to the left node. for (int i = src->start(), j = finish() + 1; i <= src->finish(); ++i, ++j) { init_child(j, src->child(i)); @@ -1799,7 +1903,7 @@ void btree_node

::merge(btree_node *src, allocator_type *alloc) { template void btree_node

::clear_and_delete(btree_node *node, allocator_type *alloc) { - if (node->leaf()) { + if (node->is_leaf()) { node->value_destroy_n(node->start(), node->count(), alloc); deallocate(LeafSize(node->max_count()), node, alloc); return; @@ -1813,7 +1917,15 @@ void btree_node

::clear_and_delete(btree_node *node, allocator_type *alloc) { btree_node *delete_root_parent = node->parent(); // Navigate to the leftmost leaf under node, and then delete upwards. - while (!node->leaf()) node = node->start_child(); + while (node->is_internal()) node = node->start_child(); +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + // When generations are enabled, we delete the leftmost leaf last in case it's + // the parent of the root and we need to check whether it's a leaf before we + // can update the root's generation. + // TODO(ezb): if we change btree_node::is_root to check a bool inside the node + // instead of checking whether the parent is a leaf, we can remove this logic. + btree_node *leftmost_leaf = node; +#endif // Use `int` because `pos` needs to be able to hold `kNodeSlots+1`, which // isn't guaranteed to be a valid `field_type`. int pos = node->position(); @@ -1823,14 +1935,17 @@ void btree_node

::clear_and_delete(btree_node *node, allocator_type *alloc) { assert(pos <= parent->finish()); do { node = parent->child(pos); - if (!node->leaf()) { + if (node->is_internal()) { // Navigate to the leftmost leaf under node. - while (!node->leaf()) node = node->start_child(); + while (node->is_internal()) node = node->start_child(); pos = node->position(); parent = node->parent(); } node->value_destroy_n(node->start(), node->count(), alloc); - deallocate(LeafSize(node->max_count()), node, alloc); +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + if (leftmost_leaf != node) +#endif + deallocate(LeafSize(node->max_count()), node, alloc); ++pos; } while (pos <= parent->finish()); @@ -1842,7 +1957,12 @@ void btree_node

::clear_and_delete(btree_node *node, allocator_type *alloc) { parent = node->parent(); node->value_destroy_n(node->start(), node->count(), alloc); deallocate(InternalSize(), node, alloc); - if (parent == delete_root_parent) return; + if (parent == delete_root_parent) { +#ifdef ABSL_BTREE_ENABLE_GENERATIONS + deallocate(LeafSize(leftmost_leaf->max_count()), leftmost_leaf, alloc); +#endif + return; + } ++pos; } while (pos > parent->finish()); } @@ -1852,49 +1972,49 @@ void btree_node

::clear_and_delete(btree_node *node, allocator_type *alloc) { // btree_iterator methods template void btree_iterator::increment_slow() { - if (node->leaf()) { - assert(position >= node->finish()); + if (node_->is_leaf()) { + assert(position_ >= node_->finish()); btree_iterator save(*this); - while (position == node->finish() && !node->is_root()) { - assert(node->parent()->child(node->position()) == node); - position = node->position(); - node = node->parent(); + while (position_ == node_->finish() && !node_->is_root()) { + assert(node_->parent()->child(node_->position()) == node_); + position_ = node_->position(); + node_ = node_->parent(); } // TODO(ezb): assert we aren't incrementing end() instead of handling. - if (position == node->finish()) { + if (position_ == node_->finish()) { *this = save; } } else { - assert(position < node->finish()); - node = node->child(position + 1); - while (!node->leaf()) { - node = node->start_child(); + assert(position_ < node_->finish()); + node_ = node_->child(position_ + 1); + while (node_->is_internal()) { + node_ = node_->start_child(); } - position = node->start(); + position_ = node_->start(); } } template void btree_iterator::decrement_slow() { - if (node->leaf()) { - assert(position <= -1); + if (node_->is_leaf()) { + assert(position_ <= -1); btree_iterator save(*this); - while (position < node->start() && !node->is_root()) { - assert(node->parent()->child(node->position()) == node); - position = node->position() - 1; - node = node->parent(); + while (position_ < node_->start() && !node_->is_root()) { + assert(node_->parent()->child(node_->position()) == node_); + position_ = node_->position() - 1; + node_ = node_->parent(); } // TODO(ezb): assert we aren't decrementing begin() instead of handling. - if (position < node->start()) { + if (position_ < node_->start()) { *this = save; } } else { - assert(position >= node->start()); - node = node->child(position); - while (!node->leaf()) { - node = node->child(node->finish()); + assert(position_ >= node_->start()); + node_ = node_->child(position_); + while (node_->is_internal()) { + node_ = node_->child(node_->finish()); } - position = node->finish() - 1; + position_ = node_->finish() - 1; } } @@ -2009,7 +2129,7 @@ auto btree

::insert_unique(const K &key, Args &&... args) } } else { iterator last = internal_last(iter); - if (last.node && !compare_keys(key, last.key())) { + if (last.node_ && !compare_keys(key, last.key())) { // The key already exists in the tree, do nothing. return {last, false}; } @@ -2067,7 +2187,7 @@ auto btree

::insert_multi(const key_type &key, ValueType &&v) -> iterator { } iterator iter = internal_upper_bound(key); - if (iter.node == nullptr) { + if (iter.node_ == nullptr) { iter = end(); } return internal_emplace(iter, std::forward(v)); @@ -2154,21 +2274,22 @@ auto btree

::operator=(btree &&other) noexcept -> btree & { template auto btree

::erase(iterator iter) -> iterator { bool internal_delete = false; - if (!iter.node->leaf()) { + if (iter.node_->is_internal()) { // Deletion of a value on an internal node. First, move the largest value // from our left child here, then delete that position (in remove_values() // below). We can get to the largest value from our left child by // decrementing iter. iterator internal_iter(iter); --iter; - assert(iter.node->leaf()); - params_type::move(mutable_allocator(), iter.node->slot(iter.position), - internal_iter.node->slot(internal_iter.position)); + assert(iter.node_->is_leaf()); + params_type::move(mutable_allocator(), iter.node_->slot(iter.position_), + internal_iter.node_->slot(internal_iter.position_)); internal_delete = true; } // Delete the key from the leaf. - iter.node->remove_values(iter.position, /*to_erase=*/1, mutable_allocator()); + iter.node_->remove_values(iter.position_, /*to_erase=*/1, + mutable_allocator()); --size_; // We want to return the next value after the one we just erased. If we @@ -2176,7 +2297,7 @@ auto btree

::erase(iterator iter) -> iterator { // value is ++(++iter). If we erased from a leaf node (internal_delete == // false) then the next value is ++iter. Note that ++iter may point to an // internal node and the value in the internal node may move to a leaf node - // (iter.node) when rebalancing is performed at the leaf level. + // (iter.node_) when rebalancing is performed at the leaf level. iterator res = rebalance_after_delete(iter); @@ -2193,14 +2314,14 @@ auto btree

::rebalance_after_delete(iterator iter) -> iterator { iterator res(iter); bool first_iteration = true; for (;;) { - if (iter.node == root()) { + if (iter.node_ == root()) { try_shrink(); if (empty()) { return end(); } break; } - if (iter.node->count() >= kMinNodeValues) { + if (iter.node_->count() >= kMinNodeValues) { break; } bool merged = try_merge_or_rebalance(&iter); @@ -2213,14 +2334,15 @@ auto btree

::rebalance_after_delete(iterator iter) -> iterator { if (!merged) { break; } - iter.position = iter.node->position(); - iter.node = iter.node->parent(); + iter.position_ = iter.node_->position(); + iter.node_ = iter.node_->parent(); } + res.update_generation(); // Adjust our return value. If we're pointing at the end of a node, advance // the iterator. - if (res.position == res.node->finish()) { - res.position = res.node->finish() - 1; + if (res.position_ == res.node_->finish()) { + res.position_ = res.node_->finish() - 1; ++res; } @@ -2242,28 +2364,31 @@ auto btree

::erase_range(iterator begin, iterator end) return {count, this->end()}; } - if (begin.node == end.node) { - assert(end.position > begin.position); - begin.node->remove_values(begin.position, end.position - begin.position, - mutable_allocator()); + if (begin.node_ == end.node_) { + assert(end.position_ > begin.position_); + begin.node_->remove_values(begin.position_, end.position_ - begin.position_, + mutable_allocator()); size_ -= count; return {count, rebalance_after_delete(begin)}; } const size_type target_size = size_ - count; while (size_ > target_size) { - if (begin.node->leaf()) { + if (begin.node_->is_leaf()) { const size_type remaining_to_erase = size_ - target_size; - const size_type remaining_in_node = begin.node->finish() - begin.position; + const size_type remaining_in_node = + begin.node_->finish() - begin.position_; const size_type to_erase = (std::min)(remaining_to_erase, remaining_in_node); - begin.node->remove_values(begin.position, to_erase, mutable_allocator()); + begin.node_->remove_values(begin.position_, to_erase, + mutable_allocator()); size_ -= to_erase; begin = rebalance_after_delete(begin); } else { begin = erase(begin); } } + begin.update_generation(); return {count, begin}; } @@ -2300,16 +2425,16 @@ void btree

::verify() const { assert(leftmost() != nullptr); assert(rightmost_ != nullptr); assert(empty() || size() == internal_verify(root(), nullptr, nullptr)); - assert(leftmost() == (++const_iterator(root(), -1)).node); - assert(rightmost_ == (--const_iterator(root(), root()->finish())).node); - assert(leftmost()->leaf()); - assert(rightmost_->leaf()); + assert(leftmost() == (++const_iterator(root(), -1)).node_); + assert(rightmost_ == (--const_iterator(root(), root()->finish())).node_); + assert(leftmost()->is_leaf()); + assert(rightmost_->is_leaf()); } template void btree

::rebalance_or_split(iterator *iter) { - node_type *&node = iter->node; - int &insert_position = iter->position; + node_type *&node = iter->node_; + int &insert_position = iter->position_; assert(node->count() == node->max_count()); assert(kNodeSlots == node->max_count()); @@ -2384,16 +2509,17 @@ void btree

::rebalance_or_split(iterator *iter) { // Create a new root node and set the current root node as the child of the // new root. parent = new_internal_node(parent); + parent->set_generation(root()->generation()); parent->init_child(parent->start(), root()); mutable_root() = parent; // If the former root was a leaf node, then it's now the rightmost node. - assert(!parent->start_child()->leaf() || + assert(parent->start_child()->is_internal() || parent->start_child() == rightmost_); } // Split the node. node_type *split_node; - if (node->leaf()) { + if (node->is_leaf()) { split_node = new_leaf_node(parent); node->split(insert_position, split_node, mutable_allocator()); if (rightmost_ == node) rightmost_ = split_node; @@ -2416,50 +2542,51 @@ void btree

::merge_nodes(node_type *left, node_type *right) { template bool btree

::try_merge_or_rebalance(iterator *iter) { - node_type *parent = iter->node->parent(); - if (iter->node->position() > parent->start()) { + node_type *parent = iter->node_->parent(); + if (iter->node_->position() > parent->start()) { // Try merging with our left sibling. - node_type *left = parent->child(iter->node->position() - 1); + node_type *left = parent->child(iter->node_->position() - 1); assert(left->max_count() == kNodeSlots); - if (1U + left->count() + iter->node->count() <= kNodeSlots) { - iter->position += 1 + left->count(); - merge_nodes(left, iter->node); - iter->node = left; + if (1U + left->count() + iter->node_->count() <= kNodeSlots) { + iter->position_ += 1 + left->count(); + merge_nodes(left, iter->node_); + iter->node_ = left; return true; } } - if (iter->node->position() < parent->finish()) { + if (iter->node_->position() < parent->finish()) { // Try merging with our right sibling. - node_type *right = parent->child(iter->node->position() + 1); + node_type *right = parent->child(iter->node_->position() + 1); assert(right->max_count() == kNodeSlots); - if (1U + iter->node->count() + right->count() <= kNodeSlots) { - merge_nodes(iter->node, right); + if (1U + iter->node_->count() + right->count() <= kNodeSlots) { + merge_nodes(iter->node_, right); return true; } // Try rebalancing with our right sibling. We don't perform rebalancing if - // we deleted the first element from iter->node and the node is not + // we deleted the first element from iter->node_ and the node is not // empty. This is a small optimization for the common pattern of deleting // from the front of the tree. if (right->count() > kMinNodeValues && - (iter->node->count() == 0 || iter->position > iter->node->start())) { - int to_move = (right->count() - iter->node->count()) / 2; + (iter->node_->count() == 0 || iter->position_ > iter->node_->start())) { + int to_move = (right->count() - iter->node_->count()) / 2; to_move = (std::min)(to_move, right->count() - 1); - iter->node->rebalance_right_to_left(to_move, right, mutable_allocator()); + iter->node_->rebalance_right_to_left(to_move, right, mutable_allocator()); return false; } } - if (iter->node->position() > parent->start()) { + if (iter->node_->position() > parent->start()) { // Try rebalancing with our left sibling. We don't perform rebalancing if - // we deleted the last element from iter->node and the node is not + // we deleted the last element from iter->node_ and the node is not // empty. This is a small optimization for the common pattern of deleting // from the back of the tree. - node_type *left = parent->child(iter->node->position() - 1); + node_type *left = parent->child(iter->node_->position() - 1); if (left->count() > kMinNodeValues && - (iter->node->count() == 0 || iter->position < iter->node->finish())) { - int to_move = (left->count() - iter->node->count()) / 2; + (iter->node_->count() == 0 || + iter->position_ < iter->node_->finish())) { + int to_move = (left->count() - iter->node_->count()) / 2; to_move = (std::min)(to_move, left->count() - 1); - left->rebalance_left_to_right(to_move, iter->node, mutable_allocator()); - iter->position += to_move; + left->rebalance_left_to_right(to_move, iter->node_, mutable_allocator()); + iter->position_ += to_move; return false; } } @@ -2473,7 +2600,7 @@ void btree

::try_shrink() { return; } // Deleted the last item on the root node, shrink the height of the tree. - if (orig_root->leaf()) { + if (orig_root->is_leaf()) { assert(size() == 0); mutable_root() = rightmost_ = EmptyNode(); } else { @@ -2487,15 +2614,16 @@ void btree

::try_shrink() { template template inline IterType btree

::internal_last(IterType iter) { - assert(iter.node != nullptr); - while (iter.position == iter.node->finish()) { - iter.position = iter.node->position(); - iter.node = iter.node->parent(); - if (iter.node->leaf()) { - iter.node = nullptr; + assert(iter.node_ != nullptr); + while (iter.position_ == iter.node_->finish()) { + iter.position_ = iter.node_->position(); + iter.node_ = iter.node_->parent(); + if (iter.node_->is_leaf()) { + iter.node_ = nullptr; break; } } + iter.update_generation(); return iter; } @@ -2503,37 +2631,39 @@ template template inline auto btree

::internal_emplace(iterator iter, Args &&... args) -> iterator { - if (!iter.node->leaf()) { + if (iter.node_->is_internal()) { // We can't insert on an internal node. Instead, we'll insert after the // previous value which is guaranteed to be on a leaf node. --iter; - ++iter.position; + ++iter.position_; } - const field_type max_count = iter.node->max_count(); + const field_type max_count = iter.node_->max_count(); allocator_type *alloc = mutable_allocator(); - if (iter.node->count() == max_count) { + if (iter.node_->count() == max_count) { // Make room in the leaf for the new item. if (max_count < kNodeSlots) { // Insertion into the root where the root is smaller than the full node // size. Simply grow the size of the root node. - assert(iter.node == root()); - iter.node = + assert(iter.node_ == root()); + iter.node_ = new_leaf_root_node((std::min)(kNodeSlots, 2 * max_count)); // Transfer the values from the old root to the new root. node_type *old_root = root(); - node_type *new_root = iter.node; + node_type *new_root = iter.node_; new_root->transfer_n(old_root->count(), new_root->start(), old_root->start(), old_root, alloc); new_root->set_finish(old_root->finish()); old_root->set_finish(old_root->start()); + new_root->set_generation(old_root->generation()); node_type::clear_and_delete(old_root, alloc); mutable_root() = rightmost_ = new_root; } else { rebalance_or_split(&iter); } } - iter.node->emplace_value(iter.position, alloc, std::forward(args)...); + iter.node_->emplace_value(iter.position_, alloc, std::forward(args)...); ++size_; + iter.update_generation(); return iter; } @@ -2544,8 +2674,8 @@ inline auto btree

::internal_locate(const K &key) const iterator iter(const_cast(root())); for (;;) { SearchResult res = - iter.node->lower_bound(key, key_comp()); - iter.position = res.value; + iter.node_->lower_bound(key, key_comp()); + iter.position_ = res.value; if (res.IsEq()) { return {iter, MatchKind::kEq}; } @@ -2553,10 +2683,10 @@ inline auto btree

::internal_locate(const K &key) const // down the tree if the keys are equal, but determining equality would // require doing an extra comparison on each node on the way down, and we // will need to go all the way to the leaf node in the expected case. - if (iter.node->leaf()) { + if (iter.node_->is_leaf()) { break; } - iter.node = iter.node->child(iter.position); + iter.node_ = iter.node_->child(iter.position_); } // Note: in the non-key-compare-to case, the key may actually be equivalent // here (and the MatchKind::kNe is ignored). @@ -2576,13 +2706,13 @@ auto btree

::internal_lower_bound(const K &key) const SearchResult res; bool seen_eq = false; for (;;) { - res = iter.node->lower_bound(key, key_comp()); - iter.position = res.value; - if (iter.node->leaf()) { + res = iter.node_->lower_bound(key, key_comp()); + iter.position_ = res.value; + if (iter.node_->is_leaf()) { break; } seen_eq = seen_eq || res.IsEq(); - iter.node = iter.node->child(iter.position); + iter.node_ = iter.node_->child(iter.position_); } if (res.IsEq()) return {iter, MatchKind::kEq}; return {internal_last(iter), seen_eq ? MatchKind::kEq : MatchKind::kNe}; @@ -2593,11 +2723,11 @@ template auto btree

::internal_upper_bound(const K &key) const -> iterator { iterator iter(const_cast(root())); for (;;) { - iter.position = iter.node->upper_bound(key, key_comp()); - if (iter.node->leaf()) { + iter.position_ = iter.node_->upper_bound(key, key_comp()); + if (iter.node_->is_leaf()) { break; } - iter.node = iter.node->child(iter.position); + iter.node_ = iter.node_->child(iter.position_); } return internal_last(iter); } @@ -2612,7 +2742,7 @@ auto btree

::internal_find(const K &key) const -> iterator { } } else { const iterator iter = internal_last(res.value); - if (iter.node != nullptr && !compare_keys(key, iter.key())) { + if (iter.node_ != nullptr && !compare_keys(key, iter.key())) { return iter; } } @@ -2634,7 +2764,7 @@ int btree

::internal_verify(const node_type *node, const key_type *lo, assert(!compare_keys(node->key(i), node->key(i - 1))); } int count = node->count(); - if (!node->leaf()) { + if (node->is_internal()) { for (int i = node->start(); i <= node->finish(); ++i) { assert(node->child(i) != nullptr); assert(node->child(i)->parent() == node); @@ -2659,8 +2789,8 @@ struct btree_access { ++it; continue; } - auto *node = it.node; - if (!node->leaf()) { + auto *node = it.node_; + if (node->is_internal()) { // Handle internal nodes normally. it = container.erase(it); continue; @@ -2669,26 +2799,28 @@ struct btree_access { // at once before doing rebalancing. // The current position to transfer slots to. - int to_pos = it.position; - node->value_destroy(it.position, alloc); - while (++it.position < node->finish()) { + int to_pos = it.position_; + node->value_destroy(it.position_, alloc); + while (++it.position_ < node->finish()) { + it.update_generation(); if (pred(*it)) { - node->value_destroy(it.position, alloc); + node->value_destroy(it.position_, alloc); } else { - node->transfer(node->slot(to_pos++), node->slot(it.position), - alloc); + node->transfer(node->slot(to_pos++), node->slot(it.position_), alloc); } } const int num_deleted = node->finish() - to_pos; tree.size_ -= num_deleted; node->set_finish(to_pos); - it.position = to_pos; + it.position_ = to_pos; it = tree.rebalance_after_delete(it); } return initial_size - container.size(); } }; +#undef ABSL_BTREE_ENABLE_GENERATIONS + } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index bae5c6e2..cc2e1793 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -537,6 +537,7 @@ class btree_multiset_container : public btree_container { using params_type = typename Tree::params_type; using init_type = typename params_type::init_type; using is_key_compare_to = typename params_type::is_key_compare_to; + friend class BtreeNodePeer; template using key_arg = typename super_type::template key_arg; @@ -668,6 +669,7 @@ template class btree_multimap_container : public btree_multiset_container { using super_type = btree_multiset_container; using params_type = typename Tree::params_type; + friend class BtreeNodePeer; public: using mapped_type = typename params_type::mapped_type; diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h index 7b2b80eb..89fa5a47 100644 --- a/absl/random/mocking_bit_gen.h +++ b/absl/random/mocking_bit_gen.h @@ -87,7 +87,7 @@ class BitGenRef; // // ON_CALL(absl::MockUniform(), Call(bitgen, testing::_, testing::_)) // .WillByDefault([] (int low, int high) { -// return (low + high) / 2; +// return low + (high - low) / 2; // }); // // EXPECT_EQ(absl::Uniform(gen, 0, 10), 5); diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 617577cd..129affec 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -271,7 +271,6 @@ cc_library( "internal/cord_rep_btree.cc", "internal/cord_rep_btree_navigator.cc", "internal/cord_rep_btree_reader.cc", - "internal/cord_rep_concat.cc", "internal/cord_rep_consume.cc", "internal/cord_rep_crc.cc", "internal/cord_rep_ring.cc", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index e73a1ea0..f31eef4d 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -568,7 +568,6 @@ absl_cc_library( "internal/cord_rep_btree.cc" "internal/cord_rep_btree_navigator.cc" "internal/cord_rep_btree_reader.cc" - "internal/cord_rep_concat.cc" "internal/cord_rep_crc.cc" "internal/cord_rep_consume.cc" "internal/cord_rep_ring.cc" diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 59722107..4ee722da 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -53,7 +53,6 @@ ABSL_NAMESPACE_BEGIN using ::absl::cord_internal::CordRep; using ::absl::cord_internal::CordRepBtree; -using ::absl::cord_internal::CordRepConcat; using ::absl::cord_internal::CordRepCrc; using ::absl::cord_internal::CordRepExternal; using ::absl::cord_internal::CordRepFlat; @@ -66,53 +65,6 @@ using ::absl::cord_internal::kMinFlatLength; using ::absl::cord_internal::kInlinedVectorSize; using ::absl::cord_internal::kMaxBytesToCopy; -constexpr uint64_t Fibonacci(unsigned char n, uint64_t a = 0, uint64_t b = 1) { - return n == 0 ? a : Fibonacci(n - 1, b, a + b); -} - -static_assert(Fibonacci(63) == 6557470319842, - "Fibonacci values computed incorrectly"); - -// Minimum length required for a given depth tree -- a tree is considered -// balanced if -// length(t) >= min_length[depth(t)] -// The root node depth is allowed to become twice as large to reduce rebalancing -// for larger strings (see IsRootBalanced). -static constexpr uint64_t min_length[] = { - Fibonacci(2), Fibonacci(3), Fibonacci(4), Fibonacci(5), - Fibonacci(6), Fibonacci(7), Fibonacci(8), Fibonacci(9), - Fibonacci(10), Fibonacci(11), Fibonacci(12), Fibonacci(13), - Fibonacci(14), Fibonacci(15), Fibonacci(16), Fibonacci(17), - Fibonacci(18), Fibonacci(19), Fibonacci(20), Fibonacci(21), - Fibonacci(22), Fibonacci(23), Fibonacci(24), Fibonacci(25), - Fibonacci(26), Fibonacci(27), Fibonacci(28), Fibonacci(29), - Fibonacci(30), Fibonacci(31), Fibonacci(32), Fibonacci(33), - Fibonacci(34), Fibonacci(35), Fibonacci(36), Fibonacci(37), - Fibonacci(38), Fibonacci(39), Fibonacci(40), Fibonacci(41), - Fibonacci(42), Fibonacci(43), Fibonacci(44), Fibonacci(45), - Fibonacci(46), Fibonacci(47), - 0xffffffffffffffffull, // Avoid overflow -}; - -static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length); - -static inline constexpr bool btree_enabled() { return true; } - -static inline bool IsRootBalanced(CordRep* node) { - if (!node->IsConcat()) { - return true; - } else if (node->concat()->depth() <= 15) { - return true; - } else if (node->concat()->depth() > kMinLengthSize) { - return false; - } else { - // Allow depth to become twice as large as implied by fibonacci rule to - // reduce rebalancing for larger strings. - return (node->length >= min_length[node->concat()->depth() / 2]); - } -} - -static CordRep* Rebalance(CordRep* node); static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, int indent = 0); static bool VerifyNode(CordRep* root, CordRep* start_node, @@ -134,24 +86,6 @@ static inline CordRep* VerifyTree(CordRep* node) { return node; } -// Return the depth of a node -static int Depth(const CordRep* rep) { - if (rep->IsConcat()) { - return rep->concat()->depth(); - } else { - return 0; - } -} - -static void SetConcatChildren(CordRepConcat* concat, CordRep* left, - CordRep* right) { - concat->left = left; - concat->right = right; - - concat->length = left->length + right->length; - concat->set_depth(1 + std::max(Depth(left), Depth(right))); -} - // Create a concatenation of the specified nodes. // Does not change the refcounts of "left" and "right". // The returned node has a refcount of 1. @@ -167,42 +101,15 @@ static CordRep* RawConcat(CordRep* left, CordRep* right) { CordRep::Unref(right); return left; } - - CordRepConcat* rep = new CordRepConcat(); - rep->tag = cord_internal::CONCAT; - SetConcatChildren(rep, left, right); - - return rep; + ABSL_INTERNAL_LOG(FATAL, "CordRepConcat is no longer supported"); + return nullptr; } static CordRep* Concat(CordRep* left, CordRep* right) { CordRep* rep = RawConcat(left, right); - if (rep != nullptr && !IsRootBalanced(rep)) { - rep = Rebalance(rep); - } return VerifyTree(rep); } -// Make a balanced tree out of an array of leaf nodes. -static CordRep* MakeBalancedTree(CordRep** reps, size_t n) { - // Make repeated passes over the array, merging adjacent pairs - // until we are left with just a single node. - while (n > 1) { - size_t dst = 0; - for (size_t src = 0; src < n; src += 2) { - if (src + 1 < n) { - reps[dst] = Concat(reps[src], reps[src + 1]); - } else { - reps[dst] = reps[src]; - } - dst++; - } - n = dst; - } - - return reps[0]; -} - static CordRepFlat* CreateFlat(const char* data, size_t length, size_t alloc_hint) { CordRepFlat* flat = CordRepFlat::New(length + alloc_hint); @@ -228,21 +135,7 @@ static CordRep* NewBtree(const char* data, size_t length, size_t alloc_hint) { // The returned node has a refcount of 1. static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) { if (length == 0) return nullptr; - if (btree_enabled()) { - return NewBtree(data, length, alloc_hint); - } - absl::FixedArray reps((length - 1) / kMaxFlatLength + 1); - size_t n = 0; - do { - const size_t len = std::min(length, kMaxFlatLength); - CordRepFlat* rep = CordRepFlat::New(len + alloc_hint); - rep->length = len; - memcpy(rep->Data(), data, len); - reps[n++] = VerifyTree(rep); - data += len; - length -= len; - } while (length != 0); - return MakeBalancedTree(reps.data(), n); + return NewBtree(data, length, alloc_hint); } namespace cord_internal { @@ -350,11 +243,7 @@ void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, assert(!is_tree()); if (!data_.is_empty()) { CordRepFlat* flat = MakeFlatWithExtraCapacity(0); - if (btree_enabled()) { - tree = CordRepBtree::Append(CordRepBtree::Create(flat), tree); - } else { - tree = Concat(flat, tree); - } + tree = CordRepBtree::Append(CordRepBtree::Create(flat), tree); } EmplaceTree(tree, method); } @@ -362,11 +251,7 @@ void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) { assert(is_tree()); const CordzUpdateScope scope(data_.cordz_info(), method); - if (btree_enabled()) { - tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree); - } else { - tree = Concat(cord_internal::RemoveCrcNode(data_.as_tree()), tree); - } + tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree); SetTree(tree, scope); } @@ -386,11 +271,7 @@ void Cord::InlineRep::PrependTreeToInlined(CordRep* tree, assert(!is_tree()); if (!data_.is_empty()) { CordRepFlat* flat = MakeFlatWithExtraCapacity(0); - if (btree_enabled()) { - tree = CordRepBtree::Prepend(CordRepBtree::Create(flat), tree); - } else { - tree = Concat(tree, flat); - } + tree = CordRepBtree::Prepend(CordRepBtree::Create(flat), tree); } EmplaceTree(tree, method); } @@ -399,11 +280,7 @@ void Cord::InlineRep::PrependTreeToTree(CordRep* tree, MethodIdentifier method) { assert(is_tree()); const CordzUpdateScope scope(data_.cordz_info(), method); - if (btree_enabled()) { - tree = CordRepBtree::Prepend(ForceBtree(data_.as_tree()), tree); - } else { - tree = Concat(tree, cord_internal::RemoveCrcNode(data_.as_tree())); - } + tree = CordRepBtree::Prepend(ForceBtree(data_.as_tree()), tree); SetTree(tree, scope); } @@ -433,12 +310,7 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, } } - // Search down the right-hand path for a non-full FLAT node. CordRep* dst = root; - while (dst->IsConcat() && dst->refcount.IsOne()) { - dst = dst->concat()->right; - } - if (!dst->IsFlat() || !dst->refcount.IsOne()) { *region = nullptr; *size = 0; @@ -453,12 +325,7 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, return false; } - size_t size_increase = std::min(capacity - in_use, max_length); - - // We need to update the length fields for all nodes, including the leaf node. - for (CordRep* rep = root; rep != dst; rep = rep->concat()->right) { - rep->length += size_increase; - } + const size_t size_increase = std::min(capacity - in_use, max_length); dst->length += size_increase; *region = dst->flat()->Data() + in_use; @@ -627,27 +494,11 @@ void Cord::InlineRep::AppendArray(absl::string_view src, return; } - if (btree_enabled()) { - // TODO(b/192061034): keep legacy 10% growth rate: consider other rates. - rep = ForceBtree(rep); - const size_t min_growth = std::max(rep->length / 10, src.size()); - rep = CordRepBtree::Append(rep->btree(), src, min_growth - src.size()); - } else { - // Use new block(s) for any remaining bytes that were not handled above. - // Alloc extra memory only if the right child of the root of the new tree - // is going to be a FLAT node, which will permit further inplace appends. - size_t length = src.size(); - if (src.size() < kMaxFlatLength) { - // The new length is either - // - old size + 10% - // - old_size + src.size() - // This will cause a reasonable conservative step-up in size that is - // still large enough to avoid excessive amounts of small fragments - // being added. - length = std::max(rep->length / 10, src.size()); - } - rep = Concat(rep, NewTree(src.data(), src.size(), length - src.size())); - } + // TODO(b/192061034): keep legacy 10% growth rate: consider other rates. + rep = ForceBtree(rep); + const size_t min_growth = std::max(rep->length / 10, src.size()); + rep = CordRepBtree::Append(rep->btree(), src, min_growth - src.size()); + CommitTree(root, rep, scope, method); } @@ -779,18 +630,6 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { absl::InlinedVector rhs_stack; assert(!node->IsCrc()); - while (node->IsConcat()) { - assert(n <= node->length); - if (n < node->concat()->left->length) { - // Push right to stack, descend left. - rhs_stack.push_back(node->concat()->right); - node = node->concat()->left; - } else { - // Drop left, descend right. - n -= node->concat()->left->length; - node = node->concat()->right; - } - } assert(n <= node->length); if (n == 0) { @@ -822,19 +661,6 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { bool inplace_ok = node->refcount.IsOne(); assert(!node->IsCrc()); - while (node->IsConcat()) { - assert(n <= node->length); - if (n < node->concat()->right->length) { - // Push left to stack, descend right. - lhs_stack.push_back(node->concat()->left); - node = node->concat()->right; - } else { - // Drop right, descend left. - n -= node->concat()->right->length; - node = node->concat()->left; - } - inplace_ok = inplace_ok && node->refcount.IsOne(); - } assert(n <= node->length); if (n == 0) { @@ -936,22 +762,12 @@ static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { results.push_back(Concat(left, right)); } else if (pos == 0 && n == node->length) { results.push_back(CordRep::Ref(node)); - } else if (!node->IsConcat()) { + } else { if (node->IsSubstring()) { pos += node->substring()->start; node = node->substring()->child; } results.push_back(NewSubstring(CordRep::Ref(node), pos, n)); - } else if (pos + n <= node->concat()->left->length) { - todo.push_back(SubRange(node->concat()->left, pos, n)); - } else if (pos >= node->concat()->left->length) { - pos -= node->concat()->left->length; - todo.push_back(SubRange(node->concat()->right, pos, n)); - } else { - size_t left_n = node->concat()->left->length - pos; - todo.push_back(SubRange(nullptr, 0, 0)); // Concat() - todo.push_back(SubRange(node->concat()->right, 0, n - left_n)); - todo.push_back(SubRange(node->concat()->left, pos, left_n)); } } while (!todo.empty()); assert(results.size() == 1); @@ -998,147 +814,6 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { return sub_cord; } -// -------------------------------------------------------------------- -// Balancing - -class CordForest { - public: - explicit CordForest(size_t length) - : root_length_(length), trees_(kMinLengthSize, nullptr) {} - - void Build(CordRep* cord_root) { - std::vector pending = {cord_root}; - assert(cord_root->IsConcat()); - - while (!pending.empty()) { - CordRep* node = pending.back(); - pending.pop_back(); - CheckNode(node); - if (ABSL_PREDICT_FALSE(!node->IsConcat())) { - AddNode(node); - continue; - } - - CordRepConcat* concat_node = node->concat(); - if (concat_node->depth() >= kMinLengthSize || - concat_node->length < min_length[concat_node->depth()]) { - pending.push_back(concat_node->right); - pending.push_back(concat_node->left); - - if (concat_node->refcount.IsOne()) { - concat_node->left = concat_freelist_; - concat_freelist_ = concat_node; - } else { - CordRep::Ref(concat_node->right); - CordRep::Ref(concat_node->left); - CordRep::Unref(concat_node); - } - } else { - AddNode(node); - } - } - } - - CordRep* ConcatNodes() { - CordRep* sum = nullptr; - for (auto* node : trees_) { - if (node == nullptr) continue; - - sum = PrependNode(node, sum); - root_length_ -= node->length; - if (root_length_ == 0) break; - } - ABSL_INTERNAL_CHECK(sum != nullptr, "Failed to locate sum node"); - return VerifyTree(sum); - } - - private: - CordRep* AppendNode(CordRep* node, CordRep* sum) { - return (sum == nullptr) ? node : MakeConcat(sum, node); - } - - CordRep* PrependNode(CordRep* node, CordRep* sum) { - return (sum == nullptr) ? node : MakeConcat(node, sum); - } - - void AddNode(CordRep* node) { - CordRep* sum = nullptr; - - // Collect together everything with which we will merge with node - int i = 0; - for (; node->length > min_length[i + 1]; ++i) { - auto& tree_at_i = trees_[i]; - - if (tree_at_i == nullptr) continue; - sum = PrependNode(tree_at_i, sum); - tree_at_i = nullptr; - } - - sum = AppendNode(node, sum); - - // Insert sum into appropriate place in the forest - for (; sum->length >= min_length[i]; ++i) { - auto& tree_at_i = trees_[i]; - if (tree_at_i == nullptr) continue; - - sum = MakeConcat(tree_at_i, sum); - tree_at_i = nullptr; - } - - // min_length[0] == 1, which means sum->length >= min_length[0] - assert(i > 0); - trees_[i - 1] = sum; - } - - // Make concat node trying to resue existing CordRepConcat nodes we - // already collected in the concat_freelist_. - CordRep* MakeConcat(CordRep* left, CordRep* right) { - if (concat_freelist_ == nullptr) return RawConcat(left, right); - - CordRepConcat* rep = concat_freelist_; - if (concat_freelist_->left == nullptr) { - concat_freelist_ = nullptr; - } else { - concat_freelist_ = concat_freelist_->left->concat(); - } - SetConcatChildren(rep, left, right); - - return rep; - } - - static void CheckNode(CordRep* node) { - ABSL_INTERNAL_CHECK(node->length != 0u, ""); - if (node->IsConcat()) { - ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ""); - ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, ""); - ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length + - node->concat()->right->length), - ""); - } - } - - size_t root_length_; - - // use an inlined vector instead of a flat array to get bounds checking - absl::InlinedVector trees_; - - // List of concat nodes we can re-use for Cord balancing. - CordRepConcat* concat_freelist_ = nullptr; -}; - -static CordRep* Rebalance(CordRep* node) { - VerifyTree(node); - assert(node->IsConcat()); - - if (node->length == 0) { - return nullptr; - } - - CordForest forest(node->length); - forest.Build(node); - return forest.ConcatNodes(); -} - // -------------------------------------------------------------------- // Comparators @@ -1203,11 +878,6 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { return tree->Data(tree->begin()); } - // Walk down the left branches until we hit a non-CONCAT node. - while (node->IsConcat()) { - node = node->concat()->left; - } - // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; @@ -1436,13 +1106,6 @@ Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() { CordRep* node = stack_of_right_children.back(); stack_of_right_children.pop_back(); - // Walk down the left branches until we hit a non-CONCAT node. Save the - // right children to the stack for subsequent traversal. - while (node->IsConcat()) { - stack_of_right_children.push_back(node->concat()->right); - node = node->concat()->left; - } - // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; @@ -1557,22 +1220,6 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { return subcord; } - // Walk down the appropriate branches until we hit a non-CONCAT node. Save the - // right children to the stack for subsequent traversal. - while (node->IsConcat()) { - if (node->concat()->left->length > n) { - // Push right, descend left. - stack_of_right_children.push_back(node->concat()->right); - node = node->concat()->left; - } else { - // Read left, descend right. - subnode = Concat(subnode, CordRep::Ref(node->concat()->left)); - n -= node->concat()->left->length; - bytes_remaining_ -= node->concat()->left->length; - node = node->concat()->right; - } - } - // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; @@ -1630,21 +1277,6 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { return; } - // Walk down the appropriate branches until we hit a non-CONCAT node. Save the - // right children to the stack for subsequent traversal. - while (node->IsConcat()) { - if (node->concat()->left->length > n) { - // Push right, descend left. - stack_of_right_children.push_back(node->concat()->right); - node = node->concat()->left; - } else { - // Skip left, descend right. - n -= node->concat()->left->length; - bytes_remaining_ -= node->concat()->left->length; - node = node->concat()->right; - } - } - // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; @@ -1681,16 +1313,6 @@ char Cord::operator[](size_t i) const { } else if (rep->IsExternal()) { // Get the "i"th character from the external array. return rep->external()->base[offset]; - } else if (rep->IsConcat()) { - // Recursively branch to the side of the concatenation that the "i"th - // character is on. - size_t left_length = rep->concat()->left->length; - if (offset < left_length) { - rep = rep->concat()->left; - } else { - offset -= left_length; - rep = rep->concat()->right; - } } else { // This must be a substring a node, so bypass it to get to the child. assert(rep->IsSubstring()); @@ -1772,43 +1394,13 @@ absl::string_view Cord::FlattenSlowPath() { return; } - int stack_pos = 0; - constexpr int stack_max = 128; - // Stack of right branches for tree traversal - absl::cord_internal::CordRep* stack[stack_max]; + // This is a leaf node, so invoke our callback. absl::cord_internal::CordRep* current_node = cord_internal::SkipCrcNode(rep); - while (true) { - if (current_node->IsConcat()) { - if (stack_pos == stack_max) { - // There's no more room on our stack array to add another right branch, - // and the idea is to avoid allocations, so call this function - // recursively to navigate this subtree further. (This is not something - // we expect to happen in practice). - ForEachChunkAux(current_node, callback); - - // Pop the next right branch and iterate. - current_node = stack[--stack_pos]; - continue; - } else { - // Save the right branch for later traversal and continue down the left - // branch. - stack[stack_pos++] = current_node->concat()->right; - current_node = current_node->concat()->left; - continue; - } - } - // This is a leaf node, so invoke our callback. - absl::string_view chunk; - bool success = GetFlatAux(current_node, &chunk); - assert(success); - if (success) { - callback(chunk); - } - if (stack_pos == 0) { - // end of traversal - return; - } - current_node = stack[--stack_pos]; + absl::string_view chunk; + bool success = GetFlatAux(current_node, &chunk); + assert(success); + if (success) { + callback(chunk); } } @@ -1823,18 +1415,11 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, *os << " ["; if (include_data) *os << static_cast(rep); *os << "]"; - *os << " " << (IsRootBalanced(rep) ? 'b' : 'u'); *os << " " << std::setw(indent) << ""; if (rep->IsCrc()) { *os << "CRC crc=" << rep->crc()->crc << "\n"; indent += kIndentStep; rep = rep->crc()->child; - } else if (rep->IsConcat()) { - *os << "CONCAT depth=" << Depth(rep) << "\n"; - indent += kIndentStep; - indents.push_back(indent); - stack.push_back(rep->concat()->right); - rep = rep->concat()->left; } else if (rep->IsSubstring()) { *os << "SUBSTRING @ " << rep->substring()->start << "\n"; indent += kIndentStep; @@ -1871,7 +1456,7 @@ static std::string ReportError(CordRep* root, CordRep* node) { } static bool VerifyNode(CordRep* root, CordRep* start_node, - bool full_validation) { + bool /* full_validation */) { absl::InlinedVector worklist; worklist.push_back(start_node); do { @@ -1884,19 +1469,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, ABSL_INTERNAL_CHECK(!node->IsCrc(), ReportError(root, node)); } - if (node->IsConcat()) { - ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, - ReportError(root, node)); - ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, - ReportError(root, node)); - ABSL_INTERNAL_CHECK((node->length == node->concat()->left->length + - node->concat()->right->length), - ReportError(root, node)); - if (full_validation) { - worklist.push_back(node->concat()->right); - worklist.push_back(node->concat()->left); - } - } else if (node->IsFlat()) { + if (node->IsFlat()) { ABSL_INTERNAL_CHECK(node->length <= node->flat()->Capacity(), ReportError(root, node)); } else if (node->IsExternal()) { @@ -1937,7 +1510,6 @@ uint8_t CordTestAccess::LengthToTag(size_t s) { ABSL_INTERNAL_CHECK(s <= kMaxFlatLength, absl::StrCat("Invalid length ", s)); return cord_internal::AllocatedSizeToTag(s + cord_internal::kFlatOverhead); } -size_t CordTestAccess::SizeofCordRepConcat() { return sizeof(CordRepConcat); } size_t CordTestAccess::SizeofCordRepExternal() { return sizeof(CordRepExternal); } diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 49d51da2..7f34ef48 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -1553,7 +1553,6 @@ class CordTestAccess { public: static size_t FlatOverhead(); static size_t MaxFlatLength(); - static size_t SizeofCordRepConcat(); static size_t SizeofCordRepExternal(); static size_t SizeofCordRepSubstring(); static size_t FlatTagToLength(uint8_t tag); diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc index c0d9ea79..3fa15b01 100644 --- a/absl/strings/cord_analysis.cc +++ b/absl/strings/cord_analysis.cc @@ -128,45 +128,6 @@ void AnalyzeDataEdge(CordRepRef rep, RawUsage& raw_usage) { raw_usage.Add(size, rep); } -// Computes the memory size of the provided Concat tree. -template -void AnalyzeConcat(CordRepRef rep, RawUsage& raw_usage) { - absl::InlinedVector, 47> pending; - - while (rep.rep != nullptr) { - const CordRepConcat* concat = rep.rep->concat(); - CordRepRef left = rep.Child(concat->left); - CordRepRef right = rep.Child(concat->right); - - raw_usage.Add(sizeof(CordRepConcat), rep); - - switch ((IsDataEdge(left.rep) ? 1 : 0) | (IsDataEdge(right.rep) ? 2 : 0)) { - case 0: // neither left or right are data edges - rep = left; - pending.push_back(right); - break; - case 1: // only left is a data edge - AnalyzeDataEdge(left, raw_usage); - rep = right; - break; - case 2: // only right is a data edge - AnalyzeDataEdge(right, raw_usage); - rep = left; - break; - case 3: // left and right are data edges - AnalyzeDataEdge(right, raw_usage); - AnalyzeDataEdge(left, raw_usage); - if (!pending.empty()) { - rep = pending.back(); - pending.pop_back(); - } else { - rep.rep = nullptr; - } - break; - } - } -} - // Computes the memory size of the provided Ring tree. template void AnalyzeRing(CordRepRef rep, RawUsage& raw_usage) { @@ -211,8 +172,6 @@ size_t GetEstimatedUsage(const CordRep* rep) { AnalyzeDataEdge(repref, raw_usage); } else if (repref.rep->tag == BTREE) { AnalyzeBtree(repref, raw_usage); - } else if (repref.rep->IsConcat()) { - AnalyzeConcat(repref, raw_usage); } else if (repref.rep->tag == RING) { AnalyzeRing(repref, raw_usage); } else { diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index 75544191..06119350 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -36,53 +36,31 @@ ABSL_CONST_INIT std::atomic cord_btree_exhaustive_validation(false); void CordRep::Destroy(CordRep* rep) { assert(rep != nullptr); - absl::InlinedVector pending; while (true) { assert(!rep->refcount.IsImmortal()); - if (rep->IsConcat()) { - CordRepConcat* rep_concat = rep->concat(); - CordRep* right = rep_concat->right; - if (!right->refcount.Decrement()) { - pending.push_back(right); - } - CordRep* left = rep_concat->left; - delete rep_concat; - rep = nullptr; - if (!left->refcount.Decrement()) { - rep = left; - continue; - } - } else if (rep->tag == BTREE) { + if (rep->tag == BTREE) { CordRepBtree::Destroy(rep->btree()); - rep = nullptr; + return; } else if (rep->tag == RING) { CordRepRing::Destroy(rep->ring()); - rep = nullptr; + return; } else if (rep->tag == EXTERNAL) { CordRepExternal::Delete(rep); - rep = nullptr; + return; } else if (rep->tag == SUBSTRING) { CordRepSubstring* rep_substring = rep->substring(); - CordRep* child = rep_substring->child; + rep = rep_substring->child; delete rep_substring; - rep = nullptr; - if (!child->refcount.Decrement()) { - rep = child; - continue; + if (rep->refcount.Decrement()) { + return; } } else if (rep->tag == CRC) { CordRepCrc::Destroy(rep->crc()); - rep = nullptr; + return; } else { + assert(rep->IsFlat()); CordRepFlat::Delete(rep); - rep = nullptr; - } - - if (!pending.empty()) { - rep = pending.back(); - pending.pop_back(); - } else { - break; + return; } } } diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index b9ecbba6..8db6aa6d 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -171,7 +171,7 @@ class CordRepBtree; // Various representations that we allow enum CordRepKind { - CONCAT = 0, + UNUSED_0 = 0, SUBSTRING = 1, CRC = 2, BTREE = 3, @@ -239,7 +239,6 @@ struct CordRep { // Returns true if this instance's tag matches the requested type. constexpr bool IsRing() const { return tag == RING; } - constexpr bool IsConcat() const { return tag == CONCAT; } constexpr bool IsSubstring() const { return tag == SUBSTRING; } constexpr bool IsCrc() const { return tag == CRC; } constexpr bool IsExternal() const { return tag == EXTERNAL; } @@ -248,8 +247,6 @@ struct CordRep { inline CordRepRing* ring(); inline const CordRepRing* ring() const; - inline CordRepConcat* concat(); - inline const CordRepConcat* concat() const; inline CordRepSubstring* substring(); inline const CordRepSubstring* substring() const; inline CordRepCrc* crc(); @@ -276,21 +273,6 @@ struct CordRep { static inline void Unref(CordRep* rep); }; -struct CordRepConcat : public CordRep { - CordRep* left; - CordRep* right; - - uint8_t depth() const { return storage[0]; } - void set_depth(uint8_t depth) { storage[0] = depth; } - - // Extracts the right-most flat in the provided concat tree if the entire path - // to that flat is not shared, and the flat has the requested extra capacity. - // Returns the (potentially new) top level tree node and the extracted flat, - // or {tree, nullptr} if no flat was extracted. - static ExtractResult ExtractAppendBuffer(CordRepConcat* tree, - size_t extra_capacity); -}; - struct CordRepSubstring : public CordRep { size_t start; // Starting offset of substring in child CordRep* child; @@ -569,16 +551,6 @@ class InlineData { static_assert(sizeof(InlineData) == kMaxInline + 1, ""); -inline CordRepConcat* CordRep::concat() { - assert(IsConcat()); - return static_cast(this); -} - -inline const CordRepConcat* CordRep::concat() const { - assert(IsConcat()); - return static_cast(this); -} - inline CordRepSubstring* CordRep::substring() { assert(IsSubstring()); return static_cast(this); diff --git a/absl/strings/internal/cord_rep_concat.cc b/absl/strings/internal/cord_rep_concat.cc deleted file mode 100644 index 3457b55c..00000000 --- a/absl/strings/internal/cord_rep_concat.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2021 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include - -#include "absl/base/config.h" -#include "absl/container/inlined_vector.h" -#include "absl/strings/internal/cord_internal.h" -#include "absl/strings/internal/cord_rep_flat.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace cord_internal { - -CordRepConcat::ExtractResult CordRepConcat::ExtractAppendBuffer( - CordRepConcat* tree, size_t extra_capacity) { - absl::InlinedVector stack; - CordRepConcat* concat = tree; - CordRep* rep = concat->right; - - // Dive down the tree, making sure no edges are shared - while (concat->refcount.IsOne() && rep->IsConcat()) { - stack.push_back(concat); - concat = rep->concat(); - rep = concat->right; - } - // Validate we ended on a non shared flat. - if (concat->refcount.IsOne() && rep->IsFlat() && - rep->refcount.IsOne()) { - // Verify it has at least the requested extra capacity - CordRepFlat* flat = rep->flat(); - size_t remaining = flat->Capacity() - flat->length; - if (extra_capacity > remaining) return {tree, nullptr}; - - // Check if we have a parent to adjust, or if we must return the left node. - rep = concat->left; - if (!stack.empty()) { - stack.back()->right = rep; - for (CordRepConcat* parent : stack) { - parent->length -= flat->length; - } - rep = tree; - } - delete concat; - return {rep, flat}; - } - return {tree, nullptr}; -} - -} // namespace cord_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/strings/internal/cord_rep_consume.cc b/absl/strings/internal/cord_rep_consume.cc index 253339be..20a55797 100644 --- a/absl/strings/internal/cord_rep_consume.cc +++ b/absl/strings/internal/cord_rep_consume.cc @@ -40,88 +40,21 @@ CordRep* ClipSubstring(CordRepSubstring* substring) { return child; } -// Unrefs the provided `concat`, and returns `{concat->left, concat->right}` -// Adds or assumes a reference on `concat->left` and `concat->right`. -// Returns an array of 2 elements containing the left and right nodes. -std::array ClipConcat(CordRepConcat* concat) { - std::array result{concat->left, concat->right}; - if (concat->refcount.IsOne()) { - delete concat; - } else { - CordRep::Ref(result[0]); - CordRep::Ref(result[1]); - CordRep::Unref(concat); - } - return result; -} +} // namespace -void Consume(bool forward, CordRep* rep, ConsumeFn consume_fn) { +void Consume(CordRep* rep, ConsumeFn consume_fn) { size_t offset = 0; size_t length = rep->length; - struct Entry { - CordRep* rep; - size_t offset; - size_t length; - }; - absl::InlinedVector stack; - - for (;;) { - if (rep->IsConcat()) { - std::array res = ClipConcat(rep->concat()); - CordRep* left = res[0]; - CordRep* right = res[1]; - - if (left->length <= offset) { - // Don't need left node - offset -= left->length; - CordRep::Unref(left); - rep = right; - continue; - } - size_t length_left = left->length - offset; - if (length_left >= length) { - // Don't need right node - CordRep::Unref(right); - rep = left; - continue; - } - - // Need both nodes - size_t length_right = length - length_left; - if (forward) { - stack.push_back({right, 0, length_right}); - rep = left; - length = length_left; - } else { - stack.push_back({left, offset, length_left}); - rep = right; - offset = 0; - length = length_right; - } - } else if (rep->tag == SUBSTRING) { - offset += rep->substring()->start; - rep = ClipSubstring(rep->substring()); - } else { - consume_fn(rep, offset, length); - if (stack.empty()) return; - - rep = stack.back().rep; - offset = stack.back().offset; - length = stack.back().length; - stack.pop_back(); - } + if (rep->tag == SUBSTRING) { + offset += rep->substring()->start; + rep = ClipSubstring(rep->substring()); } -} - -} // namespace - -void Consume(CordRep* rep, ConsumeFn consume_fn) { - return Consume(true, rep, std::move(consume_fn)); + consume_fn(rep, offset, length); } void ReverseConsume(CordRep* rep, ConsumeFn consume_fn) { - return Consume(false, rep, std::move(consume_fn)); + return Consume(rep, std::move(consume_fn)); } } // namespace cord_internal diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h index ae8b3a2a..e3e27fcd 100644 --- a/absl/strings/internal/cord_rep_flat.h +++ b/absl/strings/internal/cord_rep_flat.h @@ -73,9 +73,11 @@ static_assert(AllocatedSizeToTagUnchecked(kMinFlatSize) == FLAT, ""); static_assert(AllocatedSizeToTagUnchecked(kMaxLargeFlatSize) == MAX_FLAT_TAG, ""); -// Helper functions for rounded div, and rounding to exact sizes. -constexpr size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; } -constexpr size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; } +// RoundUp logically performs `((n + m - 1) / m) * m` to round up to the nearest +// multiple of `m`, optimized for the invariant that `m` is a power of 2. +constexpr size_t RoundUp(size_t n, size_t m) { + return (n + m - 1) & (0 - m); +} // Returns the size to the nearest equal or larger value that can be // expressed exactly as a tag value. diff --git a/absl/strings/internal/cord_rep_test_util.h b/absl/strings/internal/cord_rep_test_util.h index 692d7db5..18a0a195 100644 --- a/absl/strings/internal/cord_rep_test_util.h +++ b/absl/strings/internal/cord_rep_test_util.h @@ -114,9 +114,6 @@ inline void CordVisitReps(cord_internal::CordRep* rep, Fn&& fn) { for (cord_internal::CordRep* edge : rep->btree()->Edges()) { CordVisitReps(edge, fn); } - } else if (rep->IsConcat()) { - CordVisitReps(rep->concat()->left, fn); - CordVisitReps(rep->concat()->right, fn); } } diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index 24605260..c891d0ed 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -98,8 +98,6 @@ class CordRepAnalyzer { AnalyzeRing(repref); } else if (repref.rep->tag == BTREE) { AnalyzeBtree(repref); - } else if (repref.rep->IsConcat()) { - AnalyzeConcat(repref); } else { // We should have either a concat, btree, or ring node if not null. assert(false); @@ -141,14 +139,6 @@ class CordRepAnalyzer { } }; - // Returns `rr` if `rr.rep` is not null and a CONCAT type. - // Asserts that `rr.rep` is a concat node or null. - static RepRef AssertConcat(RepRef repref) { - const CordRep* rep = repref.rep; - assert(rep == nullptr || rep->IsConcat()); - return (rep != nullptr && rep->IsConcat()) ? repref : RepRef{nullptr, 0}; - } - // Counts a flat of the provide allocated size void CountFlat(size_t size) { statistics_.node_count++; @@ -201,34 +191,6 @@ class CordRepAnalyzer { return rep; } - // Analyzes the provided concat node in a flattened recursive way. - void AnalyzeConcat(RepRef rep) { - absl::InlinedVector pending; - - while (rep.rep != nullptr) { - const CordRepConcat* concat = rep.rep->concat(); - RepRef left = rep.Child(concat->left); - RepRef right = rep.Child(concat->right); - - statistics_.node_count++; - statistics_.node_counts.concat++; - memory_usage_.Add(sizeof(CordRepConcat), rep.refcount); - - right = AssertConcat(CountLinearReps(right, memory_usage_)); - rep = AssertConcat(CountLinearReps(left, memory_usage_)); - if (rep.rep != nullptr) { - if (right.rep != nullptr) { - pending.push_back(right); - } - } else if (right.rep != nullptr) { - rep = right; - } else if (!pending.empty()) { - rep = pending.back(); - pending.pop_back(); - } - } - } - // Analyzes the provided ring. void AnalyzeRing(RepRef rep) { statistics_.node_count++; diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc index 40f93dd3..476c38d2 100644 --- a/absl/strings/internal/cordz_info_statistics_test.cc +++ b/absl/strings/internal/cordz_info_statistics_test.cc @@ -148,10 +148,6 @@ double FairShareImpl(CordRep* rep, size_t ref) { rep->ring()->ForEach([&](CordRepRing::index_type i) { self += FairShareImpl(rep->ring()->entry_child(i), 1); }); - } else if (rep->IsConcat()) { - self = SizeOf(rep->concat()); - children = FairShareImpl(rep->concat()->left, ref) + - FairShareImpl(rep->concat()->right, ref); } else { assert(false); } -- cgit v1.2.3 From 7f850b3167fb38e6b4a9ce1824e6fabd733b5d62 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 17 Feb 2022 12:07:55 -0800 Subject: Export of internal Abseil changes -- ed829ac612f090375427c3488827c6e74deb2e3f by Derek Mauro : Update latest GCC/Clang Linux tests to Bazel 5.0.0 and CMake 3.22.2 PiperOrigin-RevId: 429369775 -- 76952303c4d942288c4e7657ffb5893cec54a132 by Martijn Vels : Optimize Cord::ChunkIterator now that CordRepConcat is removed PiperOrigin-RevId: 429321455 -- dcd0d287793649aba9b98268c5783e449a34749f by Martijn Vels : Add IsDataEdge() and DataEdgeValue() helper functions. This moves repetitive logic accessing data edges into its own header, and more strongly defines the notion of what a data edge is, enforcing the internal invariants. This will also be incorporated in optimized Cord iteration logic once CordRepConcat is totally removed from the Cord code. PiperOrigin-RevId: 429307248 -- 6a0903962155988085bf8656743fda9c4cdcba6c by Abseil Team : Make it clear that the probability function given for the zipf distribution is unnormalized, i.e., sum(p(x) for x = 0..k) != 100%. Quoting Section 7 of the paper cited in the comments, where this formula comes from (emphasis mine): "We will consider the two parameter generalization as defined in Dagpunar [1988] with the *unnormalized* probability function ..." PiperOrigin-RevId: 429068258 -- 3899ff6d444ba755148bc521a6ee031d9e9d4485 by Abseil Team : Internal Changes PiperOrigin-RevId: 428644856 -- 319de702d2b537cbb76c4c71277ae89b349b162e by Benjamin Barenblat : Support symbolization on PA-RISC Null out supervisor bits in PA-RISC addresses before symbolizing, and handle function descriptor tables correctly. Change symbolize_test.cc to use 32-bit aligned addresses, allowing that test to pass on PA-RISC. PiperOrigin-RevId: 428590564 GitOrigin-RevId: ed829ac612f090375427c3488827c6e74deb2e3f Change-Id: Ie01ff3b9365fd45e5a55f858038552679f3180d3 --- CMake/AbseilDll.cmake | 1 + absl/debugging/symbolize_elf.inc | 45 +++++- absl/debugging/symbolize_test.cc | 8 +- absl/random/zipf_distribution.h | 2 +- absl/strings/BUILD.bazel | 16 ++ absl/strings/CMakeLists.txt | 18 +++ absl/strings/cord.cc | 173 +++------------------- absl/strings/cord.h | 56 +++---- absl/strings/cord_analysis.cc | 14 +- absl/strings/cord_test.cc | 72 +++++++++ absl/strings/internal/cord_data_edge.h | 63 ++++++++ absl/strings/internal/cord_data_edge_test.cc | 130 ++++++++++++++++ absl/strings/internal/cord_rep_btree.cc | 7 +- absl/strings/internal/cord_rep_btree.h | 31 +--- absl/strings/internal/cord_rep_btree_navigator.cc | 3 +- absl/strings/internal/cord_rep_btree_reader.cc | 5 +- absl/strings/internal/cord_rep_btree_reader.h | 9 +- absl/strings/internal/cord_rep_btree_test.cc | 33 ++--- ci/linux_docker_containers.sh | 4 +- 19 files changed, 427 insertions(+), 263 deletions(-) create mode 100644 absl/strings/internal/cord_data_edge.h create mode 100644 absl/strings/internal/cord_data_edge_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 14ce9d18..0becc7a9 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -204,6 +204,7 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/charconv_bigint.h" "strings/internal/charconv_parse.cc" "strings/internal/charconv_parse.h" + "strings/internal/cord_data_edge.h" "strings/internal/cord_internal.cc" "strings/internal/cord_internal.h" "strings/internal/cord_rep_btree.cc" diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 3ff343d6..ddccd590 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -323,6 +323,7 @@ class Symbolizer { const ptrdiff_t relocation, char *out, int out_size, char *tmp_buf, int tmp_buf_size); + const char *GetUncachedSymbol(const void *pc); enum { SYMBOL_BUF_SIZE = 3072, @@ -1333,13 +1334,7 @@ static bool MaybeInitializeObjFile(ObjFile *obj) { // they are called here as well. // To keep stack consumption low, we would like this function to not // get inlined. -const char *Symbolizer::GetSymbol(const void *const pc) { - const char *entry = FindSymbolInCache(pc); - if (entry != nullptr) { - return entry; - } - symbol_buf_[0] = '\0'; - +const char *Symbolizer::GetUncachedSymbol(const void *pc) { ObjFile *const obj = FindObjFile(pc, 1); ptrdiff_t relocation = 0; int fd = -1; @@ -1427,6 +1422,42 @@ const char *Symbolizer::GetSymbol(const void *const pc) { return InsertSymbolInCache(pc, symbol_buf_); } +const char *Symbolizer::GetSymbol(const void *pc) { + const char *entry = FindSymbolInCache(pc); + if (entry != nullptr) { + return entry; + } + symbol_buf_[0] = '\0'; + +#ifdef __hppa__ + { + // In some contexts (e.g., return addresses), PA-RISC uses the lowest two + // bits of the address to indicate the privilege level. Clear those bits + // before trying to symbolize. + const auto pc_bits = reinterpret_cast(pc); + const auto address = pc_bits & ~0x3; + entry = GetUncachedSymbol(reinterpret_cast(address)); + if (entry != nullptr) { + return entry; + } + + // In some contexts, PA-RISC also uses bit 1 of the address to indicate that + // this is a cross-DSO function pointer. Such function pointers actually + // point to a procedure label, a struct whose first 32-bit (pointer) element + // actually points to the function text. With no symbol found for this + // address so far, try interpreting it as a cross-DSO function pointer and + // see how that goes. + if (pc_bits & 0x2) { + return GetUncachedSymbol(*reinterpret_cast(address)); + } + + return nullptr; + } +#else + return GetUncachedSymbol(pc); +#endif +} + bool RemoveAllSymbolDecorators(void) { if (!g_decorators_mu.TryLock()) { // Someone else is using decorators. Get out. diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index c710a3da..a62fa35d 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -392,12 +392,14 @@ TEST(Symbolize, InstallAndRemoveSymbolDecorators) { DummySymbolDecorator, &c_message), 0); - char *address = reinterpret_cast(1); - EXPECT_STREQ("abc", TrySymbolize(address++)); + // Use addresses 4 and 8 here to ensure that we always use valid addresses + // even on systems that require instructions to be 32-bit aligned. + char *address = reinterpret_cast(4); + EXPECT_STREQ("abc", TrySymbolize(address)); EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_b)); - EXPECT_STREQ("ac", TrySymbolize(address++)); + EXPECT_STREQ("ac", TrySymbolize(address + 4)); // Cleanup: remove all remaining decorators so other stack traces don't // get mystery "ac" decoration. diff --git a/absl/random/zipf_distribution.h b/absl/random/zipf_distribution.h index ed4038f1..03497b1b 100644 --- a/absl/random/zipf_distribution.h +++ b/absl/random/zipf_distribution.h @@ -30,7 +30,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN // absl::zipf_distribution produces random integer-values in the range [0, k], -// distributed according to the discrete probability function: +// distributed according to the unnormalized discrete probability function: // // P(x) = (v + x) ^ -q // diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 129affec..5f122ee9 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -276,6 +276,7 @@ cc_library( "internal/cord_rep_ring.cc", ], hdrs = [ + "internal/cord_data_edge.h", "internal/cord_internal.h", "internal/cord_rep_btree.h", "internal/cord_rep_btree_navigator.h", @@ -307,6 +308,21 @@ cc_library( ], ) +cc_test( + name = "cord_data_edge_test", + size = "small", + srcs = ["internal/cord_data_edge_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":cord_rep_test_util", + ":strings", + "//absl/base:config", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "cord_rep_btree_test", size = "medium", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index f31eef4d..5d418c86 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -554,6 +554,7 @@ absl_cc_library( NAME cord_internal HDRS + "internal/cord_data_edge.h" "internal/cord_internal.h" "internal/cord_rep_btree.h" "internal/cord_rep_btree_navigator.h" @@ -932,6 +933,23 @@ absl_cc_test( GTest::gmock_main ) +absl_cc_test( + NAME + cord_data_edge_test + SRCS + "internal/cord_data_edge_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::cord_rep_test_util + absl::core_headers + absl::strings + GTest::gmock_main +) + absl_cc_test( NAME cord_rep_btree_test diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 4ee722da..6547c2da 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -35,6 +35,7 @@ #include "absl/container/fixed_array.h" #include "absl/container/inlined_vector.h" #include "absl/strings/escaping.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_crc.h" @@ -1094,35 +1095,6 @@ void Cord::CopyToArraySlowPath(char* dst) const { } } -Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() { - auto& stack_of_right_children = stack_of_right_children_; - if (stack_of_right_children.empty()) { - assert(!current_chunk_.empty()); // Called on invalid iterator. - // We have reached the end of the Cord. - return *this; - } - - // Process the next node on the stack. - CordRep* node = stack_of_right_children.back(); - stack_of_right_children.pop_back(); - - // Get the child node if we encounter a SUBSTRING. - size_t offset = 0; - size_t length = node->length; - if (node->IsSubstring()) { - offset = node->substring()->start; - node = node->substring()->child; - } - - assert(node->IsExternal() || node->IsFlat()); - assert(length != 0); - const char* data = - node->IsExternal() ? node->external()->base : node->flat()->Data(); - current_chunk_ = absl::string_view(data + offset, length); - current_leaf_ = node; - return *this; -} - Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { ABSL_HARDENING_ASSERT(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); @@ -1165,133 +1137,36 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { return subcord; } - auto& stack_of_right_children = stack_of_right_children_; - if (n < current_chunk_.size()) { - // Range to read is a proper subrange of the current chunk. - assert(current_leaf_ != nullptr); - CordRep* subnode = CordRep::Ref(current_leaf_); - const char* data = subnode->IsExternal() ? subnode->external()->base - : subnode->flat()->Data(); - subnode = NewSubstring(subnode, current_chunk_.data() - data, n); - subcord.contents_.EmplaceTree(VerifyTree(subnode), method); - RemoveChunkPrefix(n); - return subcord; - } - - // Range to read begins with a proper subrange of the current chunk. - assert(!current_chunk_.empty()); + // Short circuit if reading the entire data edge. assert(current_leaf_ != nullptr); - CordRep* subnode = CordRep::Ref(current_leaf_); - if (current_chunk_.size() < subnode->length) { - const char* data = subnode->IsExternal() ? subnode->external()->base - : subnode->flat()->Data(); - subnode = NewSubstring(subnode, current_chunk_.data() - data, - current_chunk_.size()); - } - n -= current_chunk_.size(); - bytes_remaining_ -= current_chunk_.size(); - - // Process the next node(s) on the stack, reading whole subtrees depending on - // their length and how many bytes we are advancing. - CordRep* node = nullptr; - while (!stack_of_right_children.empty()) { - node = stack_of_right_children.back(); - stack_of_right_children.pop_back(); - if (node->length > n) break; - // TODO(qrczak): This might unnecessarily recreate existing concat nodes. - // Avoiding that would need pretty complicated logic (instead of - // current_leaf, keep current_subtree_ which points to the highest node - // such that the current leaf can be found on the path of left children - // starting from current_subtree_; delay creating subnode while node is - // below current_subtree_; find the proper node along the path of left - // children starting from current_subtree_ if this loop exits while staying - // below current_subtree_; etc.; alternatively, push parents instead of - // right children on the stack). - subnode = Concat(subnode, CordRep::Ref(node)); - n -= node->length; - bytes_remaining_ -= node->length; - node = nullptr; - } - - if (node == nullptr) { - // We have reached the end of the Cord. - assert(bytes_remaining_ == 0); - subcord.contents_.EmplaceTree(VerifyTree(subnode), method); + if (n == current_leaf_->length) { + bytes_remaining_ = 0; + current_chunk_ = {}; + CordRep* tree = CordRep::Ref(current_leaf_); + subcord.contents_.EmplaceTree(VerifyTree(tree), method); return subcord; } - // Get the child node if we encounter a SUBSTRING. - size_t offset = 0; - size_t length = node->length; - if (node->IsSubstring()) { - offset = node->substring()->start; - node = node->substring()->child; - } - - // Range to read ends with a proper (possibly empty) subrange of the current - // chunk. - assert(node->IsExternal() || node->IsFlat()); - assert(length > n); - if (n > 0) { - subnode = Concat(subnode, NewSubstring(CordRep::Ref(node), offset, n)); - } - const char* data = - node->IsExternal() ? node->external()->base : node->flat()->Data(); - current_chunk_ = absl::string_view(data + offset + n, length - n); - current_leaf_ = node; - bytes_remaining_ -= n; - subcord.contents_.EmplaceTree(VerifyTree(subnode), method); - return subcord; -} - -void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { - assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); - assert(n >= current_chunk_.size()); // This should only be called when - // iterating to a new node. - - n -= current_chunk_.size(); - bytes_remaining_ -= current_chunk_.size(); - - if (stack_of_right_children_.empty()) { - // We have reached the end of the Cord. - assert(bytes_remaining_ == 0); - return; - } - - // Process the next node(s) on the stack, skipping whole subtrees depending on - // their length and how many bytes we are advancing. - CordRep* node = nullptr; - auto& stack_of_right_children = stack_of_right_children_; - while (!stack_of_right_children.empty()) { - node = stack_of_right_children.back(); - stack_of_right_children.pop_back(); - if (node->length > n) break; - n -= node->length; - bytes_remaining_ -= node->length; - node = nullptr; - } - - if (node == nullptr) { - // We have reached the end of the Cord. - assert(bytes_remaining_ == 0); - return; - } + // From this point on, we need a partial substring node. + // Get pointer to the underlying flat or external data payload and + // compute data pointer and offset into current flat or external. + CordRep* payload = current_leaf_->IsSubstring() + ? current_leaf_->substring()->child + : current_leaf_; + const char* data = payload->IsExternal() ? payload->external()->base + : payload->flat()->Data(); + const size_t offset = current_chunk_.data() - data; - // Get the child node if we encounter a SUBSTRING. - size_t offset = 0; - size_t length = node->length; - if (node->IsSubstring()) { - offset = node->substring()->start; - node = node->substring()->child; - } + CordRepSubstring* tree = new CordRepSubstring(); + tree->tag = cord_internal::SUBSTRING; + tree->length = n; + tree->start = offset; + tree->child = CordRep::Ref(payload); - assert(node->IsExternal() || node->IsFlat()); - assert(length > n); - const char* data = - node->IsExternal() ? node->external()->base : node->flat()->Data(); - current_chunk_ = absl::string_view(data + offset + n, length - n); - current_leaf_ = node; + subcord.contents_.EmplaceTree(VerifyTree(tree), method); bytes_remaining_ -= n; + current_chunk_.remove_prefix(n); + return subcord; } char Cord::operator[](size_t i) const { diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 7f34ef48..081b6311 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -80,6 +80,7 @@ #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" #include "absl/strings/cord_analysis.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_btree_reader.h" @@ -388,12 +389,6 @@ class Cord { using CordRepBtree = absl::cord_internal::CordRepBtree; using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader; - // Stack of right children of concat nodes that we have to visit. - // Keep this at the end of the structure to avoid cache-thrashing. - // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for - // the inlined vector size (47 exists for backward compatibility). - using Stack = absl::InlinedVector; - // Constructs a `begin()` iterator from `tree`. `tree` must not be null. explicit ChunkIterator(cord_internal::CordRep* tree); @@ -409,17 +404,10 @@ class Cord { Cord AdvanceAndReadBytes(size_t n); void AdvanceBytes(size_t n); - // Stack specific operator++ - ChunkIterator& AdvanceStack(); - // Btree specific operator++ ChunkIterator& AdvanceBtree(); void AdvanceBytesBtree(size_t n); - // Iterates `n` bytes, where `n` is expected to be greater than or equal to - // `current_chunk_.size()`. - void AdvanceBytesSlowPath(size_t n); - // A view into bytes of the current `CordRep`. It may only be a view to a // suffix of bytes if this is being used by `CharIterator`. absl::string_view current_chunk_; @@ -432,9 +420,6 @@ class Cord { // Cord reader for cord btrees. Empty if not traversing a btree. CordRepBtreeReader btree_reader_; - - // See 'Stack' alias definition. - Stack stack_of_right_children_; }; // Cord::ChunkIterator::chunk_begin() @@ -725,7 +710,8 @@ class Cord { // be used by spelling absl::strings_internal::MakeStringConstant, which is // also an internal API. template - explicit constexpr Cord(strings_internal::StringConstant); + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr Cord(strings_internal::StringConstant); private: using CordRep = absl::cord_internal::CordRep; @@ -1321,25 +1307,24 @@ inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) { tree = cord_internal::SkipCrcNode(tree); if (tree->tag == cord_internal::BTREE) { current_chunk_ = btree_reader_.Init(tree->btree()); - return; + } else { + current_leaf_ = tree; + current_chunk_ = cord_internal::EdgeData(tree); } - - stack_of_right_children_.push_back(tree); - operator++(); } -inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) - : bytes_remaining_(tree->length) { +inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) { + bytes_remaining_ = tree->length; InitTree(tree); } -inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) - : bytes_remaining_(cord->size()) { - if (cord->contents_.is_tree()) { - InitTree(cord->contents_.as_tree()); +inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) { + if (CordRep* tree = cord->contents_.tree()) { + bytes_remaining_ = tree->length; + InitTree(tree); } else { - current_chunk_ = - absl::string_view(cord->contents_.data(), bytes_remaining_); + bytes_remaining_ = cord->contents_.inline_size(); + current_chunk_ = {cord->contents_.data(), bytes_remaining_}; } } @@ -1369,8 +1354,11 @@ inline Cord::ChunkIterator& Cord::ChunkIterator::operator++() { assert(bytes_remaining_ >= current_chunk_.size()); bytes_remaining_ -= current_chunk_.size(); if (bytes_remaining_ > 0) { - return btree_reader_ ? AdvanceBtree() : AdvanceStack(); - } else { + if (btree_reader_) { + return AdvanceBtree(); + } else { + assert(!current_chunk_.empty()); // Called on invalid iterator. + } current_chunk_ = {}; } return *this; @@ -1411,7 +1399,11 @@ inline void Cord::ChunkIterator::AdvanceBytes(size_t n) { if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) { RemoveChunkPrefix(n); } else if (n != 0) { - btree_reader_ ? AdvanceBytesBtree(n) : AdvanceBytesSlowPath(n); + if (btree_reader_) { + AdvanceBytesBtree(n); + } else { + bytes_remaining_ = 0; + } } } diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc index 3fa15b01..73d3c4e6 100644 --- a/absl/strings/cord_analysis.cc +++ b/absl/strings/cord_analysis.cc @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "absl/strings/cord_analysis.h" + #include #include #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/container/inlined_vector.h" -#include "absl/strings/cord_analysis.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_crc.h" @@ -98,16 +100,6 @@ struct RawUsage { } }; -// Returns true if the provided rep is a valid data edge. -bool IsDataEdge(const CordRep* rep) { - // The fast path is that `rep` is an EXTERNAL or FLAT node, making the below - // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL - // check in the slow path the SUBSTRING check to optimize for the hot path. - if (rep->tag == EXTERNAL || rep->tag >= FLAT) return true; - if (rep->tag == SUBSTRING) rep = rep->substring()->child; - return rep->tag == EXTERNAL || rep->tag >= FLAT; -} - // Computes the estimated memory size of the provided data edge. // External reps are assumed 'heap allocated at their exact size'. template diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index c26e506d..9dcc4ce5 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -1866,6 +1866,78 @@ TEST_P(CordTest, CordChunkIteratorOperations) { VerifyChunkIterator(subcords, 128); } + +TEST_P(CordTest, AdvanceAndReadOnDataEdge) { + RandomEngine rng(GTEST_FLAG_GET(random_seed)); + const std::string data = RandomLowercaseString(&rng, 2000); + for (bool as_flat : {true, false}) { + SCOPED_TRACE(as_flat ? "Flat" : "External"); + + absl::Cord cord = + as_flat ? absl::Cord(data) + : absl::MakeCordFromExternal(data, [](absl::string_view) {}); + auto it = cord.Chars().begin(); +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + EXPECT_DEATH_IF_SUPPORTED(cord.AdvanceAndRead(&it, 2001), ".*"); +#endif + + it = cord.Chars().begin(); + absl::Cord frag = cord.AdvanceAndRead(&it, 2000); + EXPECT_EQ(frag, data); + EXPECT_TRUE(it == cord.Chars().end()); + + it = cord.Chars().begin(); + frag = cord.AdvanceAndRead(&it, 200); + EXPECT_EQ(frag, data.substr(0, 200)); + EXPECT_FALSE(it == cord.Chars().end()); + + frag = cord.AdvanceAndRead(&it, 1500); + EXPECT_EQ(frag, data.substr(200, 1500)); + EXPECT_FALSE(it == cord.Chars().end()); + + frag = cord.AdvanceAndRead(&it, 300); + EXPECT_EQ(frag, data.substr(1700, 300)); + EXPECT_TRUE(it == cord.Chars().end()); + } +} + +TEST_P(CordTest, AdvanceAndReadOnSubstringDataEdge) { + RandomEngine rng(GTEST_FLAG_GET(random_seed)); + const std::string data = RandomLowercaseString(&rng, 2500); + for (bool as_flat : {true, false}) { + SCOPED_TRACE(as_flat ? "Flat" : "External"); + + absl::Cord cord = + as_flat ? absl::Cord(data) + : absl::MakeCordFromExternal(data, [](absl::string_view) {}); + cord = cord.Subcord(200, 2000); + const std::string substr = data.substr(200, 2000); + + auto it = cord.Chars().begin(); +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + EXPECT_DEATH_IF_SUPPORTED(cord.AdvanceAndRead(&it, 2001), ".*"); +#endif + + it = cord.Chars().begin(); + absl::Cord frag = cord.AdvanceAndRead(&it, 2000); + EXPECT_EQ(frag, substr); + EXPECT_TRUE(it == cord.Chars().end()); + + it = cord.Chars().begin(); + frag = cord.AdvanceAndRead(&it, 200); + EXPECT_EQ(frag, substr.substr(0, 200)); + EXPECT_FALSE(it == cord.Chars().end()); + + frag = cord.AdvanceAndRead(&it, 1500); + EXPECT_EQ(frag, substr.substr(200, 1500)); + EXPECT_FALSE(it == cord.Chars().end()); + + frag = cord.AdvanceAndRead(&it, 300); + EXPECT_EQ(frag, substr.substr(1700, 300)); + EXPECT_TRUE(it == cord.Chars().end()); + } +} + TEST_P(CordTest, CharIteratorTraits) { static_assert(std::is_copy_constructible::value, ""); diff --git a/absl/strings/internal/cord_data_edge.h b/absl/strings/internal/cord_data_edge.h new file mode 100644 index 00000000..e18b33e1 --- /dev/null +++ b/absl/strings/internal/cord_data_edge.h @@ -0,0 +1,63 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_DATA_EDGE_H_ +#define ABSL_STRINGS_INTERNAL_CORD_DATA_EDGE_H_ + +#include +#include + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Returns true if the provided rep is a FLAT, EXTERNAL or a SUBSTRING node +// holding a FLAT or EXTERNAL child rep. Requires `rep != nullptr`. +inline bool IsDataEdge(const CordRep* edge) { + assert(edge != nullptr); + + // The fast path is that `edge` is an EXTERNAL or FLAT node, making the below + // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL + // check in the slow path of the SUBSTRING check to optimize for the hot path. + if (edge->tag == EXTERNAL || edge->tag >= FLAT) return true; + if (edge->tag == SUBSTRING) edge = edge->substring()->child; + return edge->tag == EXTERNAL || edge->tag >= FLAT; +} + +// Returns the `absl::string_view` data reference for the provided data edge. +// Requires 'IsDataEdge(edge) == true`. +inline absl::string_view EdgeData(const CordRep* edge) { + assert(IsDataEdge(edge)); + + size_t offset = 0; + const size_t length = edge->length; + if (edge->IsSubstring()) { + offset = edge->substring()->start; + edge = edge->substring()->child; + } + return edge->tag >= FLAT + ? absl::string_view{edge->flat()->Data() + offset, length} + : absl::string_view{edge->external()->base + offset, length}; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_DATA_EDGE_H_ diff --git a/absl/strings/internal/cord_data_edge_test.cc b/absl/strings/internal/cord_data_edge_test.cc new file mode 100644 index 00000000..8fce3bc6 --- /dev/null +++ b/absl/strings/internal/cord_data_edge_test.cc @@ -0,0 +1,130 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/cord_data_edge.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_test_util.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::absl::cordrep_testing::MakeExternal; +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::MakeSubstring; + +TEST(CordDataEdgeTest, IsDataEdgeOnFlat) { + CordRep* rep = MakeFlat("Lorem ipsum dolor sit amet, consectetur ..."); + EXPECT_TRUE(IsDataEdge(rep)); + CordRep::Unref(rep); +} + +TEST(CordDataEdgeTest, IsDataEdgeOnExternal) { + CordRep* rep = MakeExternal("Lorem ipsum dolor sit amet, consectetur ..."); + EXPECT_TRUE(IsDataEdge(rep)); + CordRep::Unref(rep); +} + +TEST(CordDataEdgeTest, IsDataEdgeOnSubstringOfFlat) { + CordRep* rep = MakeFlat("Lorem ipsum dolor sit amet, consectetur ..."); + CordRep* substr = MakeSubstring(1, 20, rep); + EXPECT_TRUE(IsDataEdge(substr)); + CordRep::Unref(substr); +} + +TEST(CordDataEdgeTest, IsDataEdgeOnSubstringOfExternal) { + CordRep* rep = MakeExternal("Lorem ipsum dolor sit amet, consectetur ..."); + CordRep* substr = MakeSubstring(1, 20, rep); + EXPECT_TRUE(IsDataEdge(substr)); + CordRep::Unref(substr); +} + +TEST(CordDataEdgeTest, IsDataEdgeOnBtree) { + CordRep* rep = MakeFlat("Lorem ipsum dolor sit amet, consectetur ..."); + CordRepBtree* tree = CordRepBtree::New(rep); + EXPECT_FALSE(IsDataEdge(tree)); + CordRep::Unref(tree); +} + +TEST(CordDataEdgeTest, IsDataEdgeOnBadSubstr) { + CordRep* rep = MakeFlat("Lorem ipsum dolor sit amet, consectetur ..."); + CordRep* substr = MakeSubstring(1, 18, MakeSubstring(1, 20, rep)); + EXPECT_FALSE(IsDataEdge(substr)); + CordRep::Unref(substr); +} + +TEST(CordDataEdgeTest, EdgeDataOnFlat) { + absl::string_view value = "Lorem ipsum dolor sit amet, consectetur ..."; + CordRep* rep = MakeFlat(value); + EXPECT_EQ(EdgeData(rep), value); + CordRep::Unref(rep); +} + +TEST(CordDataEdgeTest, EdgeDataOnExternal) { + absl::string_view value = "Lorem ipsum dolor sit amet, consectetur ..."; + CordRep* rep = MakeExternal(value); + EXPECT_EQ(EdgeData(rep), value); + CordRep::Unref(rep); +} + +TEST(CordDataEdgeTest, EdgeDataOnSubstringOfFlat) { + absl::string_view value = "Lorem ipsum dolor sit amet, consectetur ..."; + CordRep* rep = MakeFlat(value); + CordRep* substr = MakeSubstring(1, 20, rep); + EXPECT_EQ(EdgeData(substr), value.substr(1, 20)); + CordRep::Unref(substr); +} + +TEST(CordDataEdgeTest, EdgeDataOnSubstringOfExternal) { + absl::string_view value = "Lorem ipsum dolor sit amet, consectetur ..."; + CordRep* rep = MakeExternal(value); + CordRep* substr = MakeSubstring(1, 20, rep); + EXPECT_EQ(EdgeData(substr), value.substr(1, 20)); + CordRep::Unref(substr); +} + +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + +TEST(CordDataEdgeTest, IsDataEdgeOnNullPtr) { + EXPECT_DEATH(IsDataEdge(nullptr), ".*"); +} + +TEST(CordDataEdgeTest, EdgeDataOnNullPtr) { + EXPECT_DEATH(EdgeData(nullptr), ".*"); +} + +TEST(CordDataEdgeTest, EdgeDataOnBtree) { + CordRep* rep = MakeFlat("Lorem ipsum dolor sit amet, consectetur ..."); + CordRepBtree* tree = CordRepBtree::New(rep); + EXPECT_DEATH(EdgeData(tree), ".*"); + CordRep::Unref(tree); +} + +TEST(CordDataEdgeTest, EdgeDataOnBadSubstr) { + CordRep* rep = MakeFlat("Lorem ipsum dolor sit amet, consectetur ..."); + CordRep* substr = MakeSubstring(1, 18, MakeSubstring(1, 20, rep)); + EXPECT_DEATH(EdgeData(substr), ".*"); + CordRep::Unref(substr); +} + +#endif // GTEST_HAS_DEATH_TEST && !NDEBUG + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 3ebd9f6d..2b592b47 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -22,6 +22,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_consume.h" #include "absl/strings/internal/cord_rep_flat.h" @@ -69,7 +70,7 @@ void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, // indentation and prefix / labels keeps us within roughly 80-100 wide. constexpr size_t kMaxDataLength = 60; stream << ", data = \"" - << CordRepBtree::EdgeData(r).substr(0, kMaxDataLength) + << EdgeData(r).substr(0, kMaxDataLength) << (r->length > kMaxDataLength ? "\"..." : "\""); } stream << '\n'; @@ -150,7 +151,7 @@ inline CordRep* MakeSubstring(CordRep* rep, size_t offset) { CordRep* ResizeEdge(CordRep* edge, size_t length, bool is_mutable) { assert(length > 0); assert(length <= edge->length); - assert(CordRepBtree::IsDataEdge(edge)); + assert(IsDataEdge(edge)); if (length >= edge->length) return edge; if (is_mutable && (edge->tag >= FLAT || edge->tag == SUBSTRING)) { @@ -207,7 +208,7 @@ void DeleteSubstring(CordRepSubstring* substring) { // Deletes a leaf node data edge. Requires `IsDataEdge(rep)`. void DeleteLeafEdge(CordRep* rep) { - assert(CordRepBtree::IsDataEdge(rep)); + assert(IsDataEdge(rep)); if (rep->tag >= FLAT) { CordRepFlat::Delete(rep->flat()); } else if (rep->tag == EXTERNAL) { diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index df5c994c..0e78e12c 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -22,6 +22,7 @@ #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/optimization.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/string_view.h" @@ -310,13 +311,6 @@ class CordRepBtree : public CordRep { // Requires this instance to be a leaf node, and `index` to be valid index. inline absl::string_view Data(size_t index) const; - static const char* EdgeDataPtr(const CordRep* r); - static absl::string_view EdgeData(const CordRep* r); - - // Returns true if the provided rep is a FLAT, EXTERNAL or a SUBSTRING node - // holding a FLAT or EXTERNAL child rep. - static bool IsDataEdge(const CordRep* rep); - // Diagnostics: returns true if `tree` is valid and internally consistent. // If `shallow` is false, then the provided top level node and all child nodes // below it are recursively checked. If `shallow` is true, only the provided @@ -631,34 +625,11 @@ inline absl::Span CordRepBtree::Edges(size_t begin, return {edges_ + begin, static_cast(end - begin)}; } -inline const char* CordRepBtree::EdgeDataPtr(const CordRep* r) { - assert(IsDataEdge(r)); - size_t offset = 0; - if (r->tag == SUBSTRING) { - offset = r->substring()->start; - r = r->substring()->child; - } - return (r->tag >= FLAT ? r->flat()->Data() : r->external()->base) + offset; -} - -inline absl::string_view CordRepBtree::EdgeData(const CordRep* r) { - return absl::string_view(EdgeDataPtr(r), r->length); -} - inline absl::string_view CordRepBtree::Data(size_t index) const { assert(height() == 0); return EdgeData(Edge(index)); } -inline bool CordRepBtree::IsDataEdge(const CordRep* rep) { - // The fast path is that `rep` is an EXTERNAL or FLAT node, making the below - // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL - // check in the slow path the SUBSTRING check to optimize for the hot path. - if (rep->tag == EXTERNAL || rep->tag >= FLAT) return true; - if (rep->tag == SUBSTRING) rep = rep->substring()->child; - return rep->tag == EXTERNAL || rep->tag >= FLAT; -} - inline CordRepBtree* CordRepBtree::New(int height) { CordRepBtree* tree = new CordRepBtree; tree->length = 0; diff --git a/absl/strings/internal/cord_rep_btree_navigator.cc b/absl/strings/internal/cord_rep_btree_navigator.cc index 19afbe90..9b896a3d 100644 --- a/absl/strings/internal/cord_rep_btree_navigator.cc +++ b/absl/strings/internal/cord_rep_btree_navigator.cc @@ -16,6 +16,7 @@ #include +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" @@ -39,7 +40,7 @@ inline CordRep* Substring(CordRep* rep, size_t offset, size_t n) { assert(n <= rep->length); assert(offset < rep->length); assert(offset <= rep->length - n); - assert(CordRepBtree::IsDataEdge(rep)); + assert(IsDataEdge(rep)); if (n == 0) return nullptr; if (n == rep->length) return CordRep::Ref(rep); diff --git a/absl/strings/internal/cord_rep_btree_reader.cc b/absl/strings/internal/cord_rep_btree_reader.cc index 5dc76966..0d0e8601 100644 --- a/absl/strings/internal/cord_rep_btree_reader.cc +++ b/absl/strings/internal/cord_rep_btree_reader.cc @@ -17,6 +17,7 @@ #include #include "absl/base/config.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_btree_navigator.h" @@ -44,7 +45,7 @@ absl::string_view CordRepBtreeReader::Read(size_t n, size_t chunk_size, // can directly return the substring into the current data edge as the next // chunk. We can easily establish from the above code that `navigator_.Next()` // has not been called as that requires `chunk_size` to be zero. - if (n < chunk_size) return CordRepBtree::EdgeData(edge).substr(result.n); + if (n < chunk_size) return EdgeData(edge).substr(result.n); // The amount of data taken from the last edge is `chunk_size` and `result.n` // contains the offset into the current edge trailing the read data (which can @@ -60,7 +61,7 @@ absl::string_view CordRepBtreeReader::Read(size_t n, size_t chunk_size, // We did not read all data, return remaining data from current edge. edge = navigator_.Current(); remaining_ -= consumed_by_read + edge->length; - return CordRepBtree::EdgeData(edge).substr(result.n); + return EdgeData(edge).substr(result.n); } } // namespace cord_internal diff --git a/absl/strings/internal/cord_rep_btree_reader.h b/absl/strings/internal/cord_rep_btree_reader.h index 7aa79dbf..8db8f8dd 100644 --- a/absl/strings/internal/cord_rep_btree_reader.h +++ b/absl/strings/internal/cord_rep_btree_reader.h @@ -18,6 +18,7 @@ #include #include "absl/base/config.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_btree_navigator.h" @@ -167,7 +168,7 @@ inline absl::string_view CordRepBtreeReader::Init(CordRepBtree* tree) { assert(tree != nullptr); const CordRep* edge = navigator_.InitFirst(tree); remaining_ = tree->length - edge->length; - return CordRepBtree::EdgeData(edge); + return EdgeData(edge); } inline absl::string_view CordRepBtreeReader::Next() { @@ -175,7 +176,7 @@ inline absl::string_view CordRepBtreeReader::Next() { const CordRep* edge = navigator_.Next(); assert(edge != nullptr); remaining_ -= edge->length; - return CordRepBtree::EdgeData(edge); + return EdgeData(edge); } inline absl::string_view CordRepBtreeReader::Skip(size_t skip) { @@ -190,7 +191,7 @@ inline absl::string_view CordRepBtreeReader::Skip(size_t skip) { // The combined length of all edges skipped before `pos.edge` is `skip - // pos.offset`, all of which are 'consumed', as well as the current edge. remaining_ -= skip - pos.offset + pos.edge->length; - return CordRepBtree::EdgeData(pos.edge).substr(pos.offset); + return EdgeData(pos.edge).substr(pos.offset); } inline absl::string_view CordRepBtreeReader::Seek(size_t offset) { @@ -199,7 +200,7 @@ inline absl::string_view CordRepBtreeReader::Seek(size_t offset) { remaining_ = 0; return {}; } - absl::string_view chunk = CordRepBtree::EdgeData(pos.edge).substr(pos.offset); + absl::string_view chunk = EdgeData(pos.edge).substr(pos.offset); remaining_ = length() - offset - chunk.length(); return chunk; } diff --git a/absl/strings/internal/cord_rep_btree_test.cc b/absl/strings/internal/cord_rep_btree_test.cc index 1d56b252..51b90db1 100644 --- a/absl/strings/internal/cord_rep_btree_test.cc +++ b/absl/strings/internal/cord_rep_btree_test.cc @@ -25,6 +25,7 @@ #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/cleanup/cleanup.h" +#include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_test_util.h" #include "absl/strings/str_cat.h" @@ -323,30 +324,26 @@ TEST(CordRepBtreeTest, EdgeData) { CordRep* substr2 = MakeSubstring(1, 6, CordRep::Ref(external)); CordRep* bad_substr = MakeSubstring(1, 2, CordRep::Ref(substr1)); - EXPECT_TRUE(CordRepBtree::IsDataEdge(flat)); - EXPECT_THAT(CordRepBtree::EdgeDataPtr(flat), - TypedEq(flat->Data())); - EXPECT_THAT(CordRepBtree::EdgeData(flat), Eq("Hello world")); + EXPECT_TRUE(IsDataEdge(flat)); + EXPECT_THAT(EdgeData(flat).data(), TypedEq(flat->Data())); + EXPECT_THAT(EdgeData(flat), Eq("Hello world")); - EXPECT_TRUE(CordRepBtree::IsDataEdge(external)); - EXPECT_THAT(CordRepBtree::EdgeDataPtr(external), - TypedEq(external->base)); - EXPECT_THAT(CordRepBtree::EdgeData(external), Eq("Hello external")); + EXPECT_TRUE(IsDataEdge(external)); + EXPECT_THAT(EdgeData(external).data(), TypedEq(external->base)); + EXPECT_THAT(EdgeData(external), Eq("Hello external")); - EXPECT_TRUE(CordRepBtree::IsDataEdge(substr1)); - EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr1), - TypedEq(flat->Data() + 1)); - EXPECT_THAT(CordRepBtree::EdgeData(substr1), Eq("ello w")); + EXPECT_TRUE(IsDataEdge(substr1)); + EXPECT_THAT(EdgeData(substr1).data(), TypedEq(flat->Data() + 1)); + EXPECT_THAT(EdgeData(substr1), Eq("ello w")); - EXPECT_TRUE(CordRepBtree::IsDataEdge(substr2)); - EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr2), + EXPECT_TRUE(IsDataEdge(substr2)); + EXPECT_THAT(EdgeData(substr2).data(), TypedEq(external->base + 1)); - EXPECT_THAT(CordRepBtree::EdgeData(substr2), Eq("ello e")); + EXPECT_THAT(EdgeData(substr2), Eq("ello e")); - EXPECT_FALSE(CordRepBtree::IsDataEdge(bad_substr)); + EXPECT_FALSE(IsDataEdge(bad_substr)); #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) - EXPECT_DEATH(CordRepBtree::EdgeData(bad_substr), ".*"); - EXPECT_DEATH(CordRepBtree::EdgeDataPtr(bad_substr), ".*"); + EXPECT_DEATH(EdgeData(bad_substr), ".*"); #endif CordRep::Unref(bad_substr); diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh index 37be5310..0e05564f 100644 --- a/ci/linux_docker_containers.sh +++ b/ci/linux_docker_containers.sh @@ -16,6 +16,6 @@ # Test scripts should source this file to get the identifiers. readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026" -readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220113" -readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220113" +readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220217" +readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220217" readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20210617" -- cgit v1.2.3 From 808bc202fc13e85a7948db0d7fb58f0f051200b1 Mon Sep 17 00:00:00 2001 From: imaiguo Date: Wed, 23 Feb 2022 22:56:32 +0800 Subject: Add support of loongarch64 (#1110) --- absl/debugging/internal/examine_stack.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc index 589a3ef3..2fbfea8e 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -82,6 +82,8 @@ void* GetProgramCounter(void* vuc) { return reinterpret_cast(context->uc_mcontext.gregs[16]); #elif defined(__e2k__) return reinterpret_cast(context->uc_mcontext.cr0_hi); +#elif defined(__loongarch__) + return reinterpret_cast(context->uc_mcontext.__pc); #else #error "Undefined Architecture." #endif -- cgit v1.2.3 From 0ad7994f10624cd1538b1287e56cae5ac9c0cb40 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 23 Feb 2022 06:56:15 -0800 Subject: Export of internal Abseil changes -- 91d76b3ac9edff91f206d9eee60423c39eeeaf93 by Derek Mauro : Internal change PiperOrigin-RevId: 430442277 -- 9f8a87bcc5cc5b0fd8b7f0318f37d152fd8bea06 by Evan Brown : Small refactoring to work towards allowing for node_btree_* containers. - Change common_params::transfer to rely on slot_policy transfer instead of manually doing construct/destruct. Transfer is not construct/destruct for node containers. - Move maps' value_compare into btree.h from btree_map.h so it can be reused for node_btree_map.h. - Also add a test for maps' value_compare protected members. PiperOrigin-RevId: 430245542 -- 0126e0b6295342317d9c9f0a66e2d7009b858426 by Martijn Vels : Add CordRepSubString::Create function with hard checks on IsFlat() | IsExternal() This hardens internal invariants, IsFlat() || IsExternal() is a cheap, single predicted branch, and removes boilerplate code from cord.cc PiperOrigin-RevId: 429676041 -- ed98a92af49d9e238d9f1d1b69fb4eddcd1ccbc7 by Abseil Team : tweaks to status.h documentation to reflect general-purpose communication PiperOrigin-RevId: 429584104 GitOrigin-RevId: 91d76b3ac9edff91f206d9eee60423c39eeeaf93 Change-Id: I54d6d116a564f86a842b983ca76559bf9b388f72 --- absl/container/btree_map.h | 31 ++++-------------- absl/container/btree_set.h | 21 ++++++------ absl/container/btree_test.cc | 16 ++++++++++ absl/container/internal/btree.h | 36 +++++++++++++++++---- absl/status/status.h | 10 +++--- absl/strings/cord.cc | 30 +++--------------- absl/strings/cord_test.cc | 21 +++--------- absl/strings/internal/cord_internal.cc | 7 ++++ absl/strings/internal/cord_internal.h | 58 ++++++++++++++++++++++++++-------- 9 files changed, 129 insertions(+), 101 deletions(-) diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h index ad484ce0..286817f1 100644 --- a/absl/container/btree_map.h +++ b/absl/container/btree_map.h @@ -59,7 +59,7 @@ ABSL_NAMESPACE_BEGIN namespace container_internal { template + int TargetNodeSize, bool IsMulti> struct map_params; } // namespace container_internal @@ -85,7 +85,7 @@ class btree_map : public container_internal::btree_map_container< container_internal::btree>> { + /*IsMulti=*/false>>> { using Base = typename btree_map::btree_map_container; public: @@ -507,7 +507,7 @@ class btree_multimap : public container_internal::btree_multimap_container< container_internal::btree>> { + /*IsMulti=*/true>>> { using Base = typename btree_multimap::btree_multimap_container; public: @@ -817,9 +817,9 @@ namespace container_internal { // A parameters structure for holding the type parameters for a btree_map. // Compare and Alloc should be nothrow copy-constructible. template -struct map_params : common_params> { + int TargetNodeSize, bool IsMulti> +struct map_params : common_params> { using super_type = typename map_params::common_params; using mapped_type = Data; // This type allows us to move keys when it is safe to do so. It is safe @@ -829,25 +829,6 @@ struct map_params : common_params - friend class btree; - - protected: - explicit value_compare(original_key_compare c) : comp(std::move(c)) {} - - original_key_compare comp; // NOLINT - - public: - auto operator()(const value_type &lhs, const value_type &rhs) const - -> decltype(comp(lhs.first, rhs.first)) { - return comp(lhs.first, rhs.first); - } - }; - using is_map_container = std::true_type; - template static auto key(const V &value) -> decltype(value.first) { return value.first; diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 78826830..7b6655ad 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -61,7 +61,7 @@ template struct set_slot_policy; template + bool IsMulti> struct set_params; } // namespace container_internal @@ -87,7 +87,7 @@ class btree_set : public container_internal::btree_set_container< container_internal::btree>> { + /*IsMulti=*/false>>> { using Base = typename btree_set::btree_set_container; public: @@ -427,7 +427,7 @@ class btree_multiset : public container_internal::btree_multiset_container< container_internal::btree>> { + /*IsMulti=*/true>>> { using Base = typename btree_multiset::btree_multiset_container; public: @@ -756,6 +756,12 @@ struct set_slot_policy { absl::allocator_traits::destroy(*alloc, slot); } + template + static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { + construct(alloc, new_slot, old_slot); + destroy(alloc, old_slot); + } + template static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { using std::swap; @@ -771,14 +777,11 @@ struct set_slot_policy { // A parameters structure for holding the type parameters for a btree_set. // Compare and Alloc should be nothrow copy-constructible. template -struct set_params : common_params> { + bool IsMulti> +struct set_params : common_params> { using value_type = Key; using slot_type = typename set_params::common_params::slot_type; - using value_compare = - typename set_params::common_params::original_key_compare; - using is_map_container = std::false_type; template static const V &key(const V &value) { diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index e829e0ba..4d4c64b2 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -1762,6 +1762,22 @@ TEST(Btree, ValueComp) { EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0))); } +// Test that we have the protected members from the std::map::value_compare API. +// See https://en.cppreference.com/w/cpp/container/map/value_compare. +TEST(Btree, MapValueCompProtected) { + struct key_compare { + bool operator()(int l, int r) const { return l < r; } + int id; + }; + using value_compare = absl::btree_map::value_compare; + struct value_comp_child : public value_compare { + explicit value_comp_child(key_compare kc) : value_compare(kc) {} + int GetId() const { return comp.id; } + }; + value_comp_child c(key_compare{10}); + EXPECT_EQ(c.GetId(), 10); +} + TEST(Btree, DefaultConstruction) { absl::btree_set s; absl::btree_map m; diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index 6c10b00f..a22c9bd7 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -335,8 +335,27 @@ constexpr bool compare_has_valid_result_type() { std::is_convertible::value; } +template +class map_value_compare { + template + friend class btree; + + // Note: this `protected` is part of the API of std::map::value_compare. See + // https://en.cppreference.com/w/cpp/container/map/value_compare. + protected: + explicit map_value_compare(original_key_compare c) : comp(std::move(c)) {} + + original_key_compare comp; // NOLINT + + public: + auto operator()(const value_type &lhs, const value_type &rhs) const + -> decltype(comp(lhs.first, rhs.first)) { + return comp(lhs.first, rhs.first); + } +}; + template + bool IsMulti, bool IsMap, typename SlotPolicy> struct common_params { using original_key_compare = Compare; @@ -384,6 +403,12 @@ struct common_params { using reference = value_type &; using const_reference = const value_type &; + using value_compare = + absl::conditional_t, + original_key_compare>; + using is_map_container = std::integral_constant; + // For the given lookup key type, returns whether we can have multiple // equivalent keys in the btree. If this is a multi-container, then we can. // Otherwise, we can have multiple equivalent keys only if all of the @@ -394,9 +419,9 @@ struct common_params { // that we know has the same equivalence classes for all lookup types. template constexpr static bool can_have_multiple_equivalent_keys() { - return Multi || (IsTransparent::value && - !std::is_same::value && - !kIsKeyCompareStringAdapted); + return IsMulti || (IsTransparent::value && + !std::is_same::value && + !kIsKeyCompareStringAdapted); } enum { @@ -435,8 +460,7 @@ struct common_params { slot_policy::destroy(alloc, slot); } static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { - construct(alloc, new_slot, old_slot); - destroy(alloc, old_slot); + slot_policy::transfer(alloc, new_slot, old_slot); } static void swap(Alloc *alloc, slot_type *a, slot_type *b) { slot_policy::swap(alloc, a, b); diff --git a/absl/status/status.h b/absl/status/status.h index db4b340a..2d003ce6 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -24,11 +24,11 @@ // * A set of helper functions for creating status codes and checking their // values // -// Within Google, `absl::Status` is the primary mechanism for gracefully -// handling errors across API boundaries (and in particular across RPC -// boundaries). Some of these errors may be recoverable, but others may not. -// Most functions that can produce a recoverable error should be designed to -// return an `absl::Status` (or `absl::StatusOr`). +// Within Google, `absl::Status` is the primary mechanism for communicating +// errors in C++, and is used to represent error state in both in-process +// library calls as well as RPC calls. Some of these errors may be recoverable, +// but others may not. Most functions that can produce a recoverable error +// should be designed to return an `absl::Status` (or `absl::StatusOr`). // // Example: // diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 6547c2da..b65dc58b 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -151,23 +151,6 @@ void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) { } // namespace cord_internal -static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) { - // Never create empty substring nodes - if (length == 0) { - CordRep::Unref(child); - return nullptr; - } else { - CordRepSubstring* rep = new CordRepSubstring(); - assert(child->IsExternal() || child->IsFlat()); - assert((offset + length) <= child->length); - rep->length = length; - rep->tag = cord_internal::SUBSTRING; - rep->start = offset; - rep->child = child; - return VerifyTree(rep); - } -} - // Creates a CordRep from the provided string. If the string is large enough, // and not wasteful, we move the string into an external cord rep, preserving // the already allocated string contents. @@ -643,7 +626,7 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { start += node->substring()->start; node = node->substring()->child; } - node = NewSubstring(CordRep::Ref(node), start, len); + node = CordRepSubstring::Create(CordRep::Ref(node), start, len); } while (!rhs_stack.empty()) { node = Concat(node, CordRep::Ref(rhs_stack.back())); @@ -678,7 +661,7 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { start = node->substring()->start; node = node->substring()->child; } - node = NewSubstring(CordRep::Ref(node), start, len); + node = CordRepSubstring::Create(CordRep::Ref(node), start, len); } while (!lhs_stack.empty()) { node = Concat(CordRep::Ref(lhs_stack.back()), node); @@ -768,7 +751,7 @@ static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { pos += node->substring()->start; node = node->substring()->child; } - results.push_back(NewSubstring(CordRep::Ref(node), pos, n)); + results.push_back(CordRepSubstring::Create(CordRep::Ref(node), pos, n)); } } while (!todo.empty()); assert(results.size() == 1); @@ -1157,12 +1140,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { : payload->flat()->Data(); const size_t offset = current_chunk_.data() - data; - CordRepSubstring* tree = new CordRepSubstring(); - tree->tag = cord_internal::SUBSTRING; - tree->length = n; - tree->start = offset; - tree->child = CordRep::Ref(payload); - + auto* tree = CordRepSubstring::Create(CordRep::Ref(payload), offset, n); subcord.contents_.EmplaceTree(VerifyTree(tree), method); bytes_remaining_ -= n; current_chunk_.remove_prefix(n); diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 9dcc4ce5..f69209de 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -56,6 +56,7 @@ using absl::cord_internal::CordRepCrc; using absl::cord_internal::CordRepExternal; using absl::cord_internal::CordRepFlat; using absl::cord_internal::CordRepSubstring; +using absl::cord_internal::CordzUpdateTracker; using absl::cord_internal::kFlatOverhead; using absl::cord_internal::kMaxFlatLength; @@ -212,14 +213,9 @@ class CordTestPeer { ABSL_RAW_CHECK(src.ExpectedChecksum() == absl::nullopt, "Can not be hardened"); Cord cord; - auto* rep = new cord_internal::CordRepSubstring; - rep->tag = cord_internal::SUBSTRING; - rep->child = cord_internal::CordRep::Ref( - cord_internal::SkipCrcNode(src.contents_.tree())); - rep->start = offset; - rep->length = length; - cord.contents_.EmplaceTree(rep, - cord_internal::CordzUpdateTracker::kSubCord); + auto* tree = cord_internal::SkipCrcNode(src.contents_.tree()); + auto* rep = CordRepSubstring::Create(CordRep::Ref(tree), offset, length); + cord.contents_.EmplaceTree(rep, CordzUpdateTracker::kSubCord); return cord; } }; @@ -645,15 +641,6 @@ TEST_P(CordTest, TryFlatSubstrExternal) { EXPECT_EQ(sub.TryFlat(), "ell"); } -TEST_P(CordTest, TryFlatSubstrConcat) { - absl::Cord c = absl::MakeFragmentedCord({"hello", " world"}); - absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); - MaybeHarden(sub); - EXPECT_EQ(sub.TryFlat(), absl::nullopt); - c.RemovePrefix(1); - EXPECT_EQ(c.TryFlat(), absl::nullopt); -} - TEST_P(CordTest, TryFlatCommonlyAssumedInvariants) { // 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 diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc index 06119350..b6b06cfa 100644 --- a/absl/strings/internal/cord_internal.cc +++ b/absl/strings/internal/cord_internal.cc @@ -17,11 +17,13 @@ #include #include +#include "absl/base/internal/raw_logging.h" #include "absl/container/inlined_vector.h" #include "absl/strings/internal/cord_rep_btree.h" #include "absl/strings/internal/cord_rep_crc.h" #include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/str_cat.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -33,6 +35,11 @@ ABSL_CONST_INIT std::atomic shallow_subcords_enabled( kCordShallowSubcordsDefault); ABSL_CONST_INIT std::atomic cord_btree_exhaustive_validation(false); +void LogFatalNodeType(CordRep* rep) { + ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Unexpected node type: ", + static_cast(rep->tag))); +} + void CordRep::Destroy(CordRep* rep) { assert(rep != nullptr); diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 8db6aa6d..ece3db15 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -21,6 +21,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/invoke.h" @@ -33,6 +34,19 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { +// The overhead of a vtable is too much for Cord, so we roll our own subclasses +// using only a single byte to differentiate classes from each other - the "tag" +// byte. Define the subclasses first so we can provide downcasting helper +// functions in the base class. +struct CordRep; +struct CordRepConcat; +struct CordRepExternal; +struct CordRepFlat; +struct CordRepSubstring; +struct CordRepCrc; +class CordRepRing; +class CordRepBtree; + class CordzInfo; // Default feature enable states for cord ring buffers @@ -74,6 +88,9 @@ enum Constants { kMaxBytesToCopy = 511 }; +// Emits a fatal error "Unexpected node type: xyz" and aborts the program. +ABSL_ATTRIBUTE_NORETURN void LogFatalNodeType(CordRep* rep); + // Compact class for tracking the reference count and state flags for CordRep // instances. Data is stored in an atomic int32_t for compactness and speed. class RefcountAndFlags { @@ -156,19 +173,6 @@ class RefcountAndFlags { std::atomic count_; }; -// The overhead of a vtable is too much for Cord, so we roll our own subclasses -// using only a single byte to differentiate classes from each other - the "tag" -// byte. Define the subclasses first so we can provide downcasting helper -// functions in the base class. - -struct CordRepConcat; -struct CordRepExternal; -struct CordRepFlat; -struct CordRepSubstring; -struct CordRepCrc; -class CordRepRing; -class CordRepBtree; - // Various representations that we allow enum CordRepKind { UNUSED_0 = 0, @@ -276,6 +280,12 @@ struct CordRep { struct CordRepSubstring : public CordRep { size_t start; // Starting offset of substring in child CordRep* child; + + // Creates a substring on `child`, adopting a reference on `child`. + // Requires `child` to be either a flat or external node, and `pos` and `n` to + // form a non-empty partial sub range of `'child`, i.e.: + // `n > 0 && n < length && n + pos <= length` + static inline CordRepSubstring* Create(CordRep* child, size_t pos, size_t n); }; // Type for function pointer that will invoke the releaser function and also @@ -339,6 +349,28 @@ struct CordRepExternalImpl } }; +inline CordRepSubstring* CordRepSubstring::Create(CordRep* child, size_t pos, + size_t n) { + assert(child != nullptr); + assert(n > 0); + assert(n < child->length); + assert(pos < child->length); + assert(n <= child->length - pos); + + // TODO(b/217376272): Harden internal logic. + // Move to strategical places inside the Cord logic and make this an assert. + if (ABSL_PREDICT_FALSE(!(child->IsExternal() || child->IsFlat()))) { + LogFatalNodeType(child); + } + + CordRepSubstring* rep = new CordRepSubstring(); + rep->length = n; + rep->tag = SUBSTRING; + rep->start = pos; + rep->child = child; + return rep; +} + inline void CordRepExternal::Delete(CordRep* rep) { assert(rep != nullptr && rep->IsExternal()); auto* rep_external = static_cast(rep); -- cgit v1.2.3 From 5e4ea1ce097f3571e7d87af33b6b30d11b3a211e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 23 Feb 2022 14:30:26 -0800 Subject: Export of internal Abseil changes -- 7473df9e4922c589f6b27cf546aad097381ae552 by Martijn Vels : Make ABSL_ASSUME publicly available PiperOrigin-RevId: 430540224 Change-Id: I760e2d86e3a0b676cd2a6d26e0225a77381e948f -- c83e38bc421f715b3c1e20150c2f1ffe8e633028 by Abseil Team : Alias absl::bind_front to std::bind_front if available This avoids ambiguity between the two when enabling C++20. PiperOrigin-RevId: 430486679 GitOrigin-RevId: 7473df9e4922c589f6b27cf546aad097381ae552 Change-Id: I1e9bba09a8946480ce10ddd28e86b6c86191d38c --- absl/base/optimization.h | 35 ++++++++++++++++++--------------- absl/container/internal/raw_hash_set.h | 4 ++-- absl/functional/bind_front.h | 11 ++++++++++- absl/numeric/bits_benchmark.cc | 2 +- absl/numeric/int128.cc | 4 ++-- absl/strings/internal/cord_rep_btree.h | 4 ++-- absl/strings/internal/str_format/arg.cc | 2 +- 7 files changed, 37 insertions(+), 25 deletions(-) diff --git a/absl/base/optimization.h b/absl/base/optimization.h index d090be12..6fe03c43 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h @@ -181,22 +181,7 @@ #define ABSL_PREDICT_TRUE(x) (x) #endif -// ABSL_INTERNAL_ASSUME(cond) -// Informs the compiler that a condition is always true and that it can assume -// it to be true for optimization purposes. The call has undefined behavior if -// the condition is false. -// In !NDEBUG mode, the condition is checked with an assert(). -// NOTE: The expression must not have side effects, as it will only be evaluated -// in some compilation modes and not others. -// -// Example: -// -// int x = ...; -// ABSL_INTERNAL_ASSUME(x >= 0); -// // The compiler can optimize the division to a simple right shift using the -// // assumption specified above. -// int y = x / 16; -// +// Platform and compilation mode dependent implementation of ABSL_ASSUME. #if !defined(NDEBUG) #define ABSL_INTERNAL_ASSUME(cond) assert(cond) #elif ABSL_HAVE_BUILTIN(__builtin_assume) @@ -215,6 +200,24 @@ } while (0) #endif +// ABSL_ASSUME(cond) +// Informs the compiler that a condition is always true and that it can assume +// it to be true for optimization purposes. The call has undefined behavior if +// the condition is false. +// In !NDEBUG mode, the condition is checked with an assert(). +// NOTE: The expression must not have side effects, as it may only be evaluated +// in some compilation modes and not others. +// +// Example: +// +// int x = ...; +// ABSL_ASSUME(x >= 0); +// // The compiler can optimize the division to a simple right shift using the +// // assumption specified above. +// int y = x / 16; +// +#define ABSL_ASSUME(cond) ABSL_INTERNAL_ASSUME(cond) + // ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) // This macro forces small unique name on a static file level symbols like // static local variables or static functions. This is intended to be used in diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 7409d5ec..0daf7223 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -200,7 +200,7 @@ constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) { template uint32_t TrailingZeros(T x) { - ABSL_INTERNAL_ASSUME(x != 0); + ABSL_ASSUME(x != 0); return static_cast(countr_zero(x)); } @@ -809,7 +809,7 @@ class raw_hash_set { iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) { // This assumption helps the compiler know that any non-end iterator is // not equal to any end iterator. - ABSL_INTERNAL_ASSUME(ctrl != nullptr); + ABSL_ASSUME(ctrl != nullptr); } void skip_empty_or_deleted() { diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h index 5b47970e..f9075bd1 100644 --- a/absl/functional/bind_front.h +++ b/absl/functional/bind_front.h @@ -30,6 +30,10 @@ #ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_ #define ABSL_FUNCTIONAL_BIND_FRONT_H_ +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +#include // For std::bind_front. +#endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L + #include "absl/functional/internal/front_binder.h" #include "absl/utility/utility.h" @@ -46,7 +50,8 @@ ABSL_NAMESPACE_BEGIN // specified. More importantly, it provides more reliable correctness guarantees // than `std::bind()`; while `std::bind()` will silently ignore passing more // parameters than expected, for example, `absl::bind_front()` will report such -// mis-uses as errors. +// mis-uses as errors. In C++20, `absl::bind_front` is replaced by +// `std::bind_front`. // // absl::bind_front(a...) can be seen as storing the results of // std::make_tuple(a...). @@ -170,6 +175,9 @@ ABSL_NAMESPACE_BEGIN // // Doesn't copy "hi". // absl::bind_front(Print, absl::string_view(hi))("Chuk"); // +#if defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L +using std::bind_front; +#else // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L template constexpr functional_internal::bind_front_t bind_front( F&& func, BoundArgs&&... args) { @@ -177,6 +185,7 @@ constexpr functional_internal::bind_front_t bind_front( absl::in_place, absl::forward(func), absl::forward(args)...); } +#endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/numeric/bits_benchmark.cc b/absl/numeric/bits_benchmark.cc index 3de1dbfa..b9759583 100644 --- a/absl/numeric/bits_benchmark.cc +++ b/absl/numeric/bits_benchmark.cc @@ -59,7 +59,7 @@ static void BM_bitwidth_nonzero(benchmark::State& state) { while (state.KeepRunningBatch(count)) { for (int i = 0; i < count; ++i) { const T value = values[i]; - ABSL_INTERNAL_ASSUME(value > 0); + ABSL_ASSUME(value > 0); benchmark::DoNotOptimize(value); } } diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 17d88744..2f4e081f 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -42,11 +42,11 @@ namespace { // Returns: 2 inline ABSL_ATTRIBUTE_ALWAYS_INLINE int Fls128(uint128 n) { if (uint64_t hi = Uint128High64(n)) { - ABSL_INTERNAL_ASSUME(hi != 0); + ABSL_ASSUME(hi != 0); return 127 - countl_zero(hi); } const uint64_t low = Uint128Low64(n); - ABSL_INTERNAL_ASSUME(low != 0); + ABSL_ASSUME(low != 0); return 63 - countl_zero(low); } diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h index 0e78e12c..2cbc09ec 100644 --- a/absl/strings/internal/cord_rep_btree.h +++ b/absl/strings/internal/cord_rep_btree.h @@ -716,7 +716,7 @@ inline void CordRepBtree::AlignBegin() { // size, and then do overlapping load/store of up to 4 pointers (inlined as // XMM, YMM or ZMM load/store) and up to 2 pointers (XMM / YMM), which is a) // compact and b) not clobbering any registers. - ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity); + ABSL_ASSUME(new_end <= kMaxCapacity); #ifdef __clang__ #pragma unroll 1 #endif @@ -734,7 +734,7 @@ inline void CordRepBtree::AlignEnd() { const size_t new_end = end() + delta; set_begin(new_begin); set_end(new_end); - ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity); + ABSL_ASSUME(new_end <= kMaxCapacity); #ifdef __clang__ #pragma unroll 1 #endif diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc index e28a29b1..02aeeebe 100644 --- a/absl/strings/internal/str_format/arg.cc +++ b/absl/strings/internal/str_format/arg.cc @@ -320,7 +320,7 @@ bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, return ConvertFloatImpl(static_cast(v), conv, sink); default: - ABSL_INTERNAL_ASSUME(false); + ABSL_ASSUME(false); } if (conv.is_basic()) { -- cgit v1.2.3 From 1dd160f9f091ac30fc83326306b96827ca32c3cd Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 1 Mar 2022 13:02:10 -0800 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- f0b7d230a90c82c6fee7adcb46a213d2582b6a7b by Martijn Vels : Optimize substring logic now that CONCAT is removed This CL adds a static Substring() method to CordRepSubstring, and implements substring logic in cord.cc in terms of the new function. This cleans up various helper functions and logic remaining from previous complex CONCAT logic that is no longer needed. PiperOrigin-RevId: 431756805 Change-Id: I39c875b5af119916780e68598c7fc619fb2e8476 -- fa7d1bedf0e1244303844869a332c2a5dbd9ac0f by Derek Mauro : Allow macro expansion within ABSL_FLAG and ABSL_DECLARE_FLAG args PiperOrigin-RevId: 431721184 Change-Id: I6e19713fb541205d796f940998db5ee25178d55e -- 1b328badd92304ed1c634f23e1c191be57b7bb15 by Laramie Leavitt : Add #include for std:: types PiperOrigin-RevId: 431546757 Change-Id: I75efbcd3c77e6f53e4db66494101d30d670d988e -- e25323b299d4d3840218702860f537cdd2a3926f by Thomas Köppe : Add hashing support for pointers to member. Also add tests for function pointers to the existing "pointer" test. PiperOrigin-RevId: 431067588 Change-Id: I3686010635d9fee34c47a418b72402e10737cdbc -- ab27b012a61cf10109fd51932b3b0b05ee78f32f by Laramie Leavitt : Avoid use of std::pow in ChiSquare test. PiperOrigin-RevId: 431015830 Change-Id: Idd767ff2f51009ee171de48757207b38330ffea3 -- 28c359135d89061177958580fe4a7493802499cb by Laramie Leavitt : Add #include for std::false_type PiperOrigin-RevId: 431005757 Change-Id: I85a6a918778601e19512aaea744424cf39018521 -- a920730f23669479d92e3a696d65d0bc3a5b1de1 by Laramie Leavitt : #include for std::declval PiperOrigin-RevId: 431004934 Change-Id: I295237b2d44e9a15e4083698ea121b68ce0a1bb7 GitOrigin-RevId: f0b7d230a90c82c6fee7adcb46a213d2582b6a7b --- absl/flags/declare.h | 6 +- absl/flags/flag.h | 2 +- absl/flags/flag_test.cc | 13 +++ absl/hash/hash_test.cc | 81 +++++++++++++++ absl/hash/internal/hash.h | 33 ++++++ absl/random/bit_gen_ref.h | 4 + absl/random/internal/chi_square.cc | 3 +- absl/random/internal/distribution_caller.h | 1 + absl/random/internal/mock_helpers.h | 1 + absl/strings/cord.cc | 156 ++++------------------------- absl/strings/internal/cord_internal.h | 27 +++++ 11 files changed, 188 insertions(+), 139 deletions(-) diff --git a/absl/flags/declare.h b/absl/flags/declare.h index a791b667..d1437bb9 100644 --- a/absl/flags/declare.h +++ b/absl/flags/declare.h @@ -60,7 +60,11 @@ ABSL_NAMESPACE_END // The ABSL_DECLARE_FLAG(type, name) macro expands to: // // extern absl::Flag FLAGS_name; -#define ABSL_DECLARE_FLAG(type, name) \ +#define ABSL_DECLARE_FLAG(type, name) ABSL_DECLARE_FLAG_INTERNAL(type, name) + +// Internal implementation of ABSL_DECLARE_FLAG to allow macro expansion of its +// arguments. Clients must use ABSL_DECLARE_FLAG instead. +#define ABSL_DECLARE_FLAG_INTERNAL(type, name) \ extern absl::Flag FLAGS_##name; \ namespace absl /* block flags in namespaces */ {} \ /* second redeclaration is to allow applying attributes */ \ diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 50106082..d2750b31 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -163,7 +163,6 @@ ABSL_NAMESPACE_END // Note: do not construct objects of type `absl::Flag` directly. Only use the // `ABSL_FLAG()` macro for such construction. #define ABSL_FLAG(Type, name, default_value, help) \ - extern ::absl::Flag FLAGS_##name; \ ABSL_FLAG_IMPL(Type, name, default_value, help) // ABSL_FLAG().OnUpdate() @@ -266,6 +265,7 @@ ABSL_NAMESPACE_END // global name for FLAGS_no symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. #define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + extern ::absl::Flag FLAGS_##name; \ namespace absl /* block flags in namespaces */ {} \ ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 6e974a5b..ced332d4 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -977,3 +977,16 @@ TEST_F(FlagTest, TesTypeWrappingEnum) { value = absl::GetFlag(FLAGS_test_enum_wrapper_flag); EXPECT_EQ(value.e, B); } + +// This is a compile test to ensure macros are expanded within ABSL_FLAG and +// ABSL_DECLARE_FLAG. +#define FLAG_NAME_MACRO(name) prefix_ ## name +ABSL_DECLARE_FLAG(int, FLAG_NAME_MACRO(test_macro_named_flag)); +ABSL_FLAG(int, FLAG_NAME_MACRO(test_macro_named_flag), 0, + "Testing macro expansion within ABSL_FLAG"); + +TEST_F(FlagTest, MacroWithinAbslFlag) { + EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 0); + absl::SetFlag(&FLAGS_prefix_test_macro_named_flag, 1); + EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 1); +} diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 39ff8f52..ffa45e6e 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -185,6 +185,8 @@ TEST(HashValueTest, FloatingPoint) { TEST(HashValueTest, Pointer) { EXPECT_TRUE((is_hashable::value)); + EXPECT_TRUE((is_hashable::value)); + EXPECT_TRUE((is_hashable::value)); int i; int* ptr = &i; @@ -224,6 +226,85 @@ TEST(HashValueTest, PointerAlignment) { } } +TEST(HashValueTest, PointerToMember) { + struct Bass { + void q() {} + }; + + struct A : Bass { + virtual ~A() = default; + virtual void vfa() {} + + static auto pq() -> void (A::*)() { return &A::q; } + }; + + struct B : Bass { + virtual ~B() = default; + virtual void vfb() {} + + static auto pq() -> void (B::*)() { return &B::q; } + }; + + struct Foo : A, B { + void f1() {} + void f2() const {} + + int g1() & { return 0; } + int g2() const & { return 0; } + int g3() && { return 0; } + int g4() const && { return 0; } + + int h1() & { return 0; } + int h2() const & { return 0; } + int h3() && { return 0; } + int h4() const && { return 0; } + + int a; + int b; + + const int c = 11; + const int d = 22; + }; + + EXPECT_TRUE((is_hashable::value)); + EXPECT_TRUE((is_hashable::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(&Foo::a, &Foo::b, static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(&Foo::c, &Foo::d, static_cast(nullptr), + &Foo::a, &Foo::b))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + &Foo::f1, static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + &Foo::f2, static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + &Foo::g1, &Foo::h1, static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + &Foo::g2, &Foo::h2, static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + &Foo::g3, &Foo::h3, static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + &Foo::g4, &Foo::h4, static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(static_cast(&Foo::vfa), + static_cast(&Foo::vfb), + static_cast(nullptr)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(static_cast(Foo::A::pq()), + static_cast(Foo::B::pq()), + static_cast(nullptr)))); +} + TEST(HashValueTest, PairAndTuple) { EXPECT_TRUE((is_hashable>::value)); EXPECT_TRUE((is_hashable>::value)); diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index a424e014..f8103334 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -421,6 +421,39 @@ H AbslHashValue(H hash_state, std::nullptr_t) { return H::combine(std::move(hash_state), static_cast(nullptr)); } +// AbslHashValue() for hashing pointers-to-member +template +H AbslHashValue(H hash_state, T C::* ptr) { + auto salient_ptm_size = [](std::size_t n) -> std::size_t { +#if defined(_MSC_VER) + // Pointers-to-member-function on MSVC consist of one pointer plus 0, 1, 2, + // or 3 ints. In 64-bit mode, they are 8-byte aligned and thus can contain + // padding (namely when they have 1 or 3 ints). The value below is a lower + // bound on the number of salient, non-padding bytes that we use for + // hashing. + if (alignof(T C::*) == alignof(int)) { + // No padding when all subobjects have the same size as the total + // alignment. This happens in 32-bit mode. + return n; + } else { + // Padding for 1 int (size 16) or 3 ints (size 24). + // With 2 ints, the size is 16 with no padding, which we pessimize. + return n == 24 ? 20 : n == 16 ? 12 : n; + } +#else + // On other platforms, we assume that pointers-to-members do not have + // padding. +#ifdef __cpp_lib_has_unique_object_representations + static_assert(std::has_unique_object_representations_v); +#endif // __cpp_lib_has_unique_object_representations + return n; +#endif + }; + return H::combine_contiguous(std::move(hash_state), + reinterpret_cast(&ptr), + salient_ptm_size(sizeof ptr)); +} + // ----------------------------------------------------------------------------- // AbslHashValue for Composite Types // ----------------------------------------------------------------------------- diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h index 9555460f..e475221a 100644 --- a/absl/random/bit_gen_ref.h +++ b/absl/random/bit_gen_ref.h @@ -24,6 +24,10 @@ #ifndef ABSL_RANDOM_BIT_GEN_REF_H_ #define ABSL_RANDOM_BIT_GEN_REF_H_ +#include +#include +#include + #include "absl/base/internal/fast_type_id.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" diff --git a/absl/random/internal/chi_square.cc b/absl/random/internal/chi_square.cc index 640d48ce..fbe01732 100644 --- a/absl/random/internal/chi_square.cc +++ b/absl/random/internal/chi_square.cc @@ -125,7 +125,8 @@ double ChiSquareValue(int dof, double p) { const double variance = 2.0 / (9 * dof); // Cannot use this method if the variance is 0. if (variance != 0) { - return std::pow(z * std::sqrt(variance) + mean, 3.0) * dof; + double term = z * std::sqrt(variance) + mean; + return dof * (term * term * term); } } diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h index f1ad5ccd..0f162a4e 100644 --- a/absl/random/internal/distribution_caller.h +++ b/absl/random/internal/distribution_caller.h @@ -18,6 +18,7 @@ #define ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_ #include +#include #include "absl/base/config.h" #include "absl/base/internal/fast_type_id.h" diff --git a/absl/random/internal/mock_helpers.h b/absl/random/internal/mock_helpers.h index 9d6ab21e..882b0518 100644 --- a/absl/random/internal/mock_helpers.h +++ b/absl/random/internal/mock_helpers.h @@ -18,6 +18,7 @@ #include #include +#include #include "absl/base/internal/fast_type_id.h" #include "absl/types/optional.h" diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index b65dc58b..c6ec1ecf 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -87,30 +87,6 @@ static inline CordRep* VerifyTree(CordRep* node) { return node; } -// Create a concatenation of the specified nodes. -// Does not change the refcounts of "left" and "right". -// The returned node has a refcount of 1. -static CordRep* RawConcat(CordRep* left, CordRep* right) { - // Avoid making degenerate concat nodes (one child is empty) - if (left == nullptr) return right; - if (right == nullptr) return left; - if (left->length == 0) { - CordRep::Unref(left); - return right; - } - if (right->length == 0) { - CordRep::Unref(right); - return left; - } - ABSL_INTERNAL_LOG(FATAL, "CordRepConcat is no longer supported"); - return nullptr; -} - -static CordRep* Concat(CordRep* left, CordRep* right) { - CordRep* rep = RawConcat(left, right); - return VerifyTree(rep); -} - static CordRepFlat* CreateFlat(const char* data, size_t length, size_t alloc_hint) { CordRepFlat* flat = CordRepFlat::New(length + alloc_hint); @@ -608,68 +584,6 @@ inline void Cord::Prepend(T&& src) { template void Cord::Prepend(std::string&& src); -static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { - if (n >= node->length) return nullptr; - if (n == 0) return CordRep::Ref(node); - absl::InlinedVector rhs_stack; - - assert(!node->IsCrc()); - assert(n <= node->length); - - if (n == 0) { - CordRep::Ref(node); - } else { - size_t start = n; - size_t len = node->length - n; - if (node->IsSubstring()) { - // Consider in-place update of node, similar to in RemoveSuffixFrom(). - start += node->substring()->start; - node = node->substring()->child; - } - node = CordRepSubstring::Create(CordRep::Ref(node), start, len); - } - while (!rhs_stack.empty()) { - node = Concat(node, CordRep::Ref(rhs_stack.back())); - rhs_stack.pop_back(); - } - return node; -} - -// RemoveSuffixFrom() is very similar to RemovePrefixFrom(), with the -// exception that removing a suffix has an optimization where a node may be -// edited in place iff that node and all its ancestors have a refcount of 1. -static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { - if (n >= node->length) return nullptr; - if (n == 0) return CordRep::Ref(node); - absl::InlinedVector lhs_stack; - bool inplace_ok = node->refcount.IsOne(); - assert(!node->IsCrc()); - - assert(n <= node->length); - - if (n == 0) { - CordRep::Ref(node); - } else if (inplace_ok && !node->IsExternal()) { - // Consider making a new buffer if the current node capacity is much - // larger than the new length. - CordRep::Ref(node); - node->length -= n; - } else { - size_t start = 0; - size_t len = node->length - n; - if (node->IsSubstring()) { - start = node->substring()->start; - node = node->substring()->child; - } - node = CordRepSubstring::Create(CordRep::Ref(node), start, len); - } - while (!lhs_stack.empty()) { - node = Concat(CordRep::Ref(lhs_stack.back()), node); - lhs_stack.pop_back(); - } - return node; -} - void Cord::RemovePrefix(size_t n) { ABSL_INTERNAL_CHECK(n <= size(), absl::StrCat("Requested prefix size ", n, @@ -681,14 +595,20 @@ void Cord::RemovePrefix(size_t n) { auto constexpr method = CordzUpdateTracker::kRemovePrefix; CordzUpdateScope scope(contents_.cordz_info(), method); tree = cord_internal::RemoveCrcNode(tree); - if (tree->IsBtree()) { + if (n >= tree->length) { + CordRep::Unref(tree); + tree = nullptr; + } else if (tree->IsBtree()) { CordRep* old = tree; tree = tree->btree()->SubTree(n, tree->length - n); CordRep::Unref(old); + } else if (tree->IsSubstring() && tree->refcount.IsOne()) { + tree->substring()->start += n; + tree->length -= n; } else { - CordRep* newrep = RemovePrefixFrom(tree, n); + CordRep* rep = CordRepSubstring::Substring(tree, n, tree->length - n); CordRep::Unref(tree); - tree = VerifyTree(newrep); + tree = rep; } contents_.SetTreeOrEmpty(tree, scope); } @@ -705,59 +625,23 @@ void Cord::RemoveSuffix(size_t n) { auto constexpr method = CordzUpdateTracker::kRemoveSuffix; CordzUpdateScope scope(contents_.cordz_info(), method); tree = cord_internal::RemoveCrcNode(tree); - if (tree->IsBtree()) { + if (n >= tree->length) { + CordRep::Unref(tree); + tree = nullptr; + } else if (tree->IsBtree()) { tree = CordRepBtree::RemoveSuffix(tree->btree(), n); + } else if (!tree->IsExternal() && tree->refcount.IsOne()) { + assert(tree->IsFlat() || tree->IsSubstring()); + tree->length -= n; } else { - CordRep* newrep = RemoveSuffixFrom(tree, n); + CordRep* rep = CordRepSubstring::Substring(tree, 0, tree->length - n); CordRep::Unref(tree); - tree = VerifyTree(newrep); + tree = rep; } contents_.SetTreeOrEmpty(tree, scope); } } -// Work item for NewSubRange(). -struct SubRange { - SubRange(CordRep* a_node, size_t a_pos, size_t a_n) - : node(a_node), pos(a_pos), n(a_n) {} - CordRep* node; // nullptr means concat last 2 results. - size_t pos; - size_t n; -}; - -static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { - absl::InlinedVector results; - absl::InlinedVector todo; - assert(!node->IsCrc()); - todo.push_back(SubRange(node, pos, n)); - do { - const SubRange& sr = todo.back(); - node = sr.node; - pos = sr.pos; - n = sr.n; - todo.pop_back(); - - if (node == nullptr) { - assert(results.size() >= 2); - CordRep* right = results.back(); - results.pop_back(); - CordRep* left = results.back(); - results.pop_back(); - results.push_back(Concat(left, right)); - } else if (pos == 0 && n == node->length) { - results.push_back(CordRep::Ref(node)); - } else { - if (node->IsSubstring()) { - pos += node->substring()->start; - node = node->substring()->child; - } - results.push_back(CordRepSubstring::Create(CordRep::Ref(node), pos, n)); - } - } while (!todo.empty()); - assert(results.size() == 1); - return results[0]; -} - Cord Cord::Subcord(size_t pos, size_t new_size) const { Cord sub_cord; size_t length = size(); @@ -791,7 +675,7 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { if (tree->IsBtree()) { tree = tree->btree()->SubTree(pos, new_size); } else { - tree = NewSubRange(tree, pos, new_size); + tree = CordRepSubstring::Substring(tree, pos, new_size); } sub_cord.contents_.EmplaceTree(tree, contents_.data_, CordzUpdateTracker::kSubCord); @@ -1140,7 +1024,7 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { : payload->flat()->Data(); const size_t offset = current_chunk_.data() - data; - auto* tree = CordRepSubstring::Create(CordRep::Ref(payload), offset, n); + auto* tree = CordRepSubstring::Substring(payload, offset, n); subcord.contents_.EmplaceTree(VerifyTree(tree), method); bytes_remaining_ -= n; current_chunk_.remove_prefix(n); diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index ece3db15..2087ffec 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -286,6 +286,14 @@ struct CordRepSubstring : public CordRep { // form a non-empty partial sub range of `'child`, i.e.: // `n > 0 && n < length && n + pos <= length` static inline CordRepSubstring* Create(CordRep* child, size_t pos, size_t n); + + // Creates a substring of `rep`. Does not adopt a reference on `rep`. + // Requires `IsDataEdge(rep) && n > 0 && pos + n <= rep->length`. + // If `n == rep->length` then this method returns `CordRep::Ref(rep)` + // If `rep` is a substring of a flat or external node, then this method will + // return a new substring of that flat or external node with `pos` adjusted + // with the original `start` position. + static inline CordRep* Substring(CordRep* rep, size_t pos, size_t n); }; // Type for function pointer that will invoke the releaser function and also @@ -371,6 +379,25 @@ inline CordRepSubstring* CordRepSubstring::Create(CordRep* child, size_t pos, return rep; } +inline CordRep* CordRepSubstring::Substring(CordRep* rep, size_t pos, + size_t n) { + assert(rep != nullptr); + assert(n != 0); + assert(pos < rep->length); + assert(n <= rep->length - pos); + if (n == rep->length) return CordRep::Ref(rep); + if (rep->IsSubstring()) { + pos += rep->substring()->start; + rep = rep->substring()->child; + } + CordRepSubstring* substr = new CordRepSubstring(); + substr->length = n; + substr->tag = SUBSTRING; + substr->start = pos; + substr->child = CordRep::Ref(rep); + return substr; +} + inline void CordRepExternal::Delete(CordRep* rep) { assert(rep != nullptr && rep->IsExternal()); auto* rep_external = static_cast(rep); -- cgit v1.2.3 From dfc3fa9b5ae8b204ac83e3f06e4e8626f9bb2bc2 Mon Sep 17 00:00:00 2001 From: Andrew Krasavin Date: Thu, 3 Mar 2022 14:35:39 +0000 Subject: Some trivial OpenBSD-related fixes (#1113) --- absl/base/config.h | 4 ++-- absl/base/internal/raw_logging.cc | 7 ++++--- absl/debugging/internal/elf_mem_image.h | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 373aa0cc..acbdb75c 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -414,7 +414,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ - defined(__HAIKU__) + defined(__HAIKU__) || defined(__OpenBSD__) #define ABSL_HAVE_MMAP 1 #endif @@ -425,7 +425,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(_AIX) || defined(__ros__) + defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 074e026a..509d7460 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -36,8 +36,8 @@ // This preprocessor token is also defined in raw_io.cc. If you need to copy // this, consider moving both to config.h instead. #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__Fuchsia__) || defined(__native_client__) || \ - defined(__EMSCRIPTEN__) || defined(__ASYLO__) + defined(__Fuchsia__) || defined(__native_client__) || \ + defined(__OpenBSD__) || defined(__EMSCRIPTEN__) || defined(__ASYLO__) #include @@ -50,7 +50,8 @@ // ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall // syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len); // for low level operations that want to avoid libc. -#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__) +#if (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && \ + !defined(__ANDROID__) #include #define ABSL_HAVE_SYSCALL_WRITE 1 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index a894bd42..be20256f 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -31,8 +31,8 @@ #error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set #endif -#if defined(__ELF__) && !defined(__native_client__) && !defined(__asmjs__) && \ - !defined(__wasm__) +#if defined(__ELF__) && !defined(__OpenBSD__) && \ + !defined(__native_client__) && !defined(__asmjs__) && !defined(__wasm__) #define ABSL_HAVE_ELF_MEM_IMAGE 1 #endif -- cgit v1.2.3 From ec33f404bb16564a9aea3044cd8504d6885165b0 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 3 Mar 2022 06:35:20 -0800 Subject: Export of internal Abseil changes -- f2ffea8ae1c1b152b63bf561375cfb6eb6b9dbe5 by Derek Mauro : Internal change PiperOrigin-RevId: 432180490 Change-Id: I5e318e1d06fe26eee08920fe39f8a6285eb8e217 -- e5d0de4b3293833eab3f77252a091ac4ed74b210 by Derek Mauro : absl::optional - Add a workaround for an internal compiler error in GCC5 though GCC10 PiperOrigin-RevId: 432047437 Change-Id: I524a9098dadc116d8a423dd66f7ec5ee894f2874 GitOrigin-RevId: f2ffea8ae1c1b152b63bf561375cfb6eb6b9dbe5 --- absl/types/optional.h | 31 +++++++++++++++++-------------- absl/types/optional_test.cc | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/absl/types/optional.h b/absl/types/optional.h index 61540cfd..134b2aff 100644 --- a/absl/types/optional.h +++ b/absl/types/optional.h @@ -282,15 +282,16 @@ class optional : private optional_internal::optional_data, optional& operator=(optional&& src) = default; // Value assignment operators - template < - typename U = T, - typename = typename std::enable_if, typename std::decay::type>>, - absl::negation< - absl::conjunction, - std::is_same::type>>>, - std::is_constructible, std::is_assignable>::value>::type> + template , typename std::decay::type> >, + absl::negation, + std::is_same::type> > >, + std::is_constructible, + std::is_assignable >::value>::type> optional& operator=(U&& v) { this->assign(std::forward(v)); return *this; @@ -298,13 +299,14 @@ class optional : private optional_internal::optional_data, template < typename U, + int&..., // Workaround an internal compiler error in GCC 5 to 10. typename = typename std::enable_if>, + absl::negation >, std::is_constructible, std::is_assignable, absl::negation< optional_internal:: is_constructible_convertible_assignable_from_optional< - T, U>>>::value>::type> + T, U> > >::value>::type> optional& operator=(const optional& rhs) { if (rhs) { this->assign(*rhs); @@ -315,13 +317,14 @@ class optional : private optional_internal::optional_data, } template >, std::is_constructible, - std::is_assignable, + absl::negation >, + std::is_constructible, std::is_assignable, absl::negation< optional_internal:: is_constructible_convertible_assignable_from_optional< - T, U>>>::value>::type> + T, U> > >::value>::type> optional& operator=(optional&& rhs) { if (rhs) { this->assign(std::move(*rhs)); diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc index 7ef142cb..1846e695 100644 --- a/absl/types/optional_test.cc +++ b/absl/types/optional_test.cc @@ -27,6 +27,32 @@ #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" +// The following types help test an internal compiler error in GCC5 though +// GCC10. The case OptionalTest.InternalCompilerErrorInGcc5ToGcc10 crashes the +// compiler without a workaround. This test case should remain at the beginning +// of the file as the internal compiler error is sensitive to other constructs +// in this file. +template +using GccIceHelper1 = T; +template +struct GccIceHelper2 {}; +template +class GccIce { + template &, U>> + GccIce& operator=(GccIceHelper2 const&) {} +}; + +TEST(OptionalTest, InternalCompilerErrorInGcc5ToGcc10) { + GccIce instantiate_ice_with_same_type_as_optional; + static_cast(instantiate_ice_with_same_type_as_optional); + absl::optional val1; + absl::optional val2; + val1 = val2; +} + struct Hashable {}; namespace std { -- cgit v1.2.3 From 04bde89e5cb33bf4a714a5496fac715481fc4831 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 3 Mar 2022 20:39:13 -0800 Subject: Export of internal Abseil changes -- 3358c3cca1c3770fb5d50e743ac56547af813561 by Dino Radakovic : Add ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT guard to TEST(Duration, Multiplication) PiperOrigin-RevId: 432350431 Change-Id: I28a26a12665e3e9a9ebdf50d0249eafa8336c585 -- af631611681a298c305578d5049757ec8ced03ab by Dino Radakovic : Add config macro for optional skipping absl/time:time_test cases that fail on MSVC optimized builds PiperOrigin-RevId: 432252533 Change-Id: Ic3879ccdd8e30945a1a2e980e9ae246bcef8d7bd GitOrigin-RevId: 3358c3cca1c3770fb5d50e743ac56547af813561 --- absl/time/duration_test.cc | 35 +++++++++++++++++++++++++++++++++++ absl/time/time_test.cc | 10 ++++++++++ 2 files changed, 45 insertions(+) diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index b7209e1c..b7abf4ba 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -349,6 +349,11 @@ TEST(Duration, ToChrono) { } TEST(Duration, FactoryOverloads) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + enum E { kOne = 1 }; #define TEST_FACTORY_OVERLOADS(NAME) \ EXPECT_EQ(1, NAME(kOne) / NAME(kOne)); \ @@ -879,6 +884,11 @@ TEST(Duration, RelationalOperators) { } TEST(Duration, Addition) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + #define TEST_ADD_OPS(UNIT) \ do { \ EXPECT_EQ(UNIT(2), UNIT(1) + UNIT(1)); \ @@ -972,6 +982,11 @@ TEST(Duration, Negation) { } TEST(Duration, AbsoluteValue) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + EXPECT_EQ(absl::ZeroDuration(), AbsDuration(absl::ZeroDuration())); EXPECT_EQ(absl::Seconds(1), AbsDuration(absl::Seconds(1))); EXPECT_EQ(absl::Seconds(1), AbsDuration(absl::Seconds(-1))); @@ -989,6 +1004,11 @@ TEST(Duration, AbsoluteValue) { } TEST(Duration, Multiplication) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + #define TEST_MUL_OPS(UNIT) \ do { \ EXPECT_EQ(UNIT(5), UNIT(2) * 2.5); \ @@ -1241,6 +1261,11 @@ TEST(Duration, RoundTripUnits) { } TEST(Duration, TruncConversions) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + // Tests ToTimespec()/DurationFromTimespec() const struct { absl::Duration d; @@ -1537,6 +1562,11 @@ TEST(Duration, ConversionSaturation) { } TEST(Duration, FormatDuration) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + // Example from Go's docs. EXPECT_EQ("72h3m0.5s", absl::FormatDuration(absl::Hours(72) + absl::Minutes(3) + @@ -1671,6 +1701,11 @@ TEST(Duration, FormatDuration) { } TEST(Duration, ParseDuration) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + absl::Duration d; // No specified unit. Should only work for zero and infinity. diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index cde9423f..d235e9ad 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc @@ -377,6 +377,11 @@ TEST(Time, FloorConversion) { } TEST(Time, RoundtripConversion) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + #define TEST_CONVERSION_ROUND_TRIP(SOURCE, FROM, TO, MATCHER) \ EXPECT_THAT(TO(FROM(SOURCE)), MATCHER(SOURCE)) @@ -558,6 +563,11 @@ TEST(Time, FromChrono) { } TEST(Time, ToChronoTime) { +#if defined(ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT) && \ + ABSL_SKIP_TIME_TESTS_BROKEN_ON_MSVC_OPT + GTEST_SKIP(); +#endif + EXPECT_EQ(std::chrono::system_clock::from_time_t(-1), absl::ToChronoTime(absl::FromTimeT(-1))); EXPECT_EQ(std::chrono::system_clock::from_time_t(0), -- cgit v1.2.3 From f9b99adeddbe71208e65cead5f349add7aa9c9b5 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 7 Mar 2022 22:25:14 -0800 Subject: Export of internal Abseil changes -- 0b01bad05b4cdea647ad274d64af0d6732787a1d by Gennadiy Rozental : Internal change PiperOrigin-RevId: 433124996 Change-Id: I0a081fb5cbb628901e4913a9c587468ca3b3aaa4 GitOrigin-RevId: 0b01bad05b4cdea647ad274d64af0d6732787a1d --- absl/debugging/internal/examine_stack.cc | 10 ++++++++++ absl/debugging/internal/examine_stack.h | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc index 2fbfea8e..81d216f9 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -37,6 +37,16 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { +namespace { +ABSL_CONST_INIT SymbolizeUrlEmitter debug_stack_trace_hook = nullptr; +} // namespace + +void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook) { + debug_stack_trace_hook = hook; +} + +SymbolizeUrlEmitter GetDebugStackTraceHook() { return debug_stack_trace_hook; } + // Returns the program counter from signal context, nullptr if // unknown. vuc is a ucontext_t*. We use void* to avoid the use of // ucontext_t on non-POSIX systems. diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h index 39336913..61f0056f 100644 --- a/absl/debugging/internal/examine_stack.h +++ b/absl/debugging/internal/examine_stack.h @@ -23,6 +23,21 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { +// Type of function used for printing in stack trace dumping, etc. +// We avoid closures to keep things simple. +typedef void OutputWriter(const char*, void*); + +// RegisterDebugStackTraceHook() allows to register a single routine +// `hook` that is called each time DumpStackTrace() is called. +// `hook` may be called from a signal handler. +typedef void (*SymbolizeUrlEmitter)(void* const stack[], int depth, + OutputWriter writer, void* writer_arg); + +// Registration of SymbolizeUrlEmitter for use inside of a signal handler. +// This is inherently unsafe and must be signal safe code. +void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook); +SymbolizeUrlEmitter GetDebugStackTraceHook(); + // Returns the program counter from signal context, or nullptr if // unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of // ucontext_t on non-POSIX systems. -- cgit v1.2.3 From c5a424a2a21005660b182516eb7a079cd8021699 Mon Sep 17 00:00:00 2001 From: Thomas Klausner Date: Tue, 8 Mar 2022 22:01:16 +0100 Subject: Add NetBSD support (#1121) --- absl/base/config.h | 5 +++-- absl/debugging/internal/elf_mem_image.cc | 4 ++++ absl/debugging/internal/vdso_support.cc | 9 +++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index acbdb75c..cd7781c0 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -414,7 +414,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ - defined(__HAIKU__) || defined(__OpenBSD__) + defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) #define ABSL_HAVE_MMAP 1 #endif @@ -425,7 +425,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) + defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index 29a28181..a9d66714 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -351,7 +351,11 @@ void ElfMemImage::SymbolIterator::Update(int increment) { const ElfW(Versym) *version_symbol = image->GetVersym(index_); ABSL_RAW_CHECK(symbol && version_symbol, ""); const char *const symbol_name = image->GetDynstr(symbol->st_name); +#if defined(__NetBSD__) + const int version_index = version_symbol->vs_vers & VERSYM_VERSION; +#else const ElfW(Versym) version_index = version_symbol[0] & VERSYM_VERSION; +#endif const ElfW(Verdef) *version_definition = nullptr; const char *version_name = ""; if (symbol->st_shndx == SHN_UNDEF) { diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index 8a015d55..c655cf45 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -50,6 +50,10 @@ #define AT_SYSINFO_EHDR 33 // for crosstoolv10 #endif +#if defined(__NetBSD__) +using Elf32_auxv_t = Aux32Info; +using Elf64_auxv_t = Aux64Info; +#endif #if defined(__FreeBSD__) #if defined(__ELF_WORD_SIZE) && __ELF_WORD_SIZE == 64 using Elf64_auxv_t = Elf64_Auxinfo; @@ -106,8 +110,13 @@ const void *VDSOSupport::Init() { ElfW(auxv_t) aux; while (read(fd, &aux, sizeof(aux)) == sizeof(aux)) { if (aux.a_type == AT_SYSINFO_EHDR) { +#if defined(__NetBSD__) + vdso_base_.store(reinterpret_cast(aux.a_v), + std::memory_order_relaxed); +#else vdso_base_.store(reinterpret_cast(aux.a_un.a_val), std::memory_order_relaxed); +#endif break; } } -- cgit v1.2.3 From 5ed77665c4d697f657fa1362b5a482450f858cdc Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 14 Mar 2022 08:43:59 -0700 Subject: Export of internal Abseil changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -- d3b99682554d339c42556680f4d65f83226005e2 by Martijn Vels : Inline CycleClock code and remove branch for x86 CycleClockSource function This CL removes the relaxed load for x86 as there is no acquire price to pay on x86. It inlines the UnscaledCycleClock::Now() which is a single RTDSC op for x86, and likewise inlines CycleClock::Now() for x86. The inlining should mostly have secondary benefits such as reducing spills on outlined calls. LTO may eventually hoist these functions inline for the hotspots, but it doesn't hurt to default inline this for all builds and let the compiler decide on the first pass. The perlab benchmark is noisy for the plain BM_Now, but the other benchmarks and the run on my local machine are clear. ------------- Local Benchy Benchmark name old cpu/op new cpu/op delta BM_Now 3.41ns ± 1% 2.30ns ± 2% -32.52% (p=0.000 n=50+50) BM_NowWithRegisterPresure 4.96ns ± 2% 4.19ns ± 2% -15.57% (p=0.000 n=56+55) BM_NowWithCallback 3.30ns ± 2% 1.91ns ± 2% -42.00% (p=0.000 n=47+60) ------------- Perflab Benchy Benchmark name old cpu/op new cpu/op delta BM_Now 8.20ns ±13% 4.32ns ±83% ~ (p=0.413 n=4+5) BM_NowWithRegisterPresure 7.91ns ± 1% 3.68ns ± 2% -53.45% (p=0.029 n=4+4) BM_NowWithCallback 2.66ns ±13% 1.58ns ± 0% -40.51% (p=0.008 n=5+5) PiperOrigin-RevId: 434474766 Change-Id: I991d987ae9233e50f09606c874055cf4c5a56300 -- b38330686a0af176a2679163e4d2fa1b90e2f667 by Laramie Leavitt : Style, comment, and test updates * Remove a redundant assert in uniform_real_distribution. * Update comment in internal/generate_real.h * Style updates to uniform_real_distribution_test mainly replacing TypeParam with real_type, using aliases for some limits, etc. * Add a few more minor tests. PiperOrigin-RevId: 433902174 Change-Id: Id75be8e24be2fb8f6aea05feec13e3ef320a7254 -- ab2da6047ff7f5dae3add3779fcddf73b03feabf by Abseil Team : Remove declaration of method whose definition was previously removed. PiperOrigin-RevId: 433507828 Change-Id: I0130b689813125250f7de2664e767e181f676c89 -- df0c87f4ec2c010691931c1bef9d26470a6e63a2 by Derek Mauro : Internal change PiperOrigin-RevId: 433289136 Change-Id: Iba157dc83ed99dafd17a2223d2504e49f8afbb9e -- 7445fa312f2995772900eda82467325b3401a17d by Martijn Vels : Optimize CordReader logic now that CONCAT is removed This CL cleans up various helper functions and logic remaining from previous complex CONCAT logic that is no longer needed, simplifying the CordReader logic. PiperOrigin-RevId: 433208748 Change-Id: I5f7b1883573c44e7c6f8af12c3cddbd197cb134d GitOrigin-RevId: d3b99682554d339c42556680f4d65f83226005e2 --- absl/base/config.h | 2 +- absl/base/internal/cycleclock.cc | 53 +++-------- absl/base/internal/cycleclock.h | 69 ++++++++++++++- absl/base/internal/unscaledcycleclock.cc | 6 -- absl/base/internal/unscaledcycleclock.h | 10 +++ absl/random/internal/generate_real.h | 4 +- absl/random/uniform_real_distribution.h | 2 +- absl/random/uniform_real_distribution_test.cc | 121 +++++++++++++++++--------- absl/status/status.h | 4 - absl/strings/cord.h | 6 ++ 10 files changed, 176 insertions(+), 101 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index cd7781c0..8c100d8e 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -425,7 +425,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) || \ defined(__NetBSD__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc index 0e65005b..f6e64242 100644 --- a/absl/base/internal/cycleclock.cc +++ b/absl/base/internal/cycleclock.cc @@ -25,6 +25,7 @@ #include #include // NOLINT(build/c++11) +#include "absl/base/attributes.h" #include "absl/base/internal/unscaledcycleclock.h" namespace absl { @@ -33,44 +34,18 @@ namespace base_internal { #if ABSL_USE_UNSCALED_CYCLECLOCK -namespace { +constexpr int32_t CycleClock::kShift; +constexpr double CycleClock::kFrequencyScale; -#ifdef NDEBUG -#ifdef ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY -// Not debug mode and the UnscaledCycleClock frequency is the CPU -// frequency. Scale the CycleClock to prevent overflow if someone -// tries to represent the time as cycles since the Unix epoch. -static constexpr int32_t kShift = 1; -#else -// Not debug mode and the UnscaledCycleClock isn't operating at the -// raw CPU frequency. There is no need to do any scaling, so don't -// needlessly sacrifice precision. -static constexpr int32_t kShift = 0; -#endif -#else -// In debug mode use a different shift to discourage depending on a -// particular shift value. -static constexpr int32_t kShift = 2; -#endif - -static constexpr double kFrequencyScale = 1.0 / (1 << kShift); -static std::atomic cycle_clock_source; +ABSL_CONST_INIT std::atomic + CycleClock::cycle_clock_source_{nullptr}; -CycleClockSourceFunc LoadCycleClockSource() { - // Optimize for the common case (no callback) by first doing a relaxed load; - // this is significantly faster on non-x86 platforms. - if (cycle_clock_source.load(std::memory_order_relaxed) == nullptr) { - return nullptr; - } - // This corresponds to the store(std::memory_order_release) in - // CycleClockSource::Register, and makes sure that any updates made prior to - // registering the callback are visible to this thread before the callback is - // invoked. - return cycle_clock_source.load(std::memory_order_acquire); +void CycleClockSource::Register(CycleClockSourceFunc source) { + // Corresponds to the load(std::memory_order_acquire) in LoadCycleClockSource. + CycleClock::cycle_clock_source_.store(source, std::memory_order_release); } -} // namespace - +#ifdef _WIN32 int64_t CycleClock::Now() { auto fn = LoadCycleClockSource(); if (fn == nullptr) { @@ -78,15 +53,7 @@ int64_t CycleClock::Now() { } return fn() >> kShift; } - -double CycleClock::Frequency() { - return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); -} - -void CycleClockSource::Register(CycleClockSourceFunc source) { - // Corresponds to the load(std::memory_order_acquire) in LoadCycleClockSource. - cycle_clock_source.store(source, std::memory_order_release); -} +#endif #else diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h index a18b5844..9704e388 100644 --- a/absl/base/internal/cycleclock.h +++ b/absl/base/internal/cycleclock.h @@ -42,14 +42,19 @@ #ifndef ABSL_BASE_INTERNAL_CYCLECLOCK_H_ #define ABSL_BASE_INTERNAL_CYCLECLOCK_H_ +#include #include +#include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/internal/unscaledcycleclock.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { +using CycleClockSourceFunc = int64_t (*)(); + // ----------------------------------------------------------------------------- // CycleClock // ----------------------------------------------------------------------------- @@ -68,12 +73,37 @@ class CycleClock { static double Frequency(); private: +#if ABSL_USE_UNSCALED_CYCLECLOCK + static CycleClockSourceFunc LoadCycleClockSource(); + +#ifdef NDEBUG +#ifdef ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY + // Not debug mode and the UnscaledCycleClock frequency is the CPU + // frequency. Scale the CycleClock to prevent overflow if someone + // tries to represent the time as cycles since the Unix epoch. + static constexpr int32_t kShift = 1; +#else + // Not debug mode and the UnscaledCycleClock isn't operating at the + // raw CPU frequency. There is no need to do any scaling, so don't + // needlessly sacrifice precision. + static constexpr int32_t kShift = 0; +#endif +#else // NDEBUG + // In debug mode use a different shift to discourage depending on a + // particular shift value. + static constexpr int32_t kShift = 2; +#endif // NDEBUG + + static constexpr double kFrequencyScale = 1.0 / (1 << kShift); + ABSL_CONST_INIT static std::atomic cycle_clock_source_; +#endif // ABSL_USE_UNSCALED_CYCLECLOC + CycleClock() = delete; // no instances CycleClock(const CycleClock&) = delete; CycleClock& operator=(const CycleClock&) = delete; -}; -using CycleClockSourceFunc = int64_t (*)(); + friend class CycleClockSource; +}; class CycleClockSource { private: @@ -87,6 +117,41 @@ class CycleClockSource { static void Register(CycleClockSourceFunc source); }; +#if ABSL_USE_UNSCALED_CYCLECLOCK + +inline CycleClockSourceFunc CycleClock::LoadCycleClockSource() { +#if !defined(__x86_64__) + // Optimize for the common case (no callback) by first doing a relaxed load; + // this is significantly faster on non-x86 platforms. + if (cycle_clock_source_.load(std::memory_order_relaxed) == nullptr) { + return nullptr; + } +#endif // !defined(__x86_64__) + + // This corresponds to the store(std::memory_order_release) in + // CycleClockSource::Register, and makes sure that any updates made prior to + // registering the callback are visible to this thread before the callback + // is invoked. + return cycle_clock_source_.load(std::memory_order_acquire); +} + +// Accessing globals in inlined code in Window DLLs is problematic. +#ifndef _WIN32 +inline int64_t CycleClock::Now() { + auto fn = LoadCycleClockSource(); + if (fn == nullptr) { + return base_internal::UnscaledCycleClock::Now() >> kShift; + } + return fn() >> kShift; +} +#endif + +inline double CycleClock::Frequency() { + return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); +} + +#endif // ABSL_USE_UNSCALED_CYCLECLOCK + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index 4d352bd1..7b79d19e 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -49,12 +49,6 @@ double UnscaledCycleClock::Frequency() { #elif defined(__x86_64__) -int64_t UnscaledCycleClock::Now() { - uint64_t low, high; - __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); - return (high << 32) | low; -} - double UnscaledCycleClock::Frequency() { return base_internal::NominalCPUFrequency(); } diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index 681ff8f9..07f867a6 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -115,6 +115,16 @@ class UnscaledCycleClock { friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency; }; +#if defined(__x86_64__) + +inline int64_t UnscaledCycleClock::Now() { + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + return (high << 32) | low; +} + +#endif + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h index d5fbb44c..b569450c 100644 --- a/absl/random/internal/generate_real.h +++ b/absl/random/internal/generate_real.h @@ -50,10 +50,10 @@ struct GenerateSignedTag {}; // inputs, otherwise it never returns 0. // // When a value in U(0,1) is required, use: -// Uniform64ToReal; +// GenerateRealFromBits; // // When a value in U(-1,1) is required, use: -// Uniform64ToReal; +// GenerateRealFromBits; // // This generates more distinct values than the mathematical equivalent // `U(0, 1) * 2.0 - 1.0`. diff --git a/absl/random/uniform_real_distribution.h b/absl/random/uniform_real_distribution.h index 5ba17b23..19683341 100644 --- a/absl/random/uniform_real_distribution.h +++ b/absl/random/uniform_real_distribution.h @@ -73,12 +73,12 @@ class uniform_real_distribution { : lo_(lo), hi_(hi), range_(hi - lo) { // [rand.dist.uni.real] preconditions 2 & 3 assert(lo <= hi); + // NOTE: For integral types, we can promote the range to an unsigned type, // which gives full width of the range. However for real (fp) types, this // is not possible, so value generation cannot use the full range of the // real type. assert(range_ <= (std::numeric_limits::max)()); - assert(std::isfinite(range_)); } result_type a() const { return lo_; } diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc index 035bd284..07f199d3 100644 --- a/absl/random/uniform_real_distribution_test.cc +++ b/absl/random/uniform_real_distribution_test.cc @@ -78,62 +78,74 @@ TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { GTEST_SKIP() << "Skipping the test because we detected x87 floating-point semantics"; #endif + using DistributionType = absl::uniform_real_distribution; + using real_type = TypeParam; + using param_type = typename DistributionType::param_type; - using param_type = - typename absl::uniform_real_distribution::param_type; + constexpr const real_type kMax = std::numeric_limits::max(); + constexpr const real_type kMin = std::numeric_limits::min(); + constexpr const real_type kEpsilon = + std::numeric_limits::epsilon(); + constexpr const real_type kLowest = + std::numeric_limits::lowest(); // -max - constexpr const TypeParam a{1152921504606846976}; + const real_type kDenormMax = std::nextafter(kMin, real_type{0}); + const real_type kOneMinusE = + std::nextafter(real_type{1}, real_type{0}); // 1 - epsilon + + constexpr const real_type kTwo60{1152921504606846976}; // 2^60 constexpr int kCount = 1000; absl::InsecureBitGen gen; for (const auto& param : { param_type(), - param_type(TypeParam(2.0), TypeParam(2.0)), // Same - param_type(TypeParam(-0.1), TypeParam(0.1)), - param_type(TypeParam(0.05), TypeParam(0.12)), - param_type(TypeParam(-0.05), TypeParam(0.13)), - param_type(TypeParam(-0.05), TypeParam(-0.02)), + param_type(real_type{0}, real_type{1}), + param_type(real_type(-0.1), real_type(0.1)), + param_type(real_type(0.05), real_type(0.12)), + param_type(real_type(-0.05), real_type(0.13)), + param_type(real_type(-0.05), real_type(-0.02)), + // range = 0 + param_type(real_type(2.0), real_type(2.0)), // Same // double range = 0 // 2^60 , 2^60 + 2^6 - param_type(a, TypeParam(1152921504606847040)), + param_type(kTwo60, real_type(1152921504606847040)), // 2^60 , 2^60 + 2^7 - param_type(a, TypeParam(1152921504606847104)), + param_type(kTwo60, real_type(1152921504606847104)), // double range = 2^8 // 2^60 , 2^60 + 2^8 - param_type(a, TypeParam(1152921504606847232)), + param_type(kTwo60, real_type(1152921504606847232)), // float range = 0 // 2^60 , 2^60 + 2^36 - param_type(a, TypeParam(1152921573326323712)), + param_type(kTwo60, real_type(1152921573326323712)), // 2^60 , 2^60 + 2^37 - param_type(a, TypeParam(1152921642045800448)), + param_type(kTwo60, real_type(1152921642045800448)), // float range = 2^38 // 2^60 , 2^60 + 2^38 - param_type(a, TypeParam(1152921779484753920)), + param_type(kTwo60, real_type(1152921779484753920)), // Limits - param_type(0, std::numeric_limits::max()), - param_type(std::numeric_limits::lowest(), 0), - param_type(0, std::numeric_limits::epsilon()), - param_type(-std::numeric_limits::epsilon(), - std::numeric_limits::epsilon()), - param_type(std::numeric_limits::epsilon(), - 2 * std::numeric_limits::epsilon()), + param_type(0, kMax), + param_type(kLowest, 0), + param_type(0, kMin), + param_type(0, kEpsilon), + param_type(-kEpsilon, kEpsilon), + param_type(0, kOneMinusE), + param_type(0, kDenormMax), }) { // Validate parameters. const auto a = param.a(); const auto b = param.b(); - absl::uniform_real_distribution before(a, b); + DistributionType before(a, b); EXPECT_EQ(before.a(), param.a()); EXPECT_EQ(before.b(), param.b()); { - absl::uniform_real_distribution via_param(param); + DistributionType via_param(param); EXPECT_EQ(via_param, before); } std::stringstream ss; ss << before; - absl::uniform_real_distribution after(TypeParam(1.0), - TypeParam(3.1)); + DistributionType after(real_type(1.0), real_type(3.1)); EXPECT_NE(before.a(), after.a()); EXPECT_NE(before.b(), after.b()); @@ -168,7 +180,7 @@ TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { } } - if (!std::is_same::value) { + if (!std::is_same::value) { // static_cast(long double) can overflow. std::string msg = absl::StrCat("Range: ", static_cast(sample_min), ", ", static_cast(sample_max)); @@ -182,33 +194,52 @@ TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { #pragma warning(disable:4756) // Constant arithmetic overflow. #endif TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) { + using DistributionType = absl::uniform_real_distribution; + using real_type = TypeParam; + #if GTEST_HAS_DEATH_TEST // Hi < Lo - EXPECT_DEBUG_DEATH( - { absl::uniform_real_distribution dist(10.0, 1.0); }, ""); + EXPECT_DEBUG_DEATH({ DistributionType dist(10.0, 1.0); }, ""); // Hi - Lo > numeric_limits<>::max() EXPECT_DEBUG_DEATH( { - absl::uniform_real_distribution dist( - std::numeric_limits::lowest(), - std::numeric_limits::max()); + DistributionType dist(std::numeric_limits::lowest(), + std::numeric_limits::max()); + }, + ""); + + // kEpsilon guarantees that max + kEpsilon = inf. + const auto kEpsilon = std::nexttoward( + (std::numeric_limits::max() - + std::nexttoward(std::numeric_limits::max(), 0.0)) / + 2, + std::numeric_limits::max()); + EXPECT_DEBUG_DEATH( + { + DistributionType dist(-kEpsilon, std::numeric_limits::max()); }, ""); + EXPECT_DEBUG_DEATH( + { + DistributionType dist(std::numeric_limits::lowest(), + kEpsilon); + }, + ""); + #endif // GTEST_HAS_DEATH_TEST #if defined(NDEBUG) // opt-mode, for invalid parameters, will generate a garbage value, // but should not enter an infinite loop. absl::InsecureBitGen gen; { - absl::uniform_real_distribution dist(10.0, 1.0); + DistributionType dist(10.0, 1.0); auto x = dist(gen); EXPECT_FALSE(std::isnan(x)) << x; } { - absl::uniform_real_distribution dist( - std::numeric_limits::lowest(), - std::numeric_limits::max()); + DistributionType dist(std::numeric_limits::lowest(), + std::numeric_limits::max()); auto x = dist(gen); // Infinite result. EXPECT_FALSE(std::isfinite(x)) << x; @@ -220,6 +251,8 @@ TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) { #endif TYPED_TEST(UniformRealDistributionTest, TestMoments) { + using DistributionType = absl::uniform_real_distribution; + constexpr int kSize = 1000000; std::vector values(kSize); @@ -228,7 +261,7 @@ TYPED_TEST(UniformRealDistributionTest, TestMoments) { // implementation. absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6}; - absl::uniform_real_distribution dist; + DistributionType dist; for (int i = 0; i < kSize; i++) { values[i] = dist(rng); } @@ -242,9 +275,10 @@ TYPED_TEST(UniformRealDistributionTest, TestMoments) { } TYPED_TEST(UniformRealDistributionTest, ChiSquaredTest50) { + using DistributionType = absl::uniform_real_distribution; + using param_type = typename DistributionType::param_type; + using absl::random_internal::kChiSquared; - using param_type = - typename absl::uniform_real_distribution::param_type; constexpr size_t kTrials = 100000; constexpr int kBuckets = 50; @@ -269,7 +303,7 @@ TYPED_TEST(UniformRealDistributionTest, ChiSquaredTest50) { const double factor = kBuckets / (max_val - min_val); std::vector counts(kBuckets, 0); - absl::uniform_real_distribution dist(param); + DistributionType dist(param); for (size_t i = 0; i < kTrials; i++) { auto x = dist(rng); auto bucket = static_cast((x - min_val) * factor); @@ -297,8 +331,11 @@ TYPED_TEST(UniformRealDistributionTest, ChiSquaredTest50) { } TYPED_TEST(UniformRealDistributionTest, StabilityTest) { + using DistributionType = absl::uniform_real_distribution; + using real_type = TypeParam; + // absl::uniform_real_distribution stability relies only on - // random_internal::RandU64ToDouble and random_internal::RandU64ToFloat. + // random_internal::GenerateRealFromBits. absl::random_internal::sequence_urbg urbg( {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, @@ -307,9 +344,9 @@ TYPED_TEST(UniformRealDistributionTest, StabilityTest) { std::vector output(12); - absl::uniform_real_distribution dist; + DistributionType dist; std::generate(std::begin(output), std::end(output), [&] { - return static_cast(TypeParam(1000000) * dist(urbg)); + return static_cast(real_type(1000000) * dist(urbg)); }); EXPECT_THAT( diff --git a/absl/status/status.h b/absl/status/status.h index 2d003ce6..193db8d8 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -613,10 +613,6 @@ class Status final { const status_internal::Payloads* GetPayloads() const; status_internal::Payloads* GetPayloads(); - // Takes ownership of payload. - static uintptr_t NewRep( - absl::StatusCode code, absl::string_view msg, - std::unique_ptr payload); static bool EqualsSlow(const absl::Status& a, const absl::Status& b); // MSVC 14.0 limitation requires the const. diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 081b6311..5ad4ea0c 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -768,6 +768,7 @@ class Cord { // Returns nullptr if holding bytes absl::cord_internal::CordRep* tree() const; absl::cord_internal::CordRep* as_tree() const; + const char* as_chars() const; // Returns non-null iff was holding a pointer absl::cord_internal::CordRep* clear(); // Converts to pointer if necessary. @@ -1094,6 +1095,11 @@ inline const char* Cord::InlineRep::data() const { return is_tree() ? nullptr : data_.as_chars(); } +inline const char* Cord::InlineRep::as_chars() const { + assert(!data_.is_tree()); + return data_.as_chars(); +} + inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const { assert(data_.is_tree()); return data_.as_tree(); -- cgit v1.2.3 From 231c393a170ce3c6d83e8456bc87fe917c333ecf Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 15 Mar 2022 11:12:25 -0700 Subject: Export of internal Abseil changes -- 5dc885f2b62993bccf33a3f3b99f7e460c819c89 by Derek Mauro : Remove the internal-only ABSL_INTERNAL_ASSUME now that ABSL_ASSUME is available and ABSL_INTERNAL_ASSUME has no more users. Improve the documentation to ABSL_ASSUME somewhat. PiperOrigin-RevId: 434803125 Change-Id: I7c27418463ffc1c7e10ecd50e2d17f348f686af7 -- 4aea19a0ef596228c9136a4c2446e6f25085f23c by Derek Mauro : Update documentation to warn against using absl::Hash across dynamically loaded libraries Fixes #1128 PiperOrigin-RevId: 434723247 Change-Id: Ib0c7ba03b2cab98b42e19e85be6833192d4b4067 GitOrigin-RevId: 5dc885f2b62993bccf33a3f3b99f7e460c819c89 --- absl/base/optimization.h | 51 +++++++++++++++++++++++------------------- absl/container/flat_hash_map.h | 4 ++++ absl/container/flat_hash_set.h | 4 ++++ absl/container/node_hash_map.h | 4 ++++ absl/container/node_hash_set.h | 4 ++++ absl/hash/hash.h | 5 +++++ 6 files changed, 49 insertions(+), 23 deletions(-) diff --git a/absl/base/optimization.h b/absl/base/optimization.h index 6fe03c43..db5cc097 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h @@ -181,32 +181,21 @@ #define ABSL_PREDICT_TRUE(x) (x) #endif -// Platform and compilation mode dependent implementation of ABSL_ASSUME. -#if !defined(NDEBUG) -#define ABSL_INTERNAL_ASSUME(cond) assert(cond) -#elif ABSL_HAVE_BUILTIN(__builtin_assume) -#define ABSL_INTERNAL_ASSUME(cond) __builtin_assume(cond) -#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) -#define ABSL_INTERNAL_ASSUME(cond) \ - do { \ - if (!(cond)) __builtin_unreachable(); \ - } while (0) -#elif defined(_MSC_VER) -#define ABSL_INTERNAL_ASSUME(cond) __assume(cond) -#else -#define ABSL_INTERNAL_ASSUME(cond) \ - do { \ - static_cast(false && (cond)); \ - } while (0) -#endif - // ABSL_ASSUME(cond) +// // Informs the compiler that a condition is always true and that it can assume -// it to be true for optimization purposes. The call has undefined behavior if -// the condition is false. +// it to be true for optimization purposes. +// +// WARNING: If the condition is false, the program can produce undefined and +// potentially dangerous behavior. +// // In !NDEBUG mode, the condition is checked with an assert(). +// // NOTE: The expression must not have side effects, as it may only be evaluated -// in some compilation modes and not others. +// in some compilation modes and not others. Some compilers may issue a warning +// if the compiler cannot prove the expression has no side effects. For example, +// the expression should not use a function call since the compiler cannot prove +// that a function call does not have side effects. // // Example: // @@ -216,7 +205,23 @@ // // assumption specified above. // int y = x / 16; // -#define ABSL_ASSUME(cond) ABSL_INTERNAL_ASSUME(cond) +#if !defined(NDEBUG) +#define ABSL_ASSUME(cond) assert(cond) +#elif ABSL_HAVE_BUILTIN(__builtin_assume) +#define ABSL_ASSUME(cond) __builtin_assume(cond) +#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_ASSUME(cond) \ + do { \ + if (!(cond)) __builtin_unreachable(); \ + } while (0) +#elif defined(_MSC_VER) +#define ABSL_ASSUME(cond) __assume(cond) +#else +#define ABSL_ASSUME(cond) \ + do { \ + static_cast(false && (cond)); \ + } while (0) +#endif // ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) // This macro forces small unique name on a static file level symbols like diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index 83c71029..13779eda 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -76,6 +76,10 @@ struct FlatHashMapPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // +// Using `absl::flat_hash_map` at interface boundries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// // NOTE: A `flat_hash_map` stores its value types directly inside its // implementation array to avoid memory indirection. Because a `flat_hash_map` // is designed to move data when rehashed, map values will not retain pointer diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index 0fb2ae6f..304c2abb 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -72,6 +72,10 @@ struct FlatHashSetPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // +// Using `absl::flat_hash_set` at interface boundries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// // NOTE: A `flat_hash_set` stores its keys directly inside its implementation // array to avoid memory indirection. Because a `flat_hash_set` is designed to // move data when rehashed, set keys will not retain pointer stability. If you diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index ca1ed408..f2175d16 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -78,6 +78,10 @@ class NodeHashMapPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // +// Using `absl::node_hash_map` at interface boundries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// // Example: // // // Create a node hash map of three strings (that map to strings) diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 9421e11e..a9ff7e1b 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -74,6 +74,10 @@ struct NodeHashSetPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // +// Using `absl::node_hash_set` at interface boundries in dynamically loaded +// libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may +// be randomized across dynamically loaded libraries. +// // Example: // // // Create a node hash set of three strings diff --git a/absl/hash/hash.h b/absl/hash/hash.h index f31fde40..74e2d7c0 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h @@ -40,6 +40,11 @@ // each process. E.g., `absl::Hash{}(9)` in one process and // `absl::Hash{}(9)` in another process are likely to differ. // +// `absl::Hash` may also produce different values from different dynamically +// loaded libraries. For this reason, `absl::Hash` values must never cross +// boundries in dynamically loaded libraries (including when used in types like +// hash containers.) +// // `absl::Hash` is intended to strongly mix input bits with a target of passing // an [Avalanche Test](https://en.wikipedia.org/wiki/Avalanche_effect). // -- cgit v1.2.3 From e6ae2641f6261c67adb5766ccbcf953e2bb0a8fd Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 16 Mar 2022 09:53:25 -0700 Subject: Export of internal Abseil changes -- c49308e5361da572b5accdfe664757ca6bf7fed1 by Derek Mauro : Disable TestArmThumbOverlap on platforms that don't support it TestArmThumbOverlap includes a pair of test functions, one built in arm mode and the other built in thumb mode. Unfortunately when configured for armv6 hardfloat, gcc refuses to build any functions in thumb mode (even if they don't actually use floating point) Closes #1112 PiperOrigin-RevId: 435078532 Change-Id: I090623d0f89839a5f9dde831756aba5267c9a45c GitOrigin-RevId: c49308e5361da572b5accdfe664757ca6bf7fed1 --- absl/debugging/symbolize_test.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index a62fa35d..3165c6ed 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -483,7 +483,8 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() { } } -#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) +#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) && \ + ((__ARM_ARCH >= 7) || !defined(__ARM_PCS_VFP)) // Test that we correctly identify bounds of Thumb functions on ARM. // // Thumb functions have the lowest-order bit set in their addresses in the ELF @@ -502,6 +503,10 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() { // bit in the Thumb function's entry point. It will correctly compute the end of // the Thumb function, it will find no overlap between the Thumb and ARM // functions, and it will return the name of the ARM function. +// +// Unfortunately we cannot perform this test on armv6 or lower systems that use +// the hard float ABI because gcc refuses to compile thumb functions on such +// systems with a "sorry, unimplemented: Thumb-1 hard-float VFP ABI" error. __attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) { return x * x * x; @@ -521,7 +526,8 @@ void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() { #endif } -#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) +#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) && ((__ARM_ARCH >= 7) + // || !defined(__ARM_PCS_VFP)) #elif defined(_WIN32) #if !defined(ABSL_CONSUME_DLL) @@ -596,7 +602,8 @@ int main(int argc, char **argv) { TestWithPCInsideInlineFunction(); TestWithPCInsideNonInlineFunction(); TestWithReturnAddress(); -#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) +#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) && \ + ((__ARM_ARCH >= 7) || !defined(__ARM_PCS_VFP)) TestArmThumbOverlap(); #endif #endif -- cgit v1.2.3 From 9f5b2c782ad50df4692bc66f1687e1f4d43c7308 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 17 Mar 2022 09:43:32 -0700 Subject: Export of internal Abseil changes -- 399d051d30f7a78c367bb1dc08829ffacbb619a6 by Abseil Team : Fix a glitch in symbolization which could result in the same address being correctly symbolized and "(unknown)" in the same crash report. PiperOrigin-RevId: 435371003 Change-Id: I9f6a5684144be6d3d366584601d79fda9eefdce6 -- 5dad813de52773e0bb09c6b2351e1bbadb53deac by Abseil Team : Fix ABSL_HAVE_MMAP check in direct_mmap.h This change makes the check for ABSL_HAVE_MMAP match the other checks in absl. This fixes builds where ABSL_HAVE_MMAP is not defined. PiperOrigin-RevId: 435190242 Change-Id: I11638ef315849cafcf4ea1611eee1a98c80e7dcb GitOrigin-RevId: 399d051d30f7a78c367bb1dc08829ffacbb619a6 --- absl/base/internal/direct_mmap.h | 2 +- absl/debugging/internal/examine_stack.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h index 274054cd..a01d6122 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -20,7 +20,7 @@ #include "absl/base/config.h" -#if ABSL_HAVE_MMAP +#ifdef ABSL_HAVE_MMAP #include diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc index 81d216f9..e0950b72 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -146,7 +146,8 @@ static void DumpPCAndFrameSizeAndSymbol(void (*writerfn)(const char*, void*), const char* const prefix) { char tmp[1024]; const char* symbol = "(unknown)"; - if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) { + if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp)) || + (pc != symbolize_pc && absl::Symbolize(pc, tmp, sizeof(tmp)))) { symbol = tmp; } char buf[1024]; -- cgit v1.2.3 From c33f21f86a14216336b87d48e9b552a13985b2bc Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 17 Mar 2022 13:01:57 -0700 Subject: Export of internal Abseil changes -- 199bdbf9ad253b216ed6c6386bf69bc706330204 by Abseil Team : Import of CCTZ from GitHub. PiperOrigin-RevId: 435425408 Change-Id: I502d21ca5771d14e24fe41b28dc562ea6274996d -- 245a761e96f53857cef9c2359eac9d4bbcfdfd47 by Abseil Team : Fix a -Wsign-compare -Wconversion problem in absl::btree discovered while getting cachelib to compile with those flags enabled. PiperOrigin-RevId: 435419108 Change-Id: I0a9fa53d0163f678dde8960410315a5bc8445c72 GitOrigin-RevId: 199bdbf9ad253b216ed6c6386bf69bc706330204 --- absl/container/internal/btree.h | 3 ++- absl/time/internal/cctz/testdata/version | 2 +- .../cctz/testdata/zoneinfo/America/Punta_Arenas | Bin 1209 -> 1209 bytes .../internal/cctz/testdata/zoneinfo/America/Santiago | Bin 1282 -> 1282 bytes absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza | Bin 1230 -> 1240 bytes absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron | Bin 1248 -> 1258 bytes .../internal/cctz/testdata/zoneinfo/Chile/Continental | Bin 1282 -> 1282 bytes absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev | Bin 549 -> 558 bytes .../internal/cctz/testdata/zoneinfo/Europe/Simferopol | Bin 865 -> 865 bytes .../internal/cctz/testdata/zoneinfo/Europe/Uzhgorod | Bin 530 -> 539 bytes .../internal/cctz/testdata/zoneinfo/Europe/Zaporozhye | Bin 560 -> 569 bytes 11 files changed, 3 insertions(+), 2 deletions(-) diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index a22c9bd7..da2abcb9 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -631,7 +631,8 @@ class btree_node { // Compute how many values we can fit onto a leaf node taking into account // padding. - constexpr static size_type NodeTargetSlots(const int begin, const int end) { + constexpr static size_type NodeTargetSlots(const size_type begin, + const size_type end) { return begin == end ? begin : SizeWithNSlots((begin + end) / 2 + 1) > params_type::kTargetNodeSize diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index 8ee898ba..ca002de2 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2021e +2022a diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas b/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas index 5c9a20b9..c0421040 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas and b/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago b/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago index 8d603226..cde8dbbf 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago and b/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza index ccc57c9c..effc4df5 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza and b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron index 906d8d5c..aa52bd26 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron and b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental b/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental index 8d603226..cde8dbbf 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental and b/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev index 8f83cefb..4e026859 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev and b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol index 88a6f3bd..40d23c02 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol and b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod index a5755685..d4c35914 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod and b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod differ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye index 4ea8dae4..71819a5a 100644 Binary files a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye and b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye differ -- cgit v1.2.3 From 4c015dbb49fcfac25899f3ba7da4be665b5f9aab Mon Sep 17 00:00:00 2001 From: Ben Niu Date: Fri, 18 Mar 2022 13:10:10 -0700 Subject: Exclude unsupported x64 intrinsics from ARM64EC (#1135) ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. The ARM64EC does not support _umul128 and __rdtsc as x64 intrinsics, though it provides inline function implementations for them, by emulation. Since the code already has portable code paths without using the intrinsics, instead of using the emulated intrinsic implementations, we use the said portable code paths for ARM64EC. --- absl/base/internal/unscaledcycleclock.h | 2 +- absl/numeric/int128.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index 07f867a6..a4351406 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -47,7 +47,7 @@ // The following platforms have an implementation of a hardware counter. #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ - defined(_M_IX86) || defined(_M_X64) + defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 #else #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index c7ad96be..7a899eec 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -44,7 +44,7 @@ // builtin type. We need to make sure not to define operator wchar_t() // alongside operator unsigned short() in these instances. #define ABSL_INTERNAL_WCHAR_T __wchar_t -#if defined(_M_X64) +#if defined(_M_X64) && !defined(_M_ARM64EC) #include #pragma intrinsic(_umul128) #endif // defined(_M_X64) @@ -980,7 +980,7 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { // can be used for uint128 storage. return static_cast(lhs) * static_cast(rhs); -#elif defined(_MSC_VER) && defined(_M_X64) +#elif defined(_MSC_VER) && defined(_M_X64) && !defined(_M_ARM64EC) uint64_t carry; uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry); return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) + -- cgit v1.2.3 From 6c8dab80c06820f475ce0a1fe873b8022cb274f6 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 18 Mar 2022 14:42:21 -0700 Subject: Export of internal Abseil changes -- f3ac7ee28fc7de737bc9e2e1d10ff7739781d645 by Gennadiy Rozental : Internal change PiperOrigin-RevId: 435739199 Change-Id: I8f854b742418a237f9060e4b9f23d0f20baf0bdf -- fe1329708cb40da8e72e53e4eaad79112bdb79ea by Abseil Team : Port SwissTable internals comments from github.com/google/cwisstable to Abseil. PiperOrigin-RevId: 435719801 Change-Id: I2270cc93aaa5d3d57954a8cea7e570b72b6c3956 -- a6e6fcd4b944ce370ac3307e848645c27bf21e47 by Derek Mauro : Internal change PiperOrigin-RevId: 435716325 Change-Id: I77999f69e176ee6c0d18e7c3329a7c336164f0fc GitOrigin-RevId: f3ac7ee28fc7de737bc9e2e1d10ff7739781d645 --- absl/container/internal/raw_hash_set.cc | 2 + absl/container/internal/raw_hash_set.h | 434 +++++++++++++++++++++++-------- absl/debugging/internal/examine_stack.cc | 211 +++++++++++---- absl/debugging/internal/examine_stack.h | 21 +- 4 files changed, 496 insertions(+), 172 deletions(-) diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index 687bcb8a..61bdb773 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -23,6 +23,8 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +// A single block of empty control bytes for tables without any slots allocated. +// This enables removing a branch in the hot path of find(). alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[16] = { ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 0daf7223..d24bbe83 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -53,51 +53,125 @@ // // IMPLEMENTATION DETAILS // -// The table stores elements inline in a slot array. In addition to the slot -// array the table maintains some control state per slot. The extra state is one -// byte per slot and stores empty or deleted marks, or alternatively 7 bits from -// the hash of an occupied slot. The table is split into logical groups of -// slots, like so: +// # Table Layout +// +// A raw_hash_set's backing array consists of control bytes followed by slots +// that may or may not contain objects. +// +// The layout of the backing array, for `capacity` slots, is thus, as a +// pseudo-struct: +// +// struct BackingArray { +// // Control bytes for the "real" slots. +// ctrl_t ctrl[capacity]; +// // Always `ctrl_t::kSentinel`. This is used by iterators to find when to +// // stop and serves no other purpose. +// ctrl_t sentinel; +// // A copy of the first `kWidth - 1` elements of `ctrl`. This is used so +// // that if a probe sequence picks a value near the end of `ctrl`, +// // `Group` will have valid control bytes to look at. +// ctrl_t clones[kWidth - 1]; +// // The actual slot data. +// slot_type slots[capacity]; +// }; +// +// The length of this array is computed by `AllocSize()` below. +// +// Control bytes (`ctrl_t`) are bytes (collected into groups of a +// platform-specific size) that define the state of the corresponding slot in +// the slot array. Group manipulation is tightly optimized to be as efficient +// as possible: SSE and friends on x86, clever bit operations on other arches. // // Group 1 Group 2 Group 3 // +---------------+---------------+---------------+ // | | | | | | | | | | | | | | | | | | | | | | | | | // +---------------+---------------+---------------+ // -// On lookup the hash is split into two parts: -// - H2: 7 bits (those stored in the control bytes) -// - H1: the rest of the bits -// The groups are probed using H1. For each group the slots are matched to H2 in -// parallel. Because H2 is 7 bits (128 states) and the number of slots per group -// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit. +// Each control byte is either a special value for empty slots, deleted slots +// (sometimes called *tombstones*), and a speical end-of-table marker used by +// iterators, or, if occupied, seven bits (H2) from the hash of the value in the +// corresponding slot. +// +// Storing control bytes in a separate array also has beneficial cache effects, +// since more logical slots will fit into a cache line. +// +// # Table operations. +// +// The key operations are `insert`, `find`, and `erase_at`; the operations below // -// On insert, once the right group is found (as in lookup), its slots are -// filled in order. +// `insert` and `erase` are implemented in terms of find, so we describe that +// one first. To `find` a value `x`, we compute `hash(x)`. From `H1(hash(x))` +// and the capacity, we construct a `probe_seq` that visits every group of +// slots in some interesting order. // -// On erase a slot is cleared. In case the group did not have any empty slots -// before the erase, the erased slot is marked as deleted. +// We now walk through these indices. At each index, we select the entire group +// starting with that index and extract potential candidates: occupied slots +// with a control byte equal to `H2(hash(x))`. If we find an empty slot in the +// group, we stop and return an error. Each candidate slot `y` is compared with +// `x`; if `x == y`, we are done and return `&y`; otherwise we contine to the +// next probe index. Tombstones effectively behave like full slots that never +// match the value we're looking for. // -// Groups without empty slots (but maybe with deleted slots) extend the probe -// sequence. The probing algorithm is quadratic. Given N the number of groups, -// the probing function for the i'th probe is: +// The `H2` bits ensure that if we perform a ==, a false positive is very very +// rare (assuming the hash function looks enough like a random oracle). To see +// this, note that in a group, there will be at most 8 or 16 `H2` values, but +// an `H2` can be any one of 128 values. Viewed as a birthday attack, we can use +// the rule of thumb that the probability of a collision among n choices of m +// symbols is `p(n, m) ~ n^2/2m. In table form: // -// P(0) = H1 % N +// n | p(n) | n | p(n) +// 0 | 0.000 | 8 | 0.250 +// 1 | 0.004 | 9 | 0.316 +// 2 | 0.016 | 10 | 0.391 +// 3 | 0.035 | 11 | 0.473 +// 4 | 0.062 | 12 | 0.562 +// 5 | 0.098 | 13 | 0.660 +// 6 | 0.141 | 14 | 0.766 +// 7 | 0.191 | 15 | 0.879 // -// P(i) = (P(i - 1) + i) % N +// The rule of thumb breaks down at around `n = 12`, but such groups would only +// occur for tables close to their load factor. This is far better than an +// ordinary open-addressing table, which needs to perform an == at every step of +// the probe sequence. These probabilities don't tell the full story (for +// example, because elements are inserted into a group from the front, and +// candidates are =='ed from the front, the collision is only effective in +// rare cases e.g. another probe sequence inserted into a deleted slot in front +// of us). // -// This probing function guarantees that after N probes, all the groups of the -// table will be probed exactly once. +// `insert` is implemented in terms of `unchecked_insert`, which inserts a +// value presumed to not be in the table (violating this requirement will cause +// the table to behave erratically). Given `x` and its hash `hash(x)`, to insert +// it, we construct a `probe_seq` once again, and use it to find the first +// group with an unoccupied (empty *or* deleted) slot. We place `x` into the +// first such slot in the group and mark it as full with `x`'s H2. // -// The control state and slot array are stored contiguously in a shared heap -// allocation. The layout of this allocation is: `capacity()` control bytes, -// one sentinel control byte, `Group::kWidth - 1` cloned control bytes, -// , `capacity()` slots. The sentinel control byte is used in -// iteration so we know when we reach the end of the table. The cloned control -// bytes at the end of the table are cloned from the beginning of the table so -// groups that begin near the end of the table can see a full group. In cases in -// which there are more than `capacity()` cloned control bytes, the extra bytes -// are `kEmpty`, and these ensure that we always see at least one empty slot and -// can stop an unsuccessful search. +// To `insert`, we compose `unchecked_insert` with `find`. We compute `h(x)` and +// perform a `find` to see if it's already present; if it is, we're done. If +// it's not, we may decide the table is getting overcrowded (i.e. the load +// factor is greater than 7/8 for big tables; `is_small()` tables use a max load +// factor of 1); in this case, we allocate a bigger array, `unchecked_insert` +// each element of the table into the new array (we know that no insertion here +// will insert an already-present value), and discard the old backing array. At +// this point, we may `unchecked_insert` the value `x`. +// +// Below, `unchecked_insert` is partly implemented by `prepare_insert`, which +// presents a viable, intialized slot pointee to the caller. +// +// `erase` is implemented in terms of `erase_at`, which takes an index to a +// slot. Given an offset, we simply create a tombstone and destroy its contents. +// If we can prove that the slot would not appear in a probe sequence, we can +// make the slot as empty, instead. We can prove this by observing that if a +// group has any empty slots, it has never been full (assuming we never create +// an empty slot in a group with no empties, which this heuristic guarantees we +// never do) and find would stop at this group anyways (since it does not probe +// beyond groups with empties). +// +// `erase` is `erase_at` composed with `find`: if we +// have a value `x`, we can perform a `find`, and then `erase_at` the resulting +// slot. +// +// To iterate, we simply traverse the array, skipping empty and deleted slots +// and stopping when we hit a `kSentinel`. #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ @@ -142,14 +216,36 @@ template void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/, std::false_type /* propagate_on_container_swap */) {} +// The state for a probe sequence. +// +// Currently, the sequence is a triangular progression of the form +// +// p(i) := Width * (i^2 - i)/2 + hash (mod mask + 1) +// +// The use of `Width` ensures that each probe step does not overlap groups; +// the sequence effectively outputs the addresses of *groups* (although not +// necessarily aligned to any boundary). The `Group` machinery allows us +// to check an entire group with minimal branching. +// +// Wrapping around at `mask + 1` is important, but not for the obvious reason. +// As described above, the first few entries of the control byte array +// is mirrored at the end of the array, which `Group` will find and use +// for selecting candidates. However, when those candidates' slots are +// actually inspected, there are no corresponding slots for the cloned bytes, +// so we need to make sure we've treated those offsets as "wrapping around". template class probe_seq { public: + // Creates a new probe sequence using `hash` as the initial value of the + // sequence and `mask` (usually the capacity of the table) as the mask to + // apply to each value in the progression. probe_seq(size_t hash, size_t mask) { assert(((mask + 1) & mask) == 0 && "not a mask"); mask_ = mask; offset_ = hash & mask_; } + + // The offset within the table, i.e., the value `p(i)` above. size_t offset() const { return offset_; } size_t offset(size_t i) const { return (offset_ + i) & mask_; } @@ -158,7 +254,7 @@ class probe_seq { offset_ += index_; offset_ &= mask_; } - // 0-based probe index. The i-th probe in the probe sequence. + // 0-based probe index, a multiple of `Width`. size_t index() const { return index_; } private: @@ -182,9 +278,9 @@ struct IsDecomposable : std::false_type {}; template struct IsDecomposable< - absl::void_t(), - std::declval()...))>, + absl::void_t(), + std::declval()...))>, Policy, Hash, Eq, Ts...> : std::true_type {}; // TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. @@ -204,14 +300,20 @@ uint32_t TrailingZeros(T x) { return static_cast(countr_zero(x)); } -// An abstraction over a bitmask. It provides an easy way to iterate through the -// indexes of the set bits of a bitmask. When Shift=0 (platforms with SSE), -// this is a true bitmask. On non-SSE, platforms the arithematic used to -// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as -// either 0x00 or 0x80. +// A abstract bitmask, such as that emitted by a SIMD instruction. +// +// Specifically, this type implements a simple bitset whose representation is +// controlled by `SignificantBits` and `Shift`. `SignificantBits` is the number +// of abstract bits in the bitset, while `Shift` is the log-base-two of the +// width of an abstract bit in the representation. +// +// For example, when `SignificantBits` is 16 and `Shift` is zero, this is just +// an ordinary 16-bit bitset occupying the low 16 bits of `mask`. When +// `SignificantBits` is 8 and `Shift` is 3, abstract bits are represented as +// the bytes `0x00` and `0x80`, and it occupies all 64 bits of the bitmask. // // For example: -// for (int i : BitMask(0x5)) -> yields 0, 2 +// for (int i : BitMask(0b101)) -> yields 0, 2 // for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 template class BitMask { @@ -219,7 +321,7 @@ class BitMask { static_assert(Shift == 0 || Shift == 3, ""); public: - // These are useful for unit tests (gunit). + // BitMask is an iterator over the indices of its abstract bits. using value_type = int; using iterator = BitMask; using const_iterator = BitMask; @@ -231,20 +333,26 @@ class BitMask { } explicit operator bool() const { return mask_ != 0; } uint32_t operator*() const { return LowestBitSet(); } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + + // Returns the index of the lowest *abstract* bit set in `self`. uint32_t LowestBitSet() const { return container_internal::TrailingZeros(mask_) >> Shift; } + + // Returns the index of the highest *abstract* bit set in `self`. uint32_t HighestBitSet() const { return static_cast((bit_width(mask_) - 1) >> Shift); } - BitMask begin() const { return *this; } - BitMask end() const { return BitMask(0); } - + // Return the number of trailing zero *abstract* bits. uint32_t TrailingZeros() const { return container_internal::TrailingZeros(mask_) >> Shift; } + // Return the number of leading zero *abstract* bits. uint32_t LeadingZeros() const { constexpr int total_significant_bits = SignificantBits << Shift; constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; @@ -265,8 +373,22 @@ class BitMask { using h2_t = uint8_t; // The values here are selected for maximum performance. See the static asserts -// below for details. We use an enum class so that when strict aliasing is -// enabled, the compiler knows ctrl_t doesn't alias other types. +// below for details. + +// A `ctrl_t` is a single control byte, which can have one of four +// states: empty, deleted, full (which has an associated seven-bit h2_t value) +// and the sentinel. They have the following bit patterns: +// +// empty: 1 0 0 0 0 0 0 0 +// deleted: 1 1 1 1 1 1 1 0 +// full: 0 h h h h h h h // h represents the hash bits. +// sentinel: 1 1 1 1 1 1 1 1 +// +// These values are specifically tuned for SSE-flavored SIMD. +// The static_asserts below detail the source of these choices. +// +// We use an enum class so that when strict aliasing is enabled, the compiler +// knows ctrl_t doesn't alias other types. enum class ctrl_t : int8_t { kEmpty = -128, // 0b10000000 kDeleted = -2, // 0b11111110 @@ -299,10 +421,12 @@ static_assert(ctrl_t::kDeleted == static_cast(-2), "ctrl_t::kDeleted must be -2 to make the implementation of " "ConvertSpecialToEmptyAndFullToDeleted efficient"); -// A single block of empty control bytes for tables without any slots allocated. -// This enables removing a branch in the hot path of find(). ABSL_DLL extern const ctrl_t kEmptyGroup[16]; + +// Returns a pointer to a control byte group that can be used by empty tables. inline ctrl_t* EmptyGroup() { + // Const must be cast away here; no uses of this function will actually write + // to it, because it is only used for empty tables. return const_cast(kEmptyGroup); } @@ -310,28 +434,61 @@ inline ctrl_t* EmptyGroup() { // randomize insertion order within groups. bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl); -// Returns a hash seed. +// Returns a per-table, hash salt, which changes on resize. This gets mixed into +// H1 to randomize iteration order per-table. // // The seed consists of the ctrl_ pointer, which adds enough entropy to ensure // non-determinism of iteration order in most cases. -inline size_t HashSeed(const ctrl_t* ctrl) { +inline size_t PerTableSalt(const ctrl_t* ctrl) { // The low bits of the pointer have little or no entropy because of // alignment. We shift the pointer to try to use higher entropy bits. A // good number seems to be 12 bits, because that aligns with page size. return reinterpret_cast(ctrl) >> 12; } - +// Extracts the H1 portion of a hash: 57 bits mixed with a per-table salt. inline size_t H1(size_t hash, const ctrl_t* ctrl) { - return (hash >> 7) ^ HashSeed(ctrl); + return (hash >> 7) ^ PerTableSalt(ctrl); } + +// Extracts the H2 portion of a hash: the 7 bits not used for H1. +// +// Thse are used used as an occupied control byte. inline h2_t H2(size_t hash) { return hash & 0x7F; } +// Helpers for checking the state of a control byte. inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; } inline bool IsFull(ctrl_t c) { return c >= static_cast(0); } inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +// Quick eference guide for intrinsics used below: +// +// * __m128i: An XMM (128-bit) word. +// +// * _mm_setzero_si128: Returns a zero vector. +// * _mm_set1_epi8: Returns a vector with the same i8 in each lane. +// +// * _mm_subs_epi8: Saturating-subtracts two i8 vectors. +// * _mm_and_si128: Ands two i128s together. +// * _mm_or_si128: Ors two i128s together. +// * _mm_andnot_si128: And-nots two i128s together. +// +// * _mm_cmpeq_epi8: Component-wise compares two i8 vectors for equality, +// filling each lane with 0x00 or 0xff. +// * _mm_cmpgt_epi8: Same as above, but using > rather than ==. +// +// * _mm_loadu_si128: Performs an unaligned load of an i128. +// * _mm_storeu_si128: Performs an unaligned store of a i128. +// +// * _mm_sign_epi8: Retains, negates, or zeroes each i8 lane of the first +// argument if the corresponding lane of the second +// argument is positive, negative, or zero, respectively. +// * _mm_movemask_epi8: Selects the sign bit out of each i8 lane and produces a +// bitmask consisting of those bits. +// * _mm_shuffle_epi8: Selects i8s from the first argument, using the low +// four bits of each i8 lane in the second argument as +// indices. // https://github.com/abseil/abseil-cpp/issues/209 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853 @@ -377,9 +534,8 @@ struct GroupSse2Impl { // Returns a bitmask representing the positions of empty or deleted slots. BitMask MatchEmptyOrDeleted() const { auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); - return BitMask( - static_cast( - _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)))); + return BitMask(static_cast( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)))); } // Returns the number of trailing empty or deleted elements in the group. @@ -464,26 +620,32 @@ using Group = GroupSse2Impl; using Group = GroupPortableImpl; #endif -// The number of cloned control bytes that we copy from the beginning to the -// end of the control bytes array. +// Returns he number of "cloned control bytes". +// +// This is the number of control bytes that are present both at the beginning +// of the control byte array and at the end, such that we can create a +// `Group::kWidth`-width probe window starting from any control byte. constexpr size_t NumClonedBytes() { return Group::kWidth - 1; } template class raw_hash_set; +// Returns whether `n` is a valid capacity (i.e., number of slots). +// +// A valid capacity is a non-zero integer `2^m - 1`. inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } +// Applies the following mapping to every byte in the control array: +// * kDeleted -> kEmpty +// * kEmpty -> kEmpty +// * _ -> kDeleted // PRECONDITION: // IsValidCapacity(capacity) // ctrl[capacity] == ctrl_t::kSentinel // ctrl[i] != ctrl_t::kSentinel for all i < capacity -// Applies mapping for every byte in ctrl: -// DELETED -> EMPTY -// EMPTY -> EMPTY -// FULL -> DELETED void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity); -// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1. +// Converts `n` into the next valid capacity, per `IsValidCapacity`. inline size_t NormalizeCapacity(size_t n) { return n ? ~size_t{} >> countl_zero(n) : 1; } @@ -496,8 +658,8 @@ inline size_t NormalizeCapacity(size_t n) { // never need to probe (the whole table fits in one group) so we don't need a // load factor less than 1. -// Given `capacity` of the table, returns the size (i.e. number of full slots) -// at which we should grow the capacity. +// Given `capacity`, applies the load factor; i.e., it returns the maximum +// number of values we should put into the table before a resizing rehash. inline size_t CapacityToGrowth(size_t capacity) { assert(IsValidCapacity(capacity)); // `capacity*7/8` @@ -507,8 +669,12 @@ inline size_t CapacityToGrowth(size_t capacity) { } return capacity - capacity / 8; } -// From desired "growth" to a lowerbound of the necessary capacity. -// Might not be a valid one and requires NormalizeCapacity(). + +// Given `growth`, "unapplies" the load factor to find how large the capacity +// should be to stay within the load factor. +// +// This might not be a valid capacity and `NormalizeCapacity()` should be +// called on this. inline size_t GrowthToLowerboundCapacity(size_t growth) { // `growth*8/7` if (Group::kWidth == 8 && growth == 7) { @@ -555,37 +721,33 @@ struct FindInfo { size_t probe_length; }; -// The representation of the object has two modes: -// - small: For capacities < kWidth-1 -// - large: For the rest. +// Whether a table is "small". A small table fits entirely into a probing +// group, i.e., has a capacity < `Group::kWidth`. // -// Differences: -// - In small mode we are able to use the whole capacity. The extra control -// bytes give us at least one "empty" control byte to stop the iteration. -// This is important to make 1 a valid capacity. +// In small mode we are able to use the whole capacity. The extra control +// bytes give us at least one "empty" control byte to stop the iteration. +// This is important to make 1 a valid capacity. // -// - In small mode only the first `capacity()` control bytes after the -// sentinel are valid. The rest contain dummy ctrl_t::kEmpty values that do not -// represent a real slot. This is important to take into account on -// find_first_non_full(), where we never try ShouldInsertBackwards() for -// small tables. +// In small mode only the first `capacity` control bytes after the sentinel +// are valid. The rest contain dummy ctrl_t::kEmpty values that do not +// represent a real slot. This is important to take into account on +// `find_first_or_null()`, where we never try +// `ShouldInsertBackwards()` for small tables. inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; } +// Begins a probing operation on `ctrl`, using `hash`. inline probe_seq probe(const ctrl_t* ctrl, size_t hash, size_t capacity) { return probe_seq(H1(hash, ctrl), capacity); } -// Probes the raw_hash_set with the probe sequence for hash and returns the -// pointer to the first empty or deleted slot. -// NOTE: this function must work with tables having both ctrl_t::kEmpty and -// ctrl_t::kDeleted in one group. Such tables appears during -// drop_deletes_without_resize. +// Probes an array of control bits using a probe sequence derived from `hash`, +// and returns the offset corresponding to the first deleted or empty slot. +// +// Behavior when the entire table is full is undefined. // -// This function is very useful when insertions happen and: -// - the input is already a set -// - there are enough slots -// - the element with the hash is not in the table +// NOTE: this function must work with tables having both empty and deleted +// slots in the same group. Such tables appear during `erase()`. template inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, size_t capacity) { @@ -615,7 +777,8 @@ inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, // corresponding translation unit. extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); -// Reset all ctrl bytes back to ctrl_t::kEmpty, except the sentinel. +// Sets `ctrl` to `{kEmpty, kSentinel, ..., kEmpty}`, marking the entire +// array as deleted. inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, size_t slot_size) { std::memset(ctrl, static_cast(ctrl_t::kEmpty), @@ -624,8 +787,10 @@ inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, SanitizerPoisonMemoryRegion(slot, slot_size * capacity); } -// Sets the control byte, and if `i < NumClonedBytes()`, set the cloned byte -// at the end too. +// Sets `ctrl[i]` to `h`. +// +// Unlike setting it directly, this function will perform bounds checks and +// mirror the value to the cloned tail if necessary. inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl, const void* slot, size_t slot_size) { assert(i < capacity); @@ -641,25 +806,28 @@ inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl, ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h; } +// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`. inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl, const void* slot, size_t slot_size) { SetCtrl(i, static_cast(h), capacity, ctrl, slot, slot_size); } -// The allocated block consists of `capacity + 1 + NumClonedBytes()` control -// bytes followed by `capacity` slots, which must be aligned to `slot_align`. -// SlotOffset returns the offset of the slots into the allocated block. +// Given the capacity of a table, computes the offset (from the start of the +// backing allocation) at which the slots begin. inline size_t SlotOffset(size_t capacity, size_t slot_align) { assert(IsValidCapacity(capacity)); const size_t num_control_bytes = capacity + 1 + NumClonedBytes(); return (num_control_bytes + slot_align - 1) & (~slot_align + 1); } -// Returns the size of the allocated block. See also above comment. +// Given the capacity of a table, computes the total size of the backing +// array. inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) { return SlotOffset(capacity, slot_align) + capacity * slot_size; } +// A SwissTable. +// // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface // of policy). @@ -812,6 +980,10 @@ class raw_hash_set { ABSL_ASSUME(ctrl != nullptr); } + // Fixes up `ctrl_` to point to a full by advancing it and `slot_` until + // they reach one. + // + // If a sentinel is reached, we null both of them out instead. void skip_empty_or_deleted() { while (IsEmptyOrDeleted(*ctrl_)) { uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); @@ -1108,8 +1280,7 @@ class raw_hash_set { // m.insert(std::make_pair("abc", 42)); // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc // bug. - template = 0, - class T2 = T, + template = 0, class T2 = T, typename std::enable_if::value, int>::type = 0, T* = nullptr> std::pair insert(T&& value) { @@ -1616,10 +1787,10 @@ class raw_hash_set { slot_type&& slot; }; - // "erases" the object from the container, except that it doesn't actually - // destroy the object. It only updates all the metadata of the class. - // This can be used in conjunction with Policy::transfer to move the object to - // another place. + // Erases, but does not destroy, the value pointed to by `it`. + // + // This merely updates the pertinent control byte. This can be used in + // conjunction with Policy::transfer to move the object to another place. void erase_meta_only(const_iterator it) { assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator"); --size_; @@ -1642,6 +1813,11 @@ class raw_hash_set { infoz().RecordErase(); } + // Allocates a backing array for `self` and initializes its control bytes. + // This reads `capacity_` and updates all other fields based on the result of + // the allocation. + // + // This does not free the currently held array; `capacity_` must be nonzero. void initialize_slots() { assert(capacity_); // Folks with custom allocators often make unwarranted assumptions about the @@ -1670,6 +1846,10 @@ class raw_hash_set { infoz().RecordStorageChanged(size_, capacity_); } + // Destroys all slots in the backing array, frees the backing array, and + // clears all top-level book-keeping data. + // + // This essentially implements `map = raw_hash_set();`. void destroy_slots() { if (!capacity_) return; for (size_t i = 0; i != capacity_; ++i) { @@ -1720,6 +1900,9 @@ class raw_hash_set { infoz().RecordRehash(total_probe_length); } + // Prunes control bytes to remove as many tombstones as possible. + // + // See the comment on `rehash_and_grow_if_necessary()`. void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE { assert(IsValidCapacity(capacity_)); assert(!is_small(capacity_)); @@ -1786,6 +1969,11 @@ class raw_hash_set { infoz().RecordRehash(total_probe_length); } + // Called whenever the table *might* need to conditionally grow. + // + // This function is an optimization opportunity to perform a rehash even when + // growth is unnecessary, because vacating tombstones is beneficial for + // performance in the long-run. void rehash_and_grow_if_necessary() { if (capacity_ == 0) { resize(1); @@ -1870,6 +2058,9 @@ class raw_hash_set { } protected: + // Attempts to find `key` in the table; if it isn't found, returns a slot that + // the value can be inserted into, with the control byte already set to + // `key`'s H2. template std::pair find_or_prepare_insert(const K& key) { prefetch_heap_block(); @@ -1890,6 +2081,10 @@ class raw_hash_set { return {prepare_insert(hash), true}; } + // Given the hash of a value not currently in the table, finds the next + // viable slot index to insert it at. + // + // REQUIRES: At least one non-full slot available. size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE { auto target = find_first_non_full(ctrl_, hash, capacity_); if (ABSL_PREDICT_FALSE(growth_left() == 0 && @@ -1933,12 +2128,22 @@ class raw_hash_set { growth_left() = CapacityToGrowth(capacity()) - size_; } + // The number of slots we can still fill without needing to rehash. + // + // This is stored separately due to tombstones: we do not include tombstones + // in the growth capacity, because we'd like to rehash when the table is + // otherwise filled with tombstones: otherwise, probe sequences might get + // unacceptably long without triggering a rehash. Callers can also force a + // rehash via the standard `rehash(0)`, which will recompute this value as a + // side-effect. + // + // See `CapacityToGrowth()`. size_t& growth_left() { return settings_.template get<0>(); } + // Prefetch the heap-allocated memory region to resolve potential TLB misses. + // This is intended to overlap with execution of calculating the hash for a + // key. void prefetch_heap_block() const { - // Prefetch the heap-allocated memory region to resolve potential TLB - // misses. This is intended to overlap with execution of calculating the - // hash for a key. #if defined(__GNUC__) __builtin_prefetch(static_cast(ctrl_), 0, 1); #endif // __GNUC__ @@ -1958,10 +2163,21 @@ class raw_hash_set { // TODO(alkis): Investigate removing some of these fields: // - ctrl/slots can be derived from each other // - size can be moved into the slot array - ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1 + NumClonedBytes()) * ctrl_t] - slot_type* slots_ = nullptr; // [capacity * slot_type] - size_t size_ = 0; // number of full slots - size_t capacity_ = 0; // total number of slots + + // The control bytes (and, also, a pointer to the base of the backing array). + // + // This contains `capacity_ + 1 + NumClonedBytes()` entries, even + // when the table is empty (hence EmptyGroup). + ctrl_t* ctrl_ = EmptyGroup(); + // The beginning of the slots, located at `SlotOffset()` bytes after + // `ctrl_`. May be null for empty tables. + slot_type* slots_ = nullptr; + + // The number of filled slots. + size_t size_ = 0; + + // The total number of available slots. + size_t capacity_ = 0; absl::container_internal::CompressedTuple diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc index e0950b72..5bdd341e 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -20,7 +20,13 @@ #include #endif -#ifdef __APPLE__ +#include "absl/base/config.h" + +#ifdef ABSL_HAVE_MMAP +#include +#endif + +#if defined(__linux__) || defined(__APPLE__) #include #endif @@ -38,7 +44,102 @@ ABSL_NAMESPACE_BEGIN namespace debugging_internal { namespace { +constexpr int kDefaultDumpStackFramesLimit = 64; +// The %p field width for printf() functions is two characters per byte, +// and two extra for the leading "0x". +constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*); + ABSL_CONST_INIT SymbolizeUrlEmitter debug_stack_trace_hook = nullptr; + +// Async-signal safe mmap allocator. +void* Allocate(size_t num_bytes) { +#ifdef ABSL_HAVE_MMAP + void* p = ::mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return p == MAP_FAILED ? nullptr : p; +#else + (void)num_bytes; + return nullptr; +#endif // ABSL_HAVE_MMAP +} + +void Deallocate(void* p, size_t size) { +#ifdef ABSL_HAVE_MMAP + ::munmap(p, size); +#else + (void)p; + (void)size; +#endif // ABSL_HAVE_MMAP +} + +// Print a program counter only. +void DumpPC(OutputWriter* writer, void* writer_arg, void* const pc, + const char* const prefix) { + char buf[100]; + snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, pc); + writer(buf, writer_arg); +} + +// Print a program counter and the corresponding stack frame size. +void DumpPCAndFrameSize(OutputWriter* writer, void* writer_arg, void* const pc, + int framesize, const char* const prefix) { + char buf[100]; + if (framesize <= 0) { + snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix, + kPrintfPointerFieldWidth, pc); + } else { + snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix, + kPrintfPointerFieldWidth, pc, framesize); + } + writer(buf, writer_arg); +} + +// Print a program counter and the corresponding symbol. +void DumpPCAndSymbol(OutputWriter* writer, void* writer_arg, void* const pc, + const char* const prefix) { + char tmp[1024]; + const char* symbol = "(unknown)"; + // Symbolizes the previous address of pc because pc may be in the + // next function. The overrun happens when the function ends with + // a call to a function annotated noreturn (e.g. CHECK). + // If symbolization of pc-1 fails, also try pc on the off-chance + // that we crashed on the first instruction of a function (that + // actually happens very often for e.g. __restore_rt). + const uintptr_t prev_pc = reinterpret_cast(pc) - 1; + if (absl::Symbolize(reinterpret_cast(prev_pc), tmp, + sizeof(tmp)) || + absl::Symbolize(pc, tmp, sizeof(tmp))) { + symbol = tmp; + } + char buf[1024]; + snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, kPrintfPointerFieldWidth, + pc, symbol); + writer(buf, writer_arg); +} + +// Print a program counter, its stack frame size, and its symbol name. +// Note that there is a separate symbolize_pc argument. Return addresses may be +// at the end of the function, and this allows the caller to back up from pc if +// appropriate. +void DumpPCAndFrameSizeAndSymbol(OutputWriter* writer, void* writer_arg, + void* const pc, void* const symbolize_pc, + int framesize, const char* const prefix) { + char tmp[1024]; + const char* symbol = "(unknown)"; + if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) { + symbol = tmp; + } + char buf[1024]; + if (framesize <= 0) { + snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix, + kPrintfPointerFieldWidth, pc, symbol); + } else { + snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix, + kPrintfPointerFieldWidth, pc, framesize, symbol); + } + writer(buf, writer_arg); +} + } // namespace void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook) { @@ -50,7 +151,7 @@ SymbolizeUrlEmitter GetDebugStackTraceHook() { return debug_stack_trace_hook; } // Returns the program counter from signal context, nullptr if // unknown. vuc is a ucontext_t*. We use void* to avoid the use of // ucontext_t on non-POSIX systems. -void* GetProgramCounter(void* vuc) { +void* GetProgramCounter(void* const vuc) { #ifdef __linux__ if (vuc != nullptr) { ucontext_t* context = reinterpret_cast(vuc); @@ -132,60 +233,17 @@ void* GetProgramCounter(void* vuc) { return nullptr; } -// The %p field width for printf() functions is two characters per byte, -// and two extra for the leading "0x". -static constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*); - -// Print a program counter, its stack frame size, and its symbol name. -// Note that there is a separate symbolize_pc argument. Return addresses may be -// at the end of the function, and this allows the caller to back up from pc if -// appropriate. -static void DumpPCAndFrameSizeAndSymbol(void (*writerfn)(const char*, void*), - void* writerfn_arg, void* pc, - void* symbolize_pc, int framesize, - const char* const prefix) { - char tmp[1024]; - const char* symbol = "(unknown)"; - if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp)) || - (pc != symbolize_pc && absl::Symbolize(pc, tmp, sizeof(tmp)))) { - symbol = tmp; - } - char buf[1024]; - if (framesize <= 0) { - snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix, - kPrintfPointerFieldWidth, pc, symbol); - } else { - snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix, - kPrintfPointerFieldWidth, pc, framesize, symbol); - } - writerfn(buf, writerfn_arg); -} - -// Print a program counter and the corresponding stack frame size. -static void DumpPCAndFrameSize(void (*writerfn)(const char*, void*), - void* writerfn_arg, void* pc, int framesize, - const char* const prefix) { - char buf[100]; - if (framesize <= 0) { - snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix, - kPrintfPointerFieldWidth, pc); - } else { - snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix, - kPrintfPointerFieldWidth, pc, framesize); - } - writerfn(buf, writerfn_arg); -} - -void DumpPCAndFrameSizesAndStackTrace( - void* pc, void* const stack[], int frame_sizes[], int depth, - int min_dropped_frames, bool symbolize_stacktrace, - void (*writerfn)(const char*, void*), void* writerfn_arg) { +void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[], + int frame_sizes[], int depth, + int min_dropped_frames, + bool symbolize_stacktrace, + OutputWriter* writer, void* writer_arg) { if (pc != nullptr) { // We don't know the stack frame size for PC, use 0. if (symbolize_stacktrace) { - DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, pc, pc, 0, "PC: "); + DumpPCAndFrameSizeAndSymbol(writer, writer_arg, pc, pc, 0, "PC: "); } else { - DumpPCAndFrameSize(writerfn, writerfn_arg, pc, 0, "PC: "); + DumpPCAndFrameSize(writer, writer_arg, pc, 0, "PC: "); } } for (int i = 0; i < depth; i++) { @@ -195,20 +253,61 @@ void DumpPCAndFrameSizesAndStackTrace( // call to a function annotated noreturn (e.g. CHECK). Note that we don't // do this for pc above, as the adjustment is only correct for return // addresses. - DumpPCAndFrameSizeAndSymbol(writerfn, writerfn_arg, stack[i], + DumpPCAndFrameSizeAndSymbol(writer, writer_arg, stack[i], reinterpret_cast(stack[i]) - 1, frame_sizes[i], " "); } else { - DumpPCAndFrameSize(writerfn, writerfn_arg, stack[i], frame_sizes[i], - " "); + DumpPCAndFrameSize(writer, writer_arg, stack[i], frame_sizes[i], " "); } } if (min_dropped_frames > 0) { char buf[100]; snprintf(buf, sizeof(buf), " @ ... and at least %d more frames\n", min_dropped_frames); - writerfn(buf, writerfn_arg); + writer(buf, writer_arg); + } +} + +// Dump current stack trace as directed by writer. +// Make sure this function is not inlined to avoid skipping too many top frames. +ABSL_ATTRIBUTE_NOINLINE +void DumpStackTrace(int min_dropped_frames, int max_num_frames, + bool symbolize_stacktrace, OutputWriter* writer, + void* writer_arg) { + // Print stack trace + void* stack_buf[kDefaultDumpStackFramesLimit]; + void** stack = stack_buf; + int num_stack = kDefaultDumpStackFramesLimit; + int allocated_bytes = 0; + + if (num_stack >= max_num_frames) { + // User requested fewer frames than we already have space for. + num_stack = max_num_frames; + } else { + const size_t needed_bytes = max_num_frames * sizeof(stack[0]); + void* p = Allocate(needed_bytes); + if (p != nullptr) { // We got the space. + num_stack = max_num_frames; + stack = reinterpret_cast(p); + allocated_bytes = needed_bytes; + } + } + + size_t depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1); + for (size_t i = 0; i < depth; i++) { + if (symbolize_stacktrace) { + DumpPCAndSymbol(writer, writer_arg, stack[i], " "); + } else { + DumpPC(writer, writer_arg, stack[i], " "); + } } + + auto hook = GetDebugStackTraceHook(); + if (hook != nullptr) { + (*hook)(stack, depth, writer, writer_arg); + } + + if (allocated_bytes != 0) Deallocate(stack, allocated_bytes); } } // namespace debugging_internal diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h index 61f0056f..190af87f 100644 --- a/absl/debugging/internal/examine_stack.h +++ b/absl/debugging/internal/examine_stack.h @@ -31,7 +31,7 @@ typedef void OutputWriter(const char*, void*); // `hook` that is called each time DumpStackTrace() is called. // `hook` may be called from a signal handler. typedef void (*SymbolizeUrlEmitter)(void* const stack[], int depth, - OutputWriter writer, void* writer_arg); + OutputWriter* writer, void* writer_arg); // Registration of SymbolizeUrlEmitter for use inside of a signal handler. // This is inherently unsafe and must be signal safe code. @@ -41,14 +41,21 @@ SymbolizeUrlEmitter GetDebugStackTraceHook(); // Returns the program counter from signal context, or nullptr if // unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of // ucontext_t on non-POSIX systems. -void* GetProgramCounter(void* vuc); +void* GetProgramCounter(void* const vuc); -// Uses `writerfn` to dump the program counter, stack trace, and stack +// Uses `writer` to dump the program counter, stack trace, and stack // frame sizes. -void DumpPCAndFrameSizesAndStackTrace( - void* pc, void* const stack[], int frame_sizes[], int depth, - int min_dropped_frames, bool symbolize_stacktrace, - void (*writerfn)(const char*, void*), void* writerfn_arg); +void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[], + int frame_sizes[], int depth, + int min_dropped_frames, + bool symbolize_stacktrace, + OutputWriter* writer, void* writer_arg); + +// Dump current stack trace omitting the topmost `min_dropped_frames` stack +// frames. +void DumpStackTrace(int min_dropped_frames, int max_num_frames, + bool symbolize_stacktrace, OutputWriter* writer, + void* writer_arg); } // namespace debugging_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From f3489c9ca64e0fad2a263e8560ee96718ac8b21b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 21 Mar 2022 15:16:08 -0700 Subject: Export of internal Abseil changes -- bcd349230e418f5e29d5fced1b942828fa5cb2ad by Abseil Team : Import of CCTZ from GitHub. PiperOrigin-RevId: 436317331 Change-Id: I0f8a0c4cd0d5f348a33e486c85c863072c30742a GitOrigin-RevId: bcd349230e418f5e29d5fced1b942828fa5cb2ad --- .../internal/cctz/src/time_zone_lookup_test.cc | 38 ++++++++++++++++++++++ absl/time/internal/cctz/testdata/README.zoneinfo | 7 +++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 0226ab71..134d1430 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -1182,6 +1182,44 @@ TEST(PrevTransition, AmericaNewYork) { // We have a transition but we don't know which one. } +TEST(NextTransition, Scan) { + for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) { + time_zone tz; + if (!load_time_zone(*np, &tz)) { + continue; // tolerate kTimeZoneNames/zoneinfo skew + } + SCOPED_TRACE(testing::Message() << "In " << *np); + + auto tp = time_point::min(); + time_zone::civil_transition trans; + while (tz.next_transition(tp, &trans)) { + time_zone::civil_lookup from_cl = tz.lookup(trans.from); + EXPECT_NE(from_cl.kind, time_zone::civil_lookup::REPEATED); + time_zone::civil_lookup to_cl = tz.lookup(trans.to); + EXPECT_NE(to_cl.kind, time_zone::civil_lookup::SKIPPED); + + auto trans_tp = to_cl.trans; + time_zone::absolute_lookup trans_al = tz.lookup(trans_tp); + EXPECT_EQ(trans_al.cs, trans.to); + auto pre_trans_tp = trans_tp - absl::time_internal::cctz::seconds(1); + time_zone::absolute_lookup pre_trans_al = tz.lookup(pre_trans_tp); + EXPECT_EQ(pre_trans_al.cs + 1, trans.from); + + auto offset_delta = trans_al.offset - pre_trans_al.offset; + EXPECT_EQ(offset_delta, trans.to - trans.from); + if (offset_delta == 0) { + // This "transition" is only an is_dst or abbr change. + EXPECT_EQ(to_cl.kind, time_zone::civil_lookup::UNIQUE); + if (trans_al.is_dst == pre_trans_al.is_dst) { + EXPECT_STRNE(trans_al.abbr, pre_trans_al.abbr); + } + } + + tp = trans_tp; // continue scan from transition + } + } +} + TEST(TimeZoneEdgeCase, AmericaNewYork) { const time_zone tz = LoadZone("America/New_York"); diff --git a/absl/time/internal/cctz/testdata/README.zoneinfo b/absl/time/internal/cctz/testdata/README.zoneinfo index 95fb4a91..a41c7b88 100644 --- a/absl/time/internal/cctz/testdata/README.zoneinfo +++ b/absl/time/internal/cctz/testdata/README.zoneinfo @@ -13,7 +13,12 @@ New versions can be generated using the following shell script. trap "rm -fr ${DESTDIR}" 0 2 15 ( cd ${DESTDIR} - git clone https://github.com/eggert/tz.git + if [ -n "${USE_GLOBAL_TZ}" ] + then + git clone -b global-tz https://github.com/JodaOrg/global-tz.git tz + else + git clone https://github.com/eggert/tz.git + fi make --directory=tz \ install DESTDIR=${DESTDIR} \ DATAFORM=vanguard \ -- cgit v1.2.3 From eb3db08cb3a4faf2aa09a2ba4a30b442457f36cf Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 23 Mar 2022 13:25:43 -0700 Subject: Export of internal Abseil changes -- 8c9dd24a6fbf9ed10ae81f9fa0bc2168558a9700 by Abseil Team : Improve WebAssembly detection when using Bazel. Unfortunately, the --cpu values are not standardized, and both --cpu=wasm and --cpu=wasm32 are used in the wild. Most notably, Emscripten's Bazel rules use --cpu=wasm, which was missing. While there, add support for @platforms//cpu:{wasm32,wasm64}. This change adds a dependency on @bazel_skylib, which requires adding the following http_archive() rule to the WORKSPACE file: http_archive( name = "bazel_skylib", urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"], sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728", ) PiperOrigin-RevId: 436815546 Change-Id: I4e1946070c6964abb12259f25a546f2d24e0992a -- 59514589043d9b0734a01f7aa7bc354f5b495eab by Abseil Team : Fix some typos that slipped through. PiperOrigin-RevId: 436777566 Change-Id: Ibf5c54e2671c749dc87d2bd5d36dcd220ce347d4 GitOrigin-RevId: 8c9dd24a6fbf9ed10ae81f9fa0bc2168558a9700 --- WORKSPACE | 7 ++++++ absl/BUILD.bazel | 39 +++++++++++++++++++++++++++++++++- absl/container/internal/raw_hash_set.h | 20 ++++++++--------- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 8120cd17..1a1753a8 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -35,6 +35,13 @@ http_archive( urls = ["https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.zip"], ) +# Bazel Skylib. +http_archive( + name = "bazel_skylib", + urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"], + sha256 = "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728", +) + # Bazel platform rules. http_archive( name = "platforms", diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index d799b7fe..7cccbbba 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@bazel_skylib//lib:selects.bzl", "selects") + package(default_visibility = ["//visibility:public"]) licenses(["notice"]) @@ -64,13 +66,48 @@ config_setting( ) config_setting( - name = "wasm", + name = "cpu_wasm", + values = { + "cpu": "wasm", + }, + visibility = [":__subpackages__"], +) + +config_setting( + name = "cpu_wasm32", values = { "cpu": "wasm32", }, visibility = [":__subpackages__"], ) +config_setting( + name = "platforms_wasm32", + constraint_values = [ + "@platforms//cpu:wasm32", + ], + visibility = [":__subpackages__"], +) + +config_setting( + name = "platforms_wasm64", + constraint_values = [ + "@platforms//cpu:wasm64", + ], + visibility = [":__subpackages__"], +) + +selects.config_setting_group( + name = "wasm", + match_any = [ + ":cpu_wasm", + ":cpu_wasm32", + ":platforms_wasm32", + ":platforms_wasm64", + ], + visibility = [":__subpackages__"], +) + config_setting( name = "fuchsia", values = { diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index d24bbe83..a05bebf6 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -88,7 +88,7 @@ // +---------------+---------------+---------------+ // // Each control byte is either a special value for empty slots, deleted slots -// (sometimes called *tombstones*), and a speical end-of-table marker used by +// (sometimes called *tombstones*), and a special end-of-table marker used by // iterators, or, if occupied, seven bits (H2) from the hash of the value in the // corresponding slot. // @@ -130,7 +130,7 @@ // 7 | 0.191 | 15 | 0.879 // // The rule of thumb breaks down at around `n = 12`, but such groups would only -// occur for tables close to their load factor. This is far better than an +// occur for tables close to their max load factor. This is far better than an // ordinary open-addressing table, which needs to perform an == at every step of // the probe sequence. These probabilities don't tell the full story (for // example, because elements are inserted into a group from the front, and @@ -155,7 +155,7 @@ // this point, we may `unchecked_insert` the value `x`. // // Below, `unchecked_insert` is partly implemented by `prepare_insert`, which -// presents a viable, intialized slot pointee to the caller. +// presents a viable, initialized slot pointee to the caller. // // `erase` is implemented in terms of `erase_at`, which takes an index to a // slot. Given an offset, we simply create a tombstone and destroy its contents. @@ -229,7 +229,7 @@ void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/, // // Wrapping around at `mask + 1` is important, but not for the obvious reason. // As described above, the first few entries of the control byte array -// is mirrored at the end of the array, which `Group` will find and use +// are mirrored at the end of the array, which `Group` will find and use // for selecting candidates. However, when those candidates' slots are // actually inspected, there are no corresponding slots for the cloned bytes, // so we need to make sure we've treated those offsets as "wrapping around". @@ -300,7 +300,7 @@ uint32_t TrailingZeros(T x) { return static_cast(countr_zero(x)); } -// A abstract bitmask, such as that emitted by a SIMD instruction. +// An abstract bitmask, such as that emitted by a SIMD instruction. // // Specifically, this type implements a simple bitset whose representation is // controlled by `SignificantBits` and `Shift`. `SignificantBits` is the number @@ -452,7 +452,7 @@ inline size_t H1(size_t hash, const ctrl_t* ctrl) { // Extracts the H2 portion of a hash: the 7 bits not used for H1. // -// Thse are used used as an occupied control byte. +// These are used as an occupied control byte. inline h2_t H2(size_t hash) { return hash & 0x7F; } // Helpers for checking the state of a control byte. @@ -462,7 +462,7 @@ inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 -// Quick eference guide for intrinsics used below: +// Quick reference guide for intrinsics used below: // // * __m128i: An XMM (128-bit) word. // @@ -479,7 +479,7 @@ inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } // * _mm_cmpgt_epi8: Same as above, but using > rather than ==. // // * _mm_loadu_si128: Performs an unaligned load of an i128. -// * _mm_storeu_si128: Performs an unaligned store of a i128. +// * _mm_storeu_si128: Performs an unaligned store of an i128. // // * _mm_sign_epi8: Retains, negates, or zeroes each i8 lane of the first // argument if the corresponding lane of the second @@ -731,7 +731,7 @@ struct FindInfo { // In small mode only the first `capacity` control bytes after the sentinel // are valid. The rest contain dummy ctrl_t::kEmpty values that do not // represent a real slot. This is important to take into account on -// `find_first_or_null()`, where we never try +// `find_first_non_full()`, where we never try // `ShouldInsertBackwards()` for small tables. inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; } @@ -778,7 +778,7 @@ inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); // Sets `ctrl` to `{kEmpty, kSentinel, ..., kEmpty}`, marking the entire -// array as deleted. +// array as marked as empty. inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, size_t slot_size) { std::memset(ctrl, static_cast(ctrl_t::kEmpty), -- cgit v1.2.3 From ca80034f59f43eddb6c4c72314572c0e212bf98f Mon Sep 17 00:00:00 2001 From: Calvin Ke Date: Sat, 26 Mar 2022 08:56:31 +0800 Subject: Support for QNX (#1147) --- absl/base/config.h | 3 ++- absl/debugging/failure_signal_handler.cc | 2 +- absl/debugging/internal/elf_mem_image.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 8c100d8e..78de4d1c 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -414,7 +414,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ - defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) + defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(__QNX__) #define ABSL_HAVE_MMAP 1 #endif diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index 689e5979..3fe8827a 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -52,7 +52,7 @@ #define ABSL_HAVE_SIGACTION // Apple WatchOS and TVOS don't allow sigaltstack #if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ - !(defined(TARGET_OS_TV) && TARGET_OS_TV) + !(defined(TARGET_OS_TV) && TARGET_OS_TV) && !defined(__QNX__) #define ABSL_HAVE_SIGALTSTACK #endif #endif diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index be20256f..e4bbf2d7 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -31,7 +31,7 @@ #error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set #endif -#if defined(__ELF__) && !defined(__OpenBSD__) && \ +#if defined(__ELF__) && !defined(__OpenBSD__) && !defined(__QNX__) && \ !defined(__native_client__) && !defined(__asmjs__) && !defined(__wasm__) #define ABSL_HAVE_ELF_MEM_IMAGE 1 #endif -- cgit v1.2.3 From 0c6302fe427963ec5c471d3ee660120682ab15f7 Mon Sep 17 00:00:00 2001 From: Ren Zibei <31380016+renzibei@users.noreply.github.com> Date: Tue, 29 Mar 2022 04:12:44 +0800 Subject: Replace the implementation of the Mix function in arm64 back to 128bit multiplication (#1094) --- absl/hash/internal/hash.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index f8103334..45dfdd46 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -1090,15 +1090,10 @@ class ABSL_DLL MixingHashState : public HashStateBase { } ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) { -#if defined(__aarch64__) - // On AArch64, calculating a 128-bit product is inefficient, because it - // requires a sequence of two instructions to calculate the upper and lower - // halves of the result. - using MultType = uint64_t; -#else + // Though the 128-bit product on AArch64 needs two instructions, it is + // still a good balance between speed and hash quality. using MultType = absl::conditional_t; -#endif // We do the addition in 64-bit space to make sure the 128-bit // multiplication is fast. If we were to do it as MultType the compiler has // to assume that the high word is non-zero and needs to perform 2 -- cgit v1.2.3 From 3204cc0625230e9876f0310a6dea0014210ab325 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 28 Mar 2022 13:12:19 -0700 Subject: Export of internal Abseil changes -- 291f7ef542f73e4801ab5108014bc02344ef31df by Derek Mauro : Internal change PiperOrigin-RevId: 437835981 Change-Id: I42fd92e74903894533ac9984d7f622e3ba20f468 -- 2e8caf1a57c50b518e05b4bca48e4fe1bb19af82 by Andy Getzendanner : Internal change PiperOrigin-RevId: 437832673 Change-Id: I61b35089418d01a54cecf161b254b68252bebff3 -- b927482ccc399f7e337b60582988b914d9946e4e by Derek Mauro : Simplify endian intrinsics for modern compilers All modern compilers have either __builtin_bswapN (gcc, clang) or _byteswap_TYPE (MSVC). The other intrinsic definitions are no longer necessary. PiperOrigin-RevId: 437772295 Change-Id: Ifb3d88ba24b9097f87ceb202272b36d2f5e5117f -- b6782a2247a16d5c14706a74ec577c19963d9f97 by Derek Mauro : Internal change PiperOrigin-RevId: 437373174 Change-Id: I0f77e1780dee90d7a3c32a08d96c4aeb624a57b4 -- a53e0c724e37b0b01515a99bd25394b8e21ffdfc by Derek Mauro : Unify detection of SSE2 and SSSE3 instruction sets and include the proper headers Fix the intrinsic implementation of FastHexToBufferZeroPad16 in numbers.h which only relies on SSSE3, not SSE 4.2. https://godbolt.org/z/Pf5bn1Yv9 Closes #639 PiperOrigin-RevId: 437286940 Change-Id: Ic97948399b61b91e9c0bccd09313b795b904d714 -- f173f597cb2a75ef2a989f45a496334b85e6f40d by Abseil Team : Change assertion function to enable clearer error messages. PiperOrigin-RevId: 437227057 Change-Id: If420d2f63b51feef6648762f344d5be012cd9c85 GitOrigin-RevId: 291f7ef542f73e4801ab5108014bc02344ef31df --- CMake/AbseilDll.cmake | 2 - absl/base/config.h | 32 +++++++++ absl/base/internal/endian.h | 79 +++++----------------- absl/container/BUILD.bazel | 13 +--- absl/container/CMakeLists.txt | 14 +--- absl/container/internal/hashtablez_sampler.cc | 4 +- absl/container/internal/hashtablez_sampler.h | 4 +- absl/container/internal/hashtablez_sampler_test.cc | 4 +- absl/container/internal/have_sse.h | 50 -------------- absl/container/internal/raw_hash_set.h | 51 +++++++++----- absl/container/internal/raw_hash_set_test.cc | 2 +- absl/strings/numbers.h | 17 ++--- 12 files changed, 101 insertions(+), 171 deletions(-) delete mode 100644 absl/container/internal/have_sse.h diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 0becc7a9..518c469b 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -78,7 +78,6 @@ set(ABSL_INTERNAL_DLL_FILES "container/internal/hashtablez_sampler.cc" "container/internal/hashtablez_sampler.h" "container/internal/hashtablez_sampler_force_weak_definition.cc" - "container/internal/have_sse.h" "container/internal/inlined_vector.h" "container/internal/layout.h" "container/internal/node_slot_policy.h" @@ -456,7 +455,6 @@ set(ABSL_INTERNAL_DLL_TARGETS "hashtablez_sampler" "hashtable_debug" "hashtable_debug_hooks" - "have_sse" "node_slot_policy" "raw_hash_map" "container_common" diff --git a/absl/base/config.h b/absl/base/config.h index 78de4d1c..429dd353 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -269,6 +269,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 #elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) #define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 +#elif defined(_MSC_VER) && _MSC_VER >= 1926 +#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 #endif #endif @@ -818,4 +820,34 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAS_RTTI 1 #endif // !defined(__GNUC__) || defined(__GXX_RTTI) +// ABSL_INTERNAL_HAVE_SSE2 is used for compile-time detection of SSE2 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE2 +#error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set +#elif defined(__SSE2__) +#define ABSL_INTERNAL_HAVE_SSE2 1 +#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 +// indicates that at least SSE2 was targeted with the /arch:SSE2 option. +// All x86-64 processors support SSE2, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE2 1 +#endif + +// ABSL_INTERNAL_HAVE_SSSE3 is used for compile-time detection of SSSE3 support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +// +// MSVC does not have a mode that targets SSSE3 at compile-time. To use SSSE3 +// with MSVC requires either assuming that the code will only every run on CPUs +// that support SSSE3, otherwise __cpuid() can be used to detect support at +// runtime and fallback to a non-SSSE3 implementation when SSSE3 is unsupported +// by the CPU. +#ifdef ABSL_INTERNAL_HAVE_SSSE3 +#error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set +#elif defined(__SSSE3__) +#define ABSL_INTERNAL_HAVE_SSSE3 1 +#endif + #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index dad0e9ae..50747d75 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -16,16 +16,9 @@ #ifndef ABSL_BASE_INTERNAL_ENDIAN_H_ #define ABSL_BASE_INTERNAL_ENDIAN_H_ -// The following guarantees declaration of the byte swap functions -#ifdef _MSC_VER -#include // NOLINT(build/include) -#elif defined(__FreeBSD__) -#include -#elif defined(__GLIBC__) -#include // IWYU pragma: export -#endif - #include +#include + #include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/internal/unaligned_access.h" @@ -34,47 +27,11 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// Use compiler byte-swapping intrinsics if they are available. 32-bit -// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0. -// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0. -// For simplicity, we enable them all only for GCC 4.8.0 or later. -#if defined(__clang__) || \ - (defined(__GNUC__) && \ - ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5)) inline uint64_t gbswap_64(uint64_t host_int) { +#if ABSL_HAVE_BUILTIN(__builtin_bswap64) || defined(__GNUC__) return __builtin_bswap64(host_int); -} -inline uint32_t gbswap_32(uint32_t host_int) { - return __builtin_bswap32(host_int); -} -inline uint16_t gbswap_16(uint16_t host_int) { - return __builtin_bswap16(host_int); -} - #elif defined(_MSC_VER) -inline uint64_t gbswap_64(uint64_t host_int) { return _byteswap_uint64(host_int); -} -inline uint32_t gbswap_32(uint32_t host_int) { - return _byteswap_ulong(host_int); -} -inline uint16_t gbswap_16(uint16_t host_int) { - return _byteswap_ushort(host_int); -} - -#else -inline uint64_t gbswap_64(uint64_t host_int) { -#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) - // Adapted from /usr/include/byteswap.h. Not available on Mac. - if (__builtin_constant_p(host_int)) { - return __bswap_constant_64(host_int); - } else { - uint64_t result; - __asm__("bswap %0" : "=r"(result) : "0"(host_int)); - return result; - } -#elif defined(__GLIBC__) - return bswap_64(host_int); #else return (((host_int & uint64_t{0xFF}) << 56) | ((host_int & uint64_t{0xFF00}) << 40) | @@ -84,12 +41,14 @@ inline uint64_t gbswap_64(uint64_t host_int) { ((host_int & uint64_t{0xFF0000000000}) >> 24) | ((host_int & uint64_t{0xFF000000000000}) >> 40) | ((host_int & uint64_t{0xFF00000000000000}) >> 56)); -#endif // bswap_64 +#endif } inline uint32_t gbswap_32(uint32_t host_int) { -#if defined(__GLIBC__) - return bswap_32(host_int); +#if ABSL_HAVE_BUILTIN(__builtin_bswap32) || defined(__GNUC__) + return __builtin_bswap32(host_int); +#elif defined(_MSC_VER) + return _byteswap_ulong(host_int); #else return (((host_int & uint32_t{0xFF}) << 24) | ((host_int & uint32_t{0xFF00}) << 8) | @@ -99,33 +58,29 @@ inline uint32_t gbswap_32(uint32_t host_int) { } inline uint16_t gbswap_16(uint16_t host_int) { -#if defined(__GLIBC__) - return bswap_16(host_int); +#if ABSL_HAVE_BUILTIN(__builtin_bswap16) || defined(__GNUC__) + return __builtin_bswap16(host_int); +#elif defined(_MSC_VER) + return _byteswap_ushort(host_int); #else return (((host_int & uint16_t{0xFF}) << 8) | ((host_int & uint16_t{0xFF00}) >> 8)); #endif } -#endif // intrinsics available - #ifdef ABSL_IS_LITTLE_ENDIAN -// Definitions for ntohl etc. that don't require us to include -// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather -// than just #defining them because in debug mode, gcc doesn't -// correctly handle the (rather involved) definitions of bswap_32. -// gcc guarantees that inline functions are as fast as macros, so -// this isn't a performance hit. +// Portable definitions for htonl (host-to-network) and friends on little-endian +// architectures. inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); } inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); } inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); } #elif defined ABSL_IS_BIG_ENDIAN -// These definitions are simpler on big-endian machines -// These are functions instead of macros to avoid self-assignment warnings -// on calls such as "i = ghtnol(i);". This also provides type checking. +// Portable definitions for htonl (host-to-network) etc on big-endian +// architectures. These definitions are simpler since the host byte order is the +// same as network byte order. inline uint16_t ghtons(uint16_t x) { return x; } inline uint32_t ghtonl(uint32_t x) { return x; } inline uint64_t ghtonll(uint64_t x) { return x; } diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index bc25bac9..2095e57d 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -509,8 +509,8 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":have_sse", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/debugging:stacktrace", "//absl/memory", @@ -527,7 +527,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hashtablez_sampler", - ":have_sse", + "//absl/base:config", "//absl/base:core_headers", "//absl/profiling:sample_recorder", "//absl/synchronization", @@ -569,14 +569,6 @@ cc_library( ], ) -cc_library( - name = "have_sse", - hdrs = ["internal/have_sse.h"], - copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], -) - cc_library( name = "common", hdrs = ["internal/common.h"], @@ -601,7 +593,6 @@ cc_library( ":hash_policy_traits", ":hashtable_debug_hooks", ":hashtablez_sampler", - ":have_sse", "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 648a3dba..7176907d 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -550,8 +550,8 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::config absl::exponential_biased - absl::have_sse absl::sample_recorder absl::synchronization ) @@ -564,8 +564,8 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::config absl::hashtablez_sampler - absl::have_sse GTest::gmock_main ) @@ -592,15 +592,6 @@ absl_cc_library( PUBLIC ) -absl_cc_library( - NAME - have_sse - HDRS - "internal/have_sse.h" - COPTS - ${ABSL_DEFAULT_COPTS} -) - absl_cc_library( NAME node_slot_policy @@ -670,7 +661,6 @@ absl_cc_library( absl::endian absl::hash_policy_traits absl::hashtable_debug_hooks - absl::have_sse absl::memory absl::meta absl::optional diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 322e0547..c6036d51 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -21,7 +21,7 @@ #include #include "absl/base/attributes.h" -#include "absl/container/internal/have_sse.h" +#include "absl/base/config.h" #include "absl/debugging/stacktrace.h" #include "absl/memory/memory.h" #include "absl/profiling/internal/exponential_biased.h" @@ -160,7 +160,7 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash, // SwissTables probe in groups of 16, so scale this to count items probes and // not offset from desired. size_t probe_length = distance_from_desired; -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#ifdef ABSL_INTERNAL_HAVE_SSE2 probe_length /= 16; #else probe_length /= 8; diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index e7c204ee..d4016d8a 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -44,9 +44,9 @@ #include #include +#include "absl/base/config.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/optimization.h" -#include "absl/container/internal/have_sse.h" #include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/mutex.h" #include "absl/utility/utility.h" @@ -96,7 +96,7 @@ struct HashtablezInfo : public profiling_internal::Sample { }; inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#ifdef ABSL_INTERNAL_HAVE_SSE2 total_probe_length /= 16; #else total_probe_length /= 8; diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index 77cdf2fd..665d518f 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -21,7 +21,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/attributes.h" -#include "absl/container/internal/have_sse.h" +#include "absl/base/config.h" #include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/blocking_counter.h" #include "absl/synchronization/internal/thread_pool.h" @@ -30,7 +30,7 @@ #include "absl/time/clock.h" #include "absl/time/time.h" -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#ifdef ABSL_INTERNAL_HAVE_SSE2 constexpr int kProbeLength = 16; #else constexpr int kProbeLength = 8; diff --git a/absl/container/internal/have_sse.h b/absl/container/internal/have_sse.h deleted file mode 100644 index e75e1a16..00000000 --- a/absl/container/internal/have_sse.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Shared config probing for SSE instructions used in Swiss tables. -#ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ -#define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ - -#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 -#if defined(__SSE2__) || \ - (defined(_MSC_VER) && \ - (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) -#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 1 -#else -#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 0 -#endif -#endif - -#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 -#ifdef __SSSE3__ -#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 1 -#else -#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 0 -#endif -#endif - -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 && \ - !ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 -#error "Bad configuration!" -#endif - -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 -#include -#endif - -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 -#include -#endif - -#endif // ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index a05bebf6..6a6525e2 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -176,6 +176,18 @@ #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ +#ifdef __SSE2__ +#include +#endif + +#ifdef __SSSE3__ +#include +#endif + +#ifdef _MSC_VER +#include +#endif + #include #include #include @@ -187,6 +199,7 @@ #include #include +#include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/optimization.h" #include "absl/base/port.h" @@ -196,7 +209,6 @@ #include "absl/container/internal/hash_policy_traits.h" #include "absl/container/internal/hashtable_debug_hooks.h" #include "absl/container/internal/hashtablez_sampler.h" -#include "absl/container/internal/have_sse.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" #include "absl/numeric/bits.h" @@ -461,7 +473,7 @@ inline bool IsFull(ctrl_t c) { return c >= static_cast(0); } inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#ifdef ABSL_INTERNAL_HAVE_SSE2 // Quick reference guide for intrinsics used below: // // * __m128i: An XMM (128-bit) word. @@ -522,7 +534,7 @@ struct GroupSse2Impl { // Returns a bitmask representing the positions of empty slots. BitMask MatchEmpty() const { -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 +#ifdef ABSL_INTERNAL_HAVE_SSSE3 // This only works because ctrl_t::kEmpty is -128. return BitMask( static_cast(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)))); @@ -548,7 +560,7 @@ struct GroupSse2Impl { void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { auto msbs = _mm_set1_epi8(static_cast(-128)); auto x126 = _mm_set1_epi8(126); -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 +#ifdef ABSL_INTERNAL_HAVE_SSSE3 auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs); #else auto zero = _mm_setzero_si128(); @@ -614,7 +626,7 @@ struct GroupPortableImpl { uint64_t ctrl; }; -#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#ifdef ABSL_INTERNAL_HAVE_SSE2 using Group = GroupSse2Impl; #else using Group = GroupPortableImpl; @@ -700,13 +712,8 @@ size_t SelectBucketCountForIterRange(InputIter first, InputIter last, return 0; } -inline void AssertIsFull(ctrl_t* ctrl) { - ABSL_HARDENING_ASSERT( - (ctrl != nullptr && IsFull(*ctrl)) && - "Invalid operation on iterator. The element might have " - "been erased, the table might have rehashed, or this may " - "be an end() iterator."); -} +#define ABSL_INTERNAL_ASSERT_IS_FULL(ctrl, msg) \ + ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && msg); inline void AssertIsValid(ctrl_t* ctrl) { ABSL_HARDENING_ASSERT( @@ -942,16 +949,22 @@ class raw_hash_set { // PRECONDITION: not an end() iterator. reference operator*() const { - AssertIsFull(ctrl_); + ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, + "operator*() called on invalid iterator."); return PolicyTraits::element(slot_); } // PRECONDITION: not an end() iterator. - pointer operator->() const { return &operator*(); } + pointer operator->() const { + ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, + "operator-> called on invalid iterator."); + return &operator*(); + } // PRECONDITION: not an end() iterator. iterator& operator++() { - AssertIsFull(ctrl_); + ABSL_INTERNAL_ASSERT_IS_FULL(ctrl_, + "operator++ called on invalid iterator."); ++ctrl_; ++slot_; skip_empty_or_deleted(); @@ -1500,7 +1513,8 @@ class raw_hash_set { // This overload is necessary because otherwise erase(const K&) would be // a better match if non-const iterator is passed as an argument. void erase(iterator it) { - AssertIsFull(it.ctrl_); + ABSL_INTERNAL_ASSERT_IS_FULL(it.ctrl_, + "erase() called on invalid iterator.") PolicyTraits::destroy(&alloc_ref(), it.slot_); erase_meta_only(it); } @@ -1534,7 +1548,8 @@ class raw_hash_set { } node_type extract(const_iterator position) { - AssertIsFull(position.inner_.ctrl_); + ABSL_INTERNAL_ASSERT_IS_FULL(position.inner_.ctrl_, + "extract() called on invalid iterator.") auto node = CommonAccess::Transfer(alloc_ref(), position.inner_.slot_); erase_meta_only(position); @@ -2263,4 +2278,6 @@ struct HashtableDebugAccess> { ABSL_NAMESPACE_END } // namespace absl +#undef ABSL_INTERNAL_ASSERT_IS_FULL + #endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index e7732f67..9cd88a28 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -2030,7 +2030,7 @@ TEST(TableDeathTest, EraseOfEndAsserts) { IntTable t; // Extra simple "regexp" as regexp support is highly varied across platforms. - constexpr char kDeathMsg[] = "Invalid operation on iterator"; + constexpr char kDeathMsg[] = "erase.. called on invalid iterator"; EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); } diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 3ed24669..86c84ed3 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -23,8 +23,12 @@ #ifndef ABSL_STRINGS_NUMBERS_H_ #define ABSL_STRINGS_NUMBERS_H_ -#ifdef __SSE4_2__ -#include +#ifdef __SSSE3__ +#include +#endif + +#ifdef _MSC_VER +#include #endif #include @@ -36,14 +40,7 @@ #include #include "absl/base/config.h" -#ifdef __SSE4_2__ -// TODO(jorg): Remove this when we figure out the right way -// to swap bytes on SSE 4.2 that works with the compilers -// we claim to support. Also, add tests for the compiler -// that doesn't support the Intel _bswap64 intrinsic but -// does support all the SSE 4.2 intrinsics #include "absl/base/internal/endian.h" -#endif #include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/numeric/bits.h" @@ -246,7 +243,7 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, // Returns the number of non-pad digits of the output (it can never be zero // since 0 has one digit). inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { -#ifdef __SSE4_2__ +#ifdef ABSL_INTERNAL_HAVE_SSSE3 uint64_t be = absl::big_endian::FromHost64(val); const auto kNibbleMask = _mm_set1_epi8(0xf); const auto kHexDigits = _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', -- cgit v1.2.3 From cfccbd2eb5b051f3c33f1a5bb441ec3029cb0a24 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 30 Mar 2022 10:48:56 -0700 Subject: Export of internal Abseil changes -- fb671efb2a70f452f17a884b17cf18817b977a8f by Abseil Team : Remove extra semicolon in ABSL_INTERNAL_ASSERT_IS_FULL macro To fix compilation when empty statement warning is treated as error. PiperOrigin-RevId: 438342663 Change-Id: I3067fbeffa2691888f37554e88f229f24fb55ecc -- a58c9396f1d88d11347aed36ef2e1b633071363c by Martijn Vels : Fix kMaxHeight bounds to kMaxDepth for CordrepBtreeNavigator Added unit test (confirmed failure mode with old code) and extra assertion in the implementation. PiperOrigin-RevId: 438327463 Change-Id: I32242c86b0c879b8a42cb9a92075e537d588e09f -- f348e85dbfc9187ef59085fa2b999374f1670338 by Jorge Gorbe Moya : Make the flags enum in `RefcountAndFlags` a named enum to workaround an lldb issue (https://github.com/llvm/llvm-project/issues/54602). PiperOrigin-RevId: 438146097 Change-Id: Ibc2ee26489d99de515a779a903b6458dd0befef7 -- a960a3e9fb2a2e3418f806178e73d8566b78bc85 by Gennadiy Rozental : Introduce support for std::optional/absl::optional flag types. PiperOrigin-RevId: 438129500 Change-Id: I3d925c0a7f9ce9f857277fac3b0bf664ccd3a95c GitOrigin-RevId: fb671efb2a70f452f17a884b17cf18817b977a8f --- absl/container/internal/raw_hash_set.h | 6 +- absl/flags/BUILD.bazel | 1 + absl/flags/CMakeLists.txt | 1 + absl/flags/flag_test.cc | 174 +++++++++++++++++++++ absl/flags/marshalling.h | 57 ++++++- absl/flags/marshalling_test.cc | 162 +++++++++++++++++++ absl/strings/internal/cord_internal.h | 2 +- absl/strings/internal/cord_rep_btree_navigator.h | 6 +- .../internal/cord_rep_btree_navigator_test.cc | 21 +++ 9 files changed, 423 insertions(+), 7 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 6a6525e2..046a6939 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -713,7 +713,7 @@ size_t SelectBucketCountForIterRange(InputIter first, InputIter last, } #define ABSL_INTERNAL_ASSERT_IS_FULL(ctrl, msg) \ - ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && msg); + ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && msg) inline void AssertIsValid(ctrl_t* ctrl) { ABSL_HARDENING_ASSERT( @@ -1514,7 +1514,7 @@ class raw_hash_set { // a better match if non-const iterator is passed as an argument. void erase(iterator it) { ABSL_INTERNAL_ASSERT_IS_FULL(it.ctrl_, - "erase() called on invalid iterator.") + "erase() called on invalid iterator."); PolicyTraits::destroy(&alloc_ref(), it.slot_); erase_meta_only(it); } @@ -1549,7 +1549,7 @@ class raw_hash_set { node_type extract(const_iterator position) { ABSL_INTERNAL_ASSERT_IS_FULL(position.inner_.ctrl_, - "extract() called on invalid iterator.") + "extract() called on invalid iterator."); auto node = CommonAccess::Transfer(alloc_ref(), position.inner_.slot_); erase_meta_only(position); diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 020b7911..82917fc3 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -100,6 +100,7 @@ cc_library( "//absl/base:log_severity", "//absl/strings", "//absl/strings:str_format", + "//absl/types:optional", ], ) diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 29c85ad3..79e51a11 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -87,6 +87,7 @@ absl_cc_library( absl::config absl::core_headers absl::log_severity + absl::optional absl::strings absl::str_format ) diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index ced332d4..05ec79e8 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -990,3 +990,177 @@ TEST_F(FlagTest, MacroWithinAbslFlag) { absl::SetFlag(&FLAGS_prefix_test_macro_named_flag, 1); EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 1); } + +// -------------------------------------------------------------------- + +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5 +#define ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG +#endif + +#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG +ABSL_FLAG(absl::optional, optional_bool, absl::nullopt, "help"); +#endif +ABSL_FLAG(absl::optional, optional_int, {}, "help"); +ABSL_FLAG(absl::optional, optional_double, 9.3, "help"); +ABSL_FLAG(absl::optional, optional_string, absl::nullopt, "help"); +ABSL_FLAG(absl::optional, optional_duration, absl::nullopt, + "help"); +ABSL_FLAG(absl::optional>, optional_optional_int, + absl::nullopt, "help"); +#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) +ABSL_FLAG(std::optional, std_optional_int64, std::nullopt, "help"); +#endif + +namespace { + +#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG +TEST_F(FlagTest, TestOptionalBool) { + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt); + + absl::SetFlag(&FLAGS_optional_bool, false); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), false); + + absl::SetFlag(&FLAGS_optional_bool, true); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), true); + + absl::SetFlag(&FLAGS_optional_bool, absl::nullopt); + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt); +} + +// -------------------------------------------------------------------- +#endif + +TEST_F(FlagTest, TestOptionalInt) { + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt); + + absl::SetFlag(&FLAGS_optional_int, 0); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 0); + + absl::SetFlag(&FLAGS_optional_int, 10); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 10); + + absl::SetFlag(&FLAGS_optional_int, absl::nullopt); + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagTest, TestOptionalDouble) { + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value()); + EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 9.3); + + absl::SetFlag(&FLAGS_optional_double, 0.0); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), 0.0); + + absl::SetFlag(&FLAGS_optional_double, 1.234); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value()); + EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 1.234); + + absl::SetFlag(&FLAGS_optional_double, absl::nullopt); + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_double).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), absl::nullopt); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagTest, TestOptionalString) { + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt); + + // Setting optional string to "" leads to undefined behavior. + + absl::SetFlag(&FLAGS_optional_string, " "); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), " "); + + absl::SetFlag(&FLAGS_optional_string, "QWERTY"); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), "QWERTY"); + + absl::SetFlag(&FLAGS_optional_string, absl::nullopt); + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagTest, TestOptionalDuration) { + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt); + + absl::SetFlag(&FLAGS_optional_duration, absl::ZeroDuration()); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Seconds(0)); + + absl::SetFlag(&FLAGS_optional_duration, absl::Hours(3)); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Hours(3)); + + absl::SetFlag(&FLAGS_optional_duration, absl::nullopt); + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagTest, TestOptionalOptional) { + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt); + + absl::optional nullint{absl::nullopt}; + + absl::SetFlag(&FLAGS_optional_optional_int, nullint); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); + EXPECT_NE(absl::GetFlag(FLAGS_optional_optional_int), nullint); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), + absl::optional>{nullint}); + + absl::SetFlag(&FLAGS_optional_optional_int, 0); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0); + + absl::SetFlag(&FLAGS_optional_optional_int, absl::optional{0}); + EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::optional{0}); + + absl::SetFlag(&FLAGS_optional_optional_int, absl::nullopt); + EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt); +} + +// -------------------------------------------------------------------- + +#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) + +TEST_F(FlagTest, TestStdOptional) { + EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt); + + absl::SetFlag(&FLAGS_std_optional_int64, 0); + EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0); + + absl::SetFlag(&FLAGS_std_optional_int64, 0xFFFFFFFFFF16); + EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0xFFFFFFFFFF16); + + absl::SetFlag(&FLAGS_std_optional_int64, std::nullopt); + EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); + EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt); +} + +// -------------------------------------------------------------------- + +#endif + +} // namespace diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h index 7cbc136d..0f63cdc5 100644 --- a/absl/flags/marshalling.h +++ b/absl/flags/marshalling.h @@ -162,14 +162,27 @@ #ifndef ABSL_FLAGS_MARSHALLING_H_ #define ABSL_FLAGS_MARSHALLING_H_ +#include "absl/base/config.h" + +#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) +#include +#endif #include #include -#include "absl/base/config.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" namespace absl { ABSL_NAMESPACE_BEGIN + +// Forward declaration to be used inside composable flag parse/unparse +// implementations +template +inline bool ParseFlag(absl::string_view input, T* dst, std::string* error); +template +inline std::string UnparseFlag(const T& v); + namespace flags_internal { // Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types. @@ -188,6 +201,36 @@ bool AbslParseFlag(absl::string_view, double*, std::string*); bool AbslParseFlag(absl::string_view, std::string*, std::string*); bool AbslParseFlag(absl::string_view, std::vector*, std::string*); +template +bool AbslParseFlag(absl::string_view text, absl::optional* f, + std::string* err) { + if (text.empty()) { + *f = absl::nullopt; + return true; + } + T value; + if (!absl::ParseFlag(text, &value, err)) return false; + + *f = std::move(value); + return true; +} + +#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) +template +bool AbslParseFlag(absl::string_view text, std::optional* f, + std::string* err) { + if (text.empty()) { + *f = std::nullopt; + return true; + } + T value; + if (!absl::ParseFlag(text, &value, err)) return false; + + *f = std::move(value); + return true; +} +#endif + template bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) { // Comment on next line provides a good compiler error message if T @@ -201,6 +244,18 @@ bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) { std::string AbslUnparseFlag(absl::string_view v); std::string AbslUnparseFlag(const std::vector&); +template +std::string AbslUnparseFlag(const absl::optional& f) { + return f.has_value() ? absl::UnparseFlag(*f) : ""; +} + +#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) +template +std::string AbslUnparseFlag(const std::optional& f) { + return f.has_value() ? absl::UnparseFlag(*f) : ""; +} +#endif + template std::string Unparse(const T& v) { // Comment on next line provides a good compiler error message if T does not diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc index 4a64ce11..691cd2f1 100644 --- a/absl/flags/marshalling_test.cc +++ b/absl/flags/marshalling_test.cc @@ -659,6 +659,88 @@ TEST(MarshallingTest, TestVectorOfStringParsing) { // -------------------------------------------------------------------- +TEST(MarshallingTest, TestOptionalBoolParsing) { + std::string err; + absl::optional value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(value.has_value()); + + EXPECT_TRUE(absl::ParseFlag("true", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_TRUE(*value); + + EXPECT_TRUE(absl::ParseFlag("false", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_FALSE(*value); + + EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestOptionalIntParsing) { + std::string err; + absl::optional value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(value.has_value()); + + EXPECT_TRUE(absl::ParseFlag("10", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, 10); + + EXPECT_TRUE(absl::ParseFlag("0x1F", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, 31); + + EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestOptionalDoubleParsing) { + std::string err; + absl::optional value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(value.has_value()); + + EXPECT_TRUE(absl::ParseFlag("1.11", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, 1.11); + + EXPECT_TRUE(absl::ParseFlag("-0.12", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, -0.12); + + EXPECT_FALSE(absl::ParseFlag("nullopt", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestOptionalStringParsing) { + std::string err; + absl::optional value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(value.has_value()); + + EXPECT_TRUE(absl::ParseFlag(" ", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, " "); + + EXPECT_TRUE(absl::ParseFlag("aqswde", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, "aqswde"); + + EXPECT_TRUE(absl::ParseFlag("nullopt", &value, &err)); + EXPECT_TRUE(value.has_value()); + EXPECT_EQ(*value, "nullopt"); +} + +// -------------------------------------------------------------------- + TEST(MarshallingTest, TestBoolUnparsing) { EXPECT_EQ(absl::UnparseFlag(true), "true"); EXPECT_EQ(absl::UnparseFlag(false), "false"); @@ -808,6 +890,86 @@ TEST(MarshallingTest, TestStringUnparsing) { // -------------------------------------------------------------------- +TEST(MarshallingTest, TestOptionalBoolUnparsing) { + absl::optional value; + + EXPECT_EQ(absl::UnparseFlag(value), ""); + value = true; + EXPECT_EQ(absl::UnparseFlag(value), "true"); + value = false; + EXPECT_EQ(absl::UnparseFlag(value), "false"); + value = absl::nullopt; + EXPECT_EQ(absl::UnparseFlag(value), ""); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestOptionalIntUnparsing) { + absl::optional value; + + EXPECT_EQ(absl::UnparseFlag(value), ""); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = -12; + EXPECT_EQ(absl::UnparseFlag(value), "-12"); + value = absl::nullopt; + EXPECT_EQ(absl::UnparseFlag(value), ""); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestOptionalDoubleUnparsing) { + absl::optional value; + + EXPECT_EQ(absl::UnparseFlag(value), ""); + value = 1.; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = -1.23; + EXPECT_EQ(absl::UnparseFlag(value), "-1.23"); + value = absl::nullopt; + EXPECT_EQ(absl::UnparseFlag(value), ""); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestOptionalStringUnparsing) { + absl::optional value; + + EXPECT_EQ(absl::UnparseFlag(value), ""); + value = "asdfg"; + EXPECT_EQ(absl::UnparseFlag(value), "asdfg"); + value = " "; + EXPECT_EQ(absl::UnparseFlag(value), " "); + value = ""; // This is UB to set optional string flag to "" + EXPECT_EQ(absl::UnparseFlag(value), ""); + value = absl::nullopt; + EXPECT_EQ(absl::UnparseFlag(value), ""); +} + +// -------------------------------------------------------------------- + +#if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) + +TEST(MarshallingTest, TestStdOptionalUnparsing) { + std::optional strvalue; + + EXPECT_EQ(absl::UnparseFlag(strvalue), ""); + strvalue = "asdfg"; + EXPECT_EQ(absl::UnparseFlag(strvalue), "asdfg"); + strvalue = std::nullopt; + EXPECT_EQ(absl::UnparseFlag(strvalue), ""); + + std::optional intvalue(10); + + EXPECT_EQ(absl::UnparseFlag(intvalue), "10"); + intvalue = std::nullopt; + EXPECT_EQ(absl::UnparseFlag(intvalue), ""); +} + +// -------------------------------------------------------------------- + +#endif + template void TestRoundtrip(T v) { T new_v; diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 2087ffec..5ca5e589 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -156,7 +156,7 @@ class RefcountAndFlags { // used for the StringConstant constructor to avoid collecting immutable // constant cords. // kReservedFlag is reserved for future use. - enum { + enum Flags { kNumFlags = 2, kImmortalFlag = 0x1, diff --git a/absl/strings/internal/cord_rep_btree_navigator.h b/absl/strings/internal/cord_rep_btree_navigator.h index 971b92ed..3d581c87 100644 --- a/absl/strings/internal/cord_rep_btree_navigator.h +++ b/absl/strings/internal/cord_rep_btree_navigator.h @@ -143,8 +143,8 @@ class CordRepBtreeNavigator { // `index_` and `node_` contain the navigation state as the 'path' to the // current data edge which is at `node_[0]->Edge(index_[0])`. The contents // of these are undefined until the instance is initialized (`height_ >= 0`). - uint8_t index_[CordRepBtree::kMaxHeight]; - CordRepBtree* node_[CordRepBtree::kMaxHeight]; + uint8_t index_[CordRepBtree::kMaxDepth]; + CordRepBtree* node_[CordRepBtree::kMaxDepth]; }; // Returns true if this instance is not empty. @@ -173,6 +173,7 @@ template inline CordRep* CordRepBtreeNavigator::Init(CordRepBtree* tree) { assert(tree != nullptr); assert(tree->size() > 0); + assert(tree->height() <= CordRepBtree::kMaxHeight); int height = height_ = tree->height(); size_t index = tree->index(edge_type); node_[height] = tree; @@ -206,6 +207,7 @@ inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::Seek( inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::InitOffset( CordRepBtree* tree, size_t offset) { assert(tree != nullptr); + assert(tree->height() <= CordRepBtree::kMaxHeight); if (ABSL_PREDICT_FALSE(offset >= tree->length)) return {nullptr, 0}; height_ = tree->height(); node_[height_] = tree; diff --git a/absl/strings/internal/cord_rep_btree_navigator_test.cc b/absl/strings/internal/cord_rep_btree_navigator_test.cc index ce09b199..4f9bd4e5 100644 --- a/absl/strings/internal/cord_rep_btree_navigator_test.cc +++ b/absl/strings/internal/cord_rep_btree_navigator_test.cc @@ -319,6 +319,27 @@ TEST_P(CordRepBtreeNavigatorTest, ReadBeyondLengthOfTree) { ASSERT_THAT(result.tree, Eq(nullptr)); } +TEST(CordRepBtreeNavigatorTest, NavigateMaximumTreeDepth) { + CordRepFlat* flat1 = MakeFlat("Hello world"); + CordRepFlat* flat2 = MakeFlat("World Hello"); + + CordRepBtree* node = CordRepBtree::Create(flat1); + node = CordRepBtree::Append(node, flat2); + while (node->height() < CordRepBtree::kMaxHeight) { + node = CordRepBtree::New(node); + } + + CordRepBtreeNavigator nav; + CordRep* edge = nav.InitFirst(node); + EXPECT_THAT(edge, Eq(flat1)); + EXPECT_THAT(nav.Next(), Eq(flat2)); + EXPECT_THAT(nav.Next(), Eq(nullptr)); + EXPECT_THAT(nav.Previous(), Eq(flat1)); + EXPECT_THAT(nav.Previous(), Eq(nullptr)); + + CordRep::Unref(node); +} + } // namespace } // namespace cord_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From b9ad9bbfed92199a1a58504306d026cd2597539e Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Wed, 30 Mar 2022 21:56:20 +0200 Subject: Fix build with uclibc-ng (#1145) uclibc-ng doesn't provide getauxval which results in the following build failure on arm or ppc with any user of abseil-cpp such as grpc: /home/buildroot/autobuild/instance-0/output-1/host/opt/ext-toolchain/bin/../lib/gcc/arm-buildroot-linux-uclibcgnueabi/10.3.0/../../../../arm-buildroot-linux-uclibcgnueabi/bin/ld: /home/buildroot/autobuild/instance-0/output-1/host/arm-buildroot-linux-uclibcgnueabi/sysroot/usr/lib/libabsl_random_internal_randen_hwaes.so.2111.0.0: undefined reference to `getauxval' To fix this build failure, check that __UCLIBC__ is not defined before using getauxval (as Babel is not able to check function availability) Fixes: - http://autobuild.buildroot.org/results/775f3ca3dedebff29e212b29dfa896b7613b7a02 Signed-off-by: Fabrice Fontaine --- absl/debugging/internal/vdso_support.cc | 2 +- absl/random/internal/randen_detect.cc | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index c655cf45..e63ac4a3 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -33,7 +33,7 @@ #endif #include -#if defined(__GLIBC__) && \ +#if !defined(__UCLIBC__) && defined(__GLIBC__) && \ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)) #define ABSL_HAVE_GETAUXVAL #endif diff --git a/absl/random/internal/randen_detect.cc b/absl/random/internal/randen_detect.cc index 9bb58fc6..6dababa3 100644 --- a/absl/random/internal/randen_detect.cc +++ b/absl/random/internal/randen_detect.cc @@ -24,6 +24,11 @@ #include "absl/random/internal/platform.h" +#if !defined(__UCLIBC__) && defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)) +#define ABSL_HAVE_GETAUXVAL +#endif + #if defined(ABSL_ARCH_X86_64) #define ABSL_INTERNAL_USE_X86_CPUID #elif defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \ @@ -31,7 +36,7 @@ #if defined(__ANDROID__) #define ABSL_INTERNAL_USE_ANDROID_GETAUXVAL #define ABSL_INTERNAL_USE_GETAUXVAL -#elif defined(__linux__) +#elif defined(__linux__) && defined(ABSL_HAVE_GETAUXVAL) #define ABSL_INTERNAL_USE_LINUX_GETAUXVAL #define ABSL_INTERNAL_USE_GETAUXVAL #endif -- cgit v1.2.3 From 4dcae40a07e30cbc99d4130e590047000e5fda0b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 31 Mar 2022 09:48:42 -0700 Subject: Export of internal Abseil changes -- b984c7c1cbee4253192c833046bfcfa16dca8ccf by Dino Radakovic : Restrict visibility of absl/flags:commandlineflag_internal PiperOrigin-RevId: 438591894 Change-Id: I12a6392b2c7f9f1263c741dfd6c43ae22e903aad -- 81315601aab70bb6ac2d17655a727d7f9ee2ff95 by Justin Lebar : Update example in str_join.h to use a lambda. PiperOrigin-RevId: 438390035 Change-Id: Icc707b972e5a369a71ad774004fdf0a17a9b33a7 -- 1c3c7921224e505faca8617b073f657d3737219f by Dino Radakovic : Internal change PiperOrigin-RevId: 438386036 Change-Id: I6066da1b5a1ddf5af265944a31ed298297b4f2e1 -- 2d6885f78481f04e0e7ee86060aec15b677144f3 by Abseil Team : Internal change PiperOrigin-RevId: 438378389 Change-Id: If70dd9114114eb44e85afccd521e7fb7e1436b88 -- 7f8282ddee7fcd032e01cbfe65c11c2d166cceb8 by Derek Mauro : Internal change PiperOrigin-RevId: 438374554 Change-Id: I993367952af1dc83bd5aa0ae19a64c024f457fdd -- ce65ba28f6031e45db8fa5118a05410f5166fd7a by Abseil Team : Spelling gardening: Heterogeneous has an "e" after the "n". PiperOrigin-RevId: 438374411 Change-Id: If1a9098a5d04338837998883739c7b555efa62b4 GitOrigin-RevId: b984c7c1cbee4253192c833046bfcfa16dca8ccf --- absl/base/BUILD.bazel | 4 +++- absl/flags/BUILD.bazel | 3 +++ absl/memory/BUILD.bazel | 3 +++ absl/strings/cord_ring_reader_test.cc | 2 +- absl/strings/internal/cordz_sample_token_test.cc | 2 +- absl/strings/str_join.h | 24 +++++++++--------------- absl/strings/string_view_test.cc | 2 +- absl/synchronization/BUILD.bazel | 4 +++- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 4769efda..6f2f09b9 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -157,7 +157,9 @@ cc_library( "internal/direct_mmap.h", "internal/low_level_alloc.h", ], - copts = ABSL_DEFAULT_COPTS, + copts = ABSL_DEFAULT_COPTS + select({ + "//conditions:default": [], + }), linkopts = select({ "//absl:msvc_compiler": [], "//absl:clang-cl_compiler": [], diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 82917fc3..91c6e98b 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -114,6 +114,9 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//visibility:private", + ], deps = [ "//absl/base:config", "//absl/base:fast_type_id", diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel index c16bf8a9..389aedf3 100644 --- a/absl/memory/BUILD.bazel +++ b/absl/memory/BUILD.bazel @@ -29,6 +29,9 @@ cc_library( name = "memory", hdrs = ["memory.h"], copts = ABSL_DEFAULT_COPTS, + defines = select({ + "//conditions:default": [], + }), linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:core_headers", diff --git a/absl/strings/cord_ring_reader_test.cc b/absl/strings/cord_ring_reader_test.cc index d9a9a76d..8e7183bf 100644 --- a/absl/strings/cord_ring_reader_test.cc +++ b/absl/strings/cord_ring_reader_test.cc @@ -126,7 +126,7 @@ TEST(CordRingReaderTest, SeekForward) { reader.Reset(ring); size_t consumed = 0; - size_t remaining = ring->length;; + size_t remaining = ring->length; for (int i = 0; i < flats.size(); ++i) { CordRepRing::index_type index = ring->advance(head, i); size_t offset = consumed; diff --git a/absl/strings/internal/cordz_sample_token_test.cc b/absl/strings/internal/cordz_sample_token_test.cc index 9f54301d..6be1770d 100644 --- a/absl/strings/internal/cordz_sample_token_test.cc +++ b/absl/strings/internal/cordz_sample_token_test.cc @@ -167,7 +167,7 @@ TEST(CordzSampleTokenTest, MultiThreaded) { if (cord.data.is_profiled()) { // 1) Untrack cord.data.cordz_info()->Untrack(); - cord.data.clear_cordz_info();; + cord.data.clear_cordz_info(); } else { // 2) Track CordzInfo::TrackCord(cord.data, kTrackCordMethod); diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h index 33534536..ee5ae7ef 100644 --- a/absl/strings/str_join.h +++ b/absl/strings/str_join.h @@ -72,21 +72,15 @@ ABSL_NAMESPACE_BEGIN // functions. You may provide your own Formatter to enable `absl::StrJoin()` to // work with arbitrary types. // -// The following is an example of a custom Formatter that simply uses -// `std::to_string()` to format an integer as a std::string. -// -// struct MyFormatter { -// void operator()(std::string* out, int i) const { -// out->append(std::to_string(i)); -// } -// }; -// -// You would use the above formatter by passing an instance of it as the final -// argument to `absl::StrJoin()`: -// -// std::vector v = {1, 2, 3, 4}; -// std::string s = absl::StrJoin(v, "-", MyFormatter()); -// EXPECT_EQ("1-2-3-4", s); +// The following is an example of a custom Formatter that uses +// `absl::FormatDuration` to join a list of `absl::Duration`s. +// +// std::vector v = {absl::Seconds(1), absl::Milliseconds(10)}; +// std::string s = +// absl::StrJoin(v, ", ", [](std::string* out, absl::Duration dur) { +// absl::StrAppend(out, absl::FormatDuration(dur)); +// }); +// EXPECT_EQ("1s, 10ms", s); // // The following standard formatters are provided within this file: // diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index 2c13dd1c..9d5463a1 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -1189,7 +1189,7 @@ TEST(ComparisonOpsTest, StringCompareNotAmbiguous) { EXPECT_LT("hello", std::string("world")); } -TEST(ComparisonOpsTest, HeterogenousStringViewEquals) { +TEST(ComparisonOpsTest, HeterogeneousStringViewEquals) { EXPECT_EQ(absl::string_view("hello"), std::string("hello")); EXPECT_EQ("hello", absl::string_view("hello")); } diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index d7195473..a0492c55 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -34,7 +34,9 @@ cc_library( hdrs = [ "internal/graphcycles.h", ], - copts = ABSL_DEFAULT_COPTS, + copts = ABSL_DEFAULT_COPTS + select({ + "//conditions:default": [], + }), linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", -- cgit v1.2.3 From 33ef755be756c9699829f33e8ad2eb7072f810d0 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Thu, 31 Mar 2022 14:13:19 -0400 Subject: Disable NominalCPUFrequency tests Upstream has decided NominalCPUFrequency is for internal consumption, so stop running its unit tests. --- debian/changelog | 7 ++++ debian/patches/arm-multiarch.diff | 25 ------------ .../patches/disable-nominalcpufrequency-test.diff | 46 ++++++++++++++++++++++ debian/patches/series | 2 +- 4 files changed, 54 insertions(+), 26 deletions(-) delete mode 100644 debian/patches/arm-multiarch.diff create mode 100644 debian/patches/disable-nominalcpufrequency-test.diff diff --git a/debian/changelog b/debian/changelog index 9c58851f..d20492e2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +abseil (0~20210324.2-3) UNRELEASED; urgency=medium + + * Backport an upstream patch to disable a problematic unit test. + (Closes: #1007136) + + -- Benjamin Barenblat Thu, 31 Mar 2022 14:10:30 -0400 + abseil (0~20210324.2-2) unstable; urgency=medium * Disable a test that doesn’t play well with multiarch on armel and diff --git a/debian/patches/arm-multiarch.diff b/debian/patches/arm-multiarch.diff deleted file mode 100644 index c976f730..00000000 --- a/debian/patches/arm-multiarch.diff +++ /dev/null @@ -1,25 +0,0 @@ -From: Benjamin Barenblat -Subject: Disable SysinfoTest.NominalCPUFrequency on armel/armhf - -NominalCPUFrequency has different behavior on 32-bit and 64-bit ARM -kernels. The Debian arm64 buildds assume they can build 32-bit ARM -packages, but if they do, the NominalCPUFrequency test will fail. -Disable the test when building for 32-bit ARM. - ---- a/absl/base/internal/sysinfo_test.cc -+++ b/absl/base/internal/sysinfo_test.cc -@@ -43,7 +43,13 @@ - // POWER is particularly problematic here; some Linux kernels expose the CPU - // frequency, while others do not. Since we can't predict a priori what a given - // machine is going to do, just disable this test on POWER on Linux. -+// -+// Debian also disables this test on armel and armhf, since tests for those -+// platforms could either be run on a 32-bit ARM system (where -+// NominalCPUFrequency returns a reasonable value) or a 64-bit ARM system (where -+// it does not). --#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__))) -+#if !(defined(__linux) && \ -+ (defined(__ppc64__) || defined(__PPC64__) || defined(__arm__))) - TEST(SysinfoTest, NominalCPUFrequency) { - // Linux only exposes the CPU frequency on certain architectures, and - // Emscripten doesn't expose it at all. diff --git a/debian/patches/disable-nominalcpufrequency-test.diff b/debian/patches/disable-nominalcpufrequency-test.diff new file mode 100644 index 00000000..dc15dc9e --- /dev/null +++ b/debian/patches/disable-nominalcpufrequency-test.diff @@ -0,0 +1,46 @@ +From: Derek Mauro +Subject: Remove the test for absl::base_internal::NominalCPUFrequency() from OSS code +Origin: upstream, https://github.com/abseil/abseil-cpp/commit/732b5580f089101ce4b8cdff55bb6461c59a6720 + +Remove the test for absl::base_internal::NominalCPUFrequency() from OSS code + +This is an internal-only function that should never by called by OSS code. +By its nature fails on unsupported platforms. +Google code has tests for this function on supported internal platforms. + +Fixes #1053 + +PiperOrigin-RevId: 408692861 + +--- a/absl/base/internal/sysinfo_test.cc ++++ b/absl/base/internal/sysinfo_test.cc +@@ -37,29 +37,6 @@ + << "NumCPUs() should not have the default value of 0"; + } + +-// Ensure that NominalCPUFrequency returns a reasonable value, or 1.00 on +-// platforms where the CPU frequency is not available through sysfs. +-// +-// POWER is particularly problematic here; some Linux kernels expose the CPU +-// frequency, while others do not. Since we can't predict a priori what a given +-// machine is going to do, just disable this test on POWER on Linux. +-#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__))) +-TEST(SysinfoTest, NominalCPUFrequency) { +- // Linux only exposes the CPU frequency on certain architectures, and +- // Emscripten doesn't expose it at all. +-#if defined(__linux__) && \ +- (defined(__aarch64__) || defined(__hppa__) || defined(__mips__) || \ +- defined(__riscv) || defined(__s390x__)) || \ +- defined(__EMSCRIPTEN__) +- EXPECT_EQ(NominalCPUFrequency(), 1.0) +- << "CPU frequency detection was fixed! Please update unittest."; +-#else +- EXPECT_GE(NominalCPUFrequency(), 1000.0) +- << "NominalCPUFrequency() did not return a reasonable value"; +-#endif +-} +-#endif +- + TEST(SysinfoTest, GetTID) { + EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test. + #ifdef __native_client__ diff --git a/debian/patches/series b/debian/patches/series index f68ea75c..6a2a842b 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,6 +1,5 @@ configure.diff latomic.diff -arm-multiarch.diff empty-flags-library.diff cordrepring-typo.diff thumb-function-bounds.diff @@ -14,3 +13,4 @@ big-endian-random.diff big-endian-random2.diff big-endian-random3.diff big-endian-random4.diff +disable-nominalcpufrequency-test.diff -- cgit v1.2.3 From 6f43f5bb398b6685575b36874e36cf1695734df1 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 31 Mar 2022 17:42:13 -0700 Subject: Export of internal Abseil changes -- afa44fa0245a1cfb1824ef9697b3fa77fa9615c9 by Laramie Leavitt : Comment CMakeLists.txt about internal absl_cc_library() targets. From what I can tell, these are the CMake targets that are public: absl::algorithm absl::algorithm_container absl::base absl::core_headers absl::dynamic_annotations absl::log_severity absl::cleanup absl::btree absl::fixed_array absl::flat_hash_map absl::flat_hash_set absl::inlined_vector absl::node_hash_map absl::node_hash_set absl::debugging absl::failure_signal_handler absl::leak_check absl::leak_check_disable absl::stacktrace absl::symbolize absl::flags absl::flags_commandlineflag absl::flags_config absl::flags_marshalling absl::flags_parse absl::flags_reflection absl::flags_usage absl::bind_front absl::function_ref absl::hash absl::hash_testing absl::memory absl::meta absl::type_traits absl::bits absl::int128 absl::numeric absl::numeric_representation absl::exponential_biased absl::periodic_sampler absl::sample_recorder absl::random_bit_gen_ref absl::random_distributions absl::random_mocking_bit_gen absl::random_random absl::random_seed_gen_exception absl::random_seed_sequences absl::status absl::statusor absl::cord absl::cord_test_helpers absl::str_format absl::strings absl::synchronization absl::civil_time absl::time absl::time_zone absl::any absl::bad_any_cast absl::bad_optional_access absl::bad_variant_access absl::compare absl::optional absl::span absl::variant absl::utility PiperOrigin-RevId: 438702788 Change-Id: Icf611c35e88f03cd2493a95f61617605305d4e8e -- a99f60847578e6c0df6befadb29a01c86def0d21 by Abseil Team : Internal change PiperOrigin-RevId: 438647928 Change-Id: I141eadd17d6e8607df25ebc893aecefa0239a72f -- b23e77e8f62a77023188594390c9e491c507d22c by Abseil Team : Internal change PiperOrigin-RevId: 438628502 Change-Id: I40c4297716c8c1621ba8b02a22393bfcbefb5b5e GitOrigin-RevId: afa44fa0245a1cfb1824ef9697b3fa77fa9615c9 --- absl/base/CMakeLists.txt | 16 ++++++++++++++++ absl/cleanup/CMakeLists.txt | 1 + absl/container/BUILD.bazel | 29 +++++++++++------------------ absl/container/CMakeLists.txt | 27 +++++++++++++++++++++++++++ absl/debugging/CMakeLists.txt | 4 ++++ absl/hash/CMakeLists.txt | 3 +++ absl/strings/BUILD.bazel | 1 - absl/strings/CMakeLists.txt | 13 +++++++++++++ absl/synchronization/CMakeLists.txt | 4 ++++ absl/time/CMakeLists.txt | 1 + absl/types/CMakeLists.txt | 2 ++ 11 files changed, 82 insertions(+), 19 deletions(-) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index c7233cb3..7e600f0b 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -16,6 +16,7 @@ find_library(LIBRT rt) +# Internal-only target, do not depend on directly. absl_cc_library( NAME atomic_hook @@ -28,6 +29,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME errno_saver @@ -52,6 +54,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME raw_logging_internal @@ -68,6 +71,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME spinlock_wait @@ -131,6 +135,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME malloc_internal @@ -151,6 +156,7 @@ absl_cc_library( Threads::Threads ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME base_internal @@ -207,6 +213,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME throw_delegate @@ -221,6 +228,7 @@ absl_cc_library( absl::raw_logging_internal ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME exception_testing @@ -234,6 +242,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME pretty_function @@ -243,6 +252,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME exception_safety_testing @@ -276,6 +286,7 @@ absl_cc_test( GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME atomic_hook_test_helper @@ -375,6 +386,7 @@ absl_cc_test( GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME spinlock_test_common @@ -409,6 +421,7 @@ absl_cc_test( GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME endian @@ -519,6 +532,7 @@ absl_cc_test( GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME scoped_set_env @@ -570,6 +584,7 @@ absl_cc_test( GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME strerror @@ -601,6 +616,7 @@ absl_cc_test( GTest::gtest_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME fast_type_id diff --git a/absl/cleanup/CMakeLists.txt b/absl/cleanup/CMakeLists.txt index 26a6d0dc..f5af40b4 100644 --- a/absl/cleanup/CMakeLists.txt +++ b/absl/cleanup/CMakeLists.txt @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Internal-only target, do not depend on directly. absl_cc_library( NAME cleanup_internal diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 2095e57d..d733cb24 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -217,11 +217,6 @@ cc_test( ], ) -NOTEST_TAGS_NONMOBILE = [ - "no_test_darwin_x86_64", - "no_test_loonix", -] - NOTEST_TAGS_MOBILE = [ "no_test_android_arm", "no_test_android_arm64", @@ -229,8 +224,6 @@ NOTEST_TAGS_MOBILE = [ "no_test_ios_x86_64", ] -NOTEST_TAGS = NOTEST_TAGS_MOBILE + NOTEST_TAGS_NONMOBILE - cc_library( name = "flat_hash_map", hdrs = ["flat_hash_map.h"], @@ -251,7 +244,7 @@ cc_test( srcs = ["flat_hash_map_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], deps = [ ":flat_hash_map", ":hash_generator_testing", @@ -285,7 +278,7 @@ cc_test( srcs = ["flat_hash_set_test.cc"], copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], deps = [ ":flat_hash_set", ":hash_generator_testing", @@ -321,7 +314,7 @@ cc_test( srcs = ["node_hash_map_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], deps = [ ":hash_generator_testing", ":node_hash_map", @@ -354,7 +347,7 @@ cc_test( srcs = ["node_hash_set_test.cc"], copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], deps = [ ":node_hash_set", ":unordered_set_constructor_test", @@ -383,7 +376,7 @@ cc_test( srcs = ["internal/container_memory_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], deps = [ ":container_memory", ":test_instance_tracker", @@ -410,7 +403,7 @@ cc_test( srcs = ["internal/hash_function_defaults_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS, + tags = NOTEST_TAGS_MOBILE + ["no_test_loonix"], deps = [ ":hash_function_defaults", "//absl/hash", @@ -608,7 +601,7 @@ cc_test( srcs = ["internal/raw_hash_set_test.cc"], copts = ABSL_TEST_COPTS, linkstatic = 1, - tags = NOTEST_TAGS, + tags = NOTEST_TAGS_MOBILE + ["no_test_loonix"], deps = [ ":container_memory", ":hash_function_defaults", @@ -698,7 +691,7 @@ cc_test( srcs = ["internal/layout_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS, + tags = NOTEST_TAGS_MOBILE + ["no_test_loonix"], visibility = ["//visibility:private"], deps = [ ":layout", @@ -845,7 +838,7 @@ cc_test( srcs = ["internal/unordered_set_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], deps = [ ":unordered_set_constructor_test", ":unordered_set_lookup_test", @@ -860,7 +853,7 @@ cc_test( srcs = ["internal/unordered_map_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], deps = [ ":unordered_map_constructor_test", ":unordered_map_lookup_test", @@ -875,7 +868,7 @@ cc_test( srcs = ["sample_element_size_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = NOTEST_TAGS_NONMOBILE, + tags = ["no_test_loonix"], visibility = ["//visibility:private"], deps = [ ":flat_hash_map", diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 7176907d..aad69fa2 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -42,6 +42,7 @@ absl_cc_library( absl::utility ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME btree_test_common @@ -84,6 +85,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME compressed_tuple @@ -162,6 +164,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME inlined_vector_internal @@ -194,6 +197,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME counting_allocator @@ -240,6 +244,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME test_instance_tracker @@ -411,6 +416,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME container_memory @@ -440,6 +446,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME hash_function_defaults @@ -472,6 +479,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME hash_generator_testing @@ -489,6 +497,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME hash_policy_testing @@ -514,6 +523,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME hash_policy_traits @@ -538,6 +548,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME hashtablez_sampler @@ -569,6 +580,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME hashtable_debug @@ -580,6 +592,7 @@ absl_cc_library( absl::hashtable_debug_hooks ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME hashtable_debug_hooks @@ -592,6 +605,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME node_slot_policy @@ -617,6 +631,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME raw_hash_map @@ -631,6 +646,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME container_common @@ -642,6 +658,7 @@ absl_cc_library( absl::type_traits ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME raw_hash_set @@ -704,6 +721,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME layout @@ -737,6 +755,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME tracked @@ -749,6 +768,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_map_constructor_test @@ -763,6 +783,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_map_lookup_test @@ -777,6 +798,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_map_members_test @@ -790,6 +812,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_map_modifiers_test @@ -804,6 +827,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_set_constructor_test @@ -818,6 +842,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_set_lookup_test @@ -832,6 +857,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_set_members_test @@ -845,6 +871,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME unordered_set_modifiers_test diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index b16fa007..5850bdd0 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -93,6 +93,7 @@ absl_cc_test( GTest::gmock ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME examine_stack @@ -147,6 +148,7 @@ absl_cc_test( GTest::gmock ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME debugging_internal @@ -168,6 +170,7 @@ absl_cc_library( absl::raw_logging_internal ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME demangle_internal @@ -298,6 +301,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME stack_consumption diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index 34434fa0..423b74b5 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -80,6 +80,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME spy_hash_state @@ -94,6 +95,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME city @@ -121,6 +123,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME low_level_hash diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 5f122ee9..813aef45 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -736,7 +736,6 @@ cc_test( "no_test_android_arm", "no_test_android_arm64", "no_test_android_x86", - "no_test_darwin_x86_64", "no_test_ios_x86_64", "no_test_loonix", "no_test_msvc_x64", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 5d418c86..d8715ed1 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -68,6 +68,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME strings_internal @@ -385,6 +386,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME str_format_internal @@ -523,6 +525,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME pow10_helper @@ -550,6 +553,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cord_internal @@ -588,6 +592,7 @@ absl_cc_library( absl::type_traits ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_update_tracker @@ -614,6 +619,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_functions @@ -642,6 +648,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_statistics @@ -656,6 +663,7 @@ absl_cc_library( absl::synchronization ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_handle @@ -689,6 +697,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_info @@ -756,6 +765,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_sample_token @@ -794,6 +804,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_update_scope @@ -859,6 +870,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cord_rep_test_util @@ -889,6 +901,7 @@ absl_cc_library( TESTONLY ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME cordz_test_helpers diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index 605efe2d..9335c264 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -14,6 +14,7 @@ # limitations under the License. # +# Internal-only target, do not depend on directly. absl_cc_library( NAME graphcycles_internal @@ -32,6 +33,7 @@ absl_cc_library( absl::raw_logging_internal ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME kernel_timeout_internal @@ -125,6 +127,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME thread_pool @@ -170,6 +173,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME per_thread_sem_test_common diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index f6ff8bd1..debab3ba 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt @@ -87,6 +87,7 @@ absl_cc_library( $<$:${CoreFoundation}> ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME time_internal_test_util diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index d7e8614e..830953ae 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt @@ -43,6 +43,7 @@ absl_cc_library( PUBLIC ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME bad_any_cast_impl @@ -239,6 +240,7 @@ absl_cc_test( GTest::gmock_main ) +# Internal-only target, do not depend on directly. absl_cc_library( NAME conformance_testing -- cgit v1.2.3 From 9fed77a6fea29b8c8468bd41c6259c7f67163a65 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 5 Apr 2022 13:00:39 -0700 Subject: Export of internal Abseil changes -- ef2bf829c333f378ecc12f3259e3187cdb75a3d5 by Abseil Team : debugging: fix the VDSO symbol name used for unwinding on RISC-V Linux The name listed in `man vdso` is incorrect. Instead, use the name from `linux-5.16/arch/riscv/kernel/vdso/rt_sigreturn.S` PiperOrigin-RevId: 439654174 Change-Id: Ib39d066f416681720068e806e828a2c76a14a532 -- 43dfad824afd36cfc3e5049b4fea71a2bccb066c by Benjamin Barenblat : Check printf format strings in str_format_convert_test Add ABSL_PRINTF_ATTRIBUTE to appropriate functions in strings/internal/str_format/convert_test. Correct TypedFormatConvertTest.Char, which was accidentally passing values of types larger than int to StrPrint. PiperOrigin-RevId: 439388148 Change-Id: I6cde4e8e0c6455064138192430f07f4c990be0bc -- f84b4ab2c3b070c8af0c82742ac7a8a4bf443bca by Derek Mauro : Use __builtin_memcmp in the absl::string_view implementation starting with MSVC 16.9, where it first appeared This enables more constexpr operations PiperOrigin-RevId: 439317316 Change-Id: Iaf1ce76b60901d4b2d5b96be5900c56572f57b15 GitOrigin-RevId: ef2bf829c333f378ecc12f3259e3187cdb75a3d5 --- absl/debugging/internal/stacktrace_riscv-inl.inc | 2 +- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 1 + absl/strings/internal/str_format/convert_test.cc | 34 +++++++++++++++++------- absl/strings/string_view.h | 5 ++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc index b4bdb5f1..98b09a22 100644 --- a/absl/debugging/internal/stacktrace_riscv-inl.inc +++ b/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -56,7 +56,7 @@ static const unsigned char *GetKernelRtSigreturnAddress() { absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10. auto lookup = [&](int type) { - return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_4.15", type, + return vdso.LookupSymbol("__vdso_rt_sigreturn", "LINUX_4.15", type, &symbol_info); }; if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 813aef45..1cf58ca1 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -1191,6 +1191,7 @@ cc_test( deps = [ ":str_format_internal", ":strings", + "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/types:optional", "@com_google_googletest//:gtest_main", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index d8715ed1..c4358a12 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -494,6 +494,7 @@ absl_cc_test( DEPS absl::strings absl::str_format_internal + absl::core_headers absl::raw_logging_internal absl::int128 GTest::gmock_main diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index d9fbf61c..300612b7 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -24,6 +24,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/attributes.h" #include "absl/base/internal/raw_logging.h" #include "absl/strings/internal/str_format/bind.h" #include "absl/strings/match.h" @@ -124,6 +125,7 @@ void StrAppendV(std::string *dst, const char *format, va_list ap) { delete[] buf; } +void StrAppend(std::string *, const char *, ...) ABSL_PRINTF_ATTRIBUTE(2, 3); void StrAppend(std::string *out, const char *format, ...) { va_list ap; va_start(ap, format); @@ -131,6 +133,7 @@ void StrAppend(std::string *out, const char *format, ...) { va_end(ap); } +std::string StrPrint(const char *, ...) ABSL_PRINTF_ATTRIBUTE(1, 2); std::string StrPrint(const char *format, ...) { va_list ap; va_start(ap, format); @@ -455,21 +458,32 @@ TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) { } TYPED_TEST_P(TypedFormatConvertTest, Char) { + // Pass a bunch of values of type TypeParam to both FormatPack and libc's + // vsnprintf("%c", ...) (wrapped in StrPrint) to make sure we get the same + // value. typedef TypeParam T; using remove_volatile_t = typename std::remove_volatile::type; - static const T kMin = std::numeric_limits::min(); - static const T kMax = std::numeric_limits::max(); - T kVals[] = { - remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10), - remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10), - remove_volatile_t(0), - kMin + remove_volatile_t(1), kMin, - kMax - remove_volatile_t(1), kMax + std::vector vals = { + remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10), // + remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10), // + remove_volatile_t(0), }; - for (const T &c : kVals) { + + // We'd like to test values near std::numeric_limits::min() and + // std::numeric_limits::max(), too, but vsnprintf("%c", ...) can't handle + // anything larger than an int. Add in the most extreme values we can without + // exceeding that range. + static const T kMin = + static_cast(std::numeric_limits::min()); + static const T kMax = + static_cast(std::numeric_limits::max()); + vals.insert(vals.end(), {kMin + 1, kMin, kMax - 1, kMax}); + + for (const T c : vals) { const FormatArgImpl args[] = {FormatArgImpl(c)}; UntypedFormatSpecImpl format("%c"); - EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args))); + EXPECT_EQ(StrPrint("%c", static_cast(c)), + FormatPack(format, absl::MakeSpan(args))); } } diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index a4c9a652..e3239f57 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -55,8 +55,9 @@ ABSL_NAMESPACE_END #else // ABSL_USES_STD_STRING_VIEW -#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ - (defined(__GNUC__) && !defined(__clang__)) +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1928) #define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp #else // ABSL_HAVE_BUILTIN(__builtin_memcmp) #define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp -- cgit v1.2.3 From e854df09dfcb35056c1d42420028648ee0ebebaf Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 6 Apr 2022 10:44:58 -0700 Subject: Export of internal Abseil changes -- 0c8848ebedc07470c7ab647a5bb8949481540ce9 by Dino Radakovic : Define absl::base_internal::invoke using std::invoke when C++ >= 17 PiperOrigin-RevId: 439880834 Change-Id: I3622fcf473501d54c57575118a11d54c19573446 GitOrigin-RevId: 0c8848ebedc07470c7ab647a5bb8949481540ce9 --- absl/base/internal/invoke.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index 5c71f328..e9efb2fc 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -14,6 +14,8 @@ // // absl::base_internal::invoke(f, args...) is an implementation of // INVOKE(f, args...) from section [func.require] of the C++ standard. +// When compiled as C++17 and later versions, it is implemented as an alias of +// std::invoke. // // [func.require] // Define INVOKE (f, t1, t2, ..., tN) as follows: @@ -35,6 +37,25 @@ #ifndef ABSL_BASE_INTERNAL_INVOKE_H_ #define ABSL_BASE_INTERNAL_INVOKE_H_ +#include "absl/base/config.h" + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + +#include + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +using std::invoke; +using std::invoke_result_t; + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#else // __cplusplus >= 201703L + #include #include #include @@ -184,4 +205,6 @@ invoke_result_t invoke(F&& f, Args&&... args) { ABSL_NAMESPACE_END } // namespace absl +#endif // __cplusplus >= 201703L + #endif // ABSL_BASE_INTERNAL_INVOKE_H_ -- cgit v1.2.3 From ac1398a6296de03413d7b88df4b4aa16e9e450cc Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 12 Apr 2022 09:04:16 -0700 Subject: Export of internal Abseil changes -- f4c7e510922668c68be4aa79a00867c3d3ca9f95 by Derek Mauro : Many improvements to LeakChecker builds The presence of the LeakChecker is now detected when possible. GCC users using LeakChecker in standalone mode still need to use -DLEAK_CHECKER. This is now documented in the header. The hacky targets used for testing leak checking have been removed in favor of testing in AddressSanitizer mode on Kokoro. Fixes #885 Fixes #1153 PiperOrigin-RevId: 441203393 Change-Id: Ibe64ef6b104bcaf31839ff7184e558cc86abdd1c -- 5c70a23aa83b8152ab95d2cf21662fc63c80ef7d by Abseil Team : Add a benchmark for stacktrace PiperOrigin-RevId: 441196473 Change-Id: I4c9aa2e797aa2cae09abfaaee3abe5c09eb62fc4 -- 50b406052273b9d5bad04a7860a96e4d5d956c02 by Abseil Team : Internal change. PiperOrigin-RevId: 441114481 Change-Id: I667af7a50d5631ca91289dd24c91ba90233e0184 -- 568b4eaac120b420bce5290179d407d2b57d5bae by Dino Radakovic : Internal change PiperOrigin-RevId: 440894155 Change-Id: Ia587ffc65a8321126585fb363b7c0ca8cc2a0da2 -- d53948eace4f3a10ac5a6c1496dc51b81adc412c by Abseil Team : Explicitly give internal linkage to symbols which are not used outside of their translation units. PiperOrigin-RevId: 440424519 Change-Id: I531c5e229d443375483b7550a34f48042589a99b GitOrigin-RevId: f4c7e510922668c68be4aa79a00867c3d3ca9f95 --- CMake/AbseilDll.cmake | 3 +- absl/base/config.h | 17 ++++ absl/base/internal/sysinfo.cc | 1 - absl/copts/AbseilConfigureCopts.cmake | 10 -- absl/debugging/BUILD.bazel | 113 ++++++--------------- absl/debugging/CMakeLists.txt | 72 +------------ absl/debugging/leak_check.cc | 44 ++++---- absl/debugging/leak_check.h | 19 +++- absl/debugging/leak_check_disable.cc | 20 ---- absl/debugging/leak_check_test.cc | 17 ++-- absl/debugging/stacktrace_benchmark.cc | 55 ++++++++++ absl/flags/flag_test.cc | 6 +- absl/flags/internal/usage_test.cc | 6 +- absl/hash/internal/city_test.cc | 2 + absl/profiling/internal/exponential_biased_test.cc | 2 + absl/strings/str_format_test.cc | 2 + absl/synchronization/BUILD.bazel | 6 +- absl/time/internal/cctz/BUILD.bazel | 6 +- 18 files changed, 178 insertions(+), 223 deletions(-) delete mode 100644 absl/debugging/leak_check_disable.cc create mode 100644 absl/debugging/stacktrace_benchmark.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 518c469b..4f7a287c 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -90,7 +90,6 @@ set(ABSL_INTERNAL_DLL_FILES "debugging/failure_signal_handler.cc" "debugging/failure_signal_handler.h" "debugging/leak_check.h" - "debugging/leak_check_disable.cc" "debugging/stacktrace.cc" "debugging/stacktrace.h" "debugging/symbolize.cc" @@ -345,6 +344,7 @@ set(ABSL_INTERNAL_DLL_FILES "types/internal/span.h" "types/variant.h" "utility/utility.h" + "debugging/leak_check.cc" ) set(ABSL_INTERNAL_DLL_TARGETS @@ -355,7 +355,6 @@ set(ABSL_INTERNAL_DLL_TARGETS "debugging_internal" "demangle_internal" "leak_check" - "leak_check_disable" "stack_consumption" "debugging" "hash" diff --git a/absl/base/config.h b/absl/base/config.h index 429dd353..3f5ace3f 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -797,10 +797,27 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // ABSL_HAVE_LEAK_SANITIZER // // LeakSanitizer (or lsan) is a detector of memory leaks. +// https://clang.llvm.org/docs/LeakSanitizer.html +// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// The macro ABSL_HAVE_LEAK_SANITIZER can be used to detect at compile-time +// whether the LeakSanitizer is potentially available. However, just because the +// LeakSanitizer is available does not mean it is active. Use the +// always-available run-time interface in //absl/debugging/leak_check.h for +// interacting with LeakSanitizer. #ifdef ABSL_HAVE_LEAK_SANITIZER #error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set." +#elif defined(LEAK_SANITIZER) +// GCC provides no method for detecting the presense of the standalone +// LeakSanitizer (-fsanitize=leak), so GCC users of -fsanitize=leak should also +// use -DLEAK_SANITIZER. +#define ABSL_HAVE_LEAK_SANITIZER 1 +// Clang standalone LeakSanitizer (-fsanitize=leak) #elif ABSL_HAVE_FEATURE(leak_sanitizer) #define ABSL_HAVE_LEAK_SANITIZER 1 +#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) +// GCC or Clang using the LeakSanitizer integrated into AddressSanitizer. +#define ABSL_HAVE_LEAK_SANITIZER 1 #endif // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index a7cfb461..c8366df1 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -124,7 +124,6 @@ int Win32NumCPUs() { } // namespace - static int GetNumCPUs() { #if defined(__myriad2__) return 1; diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake index 15d6c895..73435e99 100644 --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -1,8 +1,6 @@ # See absl/copts/copts.py and absl/copts/generate_copts.py include(GENERATED_AbseilCopts) -set(ABSL_LSAN_LINKOPTS "") -set(ABSL_HAVE_LSAN OFF) set(ABSL_DEFAULT_LINKOPTS "") if (BUILD_SHARED_LIBS AND MSVC) @@ -85,14 +83,6 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # MATCHES so we get both Clang an else() set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}") - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - # AppleClang doesn't have lsan - # https://developer.apple.com/documentation/code_diagnostics - if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) - set(ABSL_LSAN_LINKOPTS "-fsanitize=leak") - set(ABSL_HAVE_LSAN ON) - endif() - endif() endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}") diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index 3c4ea8dc..7f5c1cad 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -225,6 +225,7 @@ cc_library( name = "leak_check", srcs = ["leak_check.cc"], hdrs = ["leak_check.h"], + copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", @@ -232,98 +233,33 @@ cc_library( ], ) -# Adding a dependency to leak_check_disable will disable -# sanitizer leak checking (asan/lsan) in a test without -# the need to mess around with build features. -cc_library( - name = "leak_check_disable", - srcs = ["leak_check_disable.cc"], - linkopts = ABSL_DEFAULT_LINKOPTS, - linkstatic = 1, - deps = ["//absl/base:config"], - alwayslink = 1, -) - -# These targets exists for use in tests only, explicitly configuring the -# LEAK_SANITIZER macro. It must be linked with -fsanitize=leak for lsan. -ABSL_LSAN_LINKOPTS = select({ - "//absl:clang_compiler": ["-fsanitize=leak"], - "//conditions:default": [], -}) - -cc_library( - name = "leak_check_api_enabled_for_testing", - testonly = 1, - srcs = ["leak_check.cc"], - hdrs = ["leak_check.h"], - copts = select({ - "//absl:clang_compiler": ["-DLEAK_SANITIZER"], - "//conditions:default": [], - }), - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], - deps = [ - "//absl/base:config", - "//absl/base:core_headers", - ], -) - -cc_library( - name = "leak_check_api_disabled_for_testing", - testonly = 1, - srcs = ["leak_check.cc"], - hdrs = ["leak_check.h"], - copts = ["-ULEAK_SANITIZER"], - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], - deps = [ - "//absl/base:config", - "//absl/base:core_headers", - ], -) - cc_test( name = "leak_check_test", srcs = ["leak_check_test.cc"], - copts = select({ - "//absl:clang_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"], - "//conditions:default": [], - }), - linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS, - tags = ["notsan"], - deps = [ - ":leak_check_api_enabled_for_testing", - "//absl/base", - "@com_google_googletest//:gtest_main", - ], -) - -cc_test( - name = "leak_check_no_lsan_test", - srcs = ["leak_check_test.cc"], - copts = ["-UABSL_EXPECT_LEAK_SANITIZER"], + copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["noasan"], + tags = ["notsan"], deps = [ - ":leak_check_api_disabled_for_testing", - "//absl/base", # for raw_logging + ":leak_check", + "//absl/base:config", + "//absl/base:raw_logging_internal", "@com_google_googletest//:gtest_main", ], ) -# Test that leak checking is skipped when lsan is enabled but -# ":leak_check_disable" is linked in. -# -# This test should fail in the absence of a dependency on ":leak_check_disable" -cc_test( - name = "disabled_leak_check_test", +# Binary that leaks memory and expects to fail on exit. This isn't a +# test that expected to pass on its own; it exists to be called by a +# script that checks exit status and output. +# TODO(absl-team): Write a test to run this with a script that +# verifies that it correctly fails. +cc_binary( + name = "leak_check_fail_test_binary", srcs = ["leak_check_fail_test.cc"], - linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS, - tags = ["notsan"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":leak_check_api_enabled_for_testing", - ":leak_check_disable", - "//absl/base", + ":leak_check", + "//absl/base:raw_logging_internal", "@com_google_googletest//:gtest_main", ], ) @@ -356,3 +292,18 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_binary( + name = "stacktrace_benchmark", + testonly = 1, + srcs = ["stacktrace_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + deps = [ + ":stacktrace", + "//absl/base:config", + "//absl/base:core_headers", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 5850bdd0..7507ff3b 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -218,42 +218,6 @@ absl_cc_library( PUBLIC ) -absl_cc_library( - NAME - leak_check_disable - SRCS - "leak_check_disable.cc" - COPTS - ${ABSL_DEFAULT_COPTS} - PUBLIC -) - -absl_cc_library( - NAME - leak_check_api_enabled_for_testing - HDRS - "leak_check.h" - SRCS - "leak_check.cc" - COPTS - ${ABSL_DEFAULT_COPTS} - $<$:-DLEAK_SANITIZER> - TESTONLY -) - -absl_cc_library( - NAME - leak_check_api_disabled_for_testing - HDRS - "leak_check.h" - SRCS - "leak_check.cc" - COPTS - ${ABSL_DEFAULT_COPTS} - "-ULEAK_SANITIZER" - TESTONLY -) - absl_cc_test( NAME leak_check_test @@ -261,43 +225,11 @@ absl_cc_test( "leak_check_test.cc" COPTS ${ABSL_TEST_COPTS} - "$<$:-DABSL_EXPECT_LEAK_SANITIZER>" LINKOPTS - "${ABSL_LSAN_LINKOPTS}" - DEPS - absl::leak_check_api_enabled_for_testing - absl::base - GTest::gmock_main -) - -absl_cc_test( - NAME - leak_check_no_lsan_test - SRCS - "leak_check_test.cc" - COPTS - ${ABSL_TEST_COPTS} - "-UABSL_EXPECT_LEAK_SANITIZER" - DEPS - absl::leak_check_api_disabled_for_testing - absl::base - GTest::gmock_main -) - -absl_cc_test( - NAME - disabled_leak_check_test - SRCS - "leak_check_fail_test.cc" - COPTS - ${ABSL_TEST_COPTS} - LINKOPTS - "${ABSL_LSAN_LINKOPTS}" + ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::leak_check_api_enabled_for_testing - absl::leak_check_disable + absl::leak_check absl::base - absl::raw_logging_internal GTest::gmock_main ) diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc index 764ca0ad..195e82bf 100644 --- a/absl/debugging/leak_check.cc +++ b/absl/debugging/leak_check.cc @@ -11,29 +11,19 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +// // Wrappers around lsan_interface functions. -// When lsan is not linked in, these functions are not available, -// therefore Abseil code which depends on these functions is conditioned on the -// definition of LEAK_SANITIZER. -#include "absl/base/attributes.h" -#include "absl/debugging/leak_check.h" +// +// These are always-available run-time functions manipulating the LeakSanitizer, +// even when the lsan_interface (and LeakSanitizer) is not available. When +// LeakSanitizer is not linked in, these functions become no-op stubs. -#ifndef LEAK_SANITIZER +#include "absl/debugging/leak_check.h" -namespace absl { -ABSL_NAMESPACE_BEGIN -bool HaveLeakSanitizer() { return false; } -bool LeakCheckerIsActive() { return false; } -void DoIgnoreLeak(const void*) { } -void RegisterLivePointers(const void*, size_t) { } -void UnRegisterLivePointers(const void*, size_t) { } -LeakCheckDisabler::LeakCheckDisabler() { } -LeakCheckDisabler::~LeakCheckDisabler() { } -ABSL_NAMESPACE_END -} // namespace absl +#include "absl/base/attributes.h" +#include "absl/base/config.h" -#else +#if defined(ABSL_HAVE_LEAK_SANITIZER) #include @@ -66,4 +56,18 @@ LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); } ABSL_NAMESPACE_END } // namespace absl -#endif // LEAK_SANITIZER +#else // defined(ABSL_HAVE_LEAK_SANITIZER) + +namespace absl { +ABSL_NAMESPACE_BEGIN +bool HaveLeakSanitizer() { return false; } +bool LeakCheckerIsActive() { return false; } +void DoIgnoreLeak(const void*) { } +void RegisterLivePointers(const void*, size_t) { } +void UnRegisterLivePointers(const void*, size_t) { } +LeakCheckDisabler::LeakCheckDisabler() { } +LeakCheckDisabler::~LeakCheckDisabler() { } +ABSL_NAMESPACE_END +} // namespace absl + +#endif // defined(ABSL_HAVE_LEAK_SANITIZER) diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h index 5fc2b052..eff162f6 100644 --- a/absl/debugging/leak_check.h +++ b/absl/debugging/leak_check.h @@ -24,7 +24,24 @@ // Note: this leak checking API is not yet supported in MSVC. // Leak checking is enabled by default in all ASan builds. // -// See https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// https://clang.llvm.org/docs/LeakSanitizer.html +// https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer +// +// GCC and Clang both automatically enable LeakSanitizer when AddressSanitizer +// is enabled. To use the mode, simply pass `-fsanitize=address` to both the +// compiler and linker. An example Bazel command could be +// +// $ bazel test --copt=-fsanitize=address --linkopt=-fsanitize=address ... +// +// GCC and Clang auto support a standalone LeakSanitizer mode (a mode which does +// not also use AddressSanitizer). To use the mode, simply pass +// `-fsanitize=leak` to both the compiler and linker. Since GCC does not +// currently provide a way of detecting this mode at compile-time, GCC users +// must also pass -DLEAK_SANIITIZER to the compiler. An example Bazel command +// could be +// +// $ bazel test --copt=-DLEAK_SANITIZER --copt=-fsanitize=leak +// --linkopt=-fsanitize=leak ... // // ----------------------------------------------------------------------------- #ifndef ABSL_DEBUGGING_LEAK_CHECK_H_ diff --git a/absl/debugging/leak_check_disable.cc b/absl/debugging/leak_check_disable.cc deleted file mode 100644 index 924d6e3d..00000000 --- a/absl/debugging/leak_check_disable.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Disable LeakSanitizer when this file is linked in. -// This function overrides __lsan_is_turned_off from sanitizer/lsan_interface.h -extern "C" int __lsan_is_turned_off(); -extern "C" int __lsan_is_turned_off() { - return 1; -} diff --git a/absl/debugging/leak_check_test.cc b/absl/debugging/leak_check_test.cc index 9fcfc8e5..6a42e31b 100644 --- a/absl/debugging/leak_check_test.cc +++ b/absl/debugging/leak_check_test.cc @@ -15,27 +15,24 @@ #include #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/debugging/leak_check.h" namespace { -TEST(LeakCheckTest, DetectLeakSanitizer) { -#ifdef ABSL_EXPECT_LEAK_SANITIZER - EXPECT_TRUE(absl::HaveLeakSanitizer()); - EXPECT_TRUE(absl::LeakCheckerIsActive()); -#else - EXPECT_FALSE(absl::HaveLeakSanitizer()); - EXPECT_FALSE(absl::LeakCheckerIsActive()); -#endif -} - TEST(LeakCheckTest, IgnoreLeakSuppressesLeakedMemoryErrors) { + if (!absl::LeakCheckerIsActive()) { + GTEST_SKIP() << "LeakChecker is not active"; + } auto foo = absl::IgnoreLeak(new std::string("some ignored leaked string")); ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str()); } TEST(LeakCheckTest, LeakCheckDisablerIgnoresLeak) { + if (!absl::LeakCheckerIsActive()) { + GTEST_SKIP() << "LeakChecker is not active"; + } absl::LeakCheckDisabler disabler; auto foo = new std::string("some string leaked while checks are disabled"); ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str()); diff --git a/absl/debugging/stacktrace_benchmark.cc b/absl/debugging/stacktrace_benchmark.cc new file mode 100644 index 00000000..9360bafe --- /dev/null +++ b/absl/debugging/stacktrace_benchmark.cc @@ -0,0 +1,55 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/debugging/stacktrace.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +static constexpr int kMaxStackDepth = 100; +static constexpr int kCacheSize = (1 << 16); +void* pcs[kMaxStackDepth]; + +ABSL_ATTRIBUTE_NOINLINE void func(benchmark::State& state, int x, int depth) { + if (x <= 0) { + // Touch a significant amount of memory so that the stack is likely to be + // not cached in the L1 cache. + state.PauseTiming(); + int* arr = new int[kCacheSize]; + for (int i = 0; i < kCacheSize; ++i) benchmark::DoNotOptimize(arr[i] = 100); + delete[] arr; + state.ResumeTiming(); + benchmark::DoNotOptimize(absl::GetStackTrace(pcs, depth, 0)); + return; + } + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); + func(state, --x, depth); +} + +void BM_GetStackTrace(benchmark::State& state) { + int depth = state.range(0); + for (auto s : state) { + func(state, depth, depth); + } +} + +BENCHMARK(BM_GetStackTrace)->DenseRange(10, kMaxStackDepth, 10); +} // namespace +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 05ec79e8..845b4eba 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -854,7 +854,9 @@ ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr"); ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); -bool initializaion_order_fiasco_test = [] { +namespace { + +bool initialization_order_fiasco_test ABSL_ATTRIBUTE_UNUSED = [] { // Iterate over all the flags during static initialization. // This should not trigger ASan's initialization-order-fiasco. auto* handle1 = absl::FindCommandLineFlag("flag_on_separate_file"); @@ -865,8 +867,6 @@ bool initializaion_order_fiasco_test = [] { return true; }(); -namespace { - TEST_F(FlagTest, TestRetiredFlagRegistration) { auto* handle = absl::FindCommandLineFlag("old_bool_flag"); EXPECT_TRUE(handle->IsOfType()); diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 044d71c8..6a65a1a3 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -47,8 +47,10 @@ struct UDT { UDT(const UDT&) = default; UDT& operator=(const UDT&) = default; }; -bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } -std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; } +static bool AbslParseFlag(absl::string_view, UDT*, std::string*) { + return true; +} +static std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; } ABSL_FLAG(UDT, usage_reporting_test_flag_05, {}, "usage_reporting_test_flag_05 help message"); diff --git a/absl/hash/internal/city_test.cc b/absl/hash/internal/city_test.cc index 251d381d..1bbf02e0 100644 --- a/absl/hash/internal/city_test.cc +++ b/absl/hash/internal/city_test.cc @@ -22,6 +22,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace hash_internal { +namespace { static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; static const uint64_t kSeed0 = 1234567; @@ -590,6 +591,7 @@ TEST(CityHashTest, Unchanging) { TestUnchanging(testdata[i], 0, kDataSize); } +} // namespace } // namespace hash_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/profiling/internal/exponential_biased_test.cc b/absl/profiling/internal/exponential_biased_test.cc index 5675001d..6a6c317e 100644 --- a/absl/profiling/internal/exponential_biased_test.cc +++ b/absl/profiling/internal/exponential_biased_test.cc @@ -29,6 +29,7 @@ using ::testing::Ge; namespace absl { ABSL_NAMESPACE_BEGIN namespace profiling_internal { +namespace { MATCHER_P2(IsBetween, a, b, absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a, @@ -194,6 +195,7 @@ TEST(ExponentialBiasedTest, InitializationModes) { EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0)); } +} // namespace } // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index c60027ad..804e6c22 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc @@ -719,6 +719,7 @@ TEST_F(FormatWrapperTest, ParsedFormat) { ABSL_NAMESPACE_END } // namespace absl +namespace { using FormatExtensionTest = ::testing::Test; struct Point { @@ -750,6 +751,7 @@ TEST_F(FormatExtensionTest, AbslFormatConvertExample) { // FormatUntyped will return false for bad character. EXPECT_FALSE(absl::FormatUntyped(&actual, f1, {absl::FormatArg(p)})); } +} // namespace // Some codegen thunks that we can use to easily dump the generated assembly for // different StrFormat calls. diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index a0492c55..4e175892 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -97,8 +97,8 @@ cc_library( deps = [ ":graphcycles_internal", ":kernel_timeout_internal", - "//absl/base", "//absl/base:atomic_hook", + "//absl/base", "//absl/base:base_internal", "//absl/base:config", "//absl/base:core_headers", @@ -108,7 +108,9 @@ cc_library( "//absl/debugging:stacktrace", "//absl/debugging:symbolize", "//absl/time", - ], + ] + select({ + "//conditions:default": [], + }), ) cc_test( diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index bdc2e309..f0e7ae13 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -85,7 +85,11 @@ cc_library( deps = [ ":civil_time", "//absl/base:config", - ], + ] + select( + { + "//conditions:default": [], + }, + ), ) ### tests -- cgit v1.2.3 From 8c6e53ef3adb1227fffa442c50349dab134a54bc Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 14 Apr 2022 05:41:35 -0700 Subject: Export of internal Abseil changes -- 155545fafefd0a0cef83853f1addea1ecf4a4618 by Derek Mauro : Support ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS on MSVC https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address PiperOrigin-RevId: 441737758 Change-Id: Ida9b185a929ffa22ad2ffdcfbd53076f69bfc036 GitOrigin-RevId: 155545fafefd0a0cef83853f1addea1ecf4a4618 --- absl/base/attributes.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 5721356d..c71038c7 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -213,6 +213,9 @@ // https://gcc.gnu.org/gcc-4.8/changes.html #if ABSL_HAVE_ATTRIBUTE(no_sanitize_address) #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#elif defined(_MSC_VER) && _MSC_VER >= 1928 +// https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address +#define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS #endif -- cgit v1.2.3 From 9f5b49644e1cc67a6e315fa7805bfe0c688def11 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Thu, 14 Apr 2022 13:19:44 -0400 Subject: Reenable unit tests on hppa --- debian/changelog | 1 + debian/control | 2 +- debian/patches/hppa-symbolize.diff | 103 +++++++++++++++++++++ debian/patches/series | 2 + debian/patches/str-format-convert-test-printf.diff | 103 +++++++++++++++++++++ debian/rules | 5 +- 6 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 debian/patches/hppa-symbolize.diff create mode 100644 debian/patches/str-format-convert-test-printf.diff diff --git a/debian/changelog b/debian/changelog index d20492e2..a9ff3c79 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ abseil (0~20210324.2-3) UNRELEASED; urgency=medium * Backport an upstream patch to disable a problematic unit test. (Closes: #1007136) + * Reenable unit tests on hppa. -- Benjamin Barenblat Thu, 31 Mar 2022 14:10:30 -0400 diff --git a/debian/control b/debian/control index dde19493..b31cbce9 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,7 @@ Maintainer: Benjamin Barenblat Build-Depends: cmake (>= 3.5), debhelper-compat (= 12), - googletest (>= 1.10.0.20200926) [!hppa !mipsel !ppc64] + googletest (>= 1.10.0.20200926) [!mipsel !ppc64] Rules-Requires-Root: no Standards-Version: 4.6.0 Section: libs diff --git a/debian/patches/hppa-symbolize.diff b/debian/patches/hppa-symbolize.diff new file mode 100644 index 00000000..8c039df9 --- /dev/null +++ b/debian/patches/hppa-symbolize.diff @@ -0,0 +1,103 @@ +From: Benjamin Barenblat +Subject: Support symbolization on PA-RISC +Forwarded: yes +Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/7f850b3167fb38e6b4a9ce1824e6fabd733b5d62 + +Null out supervisor bits in PA-RISC addresses before symbolizing, and +handle function descriptor tables correctly. + +Change symbolize_test.cc to use 32-bit aligned addresses, allowing that +test to pass on PA-RISC. + +The author works at Google. Upstream applied this patch as Piper +revision 428590564 and exported it to GitHub; the Applied-Upstream URL +above points to the exported commit. + +--- a/absl/debugging/symbolize_elf.inc ++++ b/absl/debugging/symbolize_elf.inc +@@ -319,6 +319,7 @@ + const ptrdiff_t relocation, + char *out, int out_size, + char *tmp_buf, int tmp_buf_size); ++ const char *GetUncachedSymbol(const void *pc); + + enum { + SYMBOL_BUF_SIZE = 3072, +@@ -1329,13 +1330,7 @@ + // they are called here as well. + // To keep stack consumption low, we would like this function to not + // get inlined. +-const char *Symbolizer::GetSymbol(const void *const pc) { +- const char *entry = FindSymbolInCache(pc); +- if (entry != nullptr) { +- return entry; +- } +- symbol_buf_[0] = '\0'; +- ++const char *Symbolizer::GetUncachedSymbol(const void *pc) { + ObjFile *const obj = FindObjFile(pc, 1); + ptrdiff_t relocation = 0; + int fd = -1; +@@ -1423,6 +1418,42 @@ + return InsertSymbolInCache(pc, symbol_buf_); + } + ++const char *Symbolizer::GetSymbol(const void *pc) { ++ const char *entry = FindSymbolInCache(pc); ++ if (entry != nullptr) { ++ return entry; ++ } ++ symbol_buf_[0] = '\0'; ++ ++#ifdef __hppa__ ++ { ++ // In some contexts (e.g., return addresses), PA-RISC uses the lowest two ++ // bits of the address to indicate the privilege level. Clear those bits ++ // before trying to symbolize. ++ const auto pc_bits = reinterpret_cast(pc); ++ const auto address = pc_bits & ~0x3; ++ entry = GetUncachedSymbol(reinterpret_cast(address)); ++ if (entry != nullptr) { ++ return entry; ++ } ++ ++ // In some contexts, PA-RISC also uses bit 1 of the address to indicate that ++ // this is a cross-DSO function pointer. Such function pointers actually ++ // point to a procedure label, a struct whose first 32-bit (pointer) element ++ // actually points to the function text. With no symbol found for this ++ // address so far, try interpreting it as a cross-DSO function pointer and ++ // see how that goes. ++ if (pc_bits & 0x2) { ++ return GetUncachedSymbol(*reinterpret_cast(address)); ++ } ++ ++ return nullptr; ++ } ++#else ++ return GetUncachedSymbol(pc); ++#endif ++} ++ + bool RemoveAllSymbolDecorators(void) { + if (!g_decorators_mu.TryLock()) { + // Someone else is using decorators. Get out. +--- a/absl/debugging/symbolize_test.cc ++++ b/absl/debugging/symbolize_test.cc +@@ -378,12 +378,14 @@ + DummySymbolDecorator, &c_message), + 0); + +- char *address = reinterpret_cast(1); +- EXPECT_STREQ("abc", TrySymbolize(address++)); ++ // Use addresses 4 and 8 here to ensure that we always use valid addresses ++ // even on systems that require instructions to be 32-bit aligned. ++ char *address = reinterpret_cast(4); ++ EXPECT_STREQ("abc", TrySymbolize(address)); + + EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_b)); + +- EXPECT_STREQ("ac", TrySymbolize(address++)); ++ EXPECT_STREQ("ac", TrySymbolize(address + 4)); + + // Cleanup: remove all remaining decorators so other stack traces don't + // get mystery "ac" decoration. diff --git a/debian/patches/series b/debian/patches/series index 6a2a842b..f4bed7f4 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -14,3 +14,5 @@ big-endian-random2.diff big-endian-random3.diff big-endian-random4.diff disable-nominalcpufrequency-test.diff +hppa-symbolize.diff +str-format-convert-test-printf.diff diff --git a/debian/patches/str-format-convert-test-printf.diff b/debian/patches/str-format-convert-test-printf.diff new file mode 100644 index 00000000..1808f2e1 --- /dev/null +++ b/debian/patches/str-format-convert-test-printf.diff @@ -0,0 +1,103 @@ +From: Benjamin Barenblat +Subject: Check printf format strings in str_format_convert_test +Forwarded: yes +Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/9fed77a6fea29b8c8468bd41c6259c7f67163a65 + +Add ABSL_PRINTF_ATTRIBUTE to appropriate functions in +strings/internal/str_format/convert_test. Correct +TypedFormatConvertTest.Char, which was accidentally passing values of +types larger than int to StrPrint. + +The author works at Google. Upstream applied this patch as Piper +revision 439388148 and exported it to GitHub; the Applied-Upstream URL +above points to the exported commit. + +--- a/absl/strings/BUILD.bazel ++++ b/absl/strings/BUILD.bazel +@@ -787,6 +787,7 @@ + deps = [ + ":str_format_internal", + ":strings", ++ "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/types:optional", + "@com_google_googletest//:gtest_main", +--- a/absl/strings/CMakeLists.txt ++++ b/absl/strings/CMakeLists.txt +@@ -492,6 +492,7 @@ + DEPS + absl::strings + absl::str_format_internal ++ absl::core_headers + absl::raw_logging_internal + absl::int128 + gmock_main +--- a/absl/strings/internal/str_format/convert_test.cc ++++ b/absl/strings/internal/str_format/convert_test.cc +@@ -24,6 +24,7 @@ + + #include "gmock/gmock.h" + #include "gtest/gtest.h" ++#include "absl/base/attributes.h" + #include "absl/base/internal/raw_logging.h" + #include "absl/strings/internal/str_format/bind.h" + #include "absl/strings/match.h" +@@ -124,6 +125,7 @@ + delete[] buf; + } + ++void StrAppend(std::string *, const char *, ...) ABSL_PRINTF_ATTRIBUTE(2, 3); + void StrAppend(std::string *out, const char *format, ...) { + va_list ap; + va_start(ap, format); +@@ -131,6 +133,7 @@ + va_end(ap); + } + ++std::string StrPrint(const char *, ...) ABSL_PRINTF_ATTRIBUTE(1, 2); + std::string StrPrint(const char *format, ...) { + va_list ap; + va_start(ap, format); +@@ -452,21 +455,32 @@ + } + + TYPED_TEST_P(TypedFormatConvertTest, Char) { ++ // Pass a bunch of values of type TypeParam to both FormatPack and libc's ++ // vsnprintf("%c", ...) (wrapped in StrPrint) to make sure we get the same ++ // value. + typedef TypeParam T; + using remove_volatile_t = typename std::remove_volatile::type; +- static const T kMin = std::numeric_limits::min(); +- static const T kMax = std::numeric_limits::max(); +- T kVals[] = { +- remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10), +- remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10), +- remove_volatile_t(0), +- kMin + remove_volatile_t(1), kMin, +- kMax - remove_volatile_t(1), kMax ++ std::vector vals = { ++ remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10), // ++ remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10), // ++ remove_volatile_t(0), + }; +- for (const T &c : kVals) { ++ ++ // We'd like to test values near std::numeric_limits::min() and ++ // std::numeric_limits::max(), too, but vsnprintf("%c", ...) can't handle ++ // anything larger than an int. Add in the most extreme values we can without ++ // exceeding that range. ++ static const T kMin = ++ static_cast(std::numeric_limits::min()); ++ static const T kMax = ++ static_cast(std::numeric_limits::max()); ++ vals.insert(vals.end(), {kMin + 1, kMin, kMax - 1, kMax}); ++ ++ for (const T c : vals) { + const FormatArgImpl args[] = {FormatArgImpl(c)}; + UntypedFormatSpecImpl format("%c"); +- EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args))); ++ EXPECT_EQ(StrPrint("%c", static_cast(c)), ++ FormatPack(format, absl::MakeSpan(args))); + } + } + diff --git a/debian/rules b/debian/rules index ef27843d..536ca72a 100755 --- a/debian/rules +++ b/debian/rules @@ -19,9 +19,8 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+bindnow reproducible=+fixfilepath # Unit tests require more than 2 GB of RAM, so disable them on mipsel. # -# Unit tests are not yet passing on hppa or ppc64, so disable them there as -# well. -ifneq ($(filter $(DEB_HOST_ARCH),hppa mipsel ppc64),) +# Unit tests are not yet passing on ppc64, so disable them there as well. +ifneq ($(filter $(DEB_HOST_ARCH),mipsel ppc64),) ABSL_RUN_TESTS=OFF else ABSL_RUN_TESTS=ON -- cgit v1.2.3 From 99c09d4332c1c5e2e6233cc2e72ac10e345cfc6d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Thu, 14 Apr 2022 13:20:37 -0400 Subject: Release for unstable --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index a9ff3c79..296ed36f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,10 +1,10 @@ -abseil (0~20210324.2-3) UNRELEASED; urgency=medium +abseil (0~20210324.2-3) unstable; urgency=medium * Backport an upstream patch to disable a problematic unit test. (Closes: #1007136) * Reenable unit tests on hppa. - -- Benjamin Barenblat Thu, 31 Mar 2022 14:10:30 -0400 + -- Benjamin Barenblat Thu, 14 Apr 2022 13:20:16 -0400 abseil (0~20210324.2-2) unstable; urgency=medium -- cgit v1.2.3 From c27ab06897f330267bed99061ed3e523e2606bf1 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 15 Apr 2022 10:12:39 -0700 Subject: Export of internal Abseil changes -- 3d018c03a34bf273a4b24b3584ed77f0a6d21686 by Abseil Team : Fix a spelling typo (s/boundries/boundaries). PiperOrigin-RevId: 442041877 Change-Id: I608020697d37b85316bb9a0838e4b457659c926c -- 518b8119e51db24ce7fb0fd2fe537ec43825c3e6 by Dino Radakovic : absl/types/internal/variant: Make include guard uppercase https://google.github.io/styleguide/cppguide.html#The__define_Guard PiperOrigin-RevId: 441911692 Change-Id: I9837dd07f20204d8253f20627b0917a34dc21825 -- b91696c38310a7cae8c1ea9e2d479495f5dc3f69 by Greg Falcon : Add an internal-only API to wrap __builtin_prefetch() if available. This private API is intended for future use by the Abseil implementation. Like any internal-namespaced function, it may be changed or removed at any time. PiperOrigin-RevId: 441894616 Change-Id: Iaa48bd4680b373f4a0d5afab0cb35e2a1908595f -- 0f01e8b0551a662e02dff60840c54320f987315f by Derek Mauro : C++20: Use the standard `constinit` keyword for `ABSL_CONST_INIT` when available PiperOrigin-RevId: 441778874 Change-Id: I70c616469752ff23b326b1c615437599f42cc6aa GitOrigin-RevId: 3d018c03a34bf273a4b24b3584ed77f0a6d21686 --- CMake/AbseilDll.cmake | 1 + absl/base/BUILD.bazel | 25 ++++++++ absl/base/CMakeLists.txt | 26 ++++++++ absl/base/attributes.h | 26 ++++++-- absl/base/internal/prefetch.h | 109 ++++++++++++++++++++++++++++++++ absl/base/internal/prefetch_test.cc | 43 +++++++++++++ absl/container/flat_hash_map.h | 2 +- absl/container/flat_hash_set.h | 2 +- absl/container/node_hash_map.h | 2 +- absl/container/node_hash_set.h | 2 +- absl/debugging/internal/vdso_support.cc | 4 +- absl/strings/internal/cord_internal.h | 3 +- absl/strings/internal/escaping.cc | 2 +- absl/strings/numbers.cc | 16 ++--- absl/types/internal/variant.h | 6 +- 15 files changed, 245 insertions(+), 24 deletions(-) create mode 100644 absl/base/internal/prefetch.h create mode 100644 absl/base/internal/prefetch_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 4f7a287c..0b5f0a54 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -26,6 +26,7 @@ set(ABSL_INTERNAL_DLL_FILES "base/internal/low_level_alloc.h" "base/internal/low_level_scheduling.h" "base/internal/per_thread_tls.h" + "base/internal/prefetch.h" "base/internal/pretty_function.h" "base/internal/raw_logging.cc" "base/internal/raw_logging.h" diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 6f2f09b9..49e3a95f 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -704,6 +704,31 @@ cc_test( ], ) +cc_library( + name = "prefetch", + hdrs = ["internal/prefetch.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":config", + ], +) + +cc_test( + name = "prefetch_test", + size = "small", + srcs = ["internal/prefetch_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":prefetch", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "unique_small_name_test", size = "small", diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 7e600f0b..0e1e0413 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -642,6 +642,32 @@ absl_cc_test( GTest::gtest_main ) +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + prefetch + HDRS + "internal/prefetch.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config +) + +absl_cc_test( + NAME + prefetch_test + SRCS + "internal/prefetch_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::prefetch + GTest::gtest_main +) + absl_cc_test( NAME optimization_test diff --git a/absl/base/attributes.h b/absl/base/attributes.h index c71038c7..e4e7a3d8 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -682,9 +682,18 @@ // not compile (on supported platforms) unless the variable has a constant // initializer. This is useful for variables with static and thread storage // duration, because it guarantees that they will not suffer from the so-called -// "static init order fiasco". Prefer to put this attribute on the most visible -// declaration of the variable, if there's more than one, because code that -// accesses the variable can then use the attribute for optimization. +// "static init order fiasco". +// +// This attribute must be placed on the initializing declaration of the +// variable. Some compilers will give a -Wmissing-constinit warning when this +// attribute is placed on some other declaration but missing from the +// initializing declaration. +// +// In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can +// also be used in a non-initializing declaration to tell the compiler that a +// variable is already initialized, reducing overhead that would otherwise be +// incurred by a hidden guard variable. Thus annotating all declarations with +// this attribute is recommended to potentially enhance optimization. // // Example: // @@ -693,14 +702,19 @@ // ABSL_CONST_INIT static MyType my_var; // }; // -// MyType MyClass::my_var = MakeMyType(...); +// ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); +// +// For code or headers that are assured to only build with C++20 and up, prefer +// just using the standard `constinit` keyword directly over this macro. // // Note that this attribute is redundant if the variable is declared constexpr. -#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +#define ABSL_CONST_INIT constinit +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) #define ABSL_CONST_INIT [[clang::require_constant_initialization]] #else #define ABSL_CONST_INIT -#endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +#endif // ABSL_ATTRIBUTE_PURE_FUNCTION // diff --git a/absl/base/internal/prefetch.h b/absl/base/internal/prefetch.h new file mode 100644 index 00000000..a71b3897 --- /dev/null +++ b/absl/base/internal/prefetch.h @@ -0,0 +1,109 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_PREFETCH_H_ +#define ABSL_BASE_INTERNAL_PREFETCH_H_ + +#include "absl/base/config.h" + +// Compatibility wrappers around __builtin_prefetch, to prefetch data +// for read if supported by the toolchain. + +// Move data into the cache before it is read, or "prefetch" it. +// +// The value of `addr` is the address of the memory to prefetch. If +// the target and compiler support it, data prefetch instructions are +// generated. If the prefetch is done some time before the memory is +// read, it may be in the cache by the time the read occurs. +// +// The function names specify the temporal locality heuristic applied, +// using the names of Intel prefetch instructions: +// +// T0 - high degree of temporal locality; data should be left in as +// many levels of the cache possible +// T1 - moderate degree of temporal locality +// T2 - low degree of temporal locality +// Nta - no temporal locality, data need not be left in the cache +// after the read +// +// Incorrect or gratuitous use of these functions can degrade +// performance, so use them only when representative benchmarks show +// an improvement. +// +// Example usage: +// +// absl::base_internal::PrefetchT0(addr); +// +// Currently, the different prefetch calls behave on some Intel +// architectures as follows: +// +// SNB..SKL SKX +// PrefetchT0() L1/L2/L3 L1/L2 +// PrefetchT1() L2/L3 L2 +// PrefetchT2() L2/L3 L2 +// PrefetchNta() L1/--/L3 L1* +// +// * On SKX PrefetchNta() will bring the line into L1 but will evict +// from L3 cache. This might result in surprising behavior. +// +// SNB = Sandy Bridge, SKL = Skylake, SKX = Skylake Xeon. +// +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +void PrefetchT0(const void* addr); +void PrefetchT1(const void* addr); +void PrefetchT2(const void* addr); +void PrefetchNta(const void* addr); + +// Implementation details follow. + +#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__) + +// See __builtin_prefetch: +// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html. +// +// These functions speculatively load for read only. This is +// safe for all currently supported platforms. However, prefetch for +// store may have problems depending on the target platform. +// +inline void PrefetchT0(const void* addr) { + // Note: this uses prefetcht0 on Intel. + __builtin_prefetch(addr, 0, 3); +} +inline void PrefetchT1(const void* addr) { + // Note: this uses prefetcht1 on Intel. + __builtin_prefetch(addr, 0, 2); +} +inline void PrefetchT2(const void* addr) { + // Note: this uses prefetcht2 on Intel. + __builtin_prefetch(addr, 0, 1); +} +inline void PrefetchNta(const void* addr) { + // Note: this uses prefetchtnta on Intel. + __builtin_prefetch(addr, 0, 0); +} +#else +inline void PrefetchT0(const void*) {} +inline void PrefetchT1(const void*) {} +inline void PrefetchT2(const void*) {} +inline void PrefetchNta(const void*) {} +#endif + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_PREFETCH_H_ diff --git a/absl/base/internal/prefetch_test.cc b/absl/base/internal/prefetch_test.cc new file mode 100644 index 00000000..7c1dae46 --- /dev/null +++ b/absl/base/internal/prefetch_test.cc @@ -0,0 +1,43 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/prefetch.h" + +#include "gtest/gtest.h" + +namespace { + +int number = 42; + +TEST(Prefetch, TemporalLocalityNone) { + absl::base_internal::PrefetchNta(&number); + EXPECT_EQ(number, 42); +} + +TEST(Prefetch, TemporalLocalityLow) { + absl::base_internal::PrefetchT2(&number); + EXPECT_EQ(number, 42); +} + +TEST(Prefetch, TemporalLocalityMedium) { + absl::base_internal::PrefetchT1(&number); + EXPECT_EQ(number, 42); +} + +TEST(Prefetch, TemporalLocalityHigh) { + absl::base_internal::PrefetchT0(&number); + EXPECT_EQ(number, 42); +} + +} // namespace diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index 13779eda..cbb24693 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -76,7 +76,7 @@ struct FlatHashMapPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // -// Using `absl::flat_hash_map` at interface boundries in dynamically loaded +// Using `absl::flat_hash_map` at interface boundaries in dynamically loaded // libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may // be randomized across dynamically loaded libraries. // diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index 304c2abb..4938c703 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -72,7 +72,7 @@ struct FlatHashSetPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // -// Using `absl::flat_hash_set` at interface boundries in dynamically loaded +// Using `absl::flat_hash_set` at interface boundaries in dynamically loaded // libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may // be randomized across dynamically loaded libraries. // diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index f2175d16..c91cae69 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -78,7 +78,7 @@ class NodeHashMapPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // -// Using `absl::node_hash_map` at interface boundries in dynamically loaded +// Using `absl::node_hash_map` at interface boundaries in dynamically loaded // libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may // be randomized across dynamically loaded libraries. // diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index a9ff7e1b..f2cc70c3 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -74,7 +74,7 @@ struct NodeHashSetPolicy; // absl/hash/hash.h for information on extending Abseil hashing to user-defined // types. // -// Using `absl::node_hash_set` at interface boundries in dynamically loaded +// Using `absl::node_hash_set` at interface boundaries in dynamically loaded // libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may // be randomized across dynamically loaded libraries. // diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index e63ac4a3..40eb055f 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -69,7 +69,9 @@ ABSL_CONST_INIT std::atomic VDSOSupport::vdso_base_( debugging_internal::ElfMemImage::kInvalidBase); -std::atomic VDSOSupport::getcpu_fn_(&InitAndGetCPU); +ABSL_CONST_INIT std::atomic VDSOSupport::getcpu_fn_( + &InitAndGetCPU); + VDSOSupport::VDSOSupport() // If vdso_base_ is still set to kInvalidBase, we got here // before VDSOSupport::Init has been called. Call it now. diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 5ca5e589..6ae418e7 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -411,7 +411,8 @@ struct ConstInitExternalStorage { }; template -CordRepExternal ConstInitExternalStorage::value(Str::value); +ABSL_CONST_INIT CordRepExternal + ConstInitExternalStorage::value(Str::value); enum { kMaxInline = 15, diff --git a/absl/strings/internal/escaping.cc b/absl/strings/internal/escaping.cc index 7f87e124..cfea0961 100644 --- a/absl/strings/internal/escaping.cc +++ b/absl/strings/internal/escaping.cc @@ -21,7 +21,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace strings_internal { -const char kBase64Chars[] = +ABSL_CONST_INIT const char kBase64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) { diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index cbd84c91..e798fc69 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc @@ -757,8 +757,8 @@ struct LookupTables { // // uint128& operator/=(uint128) is not constexpr, so hardcode the resulting // array to avoid a static initializer. -template<> -const uint128 LookupTables::kVmaxOverBase[] = { +template <> +ABSL_CONST_INIT const uint128 LookupTables::kVmaxOverBase[] = { 0, 0, MakeUint128(9223372036854775807u, 18446744073709551615u), @@ -809,8 +809,8 @@ const uint128 LookupTables::kVmaxOverBase[] = { // // int128& operator/=(int128) is not constexpr, so hardcode the resulting array // to avoid a static initializer. -template<> -const int128 LookupTables::kVmaxOverBase[] = { +template <> +ABSL_CONST_INIT const int128 LookupTables::kVmaxOverBase[] = { 0, 0, MakeInt128(4611686018427387903, 18446744073709551615u), @@ -862,8 +862,8 @@ const int128 LookupTables::kVmaxOverBase[] = { // // int128& operator/=(int128) is not constexpr, so hardcode the resulting array // to avoid a static initializer. -template<> -const int128 LookupTables::kVminOverBase[] = { +template <> +ABSL_CONST_INIT const int128 LookupTables::kVminOverBase[] = { 0, 0, MakeInt128(-4611686018427387904, 0u), @@ -904,11 +904,11 @@ const int128 LookupTables::kVminOverBase[] = { }; template -const IntType LookupTables::kVmaxOverBase[] = +ABSL_CONST_INIT const IntType LookupTables::kVmaxOverBase[] = X_OVER_BASE_INITIALIZER(std::numeric_limits::max()); template -const IntType LookupTables::kVminOverBase[] = +ABSL_CONST_INIT const IntType LookupTables::kVminOverBase[] = X_OVER_BASE_INITIALIZER(std::numeric_limits::min()); #undef X_OVER_BASE_INITIALIZER diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h index 772008c7..7c402ece 100644 --- a/absl/types/internal/variant.h +++ b/absl/types/internal/variant.h @@ -16,8 +16,8 @@ // separate file to avoid cluttering the top of the API header with // implementation details. -#ifndef ABSL_TYPES_variant_internal_H_ -#define ABSL_TYPES_variant_internal_H_ +#ifndef ABSL_TYPES_VARIANT_INTERNAL_H_ +#define ABSL_TYPES_VARIANT_INTERNAL_H_ #include #include @@ -1643,4 +1643,4 @@ ABSL_NAMESPACE_END } // namespace absl #endif // !defined(ABSL_USES_STD_VARIANT) -#endif // ABSL_TYPES_variant_internal_H_ +#endif // ABSL_TYPES_VARIANT_INTERNAL_H_ -- cgit v1.2.3 From 3dccef2a91243460364312d3e1100ff1d573fb1d Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 19 Apr 2022 13:56:51 -0700 Subject: Export of internal Abseil changes -- 6457ad659de86ce4cae1e9f7cb03a701c6c2851e by Abseil Team : Introduced ErrnoToStatusCode and ErrnoToStatus to abseil. PiperOrigin-RevId: 442903450 Change-Id: I9c062b34a3811216f43eef56e631eada3b4e3e84 GitOrigin-RevId: 6457ad659de86ce4cae1e9f7cb03a701c6c2851e --- absl/status/BUILD.bazel | 2 +- absl/status/CMakeLists.txt | 11 ++-- absl/status/status.cc | 156 +++++++++++++++++++++++++++++++++++++++++++++ absl/status/status.h | 13 ++++ absl/status/status_test.cc | 20 ++++++ 5 files changed, 196 insertions(+), 6 deletions(-) diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index bae5156f..b334f31d 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -41,9 +41,9 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ "//absl/base:atomic_hook", - "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", + "//absl/base:strerror", "//absl/container:inlined_vector", "//absl/debugging:stacktrace", "//absl/debugging:symbolize", diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index f107c85b..15db36af 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt @@ -28,16 +28,17 @@ absl_cc_library( DEPS absl::atomic_hook absl::config + absl::cord absl::core_headers absl::function_ref - absl::raw_logging_internal absl::inlined_vector + absl::optional + absl::raw_logging_internal absl::stacktrace - absl::symbolize - absl::strings - absl::cord absl::str_format - absl::optional + absl::strerror + absl::strings + absl::symbolize PUBLIC ) diff --git a/absl/status/status.cc b/absl/status/status.cc index 6b316ac6..fc5a1425 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -13,9 +13,12 @@ // limitations under the License. #include "absl/status/status.h" +#include + #include #include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/strerror.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" #include "absl/status/status_payload_printer.h" @@ -443,5 +446,158 @@ bool IsUnknown(const Status& status) { return status.code() == absl::StatusCode::kUnknown; } +StatusCode ErrnoToStatusCode(int error_number) { + switch (error_number) { + case 0: + return StatusCode::kOk; + case EINVAL: // Invalid argument + case ENAMETOOLONG: // Filename too long + case E2BIG: // Argument list too long + case EDESTADDRREQ: // Destination address required + case EDOM: // Mathematics argument out of domain of function + case EFAULT: // Bad address + case EILSEQ: // Illegal byte sequence + case ENOPROTOOPT: // Protocol not available + case ENOSTR: // Not a STREAM + case ENOTSOCK: // Not a socket + case ENOTTY: // Inappropriate I/O control operation + case EPROTOTYPE: // Protocol wrong type for socket + case ESPIPE: // Invalid seek + return StatusCode::kInvalidArgument; + case ETIMEDOUT: // Connection timed out + case ETIME: // Timer expired + return StatusCode::kDeadlineExceeded; + case ENODEV: // No such device + case ENOENT: // No such file or directory +#ifdef ENOMEDIUM + case ENOMEDIUM: // No medium found +#endif + case ENXIO: // No such device or address + case ESRCH: // No such process + return StatusCode::kNotFound; + case EEXIST: // File exists + case EADDRNOTAVAIL: // Address not available + case EALREADY: // Connection already in progress +#ifdef ENOTUNIQ + case ENOTUNIQ: // Name not unique on network +#endif + return StatusCode::kAlreadyExists; + case EPERM: // Operation not permitted + case EACCES: // Permission denied +#ifdef ENOKEY + case ENOKEY: // Required key not available +#endif + case EROFS: // Read only file system + return StatusCode::kPermissionDenied; + case ENOTEMPTY: // Directory not empty + case EISDIR: // Is a directory + case ENOTDIR: // Not a directory + case EADDRINUSE: // Address already in use + case EBADF: // Invalid file descriptor +#ifdef EBADFD + case EBADFD: // File descriptor in bad state +#endif + case EBUSY: // Device or resource busy + case ECHILD: // No child processes + case EISCONN: // Socket is connected +#ifdef EISNAM + case EISNAM: // Is a named type file +#endif +#ifdef ENOTBLK + case ENOTBLK: // Block device required +#endif + case ENOTCONN: // The socket is not connected + case EPIPE: // Broken pipe +#ifdef ESHUTDOWN + case ESHUTDOWN: // Cannot send after transport endpoint shutdown +#endif + case ETXTBSY: // Text file busy +#ifdef EUNATCH + case EUNATCH: // Protocol driver not attached +#endif + return StatusCode::kFailedPrecondition; + case ENOSPC: // No space left on device +#ifdef EDQUOT + case EDQUOT: // Disk quota exceeded +#endif + case EMFILE: // Too many open files + case EMLINK: // Too many links + case ENFILE: // Too many open files in system + case ENOBUFS: // No buffer space available + case ENODATA: // No message is available on the STREAM read queue + case ENOMEM: // Not enough space + case ENOSR: // No STREAM resources +#ifdef EUSERS + case EUSERS: // Too many users +#endif + return StatusCode::kResourceExhausted; +#ifdef ECHRNG + case ECHRNG: // Channel number out of range +#endif + case EFBIG: // File too large + case EOVERFLOW: // Value too large to be stored in data type + case ERANGE: // Result too large + return StatusCode::kOutOfRange; +#ifdef ENOPKG + case ENOPKG: // Package not installed +#endif + case ENOSYS: // Function not implemented + case ENOTSUP: // Operation not supported + case EAFNOSUPPORT: // Address family not supported +#ifdef EPFNOSUPPORT + case EPFNOSUPPORT: // Protocol family not supported +#endif + case EPROTONOSUPPORT: // Protocol not supported +#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: // Socket type not supported +#endif + case EXDEV: // Improper link + return StatusCode::kUnimplemented; + case EAGAIN: // Resource temporarily unavailable +#ifdef ECOMM + case ECOMM: // Communication error on send +#endif + case ECONNREFUSED: // Connection refused + case ECONNABORTED: // Connection aborted + case ECONNRESET: // Connection reset + case EINTR: // Interrupted function call +#ifdef EHOSTDOWN + case EHOSTDOWN: // Host is down +#endif + case EHOSTUNREACH: // Host is unreachable + case ENETDOWN: // Network is down + case ENETRESET: // Connection aborted by network + case ENETUNREACH: // Network unreachable + case ENOLCK: // No locks available + case ENOLINK: // Link has been severed +#ifdef ENONET + case ENONET: // Machine is not on the network +#endif + return StatusCode::kUnavailable; + case EDEADLK: // Resource deadlock avoided +#ifdef ESTALE + case ESTALE: // Stale file handle +#endif + return StatusCode::kAborted; + case ECANCELED: // Operation cancelled + return StatusCode::kCancelled; + default: + return StatusCode::kUnknown; + } +} + +namespace { +std::string MessageForErrnoToStatus(int error_number, + absl::string_view message) { + return absl::StrCat(message, ": ", + absl::base_internal::StrError(error_number)); +} +} // namespace + +Status ErrnoToStatus(int error_number, absl::string_view message) { + return Status(ErrnoToStatusCode(error_number), + MessageForErrnoToStatus(error_number, message)); +} + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/status/status.h b/absl/status/status.h index 193db8d8..9292b83a 100644 --- a/absl/status/status.h +++ b/absl/status/status.h @@ -738,6 +738,19 @@ Status UnavailableError(absl::string_view message); Status UnimplementedError(absl::string_view message); Status UnknownError(absl::string_view message); +// ErrnoToStatusCode() +// +// Returns the StatusCode for `error_number`, which should be an `errno` value. +// See https://en.cppreference.com/w/cpp/error/errno_macros and similar +// references. +absl::StatusCode ErrnoToStatusCode(int error_number); + +// ErrnoToStatus() +// +// Convenience function that creates a `absl::Status` using an `error_number`, +// which should be an `errno` value. +Status ErrnoToStatus(int error_number, absl::string_view message); + //------------------------------------------------------------------------------ // Implementation details follow //------------------------------------------------------------------------------ diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc index 1b038f6d..89cce7df 100644 --- a/absl/status/status_test.cc +++ b/absl/status/status_test.cc @@ -14,6 +14,8 @@ #include "absl/status/status.h" +#include + #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/strings/str_cat.h" @@ -485,4 +487,22 @@ TEST(Status, Swap) { test_swap(no_payload, with_payload); test_swap(with_payload, no_payload); } + +TEST(StatusErrno, ErrnoToStatusCode) { + EXPECT_EQ(absl::ErrnoToStatusCode(0), absl::StatusCode::kOk); + + // Spot-check a few errno values. + EXPECT_EQ(absl::ErrnoToStatusCode(EINVAL), + absl::StatusCode::kInvalidArgument); + EXPECT_EQ(absl::ErrnoToStatusCode(ENOENT), absl::StatusCode::kNotFound); + + // We'll pick a very large number so it hopefully doesn't collide to errno. + EXPECT_EQ(absl::ErrnoToStatusCode(19980927), absl::StatusCode::kUnknown); +} + +TEST(StatusErrno, ErrnoToStatus) { + absl::Status status = absl::ErrnoToStatus(ENOENT, "Cannot open 'path'"); + EXPECT_EQ(status.code(), absl::StatusCode::kNotFound); + EXPECT_EQ(status.message(), "Cannot open 'path': No such file or directory"); +} } // namespace -- cgit v1.2.3 From 7c6608d0dbe43cf9bdf7f77787bc6bc89cc42f8b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 20 Apr 2022 07:20:38 -0700 Subject: Export of internal Abseil changes -- 6d4e969ad240d248bfeb644b3b76fffae0f07882 by Christian Blichmann : cmake: Fix description of `ABSL_USE_EXTERNAL_GOOGLETEST` option There is no `add_subproject()` in CMake. PiperOrigin-RevId: 443087953 Change-Id: I30c2118638f99ad1389ae197e2c81d1e5f298882 GitOrigin-RevId: 6d4e969ad240d248bfeb644b3b76fffae0f07882 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bd3415a..8a20d04a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,7 +111,7 @@ option(ABSL_BUILD_TESTING "If ON, Abseil will build all of Abseil's own tests." OFF) option(ABSL_USE_EXTERNAL_GOOGLETEST - "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subproject." OFF) + "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subdirectory." OFF) cmake_dependent_option(ABSL_FIND_GOOGLETEST "If ON, Abseil will use find_package(GTest) rather than assuming that GoogleTest is already provided by the including project." -- cgit v1.2.3 From 731d4021ae94450c66627e71767c92f378065ae9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 22 Apr 2022 15:00:05 -0400 Subject: Fix typo: "a the condition" -> "a condition". PiperOrigin-RevId: 443723710 Change-Id: Ic39b0cf2b289efa9cd9434616949dd08a1a35117 --- absl/synchronization/mutex.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 8f25e3dd..821047fd 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -2510,9 +2510,9 @@ void CondVar::Remove(PerThreadSynch *s) { // before calling Mutex::UnlockSlow(), the Mutex code might be re-entered (via // the logging code, or via a Condition function) and might potentially attempt // to block this thread. That would be a problem if the thread were already on -// a the condition variable waiter queue. Thus, we use the waitp->cv_word -// to tell the unlock code to call CondVarEnqueue() to queue the thread on the -// condition variable queue just before the mutex is to be unlocked, and (most +// a condition variable waiter queue. Thus, we use the waitp->cv_word to tell +// the unlock code to call CondVarEnqueue() to queue the thread on the condition +// variable queue just before the mutex is to be unlocked, and (most // importantly) after any call to an external routine that might re-enter the // mutex code. static void CondVarEnqueue(SynchWaitParams *waitp) { -- cgit v1.2.3 From 4d751aa40a39e420d800d2964d5891c2431fa589 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Fri, 22 Apr 2022 12:08:16 -0700 Subject: Fix typo in absl/time/time.h PiperOrigin-RevId: 443726104 Change-Id: Ibd015472fe3e403c2da49bdeeb365ca9a817b8a5 --- absl/time/time.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/time/time.h b/absl/time/time.h index 61fa159b..69d72208 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -579,7 +579,7 @@ bool ParseDuration(absl::string_view dur_string, Duration* d); // AbslParseFlag() // -// Parses a command-line flag string representation `text` into a a Duration +// Parses a command-line flag string representation `text` into a Duration // value. Duration flags must be specified in a format that is valid input for // `absl::ParseDuration()`. bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error); -- cgit v1.2.3 From 189074734aa1cf774ba2f6956b90f3f6e5680e8e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 25 Apr 2022 07:07:35 -0700 Subject: Automated visibility attribute cleanup. PiperOrigin-RevId: 444259007 Change-Id: Ic518f66a33e387b7a551f37f7c0f6003c743dcb0 --- absl/time/BUILD.bazel | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index e8c49660..aa07df3d 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -64,9 +64,7 @@ cc_library( hdrs = ["internal/test_util.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/time:__pkg__", - ], + visibility = ["//visibility:private"], deps = [ ":time", "//absl/base:config", -- cgit v1.2.3 From 731689ffc2ad7bb95cc86b5b6160dbe7858f27a0 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Mon, 25 Apr 2022 08:40:42 -0700 Subject: Adds ABSL_CONST_INIT to initializing declarations where it is missing Fixes #1159 PiperOrigin-RevId: 444278141 Change-Id: Iae055fe78b438c31150a9e7601b734f4981f002e --- absl/base/internal/thread_identity.cc | 1 + absl/base/log_severity.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index 9950e63a..db46aafb 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -56,6 +56,7 @@ void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) { // *different* instances of this ptr. // Apple platforms have the visibility attribute, but issue a compile warning // that protected visibility is unsupported. +ABSL_CONST_INIT // Must come before __attribute__((visibility("protected"))) #if ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) __attribute__((visibility("protected"))) #endif // ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) diff --git a/absl/base/log_severity.cc b/absl/base/log_severity.cc index de26b06e..60a8fc1f 100644 --- a/absl/base/log_severity.cc +++ b/absl/base/log_severity.cc @@ -16,6 +16,8 @@ #include +#include "absl/base/attributes.h" + namespace absl { ABSL_NAMESPACE_BEGIN -- cgit v1.2.3 From fad73ab077b846ff83e921ea1f5a6f831ea07ec2 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 29 Apr 2022 05:43:43 -0700 Subject: Fix a typo in a comment. PiperOrigin-RevId: 445394311 Change-Id: I265b6a83f79bbed4321e24e6da4730a2c43ddb07 --- absl/status/statusor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/status/statusor.h b/absl/status/statusor.h index d6ebdc2b..a6d29110 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -477,7 +477,7 @@ class StatusOr : private internal_statusor::StatusOrData, // StatusOr::ok() // // Returns whether or not this `absl::StatusOr` holds a `T` value. This - // member function is analagous to `absl::Status::ok()` and should be used + // member function is analogous to `absl::Status::ok()` and should be used // similarly to check the status of return values. // // Example: -- cgit v1.2.3 From d29393ceb9055722760442f51b7d44e3b2d0660f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 2 May 2022 13:07:52 -0700 Subject: Fixed typo in `try_emplace` comment. PiperOrigin-RevId: 446010475 Change-Id: I28020510f3888a11f35b1960e9af441145ebf39b --- absl/container/flat_hash_map.h | 4 ++-- absl/container/node_hash_map.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index cbb24693..e6bdbd9e 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -361,8 +361,8 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // `flat_hash_map`. // // iterator try_emplace(const_iterator hint, - // const init_type& k, Args&&... args): - // iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args): + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): // // Inserts (via copy or move) the element of the specified key into the // `flat_hash_map` using the position of `hint` as a non-binding suggestion diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index c91cae69..6868e63a 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -352,8 +352,8 @@ class node_hash_map // `node_hash_map`. // // iterator try_emplace(const_iterator hint, - // const init_type& k, Args&&... args): - // iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args): + // const key_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): // // Inserts (via copy or move) the element of the specified key into the // `node_hash_map` using the position of `hint` as a non-binding suggestion -- cgit v1.2.3 From cc04c0e07427deb78091231238189d14561a9441 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Tue, 3 May 2022 09:14:22 -0700 Subject: Rename function_ref_benchmark.cc into more generic function_type_benchmark.cc, add missing includes PiperOrigin-RevId: 446209567 Change-Id: I9aac8ce10b93ed71f1260931995af1d32db6f780 --- absl/functional/BUILD.bazel | 4 +- absl/functional/function_ref_benchmark.cc | 142 ---------------------------- absl/functional/function_type_benchmark.cc | 143 +++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 144 deletions(-) delete mode 100644 absl/functional/function_ref_benchmark.cc create mode 100644 absl/functional/function_type_benchmark.cc diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel index f9f2b9c2..dbfa81f3 100644 --- a/absl/functional/BUILD.bazel +++ b/absl/functional/BUILD.bazel @@ -78,9 +78,9 @@ cc_test( ) cc_test( - name = "function_ref_benchmark", + name = "function_type_benchmark", srcs = [ - "function_ref_benchmark.cc", + "function_type_benchmark.cc", ], copts = ABSL_TEST_COPTS, tags = ["benchmark"], diff --git a/absl/functional/function_ref_benchmark.cc b/absl/functional/function_ref_benchmark.cc deleted file mode 100644 index 045305bf..00000000 --- a/absl/functional/function_ref_benchmark.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/functional/function_ref.h" - -#include - -#include "benchmark/benchmark.h" -#include "absl/base/attributes.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace { - -int dummy = 0; - -void FreeFunction() { benchmark::DoNotOptimize(dummy); } - -struct TrivialFunctor { - void operator()() const { benchmark::DoNotOptimize(dummy); } -}; - -struct LargeFunctor { - void operator()() const { benchmark::DoNotOptimize(this); } - std::string a, b, c; -}; - -template -void ABSL_ATTRIBUTE_NOINLINE CallFunction(Function f, Args&&... args) { - f(std::forward(args)...); -} - -template -void ConstructAndCallFunctionBenchmark(benchmark::State& state, - const Callable& c, Args&&... args) { - for (auto _ : state) { - CallFunction(c, std::forward(args)...); - } -} - -void BM_TrivialStdFunction(benchmark::State& state) { - ConstructAndCallFunctionBenchmark>(state, - TrivialFunctor{}); -} -BENCHMARK(BM_TrivialStdFunction); - -void BM_TrivialFunctionRef(benchmark::State& state) { - ConstructAndCallFunctionBenchmark>(state, - TrivialFunctor{}); -} -BENCHMARK(BM_TrivialFunctionRef); - -void BM_LargeStdFunction(benchmark::State& state) { - ConstructAndCallFunctionBenchmark>(state, - LargeFunctor{}); -} -BENCHMARK(BM_LargeStdFunction); - -void BM_LargeFunctionRef(benchmark::State& state) { - ConstructAndCallFunctionBenchmark>(state, LargeFunctor{}); -} -BENCHMARK(BM_LargeFunctionRef); - -void BM_FunPtrStdFunction(benchmark::State& state) { - ConstructAndCallFunctionBenchmark>(state, FreeFunction); -} -BENCHMARK(BM_FunPtrStdFunction); - -void BM_FunPtrFunctionRef(benchmark::State& state) { - ConstructAndCallFunctionBenchmark>(state, FreeFunction); -} -BENCHMARK(BM_FunPtrFunctionRef); - -// Doesn't include construction or copy overhead in the loop. -template -void CallFunctionBenchmark(benchmark::State& state, const Callable& c, - Args... args) { - Function f = c; - for (auto _ : state) { - benchmark::DoNotOptimize(&f); - f(args...); - } -} - -struct FunctorWithTrivialArgs { - void operator()(int a, int b, int c) const { - benchmark::DoNotOptimize(a); - benchmark::DoNotOptimize(b); - benchmark::DoNotOptimize(c); - } -}; - -void BM_TrivialArgsStdFunction(benchmark::State& state) { - CallFunctionBenchmark>( - state, FunctorWithTrivialArgs{}, 1, 2, 3); -} -BENCHMARK(BM_TrivialArgsStdFunction); - -void BM_TrivialArgsFunctionRef(benchmark::State& state) { - CallFunctionBenchmark>( - state, FunctorWithTrivialArgs{}, 1, 2, 3); -} -BENCHMARK(BM_TrivialArgsFunctionRef); - -struct FunctorWithNonTrivialArgs { - void operator()(std::string a, std::string b, std::string c) const { - benchmark::DoNotOptimize(&a); - benchmark::DoNotOptimize(&b); - benchmark::DoNotOptimize(&c); - } -}; - -void BM_NonTrivialArgsStdFunction(benchmark::State& state) { - std::string a, b, c; - CallFunctionBenchmark< - std::function>( - state, FunctorWithNonTrivialArgs{}, a, b, c); -} -BENCHMARK(BM_NonTrivialArgsStdFunction); - -void BM_NonTrivialArgsFunctionRef(benchmark::State& state) { - std::string a, b, c; - CallFunctionBenchmark< - FunctionRef>( - state, FunctorWithNonTrivialArgs{}, a, b, c); -} -BENCHMARK(BM_NonTrivialArgsFunctionRef); - -} // namespace -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/functional/function_type_benchmark.cc b/absl/functional/function_type_benchmark.cc new file mode 100644 index 00000000..1b27eebf --- /dev/null +++ b/absl/functional/function_type_benchmark.cc @@ -0,0 +1,143 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "benchmark/benchmark.h" +#include "absl/base/attributes.h" +#include "absl/functional/function_ref.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +int dummy = 0; + +void FreeFunction() { benchmark::DoNotOptimize(dummy); } + +struct TrivialFunctor { + void operator()() const { benchmark::DoNotOptimize(dummy); } +}; + +struct LargeFunctor { + void operator()() const { benchmark::DoNotOptimize(this); } + std::string a, b, c; +}; + +template +void ABSL_ATTRIBUTE_NOINLINE CallFunction(Function f, Args&&... args) { + f(std::forward(args)...); +} + +template +void ConstructAndCallFunctionBenchmark(benchmark::State& state, + const Callable& c, Args&&... args) { + for (auto _ : state) { + CallFunction(c, std::forward(args)...); + } +} + +void BM_TrivialStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + TrivialFunctor{}); +} +BENCHMARK(BM_TrivialStdFunction); + +void BM_TrivialFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + TrivialFunctor{}); +} +BENCHMARK(BM_TrivialFunctionRef); + +void BM_LargeStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + LargeFunctor{}); +} +BENCHMARK(BM_LargeStdFunction); + +void BM_LargeFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, LargeFunctor{}); +} +BENCHMARK(BM_LargeFunctionRef); + +void BM_FunPtrStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, FreeFunction); +} +BENCHMARK(BM_FunPtrStdFunction); + +void BM_FunPtrFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, FreeFunction); +} +BENCHMARK(BM_FunPtrFunctionRef); + +// Doesn't include construction or copy overhead in the loop. +template +void CallFunctionBenchmark(benchmark::State& state, const Callable& c, + Args... args) { + Function f = c; + for (auto _ : state) { + benchmark::DoNotOptimize(&f); + f(args...); + } +} + +struct FunctorWithTrivialArgs { + void operator()(int a, int b, int c) const { + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); + benchmark::DoNotOptimize(c); + } +}; + +void BM_TrivialArgsStdFunction(benchmark::State& state) { + CallFunctionBenchmark>( + state, FunctorWithTrivialArgs{}, 1, 2, 3); +} +BENCHMARK(BM_TrivialArgsStdFunction); + +void BM_TrivialArgsFunctionRef(benchmark::State& state) { + CallFunctionBenchmark>( + state, FunctorWithTrivialArgs{}, 1, 2, 3); +} +BENCHMARK(BM_TrivialArgsFunctionRef); + +struct FunctorWithNonTrivialArgs { + void operator()(std::string a, std::string b, std::string c) const { + benchmark::DoNotOptimize(&a); + benchmark::DoNotOptimize(&b); + benchmark::DoNotOptimize(&c); + } +}; + +void BM_NonTrivialArgsStdFunction(benchmark::State& state) { + std::string a, b, c; + CallFunctionBenchmark< + std::function>( + state, FunctorWithNonTrivialArgs{}, a, b, c); +} +BENCHMARK(BM_NonTrivialArgsStdFunction); + +void BM_NonTrivialArgsFunctionRef(benchmark::State& state) { + std::string a, b, c; + CallFunctionBenchmark< + FunctionRef>( + state, FunctorWithNonTrivialArgs{}, a, b, c); +} +BENCHMARK(BM_NonTrivialArgsFunctionRef); + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl -- cgit v1.2.3 From 8d9cef25c70321c794ff5559cc0b1e0fa5f85e72 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 3 May 2022 13:25:53 -0700 Subject: Don't construct/destroy object twice PiperOrigin-RevId: 446274314 Change-Id: Ibf641808c533a10e0aef8d1601095e539ae5c43a --- absl/memory/memory_test.cc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/absl/memory/memory_test.cc b/absl/memory/memory_test.cc index 1990c7ba..5d5719b3 100644 --- a/absl/memory/memory_test.cc +++ b/absl/memory/memory_test.cc @@ -548,22 +548,23 @@ struct MinimalMockAllocator { TEST(AllocatorTraits, FunctionsMinimal) { int trace = 0; int hint; - TestValue x(&trace); + alignas(TestValue) char buffer[sizeof(TestValue)]; + auto* x = reinterpret_cast(buffer); MinimalMockAllocator mock; using Traits = absl::allocator_traits; - EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(&x)); - EXPECT_CALL(mock, deallocate(&x, 7)); + EXPECT_CALL(mock, allocate(7)).WillRepeatedly(Return(x)); + EXPECT_CALL(mock, deallocate(x, 7)); - EXPECT_EQ(&x, Traits::allocate(mock, 7)); + EXPECT_EQ(x, Traits::allocate(mock, 7)); static_cast(Traits::allocate(mock, 7, static_cast(&hint))); - EXPECT_EQ(&x, Traits::allocate(mock, 7, static_cast(&hint))); - Traits::deallocate(mock, &x, 7); + EXPECT_EQ(x, Traits::allocate(mock, 7, static_cast(&hint))); + Traits::deallocate(mock, x, 7); + EXPECT_EQ(0, trace); + Traits::construct(mock, x, &trace); EXPECT_EQ(1, trace); - Traits::construct(mock, &x, &trace); - EXPECT_EQ(2, trace); - Traits::destroy(mock, &x); - EXPECT_EQ(1, trace); + Traits::destroy(mock, x); + EXPECT_EQ(0, trace); EXPECT_EQ(std::numeric_limits::max() / sizeof(TestValue), Traits::max_size(mock)); -- cgit v1.2.3 From 981e2c888094c26f54adce260e05fd29e07e5fc8 Mon Sep 17 00:00:00 2001 From: Andy Getzendanner Date: Wed, 4 May 2022 09:23:27 -0700 Subject: raw_logging: Rename LogPrefixHook to reflect the other half of it's job (filtering by severity). PiperOrigin-RevId: 446476285 Change-Id: Ibd0913e06244424241200d17177a4f220fcb7861 --- absl/base/internal/raw_logging.cc | 20 +++++++++++--------- absl/base/internal/raw_logging.h | 13 +++++++------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 509d7460..acf20f2a 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -14,11 +14,12 @@ #include "absl/base/internal/raw_logging.h" -#include #include +#include #include #include #include +#include #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -78,11 +79,10 @@ namespace { // a selected set of platforms for which we expect not to be able to raw log. ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES - absl::base_internal::AtomicHook - log_prefix_hook; +absl::base_internal::AtomicHook + log_filter_and_prefix_hook; ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES - absl::base_internal::AtomicHook - abort_hook; +absl::base_internal::AtomicHook abort_hook; #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED constexpr char kTruncated[] = " ... (message truncated)\n"; @@ -151,9 +151,9 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } #endif - auto log_prefix_hook_ptr = log_prefix_hook.Load(); - if (log_prefix_hook_ptr) { - enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size); + auto log_filter_and_prefix_hook_ptr = log_filter_and_prefix_hook.Load(); + if (log_filter_and_prefix_hook_ptr) { + enabled = log_filter_and_prefix_hook_ptr(severity, file, line, &buf, &size); } else { if (enabled) { DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line); @@ -230,7 +230,9 @@ ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL absl::base_internal::AtomicHook internal_log_function(DefaultInternalLog); -void RegisterLogPrefixHook(LogPrefixHook func) { log_prefix_hook.Store(func); } +void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func) { + log_filter_and_prefix_hook.Store(func); +} void RegisterAbortHook(AbortHook func) { abort_hook.Store(func); } diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index 2bf7aaba..efe28dac 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -148,11 +148,12 @@ bool RawLoggingFullySupported(); // 'severity' is the severity level of the message being written. // 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro // was located. -// 'buffer' and 'buf_size' are pointers to the buffer and buffer size. If the -// hook writes a prefix, it must increment *buffer and decrement *buf_size +// 'buf' and 'buf_size' are pointers to the buffer and buffer size. If the +// hook writes a prefix, it must increment *buf and decrement *buf_size // accordingly. -using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, - int line, char** buffer, int* buf_size); +using LogFilterAndPrefixHook = bool (*)(absl::LogSeverity severity, + const char* file, int line, char** buf, + int* buf_size); // Function type for a raw_logging customization hook called to abort a process // when a FATAL message is logged. If the provided AbortHook() returns, the @@ -162,7 +163,7 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, // was located. // The NUL-terminated logged message lives in the buffer between 'buf_start' // and 'buf_end'. 'prefix_end' points to the first non-prefix character of the -// buffer (as written by the LogPrefixHook.) +// buffer (as written by the LogFilterAndPrefixHook.) using AbortHook = void (*)(const char* file, int line, const char* buf_start, const char* prefix_end, const char* buf_end); @@ -184,7 +185,7 @@ ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook< // // These functions are safe to call at any point during initialization; they do // not block or malloc, and are async-signal safe. -void RegisterLogPrefixHook(LogPrefixHook func); +void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func); void RegisterAbortHook(AbortHook func); void RegisterInternalLogFunction(InternalLogFunction func); -- cgit v1.2.3 From 02934b492135d7c9c40fecf8e7873380b46c2223 Mon Sep 17 00:00:00 2001 From: Evan Brown Date: Wed, 4 May 2022 11:53:25 -0700 Subject: In btree, move rightmost_ into the CompressedTuple instead of root_. We also add accessors for rightmost()/mutable_rightmost(). PiperOrigin-RevId: 446515231 Change-Id: I4b8cb46f4bd209a0f51dcdcb96c9479e480828a3 --- absl/container/internal/btree.h | 85 ++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index da2abcb9..cb39b161 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -191,8 +191,8 @@ struct key_compare_adapter { // Inherit from checked_compare_base to support function pointers and also // keep empty-base-optimization (EBO) support for classes. // Note: we can't use CompressedTuple here because that would interfere - // with the EBO for `btree::root_`. `btree::root_` is itself a CompressedTuple - // and nested `CompressedTuple`s don't support EBO. + // with the EBO for `btree::rightmost_`. `btree::rightmost_` is itself a + // CompressedTuple and nested `CompressedTuple`s don't support EBO. // TODO(b/214288561): use CompressedTuple instead once it supports EBO for // nested `CompressedTuple`s. struct checked_compare : checked_compare_base { @@ -1329,7 +1329,7 @@ class btree { public: btree(const key_compare &comp, const allocator_type &alloc) - : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {} + : root_(EmptyNode()), rightmost_(comp, alloc, EmptyNode()), size_(0) {} btree(const btree &other) : btree(other, other.allocator()) {} btree(const btree &other, const allocator_type &alloc) @@ -1337,10 +1337,10 @@ class btree { copy_or_move_values_in_order(other); } btree(btree &&other) noexcept - : root_(std::move(other.root_)), - rightmost_(absl::exchange(other.rightmost_, EmptyNode())), + : root_(absl::exchange(other.root_, EmptyNode())), + rightmost_(std::move(other.rightmost_)), size_(absl::exchange(other.size_, 0)) { - other.mutable_root() = EmptyNode(); + other.mutable_rightmost() = EmptyNode(); } btree(btree &&other, const allocator_type &alloc) : btree(other.key_comp(), alloc) { @@ -1365,9 +1365,9 @@ class btree { iterator begin() { return iterator(leftmost()); } const_iterator begin() const { return const_iterator(leftmost()); } - iterator end() { return iterator(rightmost_, rightmost_->finish()); } + iterator end() { return iterator(rightmost(), rightmost()->finish()); } const_iterator end() const { - return const_iterator(rightmost_, rightmost_->finish()); + return const_iterator(rightmost(), rightmost()->finish()); } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { @@ -1493,7 +1493,7 @@ class btree { void swap(btree &other); const key_compare &key_comp() const noexcept { - return root_.template get<0>(); + return rightmost_.template get<0>(); } template bool compare_keys(const K1 &a, const K2 &b) const { @@ -1587,10 +1587,17 @@ class btree { friend struct btree_access; // Internal accessor routines. - node_type *root() { return root_.template get<2>(); } - const node_type *root() const { return root_.template get<2>(); } - node_type *&mutable_root() noexcept { return root_.template get<2>(); } - key_compare *mutable_key_comp() noexcept { return &root_.template get<0>(); } + node_type *root() { return root_; } + const node_type *root() const { return root_; } + node_type *&mutable_root() noexcept { return root_; } + node_type *rightmost() { return rightmost_.template get<2>(); } + const node_type *rightmost() const { return rightmost_.template get<2>(); } + node_type *&mutable_rightmost() noexcept { + return rightmost_.template get<2>(); + } + key_compare *mutable_key_comp() noexcept { + return &rightmost_.template get<0>(); + } // The leftmost node is stored as the parent of the root node. node_type *leftmost() { return root()->parent(); } @@ -1598,10 +1605,10 @@ class btree { // Allocator routines. allocator_type *mutable_allocator() noexcept { - return &root_.template get<1>(); + return &rightmost_.template get<1>(); } const allocator_type &allocator() const noexcept { - return root_.template get<1>(); + return rightmost_.template get<1>(); } // Allocates a correctly aligned node of at least size bytes using the @@ -1709,15 +1716,14 @@ class btree { return res; } - // We use compressed tuple in order to save space because key_compare and - // allocator_type are usually empty. - absl::container_internal::CompressedTuple - root_; + node_type *root_; // A pointer to the rightmost node. Note that the leftmost node is stored as - // the root's parent. - node_type *rightmost_; + // the root's parent. We use compressed tuple in order to save space because + // key_compare and allocator_type are usually empty. + absl::container_internal::CompressedTuple + rightmost_; // Number of values. size_type size_; @@ -2141,7 +2147,7 @@ template auto btree

::insert_unique(const K &key, Args &&... args) -> std::pair { if (empty()) { - mutable_root() = rightmost_ = new_leaf_root_node(1); + mutable_root() = mutable_rightmost() = new_leaf_root_node(1); } SearchResult res = internal_locate(key); @@ -2208,7 +2214,7 @@ template template auto btree

::insert_multi(const key_type &key, ValueType &&v) -> iterator { if (empty()) { - mutable_root() = rightmost_ = new_leaf_root_node(1); + mutable_root() = mutable_rightmost() = new_leaf_root_node(1); } iterator iter = internal_upper_bound(key); @@ -2272,15 +2278,15 @@ auto btree

::operator=(btree &&other) noexcept -> btree & { using std::swap; if (absl::allocator_traits< allocator_type>::propagate_on_container_copy_assignment::value) { - // Note: `root_` also contains the allocator and the key comparator. swap(root_, other.root_); + // Note: `rightmost_` also contains the allocator and the key comparator. swap(rightmost_, other.rightmost_); swap(size_, other.size_); } else { if (allocator() == other.allocator()) { swap(mutable_root(), other.mutable_root()); swap(*mutable_key_comp(), *other.mutable_key_comp()); - swap(rightmost_, other.rightmost_); + swap(mutable_rightmost(), other.mutable_rightmost()); swap(size_, other.size_); } else { // We aren't allowed to propagate the allocator and the allocator is @@ -2422,8 +2428,7 @@ void btree

::clear() { if (!empty()) { node_type::clear_and_delete(root(), mutable_allocator()); } - mutable_root() = EmptyNode(); - rightmost_ = EmptyNode(); + mutable_root() = mutable_rightmost() = EmptyNode(); size_ = 0; } @@ -2432,15 +2437,15 @@ void btree

::swap(btree &other) { using std::swap; if (absl::allocator_traits< allocator_type>::propagate_on_container_swap::value) { - // Note: `root_` also contains the allocator and the key comparator. - swap(root_, other.root_); + // Note: `rightmost_` also contains the allocator and the key comparator. + swap(rightmost_, other.rightmost_); } else { // It's undefined behavior if the allocators are unequal here. assert(allocator() == other.allocator()); - swap(mutable_root(), other.mutable_root()); + swap(mutable_rightmost(), other.mutable_rightmost()); swap(*mutable_key_comp(), *other.mutable_key_comp()); } - swap(rightmost_, other.rightmost_); + swap(mutable_root(), other.mutable_root()); swap(size_, other.size_); } @@ -2448,12 +2453,12 @@ template void btree

::verify() const { assert(root() != nullptr); assert(leftmost() != nullptr); - assert(rightmost_ != nullptr); + assert(rightmost() != nullptr); assert(empty() || size() == internal_verify(root(), nullptr, nullptr)); assert(leftmost() == (++const_iterator(root(), -1)).node_); - assert(rightmost_ == (--const_iterator(root(), root()->finish())).node_); + assert(rightmost() == (--const_iterator(root(), root()->finish())).node_); assert(leftmost()->is_leaf()); - assert(rightmost_->is_leaf()); + assert(rightmost()->is_leaf()); } template @@ -2539,7 +2544,7 @@ void btree

::rebalance_or_split(iterator *iter) { mutable_root() = parent; // If the former root was a leaf node, then it's now the rightmost node. assert(parent->start_child()->is_internal() || - parent->start_child() == rightmost_); + parent->start_child() == rightmost()); } // Split the node. @@ -2547,7 +2552,7 @@ void btree

::rebalance_or_split(iterator *iter) { if (node->is_leaf()) { split_node = new_leaf_node(parent); node->split(insert_position, split_node, mutable_allocator()); - if (rightmost_ == node) rightmost_ = split_node; + if (rightmost() == node) mutable_rightmost() = split_node; } else { split_node = new_internal_node(parent); node->split(insert_position, split_node, mutable_allocator()); @@ -2562,7 +2567,7 @@ void btree

::rebalance_or_split(iterator *iter) { template void btree

::merge_nodes(node_type *left, node_type *right) { left->merge(right, mutable_allocator()); - if (rightmost_ == right) rightmost_ = left; + if (rightmost() == right) mutable_rightmost() = left; } template @@ -2627,7 +2632,7 @@ void btree

::try_shrink() { // Deleted the last item on the root node, shrink the height of the tree. if (orig_root->is_leaf()) { assert(size() == 0); - mutable_root() = rightmost_ = EmptyNode(); + mutable_root() = mutable_rightmost() = EmptyNode(); } else { node_type *child = orig_root->start_child(); child->make_root(); @@ -2681,7 +2686,7 @@ inline auto btree

::internal_emplace(iterator iter, Args &&... args) old_root->set_finish(old_root->start()); new_root->set_generation(old_root->generation()); node_type::clear_and_delete(old_root, alloc); - mutable_root() = rightmost_ = new_root; + mutable_root() = mutable_rightmost() = new_root; } else { rebalance_or_split(&iter); } -- cgit v1.2.3 From 384a25d5e19228ceb7641676aefd58c4ca7a9568 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 4 May 2022 12:04:37 -0700 Subject: Improve analysis of the number of extra `==` operations, which was overly complicated, slightly incorrect. The old analysis viewed it as birthday attack, which asks how often there are multiple values in the probe same probe sequence with the same H2. In their own words, this analysis "breaks down" at around `n = 12`. Instead we can answer a simpler question, which is, if a probe sequence examines `k` objects that are not what we are looking for, what's the number of calls `==`? The expectation is simply `k/128`. PiperOrigin-RevId: 446518063 Change-Id: Ie879bd4f6c97979822bc9d550b9e2503b1418c78 --- absl/container/internal/raw_hash_set.h | 58 ++++++++++++++++------------------ 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 046a6939..b69a77f0 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -95,14 +95,21 @@ // Storing control bytes in a separate array also has beneficial cache effects, // since more logical slots will fit into a cache line. // +// # Hashing +// +// We compute two separate hashes, `H1` and `H2`, from the hash of an object. +// `H1(hash(x))` is an index into `slots`, and essentially the starting point +// for the probe sequence. `H2(hash(x))` is a 7-bit value used to filter out +// objects that cannot possibly be the one we are looking for. +// // # Table operations. // -// The key operations are `insert`, `find`, and `erase_at`; the operations below +// The key operations are `insert`, `find`, and `erase`. // -// `insert` and `erase` are implemented in terms of find, so we describe that -// one first. To `find` a value `x`, we compute `hash(x)`. From `H1(hash(x))` -// and the capacity, we construct a `probe_seq` that visits every group of -// slots in some interesting order. +// Since `insert` and `erase` are implemented in terms of `find`, we describe +// `find` first. To `find` a value `x`, we compute `hash(x)`. From +// `H1(hash(x))` and the capacity, we construct a `probe_seq` that visits every +// group of slots in some interesting order. // // We now walk through these indices. At each index, we select the entire group // starting with that index and extract potential candidates: occupied slots @@ -112,32 +119,21 @@ // next probe index. Tombstones effectively behave like full slots that never // match the value we're looking for. // -// The `H2` bits ensure that if we perform a ==, a false positive is very very -// rare (assuming the hash function looks enough like a random oracle). To see -// this, note that in a group, there will be at most 8 or 16 `H2` values, but -// an `H2` can be any one of 128 values. Viewed as a birthday attack, we can use -// the rule of thumb that the probability of a collision among n choices of m -// symbols is `p(n, m) ~ n^2/2m. In table form: -// -// n | p(n) | n | p(n) -// 0 | 0.000 | 8 | 0.250 -// 1 | 0.004 | 9 | 0.316 -// 2 | 0.016 | 10 | 0.391 -// 3 | 0.035 | 11 | 0.473 -// 4 | 0.062 | 12 | 0.562 -// 5 | 0.098 | 13 | 0.660 -// 6 | 0.141 | 14 | 0.766 -// 7 | 0.191 | 15 | 0.879 -// -// The rule of thumb breaks down at around `n = 12`, but such groups would only -// occur for tables close to their max load factor. This is far better than an -// ordinary open-addressing table, which needs to perform an == at every step of -// the probe sequence. These probabilities don't tell the full story (for -// example, because elements are inserted into a group from the front, and -// candidates are =='ed from the front, the collision is only effective in -// rare cases e.g. another probe sequence inserted into a deleted slot in front -// of us). -// +// The `H2` bits ensure when we compare a slot to an object with `==`, we are +// likely to have actually found the object. That is, the chance is low that +// `==` is called and returns `false`. Thus, when we search for an object, we +// are unlikely to call `==` many times. This likelyhood can be analyzed as +// follows (assuming that H2 is a random enough hash function). +// +// Let's assume that there are `k` "wrong" objects that must be examined in a +// probe sequence. For example, when doing a `find` on an object that is in the +// table, `k` is the number of objects between the start of the probe sequence +// and the final found object (not including the final found object). The +// expected number of objects with an H2 match is then `k/128`. Measurements +// and analysis indicate that even at high load factors, `k` is less than 32, +// meaning that the number of "false positive" comparisons we must perform is +// less than 1/8 per `find`. + // `insert` is implemented in terms of `unchecked_insert`, which inserts a // value presumed to not be in the table (violating this requirement will cause // the table to behave erratically). Given `x` and its hash `hash(x)`, to insert -- cgit v1.2.3 From e8cda74967d1fa43182f88396e9030e93501e1a1 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 4 May 2022 13:21:23 -0700 Subject: Correct the comment about the probe sequence. It's (i/2 + i)/2 not (i/2 - i)/2. Also note that this probe sequence visits every group exactly once. PiperOrigin-RevId: 446535602 Change-Id: I13169be3f8ee6a4ddbbe8be84f1e1a482444d0cc --- absl/container/internal/raw_hash_set.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index b69a77f0..56251b22 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -228,7 +228,7 @@ void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/, // // Currently, the sequence is a triangular progression of the form // -// p(i) := Width * (i^2 - i)/2 + hash (mod mask + 1) +// p(i) := Width * (i^2 + i)/2 + hash (mod mask + 1) // // The use of `Width` ensures that each probe step does not overlap groups; // the sequence effectively outputs the addresses of *groups* (although not @@ -241,6 +241,10 @@ void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/, // for selecting candidates. However, when those candidates' slots are // actually inspected, there are no corresponding slots for the cloned bytes, // so we need to make sure we've treated those offsets as "wrapping around". +// +// It turns out that this probe sequence visits every group exactly once if the +// number of groups is a power of two, since (i^2+i)/2 is a bijection in +// Z/(2^m). See https://en.wikipedia.org/wiki/Quadratic_probing template class probe_seq { public: -- cgit v1.2.3 From 173dfe4a4c70f294a512aae4d0d0f5fc7db39241 Mon Sep 17 00:00:00 2001 From: Andy Getzendanner Date: Wed, 4 May 2022 20:58:05 -0700 Subject: raw_logging: Rename SafeWriteToStderr to indicate what about it is safe (answer: it's async-signal-safe). Also, preserve errno across calls to make it actually signal-safe. PiperOrigin-RevId: 446620926 Change-Id: I875fbec02b909e8424ddf763303b0d6007f8548f --- absl/base/BUILD.bazel | 1 + absl/base/CMakeLists.txt | 1 + absl/base/internal/raw_logging.cc | 8 ++++++-- absl/base/internal/raw_logging.h | 9 +++------ absl/debugging/BUILD.bazel | 1 - absl/debugging/CMakeLists.txt | 1 - absl/debugging/failure_signal_handler.cc | 4 +--- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 49e3a95f..b7e49359 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -75,6 +75,7 @@ cc_library( ":atomic_hook", ":config", ":core_headers", + ":errno_saver", ":log_severity", ], ) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 0e1e0413..ed55093a 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -66,6 +66,7 @@ absl_cc_library( absl::atomic_hook absl::config absl::core_headers + absl::errno_saver absl::log_severity COPTS ${ABSL_DEFAULT_COPTS} diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index acf20f2a..ca2a75f3 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -24,6 +24,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/atomic_hook.h" +#include "absl/base/internal/errno_saver.h" #include "absl/base/log_severity.h" // We know how to perform low-level writes to stderr in POSIX and Windows. For @@ -169,7 +170,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } else { DoRawLog(&buf, &size, "%s", kTruncated); } - SafeWriteToStderr(buffer, strlen(buffer)); + AsyncSignalSafeWriteToStderr(buffer, strlen(buffer)); } #else static_cast(format); @@ -196,8 +197,11 @@ void DefaultInternalLog(absl::LogSeverity severity, const char* file, int line, } // namespace -void SafeWriteToStderr(const char *s, size_t len) { +void AsyncSignalSafeWriteToStderr(const char* s, size_t len) { + absl::base_internal::ErrnoSaver errno_saver; #if defined(ABSL_HAVE_SYSCALL_WRITE) + // We prefer calling write via `syscall` to minimize the risk of libc doing + // something "helpful". syscall(SYS_write, STDERR_FILENO, s, len); #elif defined(ABSL_HAVE_POSIX_WRITE) write(STDERR_FILENO, s, len); diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index efe28dac..68a8bf21 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -109,12 +109,9 @@ namespace raw_logging_internal { void RawLog(absl::LogSeverity severity, const char* file, int line, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); -// Writes the provided buffer directly to stderr, in a safe, low-level manner. -// -// In POSIX this means calling write(), which is async-signal safe and does -// not malloc. If the platform supports the SYS_write syscall, we invoke that -// directly to side-step any libc interception. -void SafeWriteToStderr(const char *s, size_t len); +// Writes the provided buffer directly to stderr, in a signal-safe, low-level +// manner. +void AsyncSignalSafeWriteToStderr(const char* s, size_t len); // compile-time function to get the "base" filename, that is, the part of // a filename after the last "/" or "\" path separator. The search starts at diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index 7f5c1cad..dce59e1d 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -143,7 +143,6 @@ cc_library( "//absl/base", "//absl/base:config", "//absl/base:core_headers", - "//absl/base:errno_saver", "//absl/base:raw_logging_internal", ], ) diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 7507ff3b..32952734 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -126,7 +126,6 @@ absl_cc_library( absl::base absl::config absl::core_headers - absl::errno_saver absl::raw_logging_internal PUBLIC ) diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index 3fe8827a..affade3b 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -42,7 +42,6 @@ #include #include "absl/base/attributes.h" -#include "absl/base/internal/errno_saver.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/sysinfo.h" #include "absl/debugging/internal/examine_stack.h" @@ -217,8 +216,7 @@ static void InstallOneFailureHandler(FailureSignalData* data, #endif static void WriteToStderr(const char* data) { - absl::base_internal::ErrnoSaver errno_saver; - absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data)); + absl::raw_logging_internal::AsyncSignalSafeWriteToStderr(data, strlen(data)); } static void WriteSignalMessage(int signo, int cpu, -- cgit v1.2.3 From d3f0c70673ed71ba1581702bbbd0aa8865a575d1 Mon Sep 17 00:00:00 2001 From: Andy Getzendanner Date: Thu, 5 May 2022 08:42:47 -0700 Subject: raw_logging: Document that AbortHook's buffers live for as long as the process remains alive. PiperOrigin-RevId: 446725910 Change-Id: I291fa8c1c41155b1530969f64b2b7f44b1576c92 --- absl/base/internal/raw_logging.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index 68a8bf21..0747c9df 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -161,6 +161,9 @@ using LogFilterAndPrefixHook = bool (*)(absl::LogSeverity severity, // The NUL-terminated logged message lives in the buffer between 'buf_start' // and 'buf_end'. 'prefix_end' points to the first non-prefix character of the // buffer (as written by the LogFilterAndPrefixHook.) +// +// The lifetime of the filename and message buffers will not end while the +// process remains alive. using AbortHook = void (*)(const char* file, int line, const char* buf_start, const char* prefix_end, const char* buf_end); -- cgit v1.2.3 From 5073947530a9c74ce6da63b1fac16cc07564a3b8 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 10 May 2022 12:13:43 -0700 Subject: Fixes for C++20 support when not using std::optional. * Avoid warnings due to deprecation of volatile return types. Also fix up optional_test.cc due to ABSL_USES_STD_OPTIONAL always being false in its body. Bug: chromium:1284275 PiperOrigin-RevId: 447796238 Change-Id: If050206c979c6c08af22e71ff0ea91e7f7932f0c --- absl/types/optional_test.cc | 48 ++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc index 1846e695..9477c150 100644 --- a/absl/types/optional_test.cc +++ b/absl/types/optional_test.cc @@ -27,6 +27,11 @@ #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" +#if defined(__cplusplus) && __cplusplus >= 202002L +// In C++20, volatile-qualified return types are deprecated. +#define ABSL_VOLATILE_RETURN_TYPES_DEPRECATED 1 +#endif + // The following types help test an internal compiler error in GCC5 though // GCC10. The case OptionalTest.InternalCompilerErrorInGcc5ToGcc10 crashes the // compiler without a workaround. This test case should remain at the beginning @@ -231,6 +236,7 @@ TEST(optionalTest, CopyConstructor) { EXPECT_TRUE(opt42_copy); EXPECT_EQ(42, *opt42_copy); } +#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) { absl::optional empty, opt42 = 42; absl::optional empty_copy(empty); @@ -239,6 +245,7 @@ TEST(optionalTest, CopyConstructor) { EXPECT_TRUE(opt42_copy); EXPECT_EQ(42, *opt42_copy); } +#endif // test copyablility EXPECT_TRUE(std::is_copy_constructible>::value); EXPECT_TRUE(std::is_copy_constructible>::value); @@ -250,18 +257,11 @@ TEST(optionalTest, CopyConstructor) { EXPECT_FALSE( absl::is_trivially_copy_constructible>::value); -#if defined(ABSL_USES_STD_OPTIONAL) && defined(__GLIBCXX__) - // libstdc++ std::optional implementation (as of 7.2) has a bug: when T is - // trivially copyable, optional is not trivially copyable (due to one of - // its base class is unconditionally nontrivial). -#define ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG 1 -#endif -#ifndef ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG EXPECT_TRUE( absl::is_trivially_copy_constructible>::value); EXPECT_TRUE( absl::is_trivially_copy_constructible>::value); -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // See defect report "Trivial copy/move constructor for class with volatile // member" at // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2094 @@ -270,8 +270,7 @@ TEST(optionalTest, CopyConstructor) { // Also a cv-qualified scalar type should be trivially copyable. EXPECT_TRUE(absl::is_trivially_copy_constructible< absl::optional>::value); -#endif // _MSC_VER -#endif // ABSL_GLIBCXX_OPTIONAL_TRIVIALITY_BUG +#endif // !defined(_MSC_VER) && !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // constexpr copy constructor for trivially copyable types { @@ -301,17 +300,10 @@ TEST(optionalTest, CopyConstructor) { EXPECT_TRUE(absl::is_trivially_copy_constructible< absl::optional>::value); #endif - // When testing with VS 2017 15.3, there seems to be a bug in MSVC - // std::optional when T is volatile-qualified. So skipping this test. - // Bug report: - // https://connect.microsoft.com/VisualStudio/feedback/details/3142534 -#if defined(ABSL_USES_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911 -#define ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG 1 -#endif -#ifndef ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG +#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) EXPECT_FALSE(std::is_copy_constructible< absl::optional>::value); -#endif +#endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) } } @@ -331,11 +323,9 @@ TEST(optionalTest, MoveConstructor) { EXPECT_FALSE(std::is_move_constructible>::value); // test noexcept EXPECT_TRUE(std::is_nothrow_move_constructible>::value); -#ifndef ABSL_USES_STD_OPTIONAL EXPECT_EQ( absl::default_allocator_is_nothrow::value, std::is_nothrow_move_constructible>::value); -#endif EXPECT_TRUE(std::is_nothrow_move_constructible< absl::optional>::value); } @@ -664,8 +654,7 @@ TEST(optionalTest, CopyAssignment) { EXPECT_TRUE(absl::is_copy_assignable::value); EXPECT_FALSE(absl::is_trivially_copy_assignable::value); - // std::optional doesn't support volatile nontrivial types. -#ifndef ABSL_USES_STD_OPTIONAL +#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) { StructorListener listener; Listenable::listener = &listener; @@ -684,7 +673,7 @@ TEST(optionalTest, CopyAssignment) { EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_copy_assign); } -#endif // ABSL_USES_STD_OPTIONAL +#endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) } TEST(optionalTest, MoveAssignment) { @@ -707,8 +696,7 @@ TEST(optionalTest, MoveAssignment) { EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.move_assign); } - // std::optional doesn't support volatile nontrivial types. -#ifndef ABSL_USES_STD_OPTIONAL +#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) { StructorListener listener; Listenable::listener = &listener; @@ -728,7 +716,7 @@ TEST(optionalTest, MoveAssignment) { EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_move_assign); } -#endif // ABSL_USES_STD_OPTIONAL +#endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) EXPECT_FALSE(absl::is_move_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); @@ -1064,6 +1052,7 @@ TEST(optionalTest, Value) { #endif EXPECT_EQ("c&&", TypeQuals(OC(absl::in_place, "xvalue_c").value())); +#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // test on volatile type using OV = absl::optional; OV lvalue_v(absl::in_place, 42); @@ -1071,6 +1060,7 @@ TEST(optionalTest, Value) { EXPECT_EQ(42, OV(42).value()); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); +#endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // test exception throw on value() absl::optional empty; @@ -1113,6 +1103,7 @@ TEST(optionalTest, DerefOperator) { #endif EXPECT_EQ("c&&", TypeQuals(*OC(absl::in_place, "xvalue_c"))); +#if !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) // test on volatile type using OV = absl::optional; OV lvalue_v(absl::in_place, 42); @@ -1120,6 +1111,7 @@ TEST(optionalTest, DerefOperator) { EXPECT_EQ(42, *OV(42)); EXPECT_TRUE((std::is_same::value)); EXPECT_TRUE((std::is_same::value)); +#endif // !defined(ABSL_VOLATILE_RETURN_TYPES_DEPRECATED) constexpr absl::optional opt1(1); static_assert(*opt1 == 1, ""); @@ -1584,12 +1576,10 @@ TEST(optionalTest, NoExcept) { static_assert( std::is_nothrow_move_constructible>::value, ""); -#ifndef ABSL_USES_STD_OPTIONAL static_assert(absl::default_allocator_is_nothrow::value == std::is_nothrow_move_constructible< absl::optional>::value, ""); -#endif std::vector> v; for (int i = 0; i < 10; ++i) v.emplace_back(); } -- cgit v1.2.3 From abbeeef82360d4a17f82d10fa1b1bb54feb2ad24 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 11 May 2022 13:49:06 -0700 Subject: Document that Consume(Prefix|Suffix)() don't modify the input on failure PiperOrigin-RevId: 448075898 Change-Id: Ia4047f833bf27c62752b41f4ba65ab3be88a0181 --- absl/strings/strip.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/absl/strings/strip.h b/absl/strings/strip.h index 111872ca..d801774d 100644 --- a/absl/strings/strip.h +++ b/absl/strings/strip.h @@ -34,8 +34,9 @@ ABSL_NAMESPACE_BEGIN // ConsumePrefix() // -// Strips the `expected` prefix from the start of the given string, returning -// `true` if the strip operation succeeded or false otherwise. +// Strips the `expected` prefix, if found, from the start of `str`. +// If the operation succeeded, `true` is returned. If not, `false` +// is returned and `str` is not modified. // // Example: // @@ -49,8 +50,9 @@ inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) { } // ConsumeSuffix() // -// Strips the `expected` suffix from the end of the given string, returning -// `true` if the strip operation succeeded or false otherwise. +// Strips the `expected` prefix, if found, from the end of `str`. +// If the operation succeeded, `true` is returned. If not, `false` +// is returned and `str` is not modified. // // Example: // @@ -65,7 +67,7 @@ inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) { // StripPrefix() // -// Returns a view into the input string 'str' with the given 'prefix' removed, +// Returns a view into the input string `str` with the given `prefix` removed, // but leaving the original string intact. If the prefix does not match at the // start of the string, returns the original string instead. ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix( @@ -76,7 +78,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripPrefix( // StripSuffix() // -// Returns a view into the input string 'str' with the given 'suffix' removed, +// Returns a view into the input string `str` with the given `suffix` removed, // but leaving the original string intact. If the suffix does not match at the // end of the string, returns the original string instead. ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix( -- cgit v1.2.3 From 9aa7d0bd2079f287162d4fd0722a1b9032e39a6a Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 11 May 2022 22:05:28 -0700 Subject: Fix spelling error "charachter" PiperOrigin-RevId: 448159349 Change-Id: I6b25a90d8a3b6d3a888274d156aa696d77fb042d --- absl/synchronization/internal/waiter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index be3df180..4f4f692a 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -136,7 +136,7 @@ class Waiter { // REQUIRES: WinHelper::GetLock(this) must be held. void InternalCondVarPoke(); - // We can't include Windows.h in our headers, so we use aligned charachter + // We can't include Windows.h in our headers, so we use aligned character // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE. alignas(void*) unsigned char mu_storage_[sizeof(void*)]; alignas(void*) unsigned char cv_storage_[sizeof(void*)]; -- cgit v1.2.3 From 3af16ffea816504d44a60fcc083d9f58b7d40d0d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Thu, 12 May 2022 13:38:33 -0700 Subject: Fix an msan warning in cord_ringbuffer_test Stop the absl::Cord destructor from running on the constinit cord in CordTest.ConstinitConstructor. This allows inspecting the cord at any point while the test is exiting, which is important for the semantics of the test. PiperOrigin-RevId: 448327386 Change-Id: Icef9faa2b63f1f0ae60b3430dcf6184f5dead885 --- absl/strings/cord_test.cc | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index f69209de..4261c99a 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -2241,12 +2241,34 @@ class AfterExitCordTester { absl::string_view expected_; }; +// Deliberately prevents the destructor for an absl::Cord from running. The cord +// is accessible via the cord member during the lifetime of the CordLeaker. +// After the CordLeaker is destroyed, pointers to the cord will remain valid +// until the CordLeaker's memory is deallocated. +struct CordLeaker { + union { + absl::Cord cord; + }; + + template + constexpr explicit CordLeaker(const Str& str) : cord(str) {} + + ~CordLeaker() { + // Don't do anything, including running cord's destructor. (cord's + // destructor won't run automatically because cord is hidden inside a + // union.) + } +}; + template void TestConstinitConstructor(Str) { const auto expected = Str::value; // Defined before `cord` to be destroyed after it. static AfterExitCordTester exit_tester; // NOLINT - ABSL_CONST_INIT static absl::Cord cord(Str{}); // NOLINT + ABSL_CONST_INIT static CordLeaker cord_leaker(Str{}); // NOLINT + // cord_leaker is static, so this reference will remain valid through the end + // of program execution. + static absl::Cord& cord = cord_leaker.cord; static bool init_exit_tester = exit_tester.Set(&cord, expected); (void)init_exit_tester; -- cgit v1.2.3 From f7a250aa9ae11ed88645e0f0ce3a583f44443cc7 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 12 May 2022 16:07:27 -0700 Subject: Internal change PiperOrigin-RevId: 448361090 Change-Id: Iec6063b88a778dfe815081612650eaa456503265 --- absl/random/beta_distribution_test.cc | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc index 9a3eb2b7..c16fbb4f 100644 --- a/absl/random/beta_distribution_test.cc +++ b/absl/random/beta_distribution_test.cc @@ -45,15 +45,25 @@ namespace { template class BetaDistributionInterfaceTest : public ::testing::Test {}; -// double-double arithmetic is not supported well by either GCC or Clang; see -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048, -// https://bugs.llvm.org/show_bug.cgi?id=49131, and -// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests -// with double doubles until compiler support is better. -using RealTypes = - std::conditional, - ::testing::Types>::type; +constexpr bool ShouldExerciseLongDoubleTests() { + // long double arithmetic is not supported well by either GCC or Clang on + // most platforms specifically not when implemented in terms of double-double; + // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048, + // https://bugs.llvm.org/show_bug.cgi?id=49131, and + // https://bugs.llvm.org/show_bug.cgi?id=49132. + // So a conservative choice here is to disable long-double tests pretty much + // everywhere except on x64 but only if long double is not implemented as + // double-double. +#if defined(__i686__) && defined(__x86_64__) + return !absl::numeric_internal::IsDoubleDouble(); +#else + return false; +#endif +} + +using RealTypes = std::conditional, + ::testing::Types>::type; TYPED_TEST_SUITE(BetaDistributionInterfaceTest, RealTypes); TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { -- cgit v1.2.3 From 0eb5ac542531d897646caa04a0ceec564772df3c Mon Sep 17 00:00:00 2001 From: Samuel Benzaquen Date: Fri, 13 May 2022 14:50:25 -0700 Subject: Improve compiler errors for mismatched ParsedFormat inputs. PiperOrigin-RevId: 448582508 Change-Id: I67fbff5f42a083e093ea2c20749e073ca03feb0b --- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 1 + absl/strings/internal/str_format/bind.h | 42 ++++++++++++++++++++++++++++----- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 1cf58ca1..e9092165 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -1116,6 +1116,7 @@ cc_library( "//absl/numeric:representation", "//absl/types:optional", "//absl/types:span", + "//absl/utility", ], ) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index c4358a12..a1b8d6ed 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -414,6 +414,7 @@ absl_cc_library( absl::core_headers absl::numeric_representation absl::type_traits + absl::utility absl::int128 absl::span ) diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index b26cff66..dcaa5dd0 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h @@ -25,6 +25,7 @@ #include "absl/strings/internal/str_format/checker.h" #include "absl/strings/internal/str_format/parser.h" #include "absl/types/span.h" +#include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -87,6 +88,36 @@ class FormatSpecTemplate : public MakeDependent::type { using Base = typename MakeDependent::type; + template + struct ErrorMaker { + constexpr bool operator()(int) const { return res; } + }; + + template + static constexpr bool CheckArity(ErrorMaker SpecifierCount = {}, + ErrorMaker ParametersPassed = {}) { + static_assert(SpecifierCount(i) == ParametersPassed(j), + "Number of arguments passed must match the number of " + "conversion specifiers."); + return true; + } + + template + static constexpr bool CheckMatch( + ErrorMaker MismatchedArgumentNumber = {}) { + static_assert(MismatchedArgumentNumber(arg), + "Passed argument must match specified format."); + return true; + } + + template + static bool CheckMatches(absl::index_sequence) { + bool res[] = {true, CheckMatch()...}; + (void)res; + return true; + } + public: #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER @@ -133,13 +164,12 @@ class FormatSpecTemplate #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - template < - FormatConversionCharSet... C, - typename = typename std::enable_if::type, - typename = typename std::enable_if::type> + template FormatSpecTemplate(const ExtendedParsedFormat& pc) // NOLINT - : Base(&pc) {} + : Base(&pc) { + CheckArity(); + CheckMatches(absl::make_index_sequence{}); + } }; class Streamable { -- cgit v1.2.3 From 63607288c14f87999e2b3914dd64e690d75f38f9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 16 May 2022 15:25:59 -0700 Subject: Add a stress test for base_internal::ThreadIdentity reuse. PiperOrigin-RevId: 449067700 Change-Id: I972b1736c28d76ed500e9ad6fd15c7a469a5825f --- absl/synchronization/internal/per_thread_sem_test.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc index db1184e6..24a6b548 100644 --- a/absl/synchronization/internal/per_thread_sem_test.cc +++ b/absl/synchronization/internal/per_thread_sem_test.cc @@ -174,6 +174,15 @@ TEST_F(PerThreadSemTest, Timeouts) { EXPECT_TRUE(Wait(negative_timeout)); } +TEST_F(PerThreadSemTest, ThreadIdentityReuse) { + // Create a base_internal::ThreadIdentity object and keep reusing it. There + // should be no memory or resource leaks. + for (int i = 0; i < 10000; i++) { + std::thread t([]() { GetOrCreateCurrentThreadIdentity(); }); + t.join(); + } +} + } // namespace } // namespace synchronization_internal -- cgit v1.2.3 From aac2279f22eef04d01fc42e66fc183a32f08a9b4 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 17 May 2022 01:42:50 -0700 Subject: absl: fix live-lock in CondVar CondVar::WaitWithTimeout can live-lock when timeout is racing with Signal/SignalAll and Signal/SignalAll thread is not scheduled due to priorities, affinity or other scheduler artifacts. This could lead to stalls of up to tens of seconds in some cases. PiperOrigin-RevId: 449159670 Change-Id: I64bbd277c1f91964cfba3306ba8a80eeadf85f64 --- absl/synchronization/mutex.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 821047fd..52e2455d 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -2575,6 +2575,23 @@ bool CondVar::WaitCommon(Mutex *mutex, KernelTimeout t) { while (waitp.thread->state.load(std::memory_order_acquire) == PerThreadSynch::kQueued) { if (!Mutex::DecrementSynchSem(mutex, waitp.thread, t)) { + // DecrementSynchSem returned due to timeout. + // Now we will either (1) remove ourselves from the wait list in Remove + // below, in which case Remove will set thread.state = kAvailable and + // we will not call DecrementSynchSem again; or (2) Signal/SignalAll + // has removed us concurrently and is calling Wakeup, which will set + // thread.state = kAvailable and post to the semaphore. + // It's important to reset the timeout for the case (2) because otherwise + // we can live-lock in this loop since DecrementSynchSem will always + // return immediately due to timeout, but Signal/SignalAll is not + // necessary set thread.state = kAvailable yet (and is not scheduled + // due to thread priorities or other scheduler artifacts). + // Note this could also be resolved if Signal/SignalAll would set + // thread.state = kAvailable while holding the wait list spin lock. + // But this can't be easily done for SignalAll since it grabs the whole + // wait list with a single compare-exchange and does not really grab + // the spin lock. + t = KernelTimeout::Never(); this->Remove(waitp.thread); rc = true; } -- cgit v1.2.3 From 9444b11e0c4e1f079c87067b5bbab1c5ff718809 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 17 May 2022 01:44:42 -0700 Subject: absl: fix use-after-free in Mutex/CondVar Both Mutex and CondVar signal PerThreadSem/Waiter after satisfying the wait condition, as the result the waiting thread may return w/o waiting on the PerThreadSem/Waiter at all. If the waiting thread then exits, it currently destroys Waiter object. As the result Waiter::Post can be called on already destroyed object. PerThreadSem/Waiter must be type-stable after creation and must not be destroyed. The futex-based implementation is the only one that is not affected by the bug since there is effectively nothing to destroy (maybe only UBSan/ASan could complain about calling methods on a destroyed object). Here is the problematic sequence of events: 1: void Mutex::Block(PerThreadSynch *s) { 2: while (s->state.load(std::memory_order_acquire) == PerThreadSynch::kQueued) { 3: if (!DecrementSynchSem(this, s, s->waitp->timeout)) { 4: PerThreadSynch *Mutex::Wakeup(PerThreadSynch *w) { 5: ... 6: w->state.store(PerThreadSynch::kAvailable, std::memory_order_release); 7: IncrementSynchSem(this, w); 8: ... 9: } Consider line 6 is executed, then line 2 observes kAvailable and line 3 is not called. The thread executing Mutex::Block returns from the method, acquires the mutex, releases the mutex, exits and destroys PerThreadSem/Waiter. Now Mutex::Wakeup resumes and executes line 7 on the destroyed object. Boom! CondVar uses a similar pattern. Moreover the semaphore-based Waiter implementation is not even destruction-safe (the Waiter cannot be used to signal own destruction). So even if Mutex/CondVar would always pair Waiter::Post with Waiter::Wait before destroying PerThreadSem/Waiter, it would still be subject to use-after-free bug on the semaphore. PiperOrigin-RevId: 449159939 Change-Id: I497134fa8b6ce1294a422827c5f0de0e897cea31 --- .../internal/create_thread_identity.cc | 15 ++++++++----- .../internal/create_thread_identity.h | 4 ---- absl/synchronization/internal/per_thread_sem.cc | 4 ---- absl/synchronization/internal/per_thread_sem.h | 7 +----- absl/synchronization/internal/waiter.cc | 25 --------------------- absl/synchronization/internal/waiter.h | 12 +++++++--- absl/synchronization/mutex_test.cc | 26 ++++++++++++++++++++++ 7 files changed, 45 insertions(+), 48 deletions(-) diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index 53a71b34..44e6129b 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc @@ -38,7 +38,7 @@ ABSL_CONST_INIT static base_internal::ThreadIdentity* thread_identity_freelist; // A per-thread destructor for reclaiming associated ThreadIdentity objects. // Since we must preserve their storage we cache them for re-use. -void ReclaimThreadIdentity(void* v) { +static void ReclaimThreadIdentity(void* v) { base_internal::ThreadIdentity* identity = static_cast(v); @@ -48,8 +48,6 @@ void ReclaimThreadIdentity(void* v) { base_internal::LowLevelAlloc::Free(identity->per_thread_synch.all_locks); } - PerThreadSem::Destroy(identity); - // We must explicitly clear the current thread's identity: // (a) Subsequent (unrelated) per-thread destructors may require an identity. // We must guarantee a new identity is used in this case (this instructor @@ -71,7 +69,12 @@ static intptr_t RoundUp(intptr_t addr, intptr_t align) { return (addr + align - 1) & ~(align - 1); } -static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) { +void OneTimeInitThreadIdentity(base_internal::ThreadIdentity* identity) { + PerThreadSem::Init(identity); +} + +static void ResetThreadIdentityBetweenReuse( + base_internal::ThreadIdentity* identity) { base_internal::PerThreadSynch* pts = &identity->per_thread_synch; pts->next = nullptr; pts->skip = nullptr; @@ -116,8 +119,9 @@ static base_internal::ThreadIdentity* NewThreadIdentity() { identity = reinterpret_cast( RoundUp(reinterpret_cast(allocation), base_internal::PerThreadSynch::kAlignment)); + OneTimeInitThreadIdentity(identity); } - ResetThreadIdentity(identity); + ResetThreadIdentityBetweenReuse(identity); return identity; } @@ -127,7 +131,6 @@ static base_internal::ThreadIdentity* NewThreadIdentity() { // REQUIRES: CurrentThreadIdentity(false) == nullptr base_internal::ThreadIdentity* CreateThreadIdentity() { base_internal::ThreadIdentity* identity = NewThreadIdentity(); - PerThreadSem::Init(identity); // Associate the value with the current thread, and attach our destructor. base_internal::SetCurrentThreadIdentity(identity, ReclaimThreadIdentity); return identity; diff --git a/absl/synchronization/internal/create_thread_identity.h b/absl/synchronization/internal/create_thread_identity.h index e121f683..4cfde091 100644 --- a/absl/synchronization/internal/create_thread_identity.h +++ b/absl/synchronization/internal/create_thread_identity.h @@ -36,10 +36,6 @@ namespace synchronization_internal { // For private use only. base_internal::ThreadIdentity* CreateThreadIdentity(); -// A per-thread destructor for reclaiming associated ThreadIdentity objects. -// For private use only. -void ReclaimThreadIdentity(void* v); - // Returns the ThreadIdentity object representing the calling thread; guaranteed // to be unique for its lifetime. The returned object will remain valid for the // program's lifetime; although it may be re-assigned to a subsequent thread. diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc index a6031787..469e8f32 100644 --- a/absl/synchronization/internal/per_thread_sem.cc +++ b/absl/synchronization/internal/per_thread_sem.cc @@ -47,10 +47,6 @@ void PerThreadSem::Init(base_internal::ThreadIdentity *identity) { identity->is_idle.store(false, std::memory_order_relaxed); } -void PerThreadSem::Destroy(base_internal::ThreadIdentity *identity) { - Waiter::GetWaiter(identity)->~Waiter(); -} - void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) { const int ticker = identity->ticker.fetch_add(1, std::memory_order_relaxed) + 1; diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h index 7beae8ef..90a88809 100644 --- a/absl/synchronization/internal/per_thread_sem.h +++ b/absl/synchronization/internal/per_thread_sem.h @@ -66,10 +66,6 @@ class PerThreadSem { // REQUIRES: May only be called by ThreadIdentity. static void Init(base_internal::ThreadIdentity* identity); - // Destroy the PerThreadSem associated with "identity". - // REQUIRES: May only be called by ThreadIdentity. - static void Destroy(base_internal::ThreadIdentity* identity); - // Increments "identity"'s count. static inline void Post(base_internal::ThreadIdentity* identity); @@ -81,8 +77,7 @@ class PerThreadSem { // Permitted callers. friend class PerThreadSemTest; friend class absl::Mutex; - friend absl::base_internal::ThreadIdentity* CreateThreadIdentity(); - friend void ReclaimThreadIdentity(void* v); + friend void OneTimeInitThreadIdentity(absl::base_internal::ThreadIdentity*); }; } // namespace synchronization_internal diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 28ef311e..f2051d67 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -71,8 +71,6 @@ Waiter::Waiter() { futex_.store(0, std::memory_order_relaxed); } -Waiter::~Waiter() = default; - bool Waiter::Wait(KernelTimeout t) { // Loop until we can atomically decrement futex from a positive // value, waiting on a futex while we believe it is zero. @@ -161,18 +159,6 @@ Waiter::Waiter() { wakeup_count_ = 0; } -Waiter::~Waiter() { - const int err = pthread_mutex_destroy(&mu_); - if (err != 0) { - ABSL_RAW_LOG(FATAL, "pthread_mutex_destroy failed: %d", err); - } - - const int err2 = pthread_cond_destroy(&cv_); - if (err2 != 0) { - ABSL_RAW_LOG(FATAL, "pthread_cond_destroy failed: %d", err2); - } -} - bool Waiter::Wait(KernelTimeout t) { struct timespec abs_timeout; if (t.has_timeout()) { @@ -240,12 +226,6 @@ Waiter::Waiter() { wakeups_.store(0, std::memory_order_relaxed); } -Waiter::~Waiter() { - if (sem_destroy(&sem_) != 0) { - ABSL_RAW_LOG(FATAL, "sem_destroy failed with errno %d\n", errno); - } -} - bool Waiter::Wait(KernelTimeout t) { struct timespec abs_timeout; if (t.has_timeout()) { @@ -363,11 +343,6 @@ Waiter::Waiter() { wakeup_count_ = 0; } -// SRW locks and condition variables do not need to be explicitly destroyed. -// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock -// https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with -Waiter::~Waiter() = default; - bool Waiter::Wait(KernelTimeout t) { SRWLOCK *mu = WinHelper::GetLock(this); CONDITION_VARIABLE *cv = WinHelper::GetCond(this); diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 4f4f692a..b8adfeb5 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -71,9 +71,6 @@ class Waiter { Waiter(const Waiter&) = delete; Waiter& operator=(const Waiter&) = delete; - // Destroy any data to track waits. - ~Waiter(); - // Blocks the calling thread until a matching call to `Post()` or // `t` has passed. Returns `true` if woken (`Post()` called), // `false` on timeout. @@ -106,6 +103,12 @@ class Waiter { #endif private: + // The destructor must not be called since Mutex/CondVar + // can use PerThreadSem/Waiter after the thread exits. + // Waiter objects are embedded in ThreadIdentity objects, + // which are reused via a freelist and are never destroyed. + ~Waiter() = delete; + #if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX // Futexes are defined by specification to be 32-bits. // Thus std::atomic must be just an int32_t with lockfree methods. @@ -138,6 +141,9 @@ class Waiter { // We can't include Windows.h in our headers, so we use aligned character // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE. + // SRW locks and condition variables do not need to be explicitly destroyed. + // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock + // https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with alignas(void*) unsigned char mu_storage_[sizeof(void*)]; alignas(void*) unsigned char cv_storage_[sizeof(void*)]; int waiter_count_; diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc index 4f403176..99bb0175 100644 --- a/absl/synchronization/mutex_test.cc +++ b/absl/synchronization/mutex_test.cc @@ -1704,4 +1704,30 @@ TEST(Mutex, MuTime) { EXPECT_EQ(RunTest(&TestMuTime, threads, iterations, 1), threads * iterations); } +TEST(Mutex, SignalExitedThread) { + // The test may expose a race when Mutex::Unlock signals a thread + // that has already exited. +#if defined(__wasm__) || defined(__asmjs__) + constexpr int kThreads = 1; // OOMs under WASM +#else + constexpr int kThreads = 100; +#endif + std::vector top; + for (unsigned i = 0; i < 2 * std::thread::hardware_concurrency(); i++) { + top.emplace_back([&]() { + for (int i = 0; i < kThreads; i++) { + absl::Mutex mu; + std::thread t([&]() { + mu.Lock(); + mu.Unlock(); + }); + mu.Lock(); + mu.Unlock(); + t.join(); + } + }); + } + for (auto &th : top) th.join(); +} + } // namespace -- cgit v1.2.3 From 7d3b4c86928ff7b17b3b881d42355062c23f6b44 Mon Sep 17 00:00:00 2001 From: Andy Getzendanner Date: Tue, 17 May 2022 13:49:22 -0700 Subject: raw_logging: Extract the inlined no-hook-registered behavior for LogPrefixHook to a default implementation. PiperOrigin-RevId: 449306617 Change-Id: Ia3e87d2edcae7e9874998f21a0e2ff245e48fd96 --- absl/base/internal/raw_logging.cc | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index ca2a75f3..8c49f9a6 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -79,12 +79,6 @@ namespace { // Explicitly `#error` out when not `ABSL_LOW_LEVEL_WRITE_SUPPORTED`, except for // a selected set of platforms for which we expect not to be able to raw log. -ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES -absl::base_internal::AtomicHook - log_filter_and_prefix_hook; -ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES -absl::base_internal::AtomicHook abort_hook; - #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED constexpr char kTruncated[] = " ... (message truncated)\n"; @@ -132,6 +126,18 @@ bool DoRawLog(char** buf, int* size, const char* format, ...) { return true; } +bool DefaultLogFilterAndPrefix(absl::LogSeverity, const char* file, int line, + char** buf, int* buf_size) { + DoRawLog(buf, buf_size, "[%s : %d] RAW: ", file, line); + return true; +} + +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +absl::base_internal::AtomicHook + log_filter_and_prefix_hook(DefaultLogFilterAndPrefix); +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +absl::base_internal::AtomicHook abort_hook; + void RawLogVA(absl::LogSeverity severity, const char* file, int line, const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0); void RawLogVA(absl::LogSeverity severity, const char* file, int line, @@ -152,14 +158,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } #endif - auto log_filter_and_prefix_hook_ptr = log_filter_and_prefix_hook.Load(); - if (log_filter_and_prefix_hook_ptr) { - enabled = log_filter_and_prefix_hook_ptr(severity, file, line, &buf, &size); - } else { - if (enabled) { - DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line); - } - } + enabled = log_filter_and_prefix_hook(severity, file, line, &buf, &size); const char* const prefix_end = buf; #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED -- cgit v1.2.3 From e92505d8d6bf50c5d44805eb5fe73e9f56484cf4 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Tue, 17 May 2022 15:21:04 -0700 Subject: Use NullSafeStringView for const char* args to absl::StrCat, treating null pointers as "" Fixes #1167 PiperOrigin-RevId: 449328725 Change-Id: I813785db77b94efa49eeeff4c93449334c380935 --- absl/strings/str_cat.h | 3 ++- absl/strings/str_cat_test.cc | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index 4d228b09..a94bc5df 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -251,7 +251,8 @@ class AlphaNum { const strings_internal::AlphaNumBuffer& buf) : piece_(&buf.data[0], buf.size) {} - AlphaNum(const char* c_str) : piece_(c_str) {} // NOLINT(runtime/explicit) + AlphaNum(const char* c_str) // NOLINT(runtime/explicit) + : piece_(NullSafeStringView(c_str)) {} // NOLINT(runtime/explicit) AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit) template diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc index f3770dc0..69df2502 100644 --- a/absl/strings/str_cat_test.cc +++ b/absl/strings/str_cat_test.cc @@ -210,6 +210,11 @@ TEST(StrCat, CornerCases) { EXPECT_EQ(result, ""); } +TEST(StrCat, NullConstCharPtr) { + const char* null = nullptr; + EXPECT_EQ(absl::StrCat("mon", null, "key"), "monkey"); +} + // A minimal allocator that uses malloc(). template struct Mallocator { -- cgit v1.2.3 From 9df63a8beaae53802109346f0a20328140a7cb8e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 17 May 2022 17:10:09 -0700 Subject: Cast away an unused variable to play nice with -Wunused-but-set-variable. PiperOrigin-RevId: 449351955 Change-Id: Id30280107bb29f7d715327b99a2c954809513a48 --- absl/base/internal/raw_logging.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 8c49f9a6..54e71a3f 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -174,6 +174,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, #else static_cast(format); static_cast(ap); + static_cast(enabled); #endif // Abort the process after logging a FATAL message, even if the output itself -- cgit v1.2.3 From 3e04aade4e7a53aebbbed1a1268117f1f522bfb0 Mon Sep 17 00:00:00 2001 From: Greg Falcon Date: Wed, 18 May 2022 10:01:14 -0700 Subject: Replace direct uses of __builtin_prefetch from SwissTable with the wrapper functions. Add a new (internal) feature test macro to detect whether the wrappers are no-ops on a given platform. Note that one-arg __builtin_prefetch(x) is equivalent to __builtin_prefetch(x, 0, 3), per `man BUILTIN_PREFETCH(3)` and gcc docs. PiperOrigin-RevId: 449508660 Change-Id: I144e750205eec0c956d8dd62bc72e10bdb87c4f7 --- absl/base/internal/prefetch.h | 2 ++ absl/container/BUILD.bazel | 2 ++ absl/container/CMakeLists.txt | 2 ++ absl/container/internal/raw_hash_set.h | 14 +++++++------- absl/container/internal/raw_hash_set_test.cc | 1 + 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/absl/base/internal/prefetch.h b/absl/base/internal/prefetch.h index a71b3897..d4971a68 100644 --- a/absl/base/internal/prefetch.h +++ b/absl/base/internal/prefetch.h @@ -72,6 +72,8 @@ void PrefetchNta(const void* addr); #if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__) +#define ABSL_INTERNAL_HAVE_PREFETCH 1 + // See __builtin_prefetch: // https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html. // diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index d733cb24..9ef7066c 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -589,6 +589,7 @@ cc_library( "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", + "//absl/base:prefetch", "//absl/memory", "//absl/meta:type_traits", "//absl/numeric:bits", @@ -611,6 +612,7 @@ cc_test( "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:prefetch", "//absl/base:raw_logging_internal", "//absl/strings", "@com_google_googletest//:gtest_main", diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index aad69fa2..9b5c59a4 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -681,6 +681,7 @@ absl_cc_library( absl::memory absl::meta absl::optional + absl::prefetch absl::utility absl::hashtablez_sampler PUBLIC @@ -702,6 +703,7 @@ absl_cc_test( absl::base absl::config absl::core_headers + absl::prefetch absl::raw_logging_internal absl::strings GTest::gmock_main diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 56251b22..769af50f 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -197,6 +197,7 @@ #include "absl/base/config.h" #include "absl/base/internal/endian.h" +#include "absl/base/internal/prefetch.h" #include "absl/base/optimization.h" #include "absl/base/port.h" #include "absl/container/internal/common.h" @@ -1636,12 +1637,13 @@ class raw_hash_set { template void prefetch(const key_arg& key) const { (void)key; -#if defined(__GNUC__) + // Avoid probing if we won't be able to prefetch the addresses received. +#ifdef ABSL_INTERNAL_HAVE_PREFETCH prefetch_heap_block(); auto seq = probe(ctrl_, hash_ref()(key), capacity_); - __builtin_prefetch(static_cast(ctrl_ + seq.offset())); - __builtin_prefetch(static_cast(slots_ + seq.offset())); -#endif // __GNUC__ + base_internal::PrefetchT0(ctrl_ + seq.offset()); + base_internal::PrefetchT0(slots_ + seq.offset()); +#endif // ABSL_INTERNAL_HAVE_PREFETCH } // The API of find() has two extensions. @@ -2159,9 +2161,7 @@ class raw_hash_set { // This is intended to overlap with execution of calculating the hash for a // key. void prefetch_heap_block() const { -#if defined(__GNUC__) - __builtin_prefetch(static_cast(ctrl_), 0, 1); -#endif // __GNUC__ + base_internal::PrefetchT2(ctrl_); } HashtablezInfoHandle& infoz() { return settings_.template get<1>(); } diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 9cd88a28..914ec0c9 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -31,6 +31,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/cycleclock.h" +#include "absl/base/internal/prefetch.h" #include "absl/base/internal/raw_logging.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_function_defaults.h" -- cgit v1.2.3 From a168dd01438976657ddcfe09660c738dd73f8726 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 18 May 2022 16:13:48 -0700 Subject: Use SSE instructions for prefetch when __builtin_prefetch is unavailable This notably gets prefetch working on MSVC Implementation note: https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-prefetchcacheline MSVC does have PreFetchCacheLine, but that would require including in a header PiperOrigin-RevId: 449602543 Change-Id: I5e6ca4b7c3d287779aa03c2fd348b41fb65c3680 --- absl/base/config.h | 15 +++++++++++++++ absl/base/internal/prefetch.h | 27 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/absl/base/config.h b/absl/base/config.h index 3f5ace3f..a0d599fe 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -837,6 +837,21 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAS_RTTI 1 #endif // !defined(__GNUC__) || defined(__GXX_RTTI) +// ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support. +// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of +// which architectures support the various x86 instruction sets. +#ifdef ABSL_INTERNAL_HAVE_SSE +#error ABSL_INTERNAL_HAVE_SSE cannot be directly set +#elif defined(__SSE__) +#define ABSL_INTERNAL_HAVE_SSE 1 +#elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) +// MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1 +// indicates that at least SSE was targeted with the /arch:SSE option. +// All x86-64 processors support SSE, so support can be assumed. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +#define ABSL_INTERNAL_HAVE_SSE 1 +#endif + // ABSL_INTERNAL_HAVE_SSE2 is used for compile-time detection of SSE2 support. // See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of // which architectures support the various x86 instruction sets. diff --git a/absl/base/internal/prefetch.h b/absl/base/internal/prefetch.h index d4971a68..06419283 100644 --- a/absl/base/internal/prefetch.h +++ b/absl/base/internal/prefetch.h @@ -17,6 +17,15 @@ #include "absl/base/config.h" +#ifdef __SSE__ +#include +#endif + +#if defined(_MSC_VER) && defined(ABSL_INTERNAL_HAVE_SSE) +#include +#pragma intrinsic(_mm_prefetch) +#endif + // Compatibility wrappers around __builtin_prefetch, to prefetch data // for read if supported by the toolchain. @@ -97,6 +106,24 @@ inline void PrefetchNta(const void* addr) { // Note: this uses prefetchtnta on Intel. __builtin_prefetch(addr, 0, 0); } + +#elif defined(ABSL_INTERNAL_HAVE_SSE) + +#define ABSL_INTERNAL_HAVE_PREFETCH 1 + +inline void PrefetchT0(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T0); +} +inline void PrefetchT1(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T1); +} +inline void PrefetchT2(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_T2); +} +inline void PrefetchNta(const void* addr) { + _mm_prefetch(reinterpret_cast(addr), _MM_HINT_NTA); +} + #else inline void PrefetchT0(const void*) {} inline void PrefetchT1(const void*) {} -- cgit v1.2.3 From b39a7000a007e66f63c65b2490628ea34026e5e6 Mon Sep 17 00:00:00 2001 From: Tom Rybka Date: Thu, 19 May 2022 18:21:45 -0700 Subject: Don't default to the unscaled cycle clock on any Apple targets. Previously was disabled on iPhone, but still enabled for macOS. The unscaled cycle clock does not work correctly when run on a VM. PiperOrigin-RevId: 449876559 Change-Id: I679ade90b43462e8d2794b1a2b32569d59029ed9 --- absl/base/internal/unscaledcycleclock.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index a4351406..2cbeae31 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -59,8 +59,7 @@ // CycleClock that runs at atleast 1 MHz. We've found some Android // ARM64 devices where this is not the case, so we disable it by // default on Android ARM64. -#if defined(__native_client__) || \ - (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ +#if defined(__native_client__) || (defined(__APPLE__)) || \ (defined(__ANDROID__) && defined(__aarch64__)) #define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 #else -- cgit v1.2.3 From b8bbe92f84ffe1e249016cfe8b79efdffb7a35c1 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 19 May 2022 22:53:29 -0700 Subject: Change workaround for MSVC bug regarding compile-time initialization to trigger from MSC_VER 1910 to 1930. 1929 is the last _MSC_VER for Visual Studio 2019. PiperOrigin-RevId: 449909831 Change-Id: Ibca931cc31131235eba55d2a1b97c7a062f059db --- absl/time/time.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/time/time.h b/absl/time/time.h index 69d72208..f284aa37 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -162,7 +162,7 @@ class Duration { constexpr Duration() : rep_hi_(0), rep_lo_(0) {} // zero-length duration // Copyable. -#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1910 +#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1930 // Explicitly defining the constexpr copy constructor avoids an MSVC bug. constexpr Duration(const Duration& d) : rep_hi_(d.rep_hi_), rep_lo_(d.rep_lo_) {} -- cgit v1.2.3 From 9e408e050ff3c1db12f9a58081b6af10e05561c4 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Fri, 20 May 2022 16:10:45 -0700 Subject: Cord: workaround a GCC 12.1 bug that triggers a spurious warning See the GCC bug report https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105585 and Abseil bug report https://github.com/abseil/abseil-cpp/issues/1175 Fixes #1175 PiperOrigin-RevId: 450083136 Change-Id: I207aaffaec9166b335065dd6ef148a721b94048e --- absl/strings/internal/cord_internal.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index 6ae418e7..b50fb79a 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h @@ -632,7 +632,9 @@ inline const CordRepExternal* CordRep::external() const { } inline CordRep* CordRep::Ref(CordRep* rep) { - assert(rep != nullptr); + // ABSL_ASSUME is a workaround for + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105585 + ABSL_ASSUME(rep != nullptr); rep->refcount.Increment(); return rep; } -- cgit v1.2.3 From 09eac4d507d010b533665f75673d17c07d3934c6 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 23 May 2022 08:31:05 -0700 Subject: Import of CCTZ from GitHub. PiperOrigin-RevId: 450445030 Change-Id: I1c1e5ed67f81a181454f7fc6751bf42a3bc2bc48 --- absl/time/internal/cctz/src/time_zone_format_test.cc | 16 ++++++++++++++-- absl/time/internal/cctz/src/time_zone_lookup_test.cc | 10 +++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc index 6487fa93..f1f79a20 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -18,11 +18,15 @@ #include #include +#include "absl/base/config.h" +#include "absl/time/internal/cctz/include/cctz/time_zone.h" +#if defined(__linux__) +#include +#endif + #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" -#include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace chrono = std::chrono; @@ -183,8 +187,10 @@ TEST(Format, PosixConversions) { TestFormatSpecifier(tp, tz, "%F", "1970-01-01"); TestFormatSpecifier(tp, tz, "%g", "70"); TestFormatSpecifier(tp, tz, "%G", "1970"); +#if defined(__GLIBC__) TestFormatSpecifier(tp, tz, "%k", " 0"); TestFormatSpecifier(tp, tz, "%l", "12"); +#endif TestFormatSpecifier(tp, tz, "%n", "\n"); TestFormatSpecifier(tp, tz, "%R", "00:00"); TestFormatSpecifier(tp, tz, "%t", "\t"); @@ -216,7 +222,9 @@ TEST(Format, LocaleSpecific) { #if defined(__linux__) // SU/C99/TZ extensions TestFormatSpecifier(tp, tz, "%h", "Jan"); // Same as %b +#if defined(__GLIBC__) TestFormatSpecifier(tp, tz, "%P", "am"); +#endif TestFormatSpecifier(tp, tz, "%r", "12:00:00 AM"); // Modified conversion specifiers %E_ @@ -1045,9 +1053,11 @@ TEST(Parse, LocaleSpecific) { EXPECT_TRUE(parse("%h", "Feb", tz, &tp)); EXPECT_EQ(2, convert(tp, tz).month()); // Equivalent to %b +#if defined(__GLIBC__) tp = reset; EXPECT_TRUE(parse("%l %p", "5 PM", tz, &tp)); EXPECT_EQ(17, convert(tp, tz).hour()); +#endif tp = reset; EXPECT_TRUE(parse("%r", "03:44:55 PM", tz, &tp)); @@ -1055,6 +1065,7 @@ TEST(Parse, LocaleSpecific) { EXPECT_EQ(44, convert(tp, tz).minute()); EXPECT_EQ(55, convert(tp, tz).second()); +#if defined(__GLIBC__) tp = reset; EXPECT_TRUE(parse("%Ec", "Tue Nov 19 05:06:07 2013", tz, &tp)); EXPECT_EQ(convert(civil_second(2013, 11, 19, 5, 6, 7), tz), tp); @@ -1126,6 +1137,7 @@ TEST(Parse, LocaleSpecific) { EXPECT_TRUE(parse("%Oy", "04", tz, &tp)); EXPECT_EQ(2004, convert(tp, tz).year()); #endif +#endif } TEST(Parse, ExtendedSeconds) { diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 134d1430..27a53c5c 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -21,10 +21,14 @@ #include #include -#include "gtest/gtest.h" #include "absl/base/config.h" -#include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" +#if defined(__linux__) +#include +#endif + +#include "gtest/gtest.h" +#include "absl/time/internal/cctz/include/cctz/civil_time.h" namespace chrono = std::chrono; @@ -1043,7 +1047,7 @@ TEST(MakeTime, LocalTimeLibC) { // 1) we know how to change the time zone used by localtime()/mktime(), // 2) cctz and localtime()/mktime() will use similar-enough tzdata, and // 3) we have some idea about how mktime() behaves during transitions. -#if defined(__linux__) && !defined(__ANDROID__) +#if defined(__linux__) && defined(__GLIBC__) && !defined(__ANDROID__) const char* const ep = getenv("TZ"); std::string tz_name = (ep != nullptr) ? ep : ""; for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) { -- cgit v1.2.3 From 2d492a13c652df2d3fa9b4712cb4ac91f120478c Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Mon, 23 May 2022 08:36:09 -0700 Subject: Update Bazel used on MacOS CI PiperOrigin-RevId: 450446058 Change-Id: I22a878bf04cf56b8a0e1dd049353acd2f6933828 --- ci/macos_xcode_bazel.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/macos_xcode_bazel.sh b/ci/macos_xcode_bazel.sh index 1e29311f..8d69f1de 100755 --- a/ci/macos_xcode_bazel.sh +++ b/ci/macos_xcode_bazel.sh @@ -24,7 +24,7 @@ if [[ -z ${ABSEIL_ROOT:-} ]]; then fi # If we are running on Kokoro, check for a versioned Bazel binary. -KOKORO_GFILE_BAZEL_BIN="bazel-3.7.0-darwin-x86_64" +KOKORO_GFILE_BAZEL_BIN="bazel-5.1.1-darwin-x86_64" if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}" chmod +x ${BAZEL_BIN} -- cgit v1.2.3 From 1963f10ae5cb32a7ea6d96b928f69d3c7fba0139 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 24 May 2022 07:23:56 -0700 Subject: Don’t use generator expression to build .pc Libs lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building pkg-config files, compute linker flags with a string substitution rather than the JOIN generator expression. This ensures that commas in linker flags don’t get treated as argument separators in JOIN. Bug: https://bugs.debian.org/1011294 PiperOrigin-RevId: 450675966 Change-Id: I61eacc46a468bae5ff3dae2b437a564f2f1042c2 --- CMake/AbseilHelpers.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index ed87dde0..6d03381b 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -170,6 +170,7 @@ function(absl_cc_library) set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") endif() endforeach() + string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}") FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\ prefix=${CMAKE_INSTALL_PREFIX}\n\ exec_prefix=\${prefix}\n\ @@ -181,7 +182,7 @@ Description: Abseil ${_NAME} library\n\ URL: https://abseil.io/\n\ Version: ${PC_VERSION}\n\ Requires:${PC_DEPS}\n\ -Libs: -L\${libdir} $ $<$>:-labsl_${_NAME}>\n\ +Libs: -L\${libdir} ${PC_LINKOPTS} $<$>:-labsl_${_NAME}>\n\ Cflags: -I\${includedir}${PC_CFLAGS}\n") INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") -- cgit v1.2.3 From 4bb6a0e045c211576c7066e7862ae212e8333639 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 25 May 2022 13:50:51 -0700 Subject: Fix detection of ABSL_HAVE_ELF_MEM_IMAGE on Haiku Fixes #1181 ORIGINAL_AUTHOR=jerome.duval@gmail.com PiperOrigin-RevId: 451006334 Change-Id: Id61e5889fb55594d09e92e7bb98fdf8bfbc13cc4 --- absl/debugging/internal/elf_mem_image.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index e4bbf2d7..113071a9 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -31,8 +31,9 @@ #error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set #endif -#if defined(__ELF__) && !defined(__OpenBSD__) && !defined(__QNX__) && \ - !defined(__native_client__) && !defined(__asmjs__) && !defined(__wasm__) +#if defined(__ELF__) && !defined(__OpenBSD__) && !defined(__QNX__) && \ + !defined(__native_client__) && !defined(__asmjs__) && \ + !defined(__wasm__) && !defined(__HAIKU__) #define ABSL_HAVE_ELF_MEM_IMAGE 1 #endif -- cgit v1.2.3 From 591a2cdacb728f7c4f29782c31d7b694bb3a0782 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 26 May 2022 08:32:42 -0700 Subject: Optimize SwissMap iteration for aarch64 by 5-6% Benchmarks: https://pastebin.com/tZ7dr67W. Works well especially on smaller ranges. After a week on spending optimizing NEON SIMD where I almost managed to make hash tables work with NEON SIMD without performance hits (still 1 cycle to optimize and I gave up a little), I found an interesting optimization for aarch64 to use cls instruction (count leading sign bits). The loop has a property that ctrl_ group is not matched against count when the first slot is empty or deleted. ``` void skip_empty_or_deleted() { while (IsEmptyOrDeleted(*ctrl_)) { uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); ctrl_ += shift; slot_ += shift; } ... } ``` However, `kEmpty` and `kDeleted` have format of `1xxxxxx0` and `~ctrl & (ctrl >> 7)` always sets the lowest bit to 1. In naive implementation, it does +1 to start counting zero bits, however, in aarch64 we may start counting one bits immediately. This saves 1 cycle and 5% of iteration performance. Then it becomes hard to find a supported and sustainable C++ version of it. `__clsll` is not supported by GCC and was supported only since clang 8, `__builtin_clrsb` is not producing optimal codegen for clang. `__rbit` is not supported by GCC and there is no intrinsic to do that, however, in clang we have `__builtin_bitreverse{32,64}`. For now I decided to enable this only for clang, only if they have appropriate builtins. PiperOrigin-RevId: 451168570 Change-Id: I7e9256a60aecdc88ced4e6eb15ebc257281b6664 --- absl/base/config.h | 15 ++++++++++++ absl/container/internal/raw_hash_set.h | 36 ++++++++++++++++++++++++++++ absl/container/internal/raw_hash_set_test.cc | 6 ++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/absl/base/config.h b/absl/base/config.h index a0d599fe..5985358f 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -882,4 +882,19 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAVE_SSSE3 1 #endif +// ABSL_INTERNAL_HAVE_ARM_ACLE is used for compile-time detection of ACLE (ARM +// C language extensions). +#ifdef ABSL_INTERNAL_HAVE_ARM_ACLE +#error ABSL_INTERNAL_HAVE_ARM_ACLE cannot be directly set +// __cls, __rbit were added quite late in clang. They are not supported +// by GCC as well. __cls can be replaced with __builtin_clrsb but clang does +// not recognize cls instruction in latest versions. +// TODO(b/233604649): Relax to __builtin_clrsb and __builtin_bitreverse64 (note +// that the latter is not supported by GCC). +#elif defined(__ARM_ACLE) && defined(__clang__) && \ + ABSL_HAVE_BUILTIN(__builtin_arm_cls64) && \ + ABSL_HAVE_BUILTIN(__builtin_arm_rbit64) +#define ABSL_INTERNAL_HAVE_ARM_ACLE 1 +#endif + #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 769af50f..d503bc00 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -211,6 +211,10 @@ #include "absl/numeric/bits.h" #include "absl/utility/utility.h" +#ifdef ABSL_INTERNAL_HAVE_ARM_ACLE +#include +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { @@ -627,8 +631,40 @@ struct GroupPortableImpl { uint64_t ctrl; }; +#ifdef ABSL_INTERNAL_HAVE_ARM_ACLE +struct GroupAArch64Impl : public GroupPortableImpl { + static constexpr size_t kWidth = GroupPortableImpl::kWidth; + + using GroupPortableImpl::GroupPortableImpl; + + uint32_t CountLeadingEmptyOrDeleted() const { + assert(IsEmptyOrDeleted(static_cast(ctrl & 0xff))); + constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; + // cls: Count leading sign bits. + // clsll(1ull << 63) -> 0 + // clsll((1ull << 63) | (1ull << 62)) -> 1 + // clsll((1ull << 63) | (1ull << 61)) -> 0 + // clsll(~0ull) -> 63 + // clsll(1) -> 62 + // clsll(3) -> 61 + // clsll(5) -> 60 + // Note that CountLeadingEmptyOrDeleted is called when first control block + // is kDeleted or kEmpty. The implementation is similar to GroupPortableImpl + // but avoids +1 and __clsll returns result not including the high bit. Thus + // saves one cycle. + // kEmpty = -128, // 0b10000000 + // kDeleted = -2, // 0b11111110 + // ~ctrl & (ctrl >> 7) will have the lowest bit set to 1. After rbit, + // it will the highest one. + return (__clsll(__rbitll((~ctrl & (ctrl >> 7)) | gaps)) + 8) >> 3; + } +}; +#endif + #ifdef ABSL_INTERNAL_HAVE_SSE2 using Group = GroupSse2Impl; +#elif defined(ABSL_INTERNAL_HAVE_ARM_ACLE) +using Group = GroupAArch64Impl; #else using Group = GroupPortableImpl; #endif diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 914ec0c9..c79f8641 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -262,16 +262,20 @@ TEST(Group, CountLeadingEmptyOrDeleted) { for (ctrl_t empty : empty_examples) { std::vector e(Group::kWidth, empty); + EXPECT_TRUE(IsEmptyOrDeleted(e[0])); EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted()); for (ctrl_t full : full_examples) { - for (size_t i = 0; i != Group::kWidth; ++i) { + // First is always kEmpty or kDeleted. + for (size_t i = 1; i != Group::kWidth; ++i) { std::vector f(Group::kWidth, empty); f[i] = full; + EXPECT_TRUE(IsEmptyOrDeleted(f[0])); EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted()); } std::vector f(Group::kWidth, empty); f[Group::kWidth * 2 / 3] = full; f[Group::kWidth / 2] = full; + EXPECT_TRUE(IsEmptyOrDeleted(f[0])); EXPECT_EQ( Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted()); } -- cgit v1.2.3 From 0bc4bc237786a78e7b00a7c663d53ac81a03ec95 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Thu, 26 May 2022 08:50:21 -0700 Subject: Add implementation of is_invocable_r to absl::base_internal for C++ < 17, define it as alias of std::is_invocable_r when C++ >= 17 PiperOrigin-RevId: 451171660 Change-Id: I6dc0e40eabac72b82c4a19e292158e43118cb080 --- absl/base/internal/invoke.h | 21 +++++++++ absl/base/invoke_test.cc | 102 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index e9efb2fc..4a644c68 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -49,6 +49,7 @@ namespace base_internal { using std::invoke; using std::invoke_result_t; +using std::is_invocable_r; } // namespace base_internal ABSL_NAMESPACE_END @@ -201,6 +202,26 @@ invoke_result_t invoke(F&& f, Args&&... args) { return Invoker::type::Invoke(std::forward(f), std::forward(args)...); } + +template +struct IsInvocableRImpl : std::false_type {}; + +template +struct IsInvocableRImpl< + absl::void_t >, R, F, + Args...> + : std::integral_constant< + bool, + std::is_convertible, + R>::value || + std::is_void::value> {}; + +// Type trait whose member `value` is true if invoking `F` with `Args` is valid, +// and either the return type is convertible to `R`, or `R` is void. +// C++11-compatible version of `std::is_invocable_r`. +template +using is_invocable_r = IsInvocableRImpl; + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index bcdef36c..7be26f64 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -31,6 +31,14 @@ namespace { int Function(int a, int b) { return a - b; } +void VoidFunction(int& a, int& b) { + a += b; + b = a - b; + a -= b; +} + +int ZeroArgFunction() { return -1937; } + int Sink(std::unique_ptr p) { return *p; } @@ -223,6 +231,100 @@ TEST(InvokeTest, SfinaeFriendly) { EXPECT_THAT(CallMaybeWithArg(Factory), ::testing::Pointee(42)); } +TEST(IsInvocableRTest, CallableExactMatch) { + static_assert( + base_internal::is_invocable_r::value, + "Should be true for exact match of types on a free function"); +} + +TEST(IsInvocableRTest, CallableArgumentConversionMatch) { + static_assert( + base_internal::is_invocable_r::value, + "Should be true for convertible argument type"); +} + +TEST(IsInvocableRTest, CallableReturnConversionMatch) { + static_assert(base_internal::is_invocable_r::value, + "Should be true for convertible return type"); +} + +TEST(IsInvocableRTest, CallableReturnVoid) { + static_assert(base_internal::is_invocable_r::value, + "Should be true for void expected and actual return types"); + static_assert( + base_internal::is_invocable_r::value, + "Should be true for void expected and non-void actual return types"); +} + +TEST(IsInvocableRTest, CallableRefQualifierMismatch) { + static_assert(!base_internal::is_invocable_r::value, + "Should be false for reference constness mismatch"); + static_assert(!base_internal::is_invocable_r::value, + "Should be false for reference value category mismatch"); +} + +TEST(IsInvocableRTest, CallableArgumentTypeMismatch) { + static_assert(!base_internal::is_invocable_r::value, + "Should be false for argument type mismatch"); +} + +TEST(IsInvocableRTest, CallableReturnTypeMismatch) { + static_assert(!base_internal::is_invocable_r::value, + "Should be false for return type mismatch"); +} + +TEST(IsInvocableRTest, CallableTooFewArgs) { + static_assert( + !base_internal::is_invocable_r::value, + "Should be false for too few arguments"); +} + +TEST(IsInvocableRTest, CallableTooManyArgs) { + static_assert(!base_internal::is_invocable_r::value, + "Should be false for too many arguments"); +} + +TEST(IsInvocableRTest, MemberFunctionAndReference) { + static_assert(base_internal::is_invocable_r::value, + "Should be true for exact match of types on a member function " + "and class reference"); +} + +TEST(IsInvocableRTest, MemberFunctionAndPointer) { + static_assert(base_internal::is_invocable_r::value, + "Should be true for exact match of types on a member function " + "and class pointer"); +} + +TEST(IsInvocableRTest, DataMemberAndReference) { + static_assert(base_internal::is_invocable_r::value, + "Should be true for exact match of types on a data member and " + "class reference"); +} + +TEST(IsInvocableRTest, DataMemberAndPointer) { + static_assert(base_internal::is_invocable_r::value, + "Should be true for exact match of types on a data member and " + "class pointer"); +} + +TEST(IsInvocableRTest, CallableZeroArgs) { + static_assert( + base_internal::is_invocable_r::value, + "Should be true for exact match for a zero-arg free function"); +} + } // namespace } // namespace base_internal ABSL_NAMESPACE_END -- cgit v1.2.3 From 89cdaed6557dbfb8cc8fba53a9dc8baf332df8b0 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 26 May 2022 11:06:19 -0700 Subject: Enable __thread on Asylo PiperOrigin-RevId: 451201387 Change-Id: Ibeac4f24d00e28bbfc61e476936d669321a2cb24 --- absl/base/config.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 5985358f..4223629e 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -212,11 +212,12 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. -// We assume __thread is supported on Linux when compiled with Clang or compiled -// against libstdc++ with _GLIBCXX_HAVE_TLS defined. +// We assume __thread is supported on Linux or Asylo when compiled with Clang or +// compiled against libstdc++ with _GLIBCXX_HAVE_TLS defined. #ifdef ABSL_HAVE_TLS #error ABSL_HAVE_TLS cannot be directly set -#elif defined(__linux__) && (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) +#elif (defined(__linux__) || defined(__ASYLO__)) && \ + (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) #define ABSL_HAVE_TLS 1 #endif -- cgit v1.2.3 From 8cc2e34199c42543fa00baa03f1db9befcb8611f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 27 May 2022 09:17:41 -0700 Subject: Clarify the behaviour of `AssertHeld` and `AssertReaderHeld` when the calling thread doesn't hold the mutex. PiperOrigin-RevId: 451410449 Change-Id: Iffd4c7463f1051474debbed256703589d96a548c --- absl/synchronization/mutex.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index 9a3e438f..b69b7089 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -174,9 +174,12 @@ class ABSL_LOCKABLE Mutex { // Mutex::AssertHeld() // - // Return immediately if this thread holds the `Mutex` exclusively (in write - // mode). Otherwise, may report an error (typically by crashing with a - // diagnostic), or may return immediately. + // Require that the mutex be held exclusively (write mode) by this thread. + // + // If the mutex is not currently held by this thread, this function may report + // an error (typically by crashing with a diagnostic) or it may do nothing. + // This function is intended only as a tool to assist debugging; it doesn't + // guarantee correctness. void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK(); // --------------------------------------------------------------------------- @@ -236,9 +239,13 @@ class ABSL_LOCKABLE Mutex { // Mutex::AssertReaderHeld() // - // Returns immediately if this thread holds the `Mutex` in at least shared - // mode (read mode). Otherwise, may report an error (typically by - // crashing with a diagnostic), or may return immediately. + // Require that the mutex be held at least in shared mode (read mode) by this + // thread. + // + // If the mutex is not currently held by this thread, this function may report + // an error (typically by crashing with a diagnostic) or it may do nothing. + // This function is intended only as a tool to assist debugging; it doesn't + // guarantee correctness. void AssertReaderHeld() const ABSL_ASSERT_SHARED_LOCK(); // Mutex::WriterLock() -- cgit v1.2.3 From 48f72c227b94b06387106f71d4450b31e88e283b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 27 May 2022 11:12:04 -0700 Subject: Fix typos. PiperOrigin-RevId: 451434783 Change-Id: I572e77a67e18e8dd530bf0347c76863c9bb1946f --- absl/strings/cord.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 5ad4ea0c..391b2c0b 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -289,7 +289,7 @@ class Cord { // Cord::EstimatedMemoryUsage() // // Returns the *approximate* number of bytes held by this cord. - // See CordMemoryAccounting for more information on accounting method used. + // See CordMemoryAccounting for more information on the accounting method. size_t EstimatedMemoryUsage(CordMemoryAccounting accounting_method = CordMemoryAccounting::kTotal) const; @@ -341,7 +341,7 @@ class Cord { //---------------------------------------------------------------------------- // // A `Cord::ChunkIterator` allows iteration over the constituent chunks of its - // Cord. Such iteration allows you to perform non-const operatons on the data + // Cord. Such iteration allows you to perform non-const operations on the data // of a Cord without modifying it. // // Generally, you do not instantiate a `Cord::ChunkIterator` directly; @@ -462,7 +462,7 @@ class Cord { class ChunkRange { public: // Fulfill minimum c++ container requirements [container.requirements] - // Theses (partial) container type definitions allow ChunkRange to be used + // These (partial) container type definitions allow ChunkRange to be used // in various utilities expecting a subset of [container.requirements]. // For example, the below enables using `::testing::ElementsAre(...)` using value_type = absl::string_view; @@ -596,7 +596,7 @@ class Cord { // producing an iterator which can be used within a range-based for loop. // Construction of a `CharRange` will return an iterator pointing to the first // character of the Cord. Generally, do not construct a `CharRange` directly; - // instead, prefer to use the `Cord::Chars()` method show below. + // instead, prefer to use the `Cord::Chars()` method shown below. // // Implementation note: `CharRange` is simply a convenience wrapper over // `Cord::char_begin()` and `Cord::char_end()`. @@ -1500,7 +1500,7 @@ inline void Cord::ForEachChunk( } } -// Nonmember Cord-to-Cord relational operarators. +// Nonmember Cord-to-Cord relational operators. inline bool operator==(const Cord& lhs, const Cord& rhs) { if (lhs.contents_.IsSame(rhs.contents_)) return true; size_t rhs_size = rhs.size(); -- cgit v1.2.3 From 2d23a3e16abfcf8f22ba850d67e972cc77e98874 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Fri, 27 May 2022 17:04:10 -0400 Subject: Correct pkg-config file generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport a patch from upstream to make CMake generate pkg-config files correctly. Add an autopkgtest to ensure this doesn’t regress. --- debian/changelog | 8 ++++++ debian/copyright | 2 +- debian/patches/pkg-config-libs-generation.diff | 36 ++++++++++++++++++++++++++ debian/patches/series | 1 + debian/tests/bug1011294 | 24 +++++++++++++++++ debian/tests/control | 3 +++ 6 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 debian/patches/pkg-config-libs-generation.diff create mode 100755 debian/tests/bug1011294 diff --git a/debian/changelog b/debian/changelog index 296ed36f..22d44a71 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +abseil (0~20210324.2-4) unstable; urgency=medium + + * Fix "spurious -Wl flag in some pkg-config entries" by backporting a + patch from upstream that corrects CMake pkg-config generation. + (Closes: #1011294) + + -- Benjamin Barenblat Fri, 27 May 2022 16:58:38 -0400 + abseil (0~20210324.2-3) unstable; urgency=medium * Backport an upstream patch to disable a problematic unit test. diff --git a/debian/copyright b/debian/copyright index 986a4261..260a1b8e 100644 --- a/debian/copyright +++ b/debian/copyright @@ -12,7 +12,7 @@ Copyright: License: Apache-2.0 Files: debian/* -Copyright: 2020 Google LLC +Copyright: 2020-2022 Google LLC License: Apache-2.0 License: Apache-2.0 diff --git a/debian/patches/pkg-config-libs-generation.diff b/debian/patches/pkg-config-libs-generation.diff new file mode 100644 index 00000000..5f217765 --- /dev/null +++ b/debian/patches/pkg-config-libs-generation.diff @@ -0,0 +1,36 @@ +From: Benjamin Barenblat +Subject: Check printf format strings in str_format_convert_test +Forwarded: yes +Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/1963f10ae5cb32a7ea6d96b928f69d3c7fba0139 +Bug-Debian: https://bugs.debian.org/1011294 + +Don’t use generator expression to build .pc Libs lines + +When building pkg-config files, compute linker flags with a string +substitution rather than the JOIN generator expression. This ensures +that commas in linker flags don’t get treated as argument separators in +JOIN. + +The author works at Google. Upstream applied this patch as Piper +revision 450675966 and exported it to GitHub; the Applied-Upstream URL +above points to the exported commit. + +--- a/CMake/AbseilHelpers.cmake ++++ b/CMake/AbseilHelpers.cmake +@@ -167,6 +167,7 @@ + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + endif() + endforeach() ++ string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}") + FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\ + prefix=${CMAKE_INSTALL_PREFIX}\n\ + exec_prefix=\${prefix}\n\ +@@ -178,7 +179,7 @@ + URL: https://abseil.io/\n\ + Version: ${PC_VERSION}\n\ + Requires:${PC_DEPS}\n\ +-Libs: -L\${libdir} $ $<$>:-labsl_${_NAME}>\n\ ++Libs: -L\${libdir} ${PC_LINKOPTS} $<$>:-labsl_${_NAME}>\n\ + Cflags: -I\${includedir}${PC_CFLAGS}\n") + INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/debian/patches/series b/debian/patches/series index f4bed7f4..f335570e 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -16,3 +16,4 @@ big-endian-random4.diff disable-nominalcpufrequency-test.diff hppa-symbolize.diff str-format-convert-test-printf.diff +pkg-config-libs-generation.diff diff --git a/debian/tests/bug1011294 b/debian/tests/bug1011294 new file mode 100755 index 00000000..302bcf73 --- /dev/null +++ b/debian/tests/bug1011294 @@ -0,0 +1,24 @@ +#!/bin/sh +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +set -eu + +readonly TMP="$(mktemp -d)" +trap "rm -rf \"$TMP\"" EXIT +cd "$TMP" + +echo 'int main() {}' >noop.cc + +g++ -o noop noop.cc $(pkg-config --cflags absl_base) $(pkg-config --libs absl_base) diff --git a/debian/tests/control b/debian/tests/control index 43dc87cb..161ef695 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -17,3 +17,6 @@ Depends: @, g++, libgtest-dev Tests: cmake Depends: @, cmake (>= 3.5), g++, make + +Tests: bug1011294 +Depends: @, g++, pkg-config -- cgit v1.2.3 From 36a4b073f1e7e02ed7d1ac140767e36f82f09b7c Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Fri, 27 May 2022 22:27:58 +0100 Subject: absl/strings/internal/str_format/extension.h: add missing include Without the change absl-cpp build fails on this week's gcc-13 snapshot as: /build/abseil-cpp/absl/strings/internal/str_format/extension.h:34:33: error: found ':' in nested-name-specifier, expected '::' 34 | enum class FormatConversionChar : uint8_t; | ^ | :: --- absl/strings/internal/str_format/extension.h | 1 + 1 file changed, 1 insertion(+) diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index c47536d6..08c3fbeb 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h @@ -17,6 +17,7 @@ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ #include +#include #include #include -- cgit v1.2.3 From e82b2a48a446aae7e857ad0a7923d499a9ad838c Mon Sep 17 00:00:00 2001 From: Jose Renau Date: Mon, 30 May 2022 18:11:04 -0700 Subject: Avoid variable shadowing which can be a compile error depending on compile flags --- absl/container/internal/btree.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index cb39b161..9215ab44 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -504,8 +504,8 @@ struct SearchResult { template struct SearchResult { SearchResult() {} - explicit SearchResult(V value) : value(value) {} - SearchResult(V value, MatchKind /*match*/) : value(value) {} + explicit SearchResult(V va) : value(va) {} + SearchResult(V va, MatchKind /*match*/) : value(va) {} V value; -- cgit v1.2.3 From f2463433d6c073381df2d9ca8c3d8f53e5ae1362 Mon Sep 17 00:00:00 2001 From: Anqi D Date: Mon, 30 May 2022 22:17:57 -0700 Subject: time.h: Use uint32_t literals for calls to overloaded MakeDuration This fixes an overload that is ambiguous for some toolchains, because 0U does not always refer to a uint32_t (on some toolchains, uint32_t is an unsigned long). PiperOrigin-RevId: 451962182 Change-Id: Id13700817ea3eb6d04e2cc02f20726040eb447fb --- absl/time/time.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/absl/time/time.h b/absl/time/time.h index f284aa37..bd01867e 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -750,23 +750,24 @@ constexpr Time UnixEpoch() { return Time(); } constexpr Time UniversalEpoch() { // 719162 is the number of days from 0001-01-01 to 1970-01-01, // assuming the Gregorian calendar. - return Time(time_internal::MakeDuration(-24 * 719162 * int64_t{3600}, 0U)); + return Time( + time_internal::MakeDuration(-24 * 719162 * int64_t{3600}, uint32_t{0})); } // InfiniteFuture() // // Returns an `absl::Time` that is infinitely far in the future. constexpr Time InfiniteFuture() { - return Time( - time_internal::MakeDuration((std::numeric_limits::max)(), ~0U)); + return Time(time_internal::MakeDuration((std::numeric_limits::max)(), + ~uint32_t{0})); } // InfinitePast() // // Returns an `absl::Time` that is infinitely far in the past. constexpr Time InfinitePast() { - return Time( - time_internal::MakeDuration((std::numeric_limits::min)(), ~0U)); + return Time(time_internal::MakeDuration((std::numeric_limits::min)(), + ~uint32_t{0})); } // FromUnixNanos() @@ -1422,14 +1423,17 @@ constexpr int64_t GetRepHi(Duration d) { return d.rep_hi_; } constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; } // Returns true iff d is positive or negative infinity. -constexpr bool IsInfiniteDuration(Duration d) { return GetRepLo(d) == ~0U; } +constexpr bool IsInfiniteDuration(Duration d) { + return GetRepLo(d) == ~uint32_t{0}; +} // Returns an infinite Duration with the opposite sign. // REQUIRES: IsInfiniteDuration(d) constexpr Duration OppositeInfinity(Duration d) { return GetRepHi(d) < 0 - ? MakeDuration((std::numeric_limits::max)(), ~0U) - : MakeDuration((std::numeric_limits::min)(), ~0U); + ? MakeDuration((std::numeric_limits::max)(), ~uint32_t{0}) + : MakeDuration((std::numeric_limits::min)(), + ~uint32_t{0}); } // Returns (-n)-1 (equivalently -(n+1)) without avoidable overflow. @@ -1568,7 +1572,7 @@ constexpr Duration operator-(Duration d) { constexpr Duration InfiniteDuration() { return time_internal::MakeDuration((std::numeric_limits::max)(), - ~0U); + ~uint32_t{0}); } constexpr Duration FromChrono(const std::chrono::nanoseconds& d) { -- cgit v1.2.3 From ea78ded7a5f999f19a12b71f5a4988f6f819f64f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 31 May 2022 00:46:25 -0700 Subject: Automated rollback of commit f2463433d6c073381df2d9ca8c3d8f53e5ae1362. PiperOrigin-RevId: 451979149 Change-Id: Ic9b02306f2c5324b6648989a895f128c9eb5743d --- absl/time/time.h | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/absl/time/time.h b/absl/time/time.h index bd01867e..f284aa37 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -750,24 +750,23 @@ constexpr Time UnixEpoch() { return Time(); } constexpr Time UniversalEpoch() { // 719162 is the number of days from 0001-01-01 to 1970-01-01, // assuming the Gregorian calendar. - return Time( - time_internal::MakeDuration(-24 * 719162 * int64_t{3600}, uint32_t{0})); + return Time(time_internal::MakeDuration(-24 * 719162 * int64_t{3600}, 0U)); } // InfiniteFuture() // // Returns an `absl::Time` that is infinitely far in the future. constexpr Time InfiniteFuture() { - return Time(time_internal::MakeDuration((std::numeric_limits::max)(), - ~uint32_t{0})); + return Time( + time_internal::MakeDuration((std::numeric_limits::max)(), ~0U)); } // InfinitePast() // // Returns an `absl::Time` that is infinitely far in the past. constexpr Time InfinitePast() { - return Time(time_internal::MakeDuration((std::numeric_limits::min)(), - ~uint32_t{0})); + return Time( + time_internal::MakeDuration((std::numeric_limits::min)(), ~0U)); } // FromUnixNanos() @@ -1423,17 +1422,14 @@ constexpr int64_t GetRepHi(Duration d) { return d.rep_hi_; } constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; } // Returns true iff d is positive or negative infinity. -constexpr bool IsInfiniteDuration(Duration d) { - return GetRepLo(d) == ~uint32_t{0}; -} +constexpr bool IsInfiniteDuration(Duration d) { return GetRepLo(d) == ~0U; } // Returns an infinite Duration with the opposite sign. // REQUIRES: IsInfiniteDuration(d) constexpr Duration OppositeInfinity(Duration d) { return GetRepHi(d) < 0 - ? MakeDuration((std::numeric_limits::max)(), ~uint32_t{0}) - : MakeDuration((std::numeric_limits::min)(), - ~uint32_t{0}); + ? MakeDuration((std::numeric_limits::max)(), ~0U) + : MakeDuration((std::numeric_limits::min)(), ~0U); } // Returns (-n)-1 (equivalently -(n+1)) without avoidable overflow. @@ -1572,7 +1568,7 @@ constexpr Duration operator-(Duration d) { constexpr Duration InfiniteDuration() { return time_internal::MakeDuration((std::numeric_limits::max)(), - ~uint32_t{0}); + ~0U); } constexpr Duration FromChrono(const std::chrono::nanoseconds& d) { -- cgit v1.2.3 From a4cc270df18b47685e568e01bb5c825493f58d25 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 31 May 2022 08:00:22 -0700 Subject: Stop using sleep timeouts for Linux futex-based SpinLock Timeouts were once necessary when the SpinLock Unlock used an atomic store and could therefore have a race and a missed wakeup, however, the Unlock path now uses an atomic exchange, so the missed wakeup cannot happen. Fixes #1179 PiperOrigin-RevId: 452047517 Change-Id: I844944879b51b7f7ddac148e063a376cddd0d05a --- absl/base/internal/spinlock_linux.inc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc index 202f7cdf..fe8ba674 100644 --- a/absl/base/internal/spinlock_linux.inc +++ b/absl/base/internal/spinlock_linux.inc @@ -57,13 +57,10 @@ static_assert(sizeof(std::atomic) == sizeof(int), extern "C" { ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( - std::atomic *w, uint32_t value, int loop, + std::atomic *w, uint32_t value, int, absl::base_internal::SchedulingMode) { absl::base_internal::ErrnoSaver errno_saver; - struct timespec tm; - tm.tv_sec = 0; - tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); - syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); + syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, nullptr); } ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( -- cgit v1.2.3 From aeb2dc9c34e2dbddb5762d2ffca356e23eb55b56 Mon Sep 17 00:00:00 2001 From: Evan Brown Date: Tue, 31 May 2022 09:13:25 -0700 Subject: Allow for using b-tree with `value_type`s that can only be constructed by the allocator (ignoring copy/move constructors). We were using `init_type`s for temp values that we would move into slots, but in this case, we need to have actual slots. We use node handles for managing slots outside of nodes. Also, in btree::copy_or_move_values_in_order, pass the slots from the iterators rather than references to values. This allows for moving from map keys instead of copying for standard layout types. In the test, fix a couple of ClangTidy warnings from missing includes and calling `new` instead of `make_unique`. PiperOrigin-RevId: 452062967 Change-Id: I870e89ae1aa5b3cfa62ae6e75b73ffc3d52e731c --- absl/container/btree_set.h | 5 ++ absl/container/btree_test.cc | 109 ++++++++++++++++++++++++++++- absl/container/internal/btree.h | 24 +++---- absl/container/internal/btree_container.h | 33 ++++++--- absl/container/internal/common.h | 11 +-- absl/container/internal/container_memory.h | 9 +++ 6 files changed, 161 insertions(+), 30 deletions(-) diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 7b6655ad..32a7c506 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -751,6 +751,11 @@ struct set_slot_policy { absl::allocator_traits::construct(*alloc, slot, std::move(*other)); } + template + static void construct(Alloc *alloc, slot_type *slot, const slot_type *other) { + absl::allocator_traits::construct(*alloc, slot, *other); + } + template static void destroy(Alloc *alloc, slot_type *slot) { absl::allocator_traits::destroy(*alloc, slot); diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index 4d4c64b2..b3fa98f4 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -14,11 +14,14 @@ #include "absl/container/btree_test.h" +#include +#include #include #include #include #include #include +#include #include #include #include @@ -1291,7 +1294,7 @@ TEST(Btree, BtreeMapCanHoldMoveOnlyTypes) { std::unique_ptr &v = m["A"]; EXPECT_TRUE(v == nullptr); - v.reset(new std::string("X")); + v = absl::make_unique("X"); auto iter = m.find("A"); EXPECT_EQ("X", *iter->second); @@ -3081,6 +3084,110 @@ TEST(Btree, InvalidIteratorUse) { } #endif +class OnlyConstructibleByAllocator { + explicit OnlyConstructibleByAllocator(int i) : i_(i) {} + + public: + OnlyConstructibleByAllocator(const OnlyConstructibleByAllocator &other) + : i_(other.i_) {} + OnlyConstructibleByAllocator &operator=( + const OnlyConstructibleByAllocator &other) { + i_ = other.i_; + return *this; + } + int Get() const { return i_; } + bool operator==(int i) const { return i_ == i; } + + private: + template + friend class OnlyConstructibleAllocator; + + int i_; +}; + +template +class OnlyConstructibleAllocator : public std::allocator { + public: + OnlyConstructibleAllocator() = default; + template + explicit OnlyConstructibleAllocator(const OnlyConstructibleAllocator &) {} + + void construct(OnlyConstructibleByAllocator *p, int i) { + new (p) OnlyConstructibleByAllocator(i); + } + template + void construct(Pair *p, const int i) { + OnlyConstructibleByAllocator only(i); + new (p) Pair(std::move(only), i); + } + + template + struct rebind { + using other = OnlyConstructibleAllocator; + }; +}; + +struct OnlyConstructibleByAllocatorComp { + using is_transparent = void; + bool operator()(OnlyConstructibleByAllocator a, + OnlyConstructibleByAllocator b) const { + return a.Get() < b.Get(); + } + bool operator()(int a, OnlyConstructibleByAllocator b) const { + return a < b.Get(); + } + bool operator()(OnlyConstructibleByAllocator a, int b) const { + return a.Get() < b; + } +}; + +TEST(Btree, OnlyConstructibleByAllocatorType) { + const std::array arr = {3, 4}; + { + absl::btree_set> + set; + set.emplace(1); + set.emplace_hint(set.end(), 2); + set.insert(arr.begin(), arr.end()); + EXPECT_THAT(set, ElementsAre(1, 2, 3, 4)); + } + { + absl::btree_multiset> + set; + set.emplace(1); + set.emplace_hint(set.end(), 2); + // TODO(ezb): fix insert_multi to allow this to compile. + // set.insert(arr.begin(), arr.end()); + EXPECT_THAT(set, ElementsAre(1, 2)); + } + { + absl::btree_map> + map; + map.emplace(1); + map.emplace_hint(map.end(), 2); + map.insert(arr.begin(), arr.end()); + EXPECT_THAT(map, + ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(4, 4))); + } + { + absl::btree_multimap> + map; + map.emplace(1); + map.emplace_hint(map.end(), 2); + // TODO(ezb): fix insert_multi to allow this to compile. + // map.insert(arr.begin(), arr.end()); + EXPECT_THAT(map, ElementsAre(Pair(1, 1), Pair(2, 2))); + } +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index cb39b161..79b56e0d 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -1196,7 +1196,9 @@ class btree_iterator { } const key_type &key() const { return node_->key(position_); } - slot_type *slot() { return node_->slot(position_); } + decltype(std::declval()->slot(0)) slot() { + return node_->slot(position_); + } void assert_valid_generation() const { #ifdef ABSL_BTREE_ENABLE_GENERATIONS @@ -1225,7 +1227,6 @@ template class btree { using node_type = btree_node; using is_key_compare_to = typename Params::is_key_compare_to; - using init_type = typename Params::init_type; using field_type = typename node_type::field_type; // We use a static empty node for the root/leftmost/rightmost of empty btrees @@ -1309,14 +1310,6 @@ class btree { using slot_type = typename Params::slot_type; private: - // For use in copy_or_move_values_in_order. - const value_type &maybe_move_from_iterator(const_iterator it) { return *it; } - value_type &&maybe_move_from_iterator(iterator it) { - // This is a destructive operation on the other container so it's safe for - // us to const_cast and move from the keys here even if it's a set. - return std::move(const_cast(*it)); - } - // Copies or moves (depending on the template parameter) the values in // other into this btree in their order in other. This btree must be empty // before this method is called. This method is used in copy construction, @@ -2063,12 +2056,12 @@ void btree

::copy_or_move_values_in_order(Btree &other) { // values is the same order we'll store them in. auto iter = other.begin(); if (iter == other.end()) return; - insert_multi(maybe_move_from_iterator(iter)); + insert_multi(iter.slot()); ++iter; for (; iter != other.end(); ++iter) { // If the btree is not empty, we can just insert the new value at the end // of the tree. - internal_emplace(end(), maybe_move_from_iterator(iter)); + internal_emplace(end(), iter.slot()); } } @@ -2205,8 +2198,11 @@ template template void btree

::insert_iterator_unique(InputIterator b, InputIterator e, char) { for (; b != e; ++b) { - init_type value(*b); - insert_hint_unique(end(), params_type::key(value), std::move(value)); + // Use a node handle to manage a temp slot. + auto node_handle = + CommonAccess::Construct(get_allocator(), *b); + slot_type *slot = CommonAccess::GetSlot(node_handle); + insert_hint_unique(end(), params_type::key(slot), slot); } } diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index cc2e1793..fc2f740a 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -166,9 +166,10 @@ class btree_container { // Extract routines. node_type extract(iterator position) { - // Use Move instead of Transfer, because the rebalancing code expects to - // have a valid object to scribble metadata bits on top of. - auto node = CommonAccess::Move(get_allocator(), position.slot()); + // Use Construct instead of Transfer because the rebalancing code will + // destroy the slot later. + auto node = + CommonAccess::Construct(get_allocator(), position.slot()); erase(position); return node; } @@ -291,8 +292,11 @@ class btree_set_container : public btree_container { } template std::pair emplace(Args &&... args) { - init_type v(std::forward(args)...); - return this->tree_.insert_unique(params_type::key(v), std::move(v)); + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + auto *slot = CommonAccess::GetSlot(node); + return this->tree_.insert_unique(params_type::key(slot), slot); } iterator insert(const_iterator hint, const value_type &v) { return this->tree_ @@ -306,9 +310,12 @@ class btree_set_container : public btree_container { } template iterator emplace_hint(const_iterator hint, Args &&... args) { - init_type v(std::forward(args)...); + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + auto *slot = CommonAccess::GetSlot(node); return this->tree_ - .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v)) + .insert_hint_unique(iterator(hint), params_type::key(slot), slot) .first; } template @@ -598,12 +605,18 @@ class btree_multiset_container : public btree_container { } template iterator emplace(Args &&... args) { - return this->tree_.insert_multi(init_type(std::forward(args)...)); + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + return this->tree_.insert_multi(CommonAccess::GetSlot(node)); } template iterator emplace_hint(const_iterator hint, Args &&... args) { - return this->tree_.insert_hint_multi( - iterator(hint), init_type(std::forward(args)...)); + // Use a node handle to manage a temp slot. + auto node = CommonAccess::Construct(this->get_allocator(), + std::forward(args)...); + return this->tree_.insert_hint_multi(iterator(hint), + CommonAccess::GetSlot(node)); } iterator insert(node_type &&node) { if (!node) return this->end(); diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 030e9d4a..416d9aa3 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h @@ -84,10 +84,11 @@ class node_handle_base { PolicyTraits::transfer(alloc(), slot(), s); } - struct move_tag_t {}; - node_handle_base(move_tag_t, const allocator_type& a, slot_type* s) + struct construct_tag_t {}; + template + node_handle_base(construct_tag_t, const allocator_type& a, Args&&... args) : alloc_(a) { - PolicyTraits::construct(alloc(), slot(), s); + PolicyTraits::construct(alloc(), slot(), std::forward(args)...); } void destroy() { @@ -186,8 +187,8 @@ struct CommonAccess { } template - static T Move(Args&&... args) { - return T(typename T::move_tag_t{}, std::forward(args)...); + static T Construct(Args&&... args) { + return T(typename T::construct_tag_t{}, std::forward(args)...); } }; diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index e67529ec..df492236 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h @@ -402,6 +402,15 @@ struct map_slot_policy { } } + // Construct this slot by copying from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, + const slot_type* other) { + emplace(slot); + absl::allocator_traits::construct(*alloc, &slot->value, + other->value); + } + template static void destroy(Allocator* alloc, slot_type* slot) { if (kMutableKeys::value) { -- cgit v1.2.3 From a56715378721d8abd0ae379a83b9c1ee0b1a9a30 Mon Sep 17 00:00:00 2001 From: Greg Falcon Date: Tue, 31 May 2022 14:00:42 -0700 Subject: Add an internal helper for logging (upcoming). PiperOrigin-RevId: 452134803 Change-Id: I8660df850ab537c441399545b25eb32399b2a3ef --- absl/status/internal/status_internal.h | 9 +++++++++ absl/status/status.cc | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index 34914d2e..fc1e78bc 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h @@ -69,6 +69,15 @@ struct StatusRep { }; absl::StatusCode MapToLocalCode(int value); + +// If `status` is not OK, returns a pointer to a newly-allocated string with the +// given `prefix`, suitable for output as an error message in assertion/CHECK() +// failures. Otherwise returns nullptr. +// +// This is an internal implementation detail for Abseil logging. +std::string* MakeCheckFailString(const absl::Status& status, + const char* prefix); + } // namespace status_internal ABSL_NAMESPACE_END diff --git a/absl/status/status.cc b/absl/status/status.cc index fc5a1425..c66009d6 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -599,5 +599,17 @@ Status ErrnoToStatus(int error_number, absl::string_view message) { MessageForErrnoToStatus(error_number, message)); } +namespace status_internal { + +std::string* MakeCheckFailString(const absl::Status& status, + const char* prefix) { + if (status.ok()) { return nullptr; } + return new std::string( + absl::StrCat(prefix, " (", + status.ToString(StatusToStringMode::kWithEverything), ")")); +} + +} // namespace status_internal + ABSL_NAMESPACE_END } // namespace absl -- cgit v1.2.3 From 2617970857c46e6ec971865d54f00445c260f682 Mon Sep 17 00:00:00 2001 From: Anqi D Date: Tue, 31 May 2022 15:59:20 -0700 Subject: Rollforward of commit ea78ded7a5f999f19a12b71f5a4988f6f819f64f. PiperOrigin-RevId: 452161150 Change-Id: Ia5515eae52502ca0b79c7e7dff0a57aa5899e354 --- absl/time/time.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/absl/time/time.h b/absl/time/time.h index f284aa37..bd01867e 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -750,23 +750,24 @@ constexpr Time UnixEpoch() { return Time(); } constexpr Time UniversalEpoch() { // 719162 is the number of days from 0001-01-01 to 1970-01-01, // assuming the Gregorian calendar. - return Time(time_internal::MakeDuration(-24 * 719162 * int64_t{3600}, 0U)); + return Time( + time_internal::MakeDuration(-24 * 719162 * int64_t{3600}, uint32_t{0})); } // InfiniteFuture() // // Returns an `absl::Time` that is infinitely far in the future. constexpr Time InfiniteFuture() { - return Time( - time_internal::MakeDuration((std::numeric_limits::max)(), ~0U)); + return Time(time_internal::MakeDuration((std::numeric_limits::max)(), + ~uint32_t{0})); } // InfinitePast() // // Returns an `absl::Time` that is infinitely far in the past. constexpr Time InfinitePast() { - return Time( - time_internal::MakeDuration((std::numeric_limits::min)(), ~0U)); + return Time(time_internal::MakeDuration((std::numeric_limits::min)(), + ~uint32_t{0})); } // FromUnixNanos() @@ -1422,14 +1423,17 @@ constexpr int64_t GetRepHi(Duration d) { return d.rep_hi_; } constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; } // Returns true iff d is positive or negative infinity. -constexpr bool IsInfiniteDuration(Duration d) { return GetRepLo(d) == ~0U; } +constexpr bool IsInfiniteDuration(Duration d) { + return GetRepLo(d) == ~uint32_t{0}; +} // Returns an infinite Duration with the opposite sign. // REQUIRES: IsInfiniteDuration(d) constexpr Duration OppositeInfinity(Duration d) { return GetRepHi(d) < 0 - ? MakeDuration((std::numeric_limits::max)(), ~0U) - : MakeDuration((std::numeric_limits::min)(), ~0U); + ? MakeDuration((std::numeric_limits::max)(), ~uint32_t{0}) + : MakeDuration((std::numeric_limits::min)(), + ~uint32_t{0}); } // Returns (-n)-1 (equivalently -(n+1)) without avoidable overflow. @@ -1568,7 +1572,7 @@ constexpr Duration operator-(Duration d) { constexpr Duration InfiniteDuration() { return time_internal::MakeDuration((std::numeric_limits::max)(), - ~0U); + ~uint32_t{0}); } constexpr Duration FromChrono(const std::chrono::nanoseconds& d) { -- cgit v1.2.3 From 6285deef8ccbe546798bc248407bec4d8fe528f1 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 2 Jun 2022 07:53:06 -0700 Subject: Fixed typo in a comment. PiperOrigin-RevId: 452537510 Change-Id: I7d2a19d1206aa08fce131bacda461fdefebe8713 --- absl/container/internal/container_memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index df492236..94185c6e 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h @@ -174,7 +174,7 @@ decltype(std::declval()(std::declval())) WithConstructed( // // 2. auto a = PairArgs(args...); // std::pair p(std::piecewise_construct, -// std::move(p.first), std::move(p.second)); +// std::move(a.first), std::move(a.second)); inline std::pair, std::tuple<>> PairArgs() { return {}; } template std::pair, std::tuple> PairArgs(F&& f, S&& s) { -- cgit v1.2.3 From c34c552a1a547a47474f32f1b89de030aa4ff949 Mon Sep 17 00:00:00 2001 From: Tom Rybka Date: Thu, 2 Jun 2022 08:22:18 -0700 Subject: Disable tests on some platforms where they currently fail. PiperOrigin-RevId: 452542838 Change-Id: I45d80b220c0450d27423bb23504e95c25811877b --- absl/base/BUILD.bazel | 7 +++++++ absl/container/BUILD.bazel | 7 +++++++ absl/flags/BUILD.bazel | 27 +++++++++++++++++++++++++++ absl/profiling/BUILD.bazel | 3 +++ absl/random/BUILD.bazel | 15 +++++++++++++++ absl/random/internal/BUILD.bazel | 1 + absl/synchronization/BUILD.bazel | 14 +++++++++++++- absl/time/internal/cctz/BUILD.bazel | 2 ++ 8 files changed, 75 insertions(+), 1 deletion(-) diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index b7e49359..bd023ad8 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -435,6 +435,9 @@ cc_test( srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":base", ":base_internal", @@ -560,6 +563,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, tags = [ "no_test_ios_x86_64", + "no_test_wasm", ], deps = [ ":malloc_internal", @@ -573,6 +577,9 @@ cc_test( srcs = ["internal/thread_identity_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":base", ":core_headers", diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 9ef7066c..d01d78e5 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -518,6 +518,9 @@ cc_test( name = "hashtablez_sampler_test", srcs = ["internal/hashtablez_sampler_test.cc"], linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":hashtablez_sampler", "//absl/base:config", @@ -936,6 +939,10 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, shard_count = 10, + tags = [ + "no_test_ios", + "no_test_wasm", + ], visibility = ["//visibility:private"], deps = [ ":btree", diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 91c6e98b..4ca687ee 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -326,6 +326,11 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_android", + "no_test_ios", + "no_test_wasm", + ], deps = [ ":commandlineflag", ":commandlineflag_internal", @@ -362,6 +367,11 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_android", + "no_test_ios", + "no_test_wasm", + ], deps = [ ":config", ":flag", @@ -423,6 +433,11 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_android", + "no_test_ios", + "no_test_wasm", + ], deps = [ ":flag", ":parse", @@ -458,6 +473,7 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["no_test_wasm"], deps = [ ":program_name", "//absl/strings", @@ -473,6 +489,11 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_android", + "no_test_ios", + "no_test_wasm", + ], deps = [ ":commandlineflag_internal", ":flag", @@ -495,6 +516,7 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, shard_count = 31, + tags = ["no_test_wasm"], deps = [ ":flag_internal", "//absl/base", @@ -529,6 +551,11 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_android", + "no_test_ios", + "no_test_wasm", + ], deps = [ ":config", ":flag", diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel index 496a06b2..3392c96c 100644 --- a/absl/profiling/BUILD.bazel +++ b/absl/profiling/BUILD.bazel @@ -43,6 +43,9 @@ cc_test( name = "sample_recorder_test", srcs = ["internal/sample_recorder_test.cc"], linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":sample_recorder", "//absl/base:core_headers", diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index fdde78b6..27f3e334 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -183,6 +183,9 @@ cc_test( copts = ABSL_TEST_COPTS, flaky = 1, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":distributions", ":random", @@ -235,6 +238,9 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", # Does not converge on WASM. + ], deps = [ ":distributions", ":random", @@ -429,6 +435,9 @@ cc_test( srcs = ["mocking_bit_gen_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":bit_gen_ref", ":mock_distributions", @@ -444,6 +453,9 @@ cc_test( srcs = ["mock_distributions_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":mock_distributions", ":mocking_bit_gen", @@ -458,6 +470,9 @@ cc_test( srcs = ["examples_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":random", "@com_google_googletest//:gtest_main", diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 67808aa0..73535e5b 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -693,6 +693,7 @@ cc_test( "benchmark", "no_test_ios_x86_64", "no_test_loonix", # Crashing. + "no_test_wasm", ], deps = [ ":nanobenchmark", diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 4e175892..64d3b929 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -119,6 +119,9 @@ cc_test( srcs = ["barrier_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":synchronization", "//absl/time", @@ -132,6 +135,9 @@ cc_test( srcs = ["blocking_counter_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":synchronization", "//absl/time", @@ -282,6 +288,9 @@ cc_test( size = "medium", copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":per_thread_sem_test_common", ":synchronization", @@ -298,7 +307,10 @@ cc_test( ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["no_test_ios_x86_64"], + tags = [ + "no_test_ios_x86_64", + "no_test_wasm", + ], deps = [ ":synchronization", "//absl/base:core_headers", diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index f0e7ae13..7304d40d 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -119,6 +119,7 @@ cc_test( "no_test_android_arm", "no_test_android_arm64", "no_test_android_x86", + "no_test_wasm", ], deps = [ ":civil_time", @@ -138,6 +139,7 @@ cc_test( "no_test_android_arm", "no_test_android_arm64", "no_test_android_x86", + "no_test_wasm", ], deps = [ ":civil_time", -- cgit v1.2.3 From bed94589f27d7fdfa34ede5988203369d170cec3 Mon Sep 17 00:00:00 2001 From: Laramie Leavitt Date: Thu, 2 Jun 2022 10:08:08 -0700 Subject: Rework NonsecureURBGBase seed sequence. Decompose RandenPoolSeedSeq from NonsecureURBGBase. Adjust how the RandenPoolSeedSeq detects contiguous buffers passed to the generate function. Previously it made incorrect assumptions regarding the contiguous concept, which have been replaced with some type-based tests for a small number of known contiguous random access iterator types, including raw pointers. PiperOrigin-RevId: 452564114 Change-Id: Idab1df9dd078d8e5c565c7fa7ccb9c0d3d392ad2 --- absl/random/CMakeLists.txt | 2 +- absl/random/internal/BUILD.bazel | 2 +- absl/random/internal/nonsecure_base.h | 107 +++++++++++++++------------- absl/random/internal/nonsecure_base_test.cc | 66 +++++++---------- absl/random/internal/salted_seed_seq.h | 50 +++++++------ 5 files changed, 109 insertions(+), 118 deletions(-) diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index b4ada69b..c3f0b450 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -727,7 +727,7 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::core_headers - absl::optional + absl::inlined_vector absl::random_internal_pool_urbg absl::random_internal_salted_seed_seq absl::random_internal_seed_material diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 73535e5b..69f9f80c 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -222,8 +222,8 @@ cc_library( ":salted_seed_seq", ":seed_material", "//absl/base:core_headers", + "//absl/container:inlined_vector", "//absl/meta:type_traits", - "//absl/types:optional", "//absl/types:span", ], ) diff --git a/absl/random/internal/nonsecure_base.h b/absl/random/internal/nonsecure_base.h index 730fa2ea..c7d7fa4b 100644 --- a/absl/random/internal/nonsecure_base.h +++ b/absl/random/internal/nonsecure_base.h @@ -17,28 +17,82 @@ #include #include -#include #include -#include -#include #include +#include #include #include "absl/base/macros.h" +#include "absl/container/inlined_vector.h" #include "absl/meta/type_traits.h" #include "absl/random/internal/pool_urbg.h" #include "absl/random/internal/salted_seed_seq.h" #include "absl/random/internal/seed_material.h" -#include "absl/types/optional.h" #include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { +// RandenPoolSeedSeq is a custom seed sequence type where generate() fills the +// provided buffer via the RandenPool entropy source. +class RandenPoolSeedSeq { + private: + struct ContiguousTag {}; + struct BufferTag {}; + + // Generate random unsigned values directly into the buffer. + template + void generate_impl(ContiguousTag, Contiguous begin, Contiguous end) { + const size_t n = std::distance(begin, end); + auto* a = &(*begin); + RandenPool::Fill( + absl::MakeSpan(reinterpret_cast(a), sizeof(*a) * n)); + } + + // Construct a buffer of size n and fill it with values, then copy + // those values into the seed iterators. + template + void generate_impl(BufferTag, RandomAccessIterator begin, + RandomAccessIterator end) { + const size_t n = std::distance(begin, end); + absl::InlinedVector data(n, 0); + RandenPool::Fill(absl::MakeSpan(data.begin(), data.end())); + std::copy(std::begin(data), std::end(data), begin); + } + + public: + using result_type = uint32_t; + + size_t size() { return 0; } + + template + void param(OutIterator) const {} + + template + void generate(RandomAccessIterator begin, RandomAccessIterator end) { + // RandomAccessIterator must be assignable from uint32_t + if (begin != end) { + using U = typename std::iterator_traits::value_type; + // ContiguousTag indicates the common case of a known contiguous buffer, + // which allows directly filling the buffer. In C++20, + // std::contiguous_iterator_tag provides a mechanism for testing this + // capability, however until Abseil's support requirements allow us to + // assume C++20, limit checks to a few common cases. + using TagType = absl::conditional_t< + (std::is_pointer::value || + std::is_same::iterator>::value), + ContiguousTag, BufferTag>; + + generate_impl(TagType{}, begin, end); + } + } +}; + // Each instance of NonsecureURBGBase will be seeded by variates produced // by a thread-unique URBG-instance. -template +template class NonsecureURBGBase { public: using result_type = typename URBG::result_type; @@ -85,49 +139,6 @@ class NonsecureURBGBase { } private: - // Seeder is a custom seed sequence type where generate() fills the provided - // buffer via the RandenPool entropy source. - struct Seeder { - using result_type = uint32_t; - - size_t size() { return 0; } - - template - void param(OutIterator) const {} - - template - void generate(RandomAccessIterator begin, RandomAccessIterator end) { - if (begin != end) { - // begin, end must be random access iterators assignable from uint32_t. - generate_impl( - std::integral_constant{}, - begin, end); - } - } - - // Commonly, generate is invoked with a pointer to a buffer which - // can be cast to a uint32_t. - template - void generate_impl(std::integral_constant, - RandomAccessIterator begin, RandomAccessIterator end) { - auto buffer = absl::MakeSpan(begin, end); - auto target = absl::MakeSpan(reinterpret_cast(buffer.data()), - buffer.size()); - RandenPool::Fill(target); - } - - // The non-uint32_t case should be uncommon, and involves an extra copy, - // filling the uint32_t buffer and then mixing into the output. - template - void generate_impl(std::integral_constant, - RandomAccessIterator begin, RandomAccessIterator end) { - const size_t n = std::distance(begin, end); - absl::InlinedVector data(n, 0); - RandenPool::Fill(absl::MakeSpan(data.begin(), data.end())); - std::copy(std::begin(data), std::end(data), begin); - } - }; - static URBG ConstructURBG() { Seeder seeder; return URBG(seeder); diff --git a/absl/random/internal/nonsecure_base_test.cc b/absl/random/internal/nonsecure_base_test.cc index 698027fc..3502243e 100644 --- a/absl/random/internal/nonsecure_base_test.cc +++ b/absl/random/internal/nonsecure_base_test.cc @@ -15,6 +15,7 @@ #include "absl/random/internal/nonsecure_base.h" #include +#include #include #include #include @@ -192,54 +193,35 @@ TEST(NonsecureURBGBase, EqualSeedSequencesYieldEqualVariates) { } } -// This is a PRNG-compatible type specifically designed to test -// that NonsecureURBGBase::Seeder can correctly handle iterators -// to arbitrary non-uint32_t size types. -template -struct SeederTestEngine { - using result_type = T; +TEST(RandenPoolSeedSeqTest, SeederWorksForU32) { + absl::random_internal::RandenPoolSeedSeq seeder; - static constexpr result_type(min)() { - return (std::numeric_limits::min)(); - } - static constexpr result_type(max)() { - return (std::numeric_limits::max)(); - } - - template ::value>> - explicit SeederTestEngine(SeedSequence&& seq) { - seed(seq); - } - - SeederTestEngine(const SeederTestEngine&) = default; - SeederTestEngine& operator=(const SeederTestEngine&) = default; - SeederTestEngine(SeederTestEngine&&) = default; - SeederTestEngine& operator=(SeederTestEngine&&) = default; + uint32_t state[2] = {0, 0}; + seeder.generate(std::begin(state), std::end(state)); + EXPECT_FALSE(state[0] == 0 && state[1] == 0); +} - result_type operator()() { return state[0]; } +TEST(RandenPoolSeedSeqTest, SeederWorksForU64) { + absl::random_internal::RandenPoolSeedSeq seeder; - template - void seed(SeedSequence&& seq) { - std::fill(std::begin(state), std::end(state), T(0)); - seq.generate(std::begin(state), std::end(state)); - } + uint64_t state[2] = {0, 0}; + seeder.generate(std::begin(state), std::end(state)); + EXPECT_FALSE(state[0] == 0 && state[1] == 0); + EXPECT_FALSE((state[0] >> 32) == 0 && (state[1] >> 32) == 0); +} - T state[2]; -}; +TEST(RandenPoolSeedSeqTest, SeederWorksForS32) { + absl::random_internal::RandenPoolSeedSeq seeder; -TEST(NonsecureURBGBase, SeederWorksForU32) { - using U32 = - absl::random_internal::NonsecureURBGBase>; - U32 x; - EXPECT_NE(0, x()); + int32_t state[2] = {0, 0}; + seeder.generate(std::begin(state), std::end(state)); + EXPECT_FALSE(state[0] == 0 && state[1] == 0); } -TEST(NonsecureURBGBase, SeederWorksForU64) { - using U64 = - absl::random_internal::NonsecureURBGBase>; +TEST(RandenPoolSeedSeqTest, SeederWorksForVector) { + absl::random_internal::RandenPoolSeedSeq seeder; - U64 x; - EXPECT_NE(0, x()); + std::vector state(2); + seeder.generate(std::begin(state), std::end(state)); + EXPECT_FALSE(state[0] == 0 && state[1] == 0); } diff --git a/absl/random/internal/salted_seed_seq.h b/absl/random/internal/salted_seed_seq.h index 5953a090..06291865 100644 --- a/absl/random/internal/salted_seed_seq.h +++ b/absl/random/internal/salted_seed_seq.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "absl/container/inlined_vector.h" #include "absl/meta/type_traits.h" @@ -65,15 +66,19 @@ class SaltedSeedSeq { template void generate(RandomAccessIterator begin, RandomAccessIterator end) { + using U = typename std::iterator_traits::value_type; + // The common case is that generate is called with ContiguousIterators // to uint arrays. Such contiguous memory regions may be optimized, // which we detect here. - using tag = absl::conditional_t< - (std::is_pointer::value && - std::is_same, uint32_t>::value), + using TagType = absl::conditional_t< + (std::is_same::value && + (std::is_pointer::value || + std::is_same::iterator>::value)), ContiguousAndUint32Tag, DefaultTag>; if (begin != end) { - generate_impl(begin, end, tag{}); + generate_impl(TagType{}, begin, end, std::distance(begin, end)); } } @@ -89,8 +94,15 @@ class SaltedSeedSeq { struct DefaultTag {}; // Generate which requires the iterators are contiguous pointers to uint32_t. - void generate_impl(uint32_t* begin, uint32_t* end, ContiguousAndUint32Tag) { - generate_contiguous(absl::MakeSpan(begin, end)); + // Fills the initial seed buffer the underlying SSeq::generate() call, + // then mixes in the salt material. + template + void generate_impl(ContiguousAndUint32Tag, Contiguous begin, Contiguous end, + size_t n) { + seq_->generate(begin, end); + const uint32_t salt = absl::random_internal::GetSaltMaterial().value_or(0); + auto span = absl::Span(&*begin, n); + MixIntoSeedMaterial(absl::MakeConstSpan(&salt, 1), span); } // The uncommon case for generate is that it is called with iterators over @@ -98,27 +110,13 @@ class SaltedSeedSeq { // case we allocate a temporary 32-bit buffer and then copy-assign back // to the initial inputs. template - void generate_impl(RandomAccessIterator begin, RandomAccessIterator end, - DefaultTag) { - return generate_and_copy(std::distance(begin, end), begin); - } - - // Fills the initial seed buffer the underlying SSeq::generate() call, - // mixing in the salt material. - void generate_contiguous(absl::Span buffer) { - seq_->generate(buffer.begin(), buffer.end()); - const uint32_t salt = absl::random_internal::GetSaltMaterial().value_or(0); - MixIntoSeedMaterial(absl::MakeConstSpan(&salt, 1), buffer); - } - - // Allocates a seed buffer of `n` elements, generates the seed, then - // copies the result into the `out` iterator. - template - void generate_and_copy(size_t n, Iterator out) { - // Allocate a temporary buffer, generate, and then copy. + void generate_impl(DefaultTag, RandomAccessIterator begin, + RandomAccessIterator, size_t n) { + // Allocates a seed buffer of `n` elements, generates the seed, then + // copies the result into the `out` iterator. absl::InlinedVector data(n, 0); - generate_contiguous(absl::MakeSpan(data.data(), data.size())); - std::copy(data.begin(), data.end(), out); + generate_impl(ContiguousAndUint32Tag{}, data.begin(), data.end(), n); + std::copy(data.begin(), data.end(), begin); } // Because [rand.req.seedseq] is not required to be copy-constructible, -- cgit v1.2.3 From 859c63524fa542abde71c2564953f4a9aefdc952 Mon Sep 17 00:00:00 2001 From: Andy Getzendanner Date: Thu, 2 Jun 2022 12:26:48 -0700 Subject: Include proper #includes for POSIX thread identity implementation when using that implementation on MinGW. Fixes #1124 PiperOrigin-RevId: 452596638 Change-Id: Iab34b8e112dc050ffe346a418fa7b499983f0dcf --- absl/base/internal/thread_identity.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index db46aafb..79853f09 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -14,7 +14,7 @@ #include "absl/base/internal/thread_identity.h" -#ifndef _WIN32 +#if !defined(_WIN32) || defined(__MINGW32__) #include #include #endif -- cgit v1.2.3 From 47345f63b94a0ea8fbfb3c49ed5b185f2b649f1a Mon Sep 17 00:00:00 2001 From: Samuel Benzaquen Date: Fri, 3 Jun 2022 13:01:35 -0700 Subject: Improve the compiler error by removing some noise from it. The "deleted" overload error is useless to users. By passing some dummy string to the base class constructor we use a valid constructor and remove the unintended use of the deleted default constructor. PiperOrigin-RevId: 452826509 Change-Id: I5430a373c8e7e3a13336d2c42899e0e59444620b --- absl/strings/internal/str_format/bind.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index dcaa5dd0..80f29654 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h @@ -143,7 +143,8 @@ class FormatSpecTemplate template FormatSpecTemplate(string_view s) // NOLINT __attribute__((enable_if(str_format_internal::EnsureConstexpr(s), - "constexpr trap"))) { + "constexpr trap"))) + : Base("to avoid noise in the compiler error") { static_assert(sizeof(T*) == 0, "Format specified does not match the arguments passed."); } -- cgit v1.2.3 From 9cdb98e73118f0485fdf5fadcb3e57ab852e65a1 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Mon, 6 Jun 2022 07:43:30 -0700 Subject: InlinedVector: Limit the scope of the maybe-uninitialized warning suppression Due to changes in GCC 12, without this change, the warning fires PiperOrigin-RevId: 453197246 Change-Id: I2e31cbff1707ab09868cf77dcf040b033984e654 --- absl/container/inlined_vector.h | 12 ++++++++++++ absl/container/internal/inlined_vector.h | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 711b29c1..bc1c4a77 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -585,8 +585,20 @@ class InlinedVector { if (ABSL_PREDICT_TRUE(n != 0)) { value_type dealias = v; + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 + // It appears that GCC thinks that since `pos` is a const pointer and may + // point to uninitialized memory at this point, a warning should be + // issued. But `pos` is actually only used to compute an array index to + // write to. +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif return storage_.Insert(pos, CopyValueAdapter(std::addressof(dealias)), n); +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif } else { return const_cast(pos); } diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 2baf26f3..bc1b8c1b 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -40,7 +40,6 @@ namespace inlined_vector_internal { #if !defined(__clang__) && defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif template @@ -942,7 +941,7 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { swap(GetAllocator(), other_storage_ptr->GetAllocator()); } -// End ignore "array-bounds" and "maybe-uninitialized" +// End ignore "array-bounds" #if !defined(__clang__) && defined(__GNUC__) #pragma GCC diagnostic pop #endif -- cgit v1.2.3 From 48419595d31609762985a6b08be504ebe6d593e7 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Mon, 6 Jun 2022 09:09:23 -0700 Subject: Release absl::CordBuffer absl::CordBuffer holds data for eventual inclusion within an existing absl::Cord. CordBuffers are useful for building large Cords that may require custom allocation of its associated memory, a pattern that is common in zero-copy APIs. PiperOrigin-RevId: 453212229 Change-Id: I6a8adc3a8d206691cb1b0001a9161e5080dd1c5f --- CMake/AbseilDll.cmake | 4 +- absl/strings/BUILD.bazel | 18 ++ absl/strings/CMakeLists.txt | 2 + absl/strings/cord.cc | 68 +++++ absl/strings/cord.h | 79 ++++++ absl/strings/cord_buffer.cc | 28 ++ absl/strings/cord_buffer.h | 571 +++++++++++++++++++++++++++++++++++++++ absl/strings/cord_buffer_test.cc | 320 ++++++++++++++++++++++ absl/strings/cord_test.cc | 278 +++++++++++++++++++ 9 files changed, 1367 insertions(+), 1 deletion(-) create mode 100644 absl/strings/cord_buffer.cc create mode 100644 absl/strings/cord_buffer.h create mode 100644 absl/strings/cord_buffer_test.cc diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 0b5f0a54..f81cb0ab 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -194,9 +194,11 @@ set(ABSL_INTERNAL_DLL_FILES "strings/charconv.cc" "strings/charconv.h" "strings/cord.cc" + "strings/cord.h" "strings/cord_analysis.cc" "strings/cord_analysis.h" - "strings/cord.h" + "strings/cord_buffer.cc" + "strings/cord_buffer.h" "strings/escaping.cc" "strings/escaping.h" "strings/internal/charconv_bigint.cc" diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index e9092165..81f5a589 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -415,9 +415,11 @@ cc_library( "cord.cc", "cord_analysis.cc", "cord_analysis.h", + "cord_buffer.cc", ], hdrs = [ "cord.h", + "cord_buffer.h", ], copts = ABSL_DEFAULT_COPTS, deps = [ @@ -701,6 +703,22 @@ cc_library( ], ) +cc_test( + name = "cord_buffer_test", + size = "small", + srcs = ["cord_buffer_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord", + ":cord_internal", + ":cord_rep_test_util", + "//absl/base:config", + "//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "cord_test", size = "medium", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index a1b8d6ed..bb073301 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -845,10 +845,12 @@ absl_cc_library( cord HDRS "cord.h" + "cord_buffer.h" SRCS "cord.cc" "cord_analysis.cc" "cord_analysis.h" + "cord_buffer.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index c6ec1ecf..04dc7b90 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -34,6 +34,7 @@ #include "absl/base/port.h" #include "absl/container/fixed_array.h" #include "absl/container/inlined_vector.h" +#include "absl/strings/cord_buffer.h" #include "absl/strings/escaping.h" #include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" @@ -520,6 +521,46 @@ inline void Cord::AppendImpl(C&& src) { contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord); } +static CordRep::ExtractResult ExtractAppendBuffer(CordRep* rep, + size_t min_capacity) { + switch (rep->tag) { + case cord_internal::BTREE: + return CordRepBtree::ExtractAppendBuffer(rep->btree(), min_capacity); + default: + if (rep->IsFlat() && rep->refcount.IsOne() && + rep->flat()->Capacity() - rep->length >= min_capacity) { + return {nullptr, rep}; + } + return {rep, nullptr}; + } +} + +static CordBuffer CreateAppendBuffer(InlineData& data, size_t capacity) { + // Watch out for overflow, people can ask for size_t::max(). + const size_t size = data.inline_size(); + capacity = (std::min)(std::numeric_limits::max() - size, capacity); + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(size + capacity); + cord_internal::SmallMemmove(buffer.data(), data.as_chars(), size); + buffer.SetLength(size); + data = {}; + return buffer; +} + +CordBuffer Cord::GetAppendBufferSlowPath(size_t capacity, size_t min_capacity) { + auto constexpr method = CordzUpdateTracker::kGetAppendBuffer; + CordRep* tree = contents_.tree(); + if (tree != nullptr) { + CordzUpdateScope scope(contents_.cordz_info(), method); + CordRep::ExtractResult result = ExtractAppendBuffer(tree, min_capacity); + if (result.extracted != nullptr) { + contents_.SetTreeOrEmpty(result.tree, scope); + return CordBuffer(result.extracted->flat()); + } + return CordBuffer::CreateWithDefaultLimit(capacity); + } + return CreateAppendBuffer(contents_.data_, capacity); +} + void Cord::Append(const Cord& src) { AppendImpl(src); } @@ -572,6 +613,33 @@ void Cord::PrependArray(absl::string_view src, MethodIdentifier method) { contents_.PrependTree(rep, method); } +void Cord::AppendPrecise(absl::string_view src, MethodIdentifier method) { + assert(!src.empty()); + assert(src.size() <= cord_internal::kMaxFlatLength); + if (contents_.remaining_inline_capacity() >= src.size()) { + const size_t inline_length = contents_.inline_size(); + memcpy(contents_.data_.as_chars() + inline_length, src.data(), src.size()); + contents_.set_inline_size(inline_length + src.size()); + } else { + contents_.AppendTree(CordRepFlat::Create(src), method); + } +} + +void Cord::PrependPrecise(absl::string_view src, MethodIdentifier method) { + assert(!src.empty()); + assert(src.size() <= cord_internal::kMaxFlatLength); + if (contents_.remaining_inline_capacity() >= src.size()) { + const size_t inline_length = contents_.inline_size(); + char data[InlineRep::kMaxInline + 1] = {0}; + memcpy(data, src.data(), src.size()); + memcpy(data + src.size(), contents_.data(), inline_length); + memcpy(contents_.data_.as_chars(), data, InlineRep::kMaxInline + 1); + contents_.set_inline_size(inline_length + src.size()); + } else { + contents_.PrependTree(CordRepFlat::Create(src), method); + } +} + template > inline void Cord::Prepend(T&& src) { if (src.size() <= kMaxBytesToCopy) { diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 391b2c0b..f378dc07 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -80,6 +80,7 @@ #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" #include "absl/strings/cord_analysis.h" +#include "absl/strings/cord_buffer.h" #include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" @@ -244,6 +245,45 @@ class Cord { template = 0> void Append(T&& src); + // Appends `buffer` to this cord, unless `buffer` has a zero length in which + // case this method has no effect on this cord instance. + // This method is guaranteed to consume `buffer`. + void Append(CordBuffer buffer); + + // Returns a CordBuffer, re-using potential existing capacity in this cord. + // + // Cord instances may have additional unused capacity in the last (or first) + // nodes of the underlying tree to facilitate amortized growth. This method + // allows applications to explicitly use this spare capacity if available, + // or create a new CordBuffer instance otherwise. + // If this cord has a final non-shared node with at least `min_capacity` + // available, then this method will return that buffer including its data + // contents. I.e.; the returned buffer will have a non-zero length, and + // a capacity of at least `buffer.length + min_capacity`. Otherwise, this + // method will return `CordBuffer::CreateWithDefaultLimit(capacity)`. + // + // Below an example of using GetAppendBuffer. Notice that in this example we + // use `GetAppendBuffer()` only on the first iteration. As we know nothing + // about any initial extra capacity in `cord`, we may be able to use the extra + // capacity. But as we add new buffers with fully utilized contents after that + // we avoid calling `GetAppendBuffer()` on subsequent iterations: while this + // works fine, it results in an unnecessary inspection of cord contents: + // + // void AppendRandomDataToCord(absl::Cord &cord, size_t n) { + // bool first = true; + // while (n > 0) { + // CordBuffer buffer = first ? cord.GetAppendBuffer(n) + // : CordBuffer::CreateWithDefaultLimit(n); + // absl::Span data = buffer.available_up_to(n); + // FillRandomValues(data.data(), data.size()); + // buffer.IncreaseLengthBy(data.size()); + // cord.Append(std::move(buffer)); + // n -= data.size(); + // first = false; + // } + // } + CordBuffer GetAppendBuffer(size_t capacity, size_t min_capacity = 16); + // Cord::Prepend() // // Prepends data to the Cord, which may come from another Cord or other string @@ -253,6 +293,11 @@ class Cord { template = 0> void Prepend(T&& src); + // Prepends `buffer` to this cord, unless `buffer` has a zero length in which + // case this method has no effect on this cord instance. + // This method is guaranteed to consume `buffer`. + void Prepend(CordBuffer buffer); + // Cord::RemovePrefix() // // Removes the first `n` bytes of a Cord. @@ -928,6 +973,15 @@ class Cord { template void AppendImpl(C&& src); + // Appends / Prepends `src` to this instance, using precise sizing. + // This method does explicitly not attempt to use any spare capacity + // in any pending last added private owned flat. + // Requires `src` to be <= kMaxFlatLength. + void AppendPrecise(absl::string_view src, MethodIdentifier method); + void PrependPrecise(absl::string_view src, MethodIdentifier method); + + CordBuffer GetAppendBufferSlowPath(size_t capacity, size_t min_capacity); + // Prepends the provided data to this instance. `method` contains the public // API method for this action which is tracked for Cordz sampling purposes. void PrependArray(absl::string_view src, MethodIdentifier method); @@ -1284,6 +1338,31 @@ inline void Cord::Prepend(absl::string_view src) { PrependArray(src, CordzUpdateTracker::kPrependString); } +inline void Cord::Append(CordBuffer buffer) { + if (ABSL_PREDICT_FALSE(buffer.length() == 0)) return; + absl::string_view short_value; + if (CordRep* rep = buffer.ConsumeValue(short_value)) { + contents_.AppendTree(rep, CordzUpdateTracker::kAppendCordBuffer); + } else { + AppendPrecise(short_value, CordzUpdateTracker::kAppendCordBuffer); + } +} + +inline void Cord::Prepend(CordBuffer buffer) { + if (ABSL_PREDICT_FALSE(buffer.length() == 0)) return; + absl::string_view short_value; + if (CordRep* rep = buffer.ConsumeValue(short_value)) { + contents_.PrependTree(rep, CordzUpdateTracker::kPrependCordBuffer); + } else { + PrependPrecise(short_value, CordzUpdateTracker::kPrependCordBuffer); + } +} + +inline CordBuffer Cord::GetAppendBuffer(size_t capacity, size_t min_capacity) { + if (empty()) return CordBuffer::CreateWithDefaultLimit(capacity); + return GetAppendBufferSlowPath(capacity, min_capacity); +} + extern template void Cord::Append(std::string&& src); extern template void Cord::Prepend(std::string&& src); diff --git a/absl/strings/cord_buffer.cc b/absl/strings/cord_buffer.cc new file mode 100644 index 00000000..fd4045bd --- /dev/null +++ b/absl/strings/cord_buffer.cc @@ -0,0 +1,28 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/cord_buffer.h" + +#include + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +constexpr size_t CordBuffer::kDefaultLimit; +constexpr size_t CordBuffer::kCustomLimit; + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/cord_buffer.h b/absl/strings/cord_buffer.h new file mode 100644 index 00000000..4f4bdc0e --- /dev/null +++ b/absl/strings/cord_buffer.h @@ -0,0 +1,571 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: cord_buffer.h +// ----------------------------------------------------------------------------- +// +// This file defines an `absl::CordBuffer` data structure to hold data for +// eventual inclusion within an existing `Cord` data structure. Cord buffers are +// useful for building large Cords that may require custom allocation of its +// associated memory. +// +#ifndef ABSL_STRINGS_CORD_BUFFER_H_ +#define ABSL_STRINGS_CORD_BUFFER_H_ + +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/numeric/bits.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class Cord; +class CordBufferTestPeer; + +// CordBuffer +// +// CordBuffer manages memory buffers for purposes such as zero-copy APIs as well +// as applications building cords with large data requiring granular control +// over the allocation and size of cord data. For example, a function creating +// a cord of random data could use a CordBuffer as follows: +// +// absl::Cord CreateRandomCord(size_t length) { +// absl::Cord cord; +// while (length > 0) { +// CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(length); +// absl::Span data = buffer.available_up_to(length); +// FillRandomValues(data.data(), data.size()); +// buffer.IncreaseLengthBy(data.size()); +// cord.Append(std::move(buffer)); +// length -= data.size(); +// } +// return cord; +// } +// +// CordBuffer instances are by default limited to a capacity of `kDefaultLimit` +// bytes. `kDefaultLimit` is currently just under 4KiB, but this default may +// change in the future and/or for specific architectures. The default limit is +// aimed to provide a good trade-off between performance and memory overhead. +// Smaller buffers typically incur more compute cost while larger buffers are +// more CPU efficient but create significant memory overhead because of such +// allocations being less granular. Using larger buffers may also increase the +// risk of memory fragmentation. +// +// Applications create a buffer using one of the `CreateWithDefaultLimit()` or +// `CreateWithCustomLimit()` methods. The returned instance will have a non-zero +// capacity and a zero length. Applications use the `data()` method to set the +// contents of the managed memory, and once done filling the buffer, use the +// `IncreaseLengthBy()` or 'SetLength()' method to specify the length of the +// initialized data before adding the buffer to a Cord. +// +// The `CreateWithCustomLimit()` method is intended for applications needing +// larger buffers than the default memory limit, allowing the allocation of up +// to a capacity of `kCustomLimit` bytes minus some minimum internal overhead. +// The usage of `CreateWithCustomLimit()` should be limited to only those use +// cases where the distribution of the input is relatively well known, and/or +// where the trade-off between the efficiency gains outweigh the risk of memory +// fragmentation. See the documentation for `CreateWithCustomLimit()` for more +// information on using larger custom limits. +// +// The capacity of a `CordBuffer` returned by one of the `Create` methods may +// be larger than the requested capacity due to rounding, alignment and +// granularity of the memory allocator. Applications should use the `capacity` +// method to obtain the effective capacity of the returned instance as +// demonstrated in the provided example above. +// +// CordBuffer is a move-only class. All references into the managed memory are +// invalidated when an instance is moved into either another CordBuffer instance +// or a Cord. Writing to a location obtained by a previous call to `data()` +// after an instance was moved will lead to undefined behavior. +// +// A `moved from` CordBuffer instance will have a valid, but empty state. +// CordBuffer is thread compatible. +class CordBuffer { + public: + // kDefaultLimit + // + // Default capacity limits of allocated CordBuffers. + // See the class comments for more information on allocation limits. + static constexpr size_t kDefaultLimit = cord_internal::kMaxFlatLength; + + // kCustomLimit + // + // Maximum size for CreateWithCustomLimit() allocated buffers. + // Note that the effective capacity may be slightly less + // because of internal overhead of internal cord buffers. + static constexpr size_t kCustomLimit = 64U << 10; + + // Constructors, Destructors and Assignment Operators + + // Creates an empty CordBuffer. + CordBuffer() = default; + + // Destroys this CordBuffer instance and, if not empty, releases any memory + // managed by this instance, invalidating previously returned references. + ~CordBuffer(); + + // CordBuffer is move-only + CordBuffer(CordBuffer&& rhs) noexcept; + CordBuffer& operator=(CordBuffer&&) noexcept; + CordBuffer(const CordBuffer&) = delete; + CordBuffer& operator=(const CordBuffer&) = delete; + + // CordBuffer::MaximumPayload() + // + // Returns the guaranteed maximum payload for a CordBuffer returned by the + // `CreateWithDefaultLimit()` method. While small, each internal buffer inside + // a Cord incurs an overhead to manage the length, type and reference count + // for the buffer managed inside the cord tree. Applications can use this + // method to get approximate number of buffers required for a given byte + // size, etc. + // + // For example: + // const size_t payload = absl::CordBuffer::MaximumPayload(); + // const size_t buffer_count = (total_size + payload - 1) / payload; + // buffers.reserve(buffer_count); + static constexpr size_t MaximumPayload(); + + // Overload to the above `MaximumPayload()` except that it returns the + // maximum payload for a CordBuffer returned by the `CreateWithCustomLimit()` + // method given the provided `block_size`. + static constexpr size_t MaximumPayload(size_t block_size); + + // CordBuffer::CreateWithDefaultLimit() + // + // Creates a CordBuffer instance of the desired `capacity`, capped at the + // default limit `kDefaultLimit`. The returned buffer has a guaranteed + // capacity of at least `min(kDefaultLimit, capacity)`. See the class comments + // for more information on buffer capacities and intended usage. + static CordBuffer CreateWithDefaultLimit(size_t capacity); + + + // CordBuffer::CreateWithCustomLimit() + // + // Creates a CordBuffer instance of the desired `capacity` rounded to an + // appropriate power of 2 size less than, or equal to `block_size`. + // Requires `block_size` to be a power of 2. + // + // If `capacity` is less than or equal to `kDefaultLimit`, then this method + // behaves identical to `CreateWithDefaultLimit`, which means that the caller + // is guaranteed to get a buffer of at least the requested capacity. + // + // If `capacity` is greater than or equal to `block_size`, then this method + // returns a buffer with an `allocated size` of `block_size` bytes. Otherwise, + // this methods returns a buffer with a suitable smaller power of 2 block size + // to satisfy the request. The actual size depends on a number of factors, and + // is typically (but not necessarily) the highest or second highest power of 2 + // value less than or equal to `capacity`. + // + // The 'allocated size' includes a small amount of overhead required for + // internal state, which is currently 13 bytes on 64-bit platforms. For + // example: a buffer created with `block_size` and `capacity' set to 8KiB + // will have an allocated size of 8KiB, and an effective internal `capacity` + // of 8KiB - 13 = 8179 bytes. + // + // To demonstrate this in practice, let's assume we want to read data from + // somewhat larger files using approximately 64KiB buffers: + // + // absl::Cord ReadFromFile(int fd, size_t n) { + // absl::Cord cord; + // while (n > 0) { + // CordBuffer buffer = CordBuffer::CreateWithCustomLimit(64 << 10, n); + // absl::Span data = buffer.available_up_to(n); + // ReadFileDataOrDie(fd, data.data(), data.size()); + // buffer.IncreaseLengthBy(data.size()); + // cord.Append(std::move(buffer)); + // n -= data.size(); + // } + // return cord; + // } + // + // If we'd use this function to read a file of 659KiB, we may get the + // following pattern of allocated cord buffer sizes: + // + // CreateWithCustomLimit(64KiB, 674816) --> ~64KiB (65523) + // CreateWithCustomLimit(64KiB, 674816) --> ~64KiB (65523) + // ... + // CreateWithCustomLimit(64KiB, 19586) --> ~16KiB (16371) + // CreateWithCustomLimit(64KiB, 3215) --> 3215 (at least 3215) + // + // The reason the method returns a 16K buffer instead of a roughly 19K buffer + // is to reduce memory overhead and fragmentation risks. Using carefully + // chosen power of 2 values reduces the entropy of allocated memory sizes. + // + // Additionally, let's assume we'd use the above function on files that are + // generally smaller than 64K. If we'd use 'precise' sized buffers for such + // files, than we'd get a very wide distribution of allocated memory sizes + // rounded to 4K page sizes, and we'd end up with a lot of unused capacity. + // + // In general, application should only use custom sizes if the data they are + // consuming or storing is expected to be many times the chosen block size, + // and be based on objective data and performance metrics. For example, a + // compress function may work faster and consume less CPU when using larger + // buffers. Such an application should pick a size offering a reasonable + // trade-off between expected data size, compute savings with larger buffers, + // and the cost or fragmentation effect of larger buffers. + // Applications must pick a reasonable spot on that curve, and make sure their + // data meets their expectations in size distributions such as "mostly large". + static CordBuffer CreateWithCustomLimit(size_t block_size, size_t capacity); + + // CordBuffer::available() + // + // Returns the span delineating the available capacity in this buffer + // which is defined as `{ data() + length(), capacity() - length() }`. + absl::Span available(); + + // CordBuffer::available_up_to() + // + // Returns the span delineating the available capacity in this buffer limited + // to `size` bytes. This is equivalent to `available().subspan(0, size)`. + absl::Span available_up_to(size_t size); + + // CordBuffer::data() + // + // Returns a non-null reference to the data managed by this instance. + // Applications are allowed to write up to `capacity` bytes of instance data. + // CordBuffer data is uninitialized by default. Reading data from an instance + // that has not yet been initialized will lead to undefined behavior. + char* data(); + const char* data() const; + + // CordBuffer::length() + // + // Returns the length of this instance. The default length of a CordBuffer is + // 0, indicating an 'empty' CordBuffer. Applications must specify the length + // of the data in a CordBuffer before adding it to a Cord. + size_t length() const; + + // CordBuffer::capacity() + // + // Returns the capacity of this instance. All instances have a non-zero + // capacity: default and `moved from` instances have a small internal buffer. + size_t capacity() const; + + // CordBuffer::IncreaseLengthBy() + // + // Increases the length of this buffer by the specified 'n' bytes. + // Applications must make sure all data in this buffer up to the new length + // has been initialized before adding a CordBuffer to a Cord: failure to do so + // will lead to undefined behavior. Requires `length() + n <= capacity()`. + // Typically, applications will use 'available_up_to()` to get a span of the + // desired capacity, and use `span.size()` to increase the length as in: + // absl::Span span = buffer.available_up_to(desired); + // buffer.IncreaseLengthBy(span.size()); + // memcpy(span.data(), src, span.size()); + // etc... + void IncreaseLengthBy(size_t n); + + // CordBuffer::SetLength() + // + // Sets the data length of this instance. Applications must make sure all data + // of the specified length has been initialized before adding a CordBuffer to + // a Cord: failure to do so will lead to undefined behavior. + // Setting the length to a small value or zero does not release any memory + // held by this CordBuffer instance. Requires `length <= capacity()`. + // Applications should preferably use the `IncreaseLengthBy()` method above + // in combination with the 'available()` or `available_up_to()` methods. + void SetLength(size_t length); + + private: + // Make sure we don't accidentally over promise. + static_assert(kCustomLimit <= cord_internal::kMaxLargeFlatSize, ""); + + // Assume the cost of an 'uprounded' allocation to CeilPow2(size) versus + // the cost of allocating at least 1 extra flat <= 4KB: + // - Flat overhead = 13 bytes + // - Btree amortized cost / node =~ 13 bytes + // - 64 byte granularity of tcmalloc at 4K =~ 32 byte average + // CPU cost and efficiency requires we should at least 'save' something by + // splitting, as a poor man's measure, we say the slop needs to be + // at least double the cost offset to make it worth splitting: ~128 bytes. + static constexpr size_t kMaxPageSlop = 128; + + // Overhead for allocation a flat. + static constexpr size_t kOverhead = cord_internal::kFlatOverhead; + + using CordRepFlat = cord_internal::CordRepFlat; + + // `Rep` is the internal data representation of a CordBuffer. The internal + // representation has an internal small size optimization similar to + // std::string (SSO). + struct Rep { + // Inline SSO size of a CordBuffer + static constexpr size_t kInlineCapacity = sizeof(intptr_t) * 2 - 1; + + // Creates a default instance with kInlineCapacity. + Rep() : short_rep{} {} + + // Creates an instance managing an allocated non zero CordRep. + explicit Rep(cord_internal::CordRepFlat* rep) : long_rep{rep} { + assert(rep != nullptr); + } + + // Returns true if this instance manages the SSO internal buffer. + bool is_short() const { + constexpr size_t offset = offsetof(Short, raw_size); + return (reinterpret_cast(this)[offset] & 1) != 0; + } + + // Returns the available area of the internal SSO data + absl::Span short_available() { + assert(is_short()); + const size_t length = (short_rep.raw_size >> 1); + return absl::Span(short_rep.data + length, + kInlineCapacity - length); + } + + // Returns the available area of the internal SSO data + absl::Span long_available() { + assert(!is_short()); + const size_t length = long_rep.rep->length; + return absl::Span(long_rep.rep->Data() + length, + long_rep.rep->Capacity() - length); + } + + // Returns the length of the internal SSO data. + size_t short_length() const { + assert(is_short()); + return short_rep.raw_size >> 1; + } + + // Sets the length of the internal SSO data. + // Disregards any previously set CordRep instance. + void set_short_length(size_t length) { + short_rep.raw_size = static_cast((length << 1) + 1); + } + + // Adds `n` to the current short length. + void add_short_length(size_t n) { + assert(is_short()); + short_rep.raw_size += static_cast(n << 1); + } + + // Returns reference to the internal SSO data buffer. + char* data() { + assert(is_short()); + return short_rep.data; + } + const char* data() const { + assert(is_short()); + return short_rep.data; + } + + // Returns a pointer the external CordRep managed by this instance. + cord_internal::CordRepFlat* rep() const { + assert(!is_short()); + return long_rep.rep; + } + + // The internal representation takes advantage of the fact that allocated + // memory is always on an even address, and uses the least significant bit + // of the first or last byte (depending on endianness) as the inline size + // indicator overlapping with the least significant byte of the CordRep*. +#if defined(ABSL_IS_BIG_ENDIAN) + struct Long { + explicit Long(cord_internal::CordRepFlat* rep_arg) : rep(rep_arg) {} + void* padding; + cord_internal::CordRepFlat* rep; + }; + struct Short { + char data[sizeof(Long) - 1]; + char raw_size = 1; + }; +#else + struct Long { + explicit Long(cord_internal::CordRepFlat* rep_arg) : rep(rep_arg) {} + cord_internal::CordRepFlat* rep; + void* padding; + }; + struct Short { + char raw_size = 1; + char data[sizeof(Long) - 1]; + }; +#endif + + union { + Long long_rep; + Short short_rep; + }; + }; + + // Power2 functions + static bool IsPow2(size_t size) { return absl::has_single_bit(size); } + static size_t Log2Floor(size_t size) { return absl::bit_width(size) - 1; } + static size_t Log2Ceil(size_t size) { return absl::bit_width(size - 1); } + + // Implementation of `CreateWithCustomLimit()`. + // This implementation allows for future memory allocation hints to + // be passed down into the CordRepFlat allocation function. + template + static CordBuffer CreateWithCustomLimitImpl(size_t block_size, + size_t capacity, + AllocationHints... hints); + + // Consumes the value contained in this instance and resets the instance. + // This method returns a non-null Cordrep* if the current instances manages a + // CordRep*, and resets the instance to an empty SSO instance. If the current + // instance is an SSO instance, then this method returns nullptr and sets + // `short_value` to the inlined data value. In either case, the current + // instance length is reset to zero. + // This method is intended to be used by Cord internal functions only. + cord_internal::CordRep* ConsumeValue(absl::string_view& short_value) { + cord_internal::CordRep* rep = nullptr; + if (rep_.is_short()) { + short_value = absl::string_view(rep_.data(), rep_.short_length()); + } else { + rep = rep_.rep(); + } + rep_.set_short_length(0); + return rep; + } + + // Internal constructor. + explicit CordBuffer(cord_internal::CordRepFlat* rep) : rep_(rep) { + assert(rep != nullptr); + } + + Rep rep_; + + friend class Cord; + friend class CordBufferTestPeer; +}; + +inline constexpr size_t CordBuffer::MaximumPayload() { + return cord_internal::kMaxFlatLength; +} + +inline constexpr size_t CordBuffer::MaximumPayload(size_t block_size) { + // TODO(absl-team): Use std::min when C++11 support is dropped. + return (kCustomLimit < block_size ? kCustomLimit : block_size) - + cord_internal::kFlatOverhead; +} + +inline CordBuffer CordBuffer::CreateWithDefaultLimit(size_t capacity) { + if (capacity > Rep::kInlineCapacity) { + auto* rep = cord_internal::CordRepFlat::New(capacity); + rep->length = 0; + return CordBuffer(rep); + } + return CordBuffer(); +} + +template +inline CordBuffer CordBuffer::CreateWithCustomLimitImpl( + size_t block_size, size_t capacity, AllocationHints... hints) { + assert(IsPow2(block_size)); + capacity = (std::min)(capacity, kCustomLimit); + block_size = (std::min)(block_size, kCustomLimit); + if (capacity + kOverhead >= block_size) { + capacity = block_size; + } else if (capacity <= kDefaultLimit) { + capacity = capacity + kOverhead; + } else if (!IsPow2(capacity)) { + // Check if rounded up to next power 2 is a good enough fit + // with limited waste making it an acceptable direct fit. + const size_t rounded_up = size_t{1} << Log2Ceil(capacity); + const size_t slop = rounded_up - capacity; + if (slop >= kOverhead && slop <= kMaxPageSlop + kOverhead) { + capacity = rounded_up; + } else { + // Round down to highest power of 2 <= capacity. + // Consider a more aggressive step down if that may reduce the + // risk of fragmentation where 'people are holding it wrong'. + const size_t rounded_down = size_t{1} << Log2Floor(capacity); + capacity = rounded_down; + } + } + const size_t length = capacity - kOverhead; + auto* rep = CordRepFlat::New(CordRepFlat::Large(), length, hints...); + rep->length = 0; + return CordBuffer(rep); +} + +inline CordBuffer CordBuffer::CreateWithCustomLimit(size_t block_size, + size_t capacity) { + return CreateWithCustomLimitImpl(block_size, capacity); +} + +inline CordBuffer::~CordBuffer() { + if (!rep_.is_short()) { + cord_internal::CordRepFlat::Delete(rep_.rep()); + } +} + +inline CordBuffer::CordBuffer(CordBuffer&& rhs) noexcept : rep_(rhs.rep_) { + rhs.rep_.set_short_length(0); +} + +inline CordBuffer& CordBuffer::operator=(CordBuffer&& rhs) noexcept { + if (!rep_.is_short()) cord_internal::CordRepFlat::Delete(rep_.rep()); + rep_ = rhs.rep_; + rhs.rep_.set_short_length(0); + return *this; +} + +inline absl::Span CordBuffer::available() { + return rep_.is_short() ? rep_.short_available() : rep_.long_available(); +} + +inline absl::Span CordBuffer::available_up_to(size_t size) { + return available().subspan(0, size); +} + +inline char* CordBuffer::data() { + return rep_.is_short() ? rep_.data() : rep_.rep()->Data(); +} + +inline const char* CordBuffer::data() const { + return rep_.is_short() ? rep_.data() : rep_.rep()->Data(); +} + +inline size_t CordBuffer::capacity() const { + return rep_.is_short() ? Rep::kInlineCapacity : rep_.rep()->Capacity(); +} + +inline size_t CordBuffer::length() const { + return rep_.is_short() ? rep_.short_length() : rep_.rep()->length; +} + +inline void CordBuffer::SetLength(size_t length) { + assert(length <= capacity()); + if (rep_.is_short()) { + rep_.set_short_length(length); + } else { + rep_.rep()->length = length; + } +} + +inline void CordBuffer::IncreaseLengthBy(size_t n) { + assert(n <= capacity() && length() + n <= capacity()); + if (rep_.is_short()) { + rep_.add_short_length(n); + } else { + rep_.rep()->length += n; + } +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORD_BUFFER_H_ diff --git a/absl/strings/cord_buffer_test.cc b/absl/strings/cord_buffer_test.cc new file mode 100644 index 00000000..5c7437ae --- /dev/null +++ b/absl/strings/cord_buffer_test.cc @@ -0,0 +1,320 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/cord_buffer.h" + + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cord_rep_test_util.h" +#include "absl/types/span.h" + +using testing::Eq; +using testing::Ge; +using testing::Le; +using testing::Ne; + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class CordBufferTestPeer { + public: + static cord_internal::CordRep* ConsumeValue(CordBuffer& buffer, + absl::string_view& short_value) { + return buffer.ConsumeValue(short_value); + } +}; + +namespace { + +using ::absl::cordrep_testing::CordToString; + +constexpr size_t kInlinedSize = sizeof(CordBuffer) - 1; +constexpr size_t kDefaultLimit = CordBuffer::kDefaultLimit; +constexpr size_t kCustomLimit = CordBuffer::kCustomLimit; +constexpr size_t kMaxFlatSize = cord_internal::kMaxFlatSize; +constexpr size_t kMaxFlatLength = cord_internal::kMaxFlatLength; +constexpr size_t kFlatOverhead = cord_internal::kFlatOverhead; + +constexpr size_t k8KiB = 8 << 10; +constexpr size_t k16KiB = 16 << 10; +constexpr size_t k64KiB = 64 << 10; +constexpr size_t k1MB = 1 << 20; + +class CordBufferTest : public testing::TestWithParam {}; + +INSTANTIATE_TEST_SUITE_P(MediumSize, CordBufferTest, + testing::Values(1, kInlinedSize - 1, kInlinedSize, + kInlinedSize + 1, kDefaultLimit - 1, + kDefaultLimit)); + +TEST_P(CordBufferTest, MaximumPayload) { + EXPECT_THAT(CordBuffer::MaximumPayload(), Eq(kMaxFlatLength)); + EXPECT_THAT(CordBuffer::MaximumPayload(512), Eq(512 - kFlatOverhead)); + EXPECT_THAT(CordBuffer::MaximumPayload(k64KiB), Eq(k64KiB - kFlatOverhead)); + EXPECT_THAT(CordBuffer::MaximumPayload(k1MB), Eq(k64KiB - kFlatOverhead)); +} + +TEST(CordBufferTest, ConstructDefault) { + CordBuffer buffer; + EXPECT_THAT(buffer.capacity(), Eq(sizeof(CordBuffer) - 1)); + EXPECT_THAT(buffer.length(), Eq(0)); + EXPECT_THAT(buffer.data(), Ne(nullptr)); + EXPECT_THAT(buffer.available().data(), Eq(buffer.data())); + EXPECT_THAT(buffer.available().size(), Eq(buffer.capacity())); + memset(buffer.data(), 0xCD, buffer.capacity()); +} + +TEST(CordBufferTest, CreateSsoWithDefaultLimit) { + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(3); + EXPECT_THAT(buffer.capacity(), Ge(3)); + EXPECT_THAT(buffer.capacity(), Le(sizeof(CordBuffer))); + EXPECT_THAT(buffer.length(), Eq(0)); + memset(buffer.data(), 0xCD, buffer.capacity()); + + memcpy(buffer.data(), "Abc", 3); + buffer.SetLength(3); + EXPECT_THAT(buffer.length(), Eq(3)); + absl::string_view short_value; + EXPECT_THAT(CordBufferTestPeer::ConsumeValue(buffer, short_value), + Eq(nullptr)); + EXPECT_THAT(absl::string_view(buffer.data(), 3), Eq("Abc")); + EXPECT_THAT(short_value, Eq("Abc")); +} + +TEST_P(CordBufferTest, Available) { + const size_t requested = GetParam(); + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(requested); + EXPECT_THAT(buffer.available().data(), Eq(buffer.data())); + EXPECT_THAT(buffer.available().size(), Eq(buffer.capacity())); + + buffer.SetLength(2); + EXPECT_THAT(buffer.available().data(), Eq(buffer.data() + 2)); + EXPECT_THAT(buffer.available().size(), Eq(buffer.capacity() - 2)); +} + +TEST_P(CordBufferTest, IncreaseLengthBy) { + const size_t requested = GetParam(); + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(requested); + buffer.IncreaseLengthBy(2); + EXPECT_THAT(buffer.length(), Eq(2)); + buffer.IncreaseLengthBy(5); + EXPECT_THAT(buffer.length(), Eq(7)); +} + +TEST_P(CordBufferTest, AvailableUpTo) { + const size_t requested = GetParam(); + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(requested); + size_t expected_up_to = std::min(3, buffer.capacity()); + EXPECT_THAT(buffer.available_up_to(3).data(), Eq(buffer.data())); + EXPECT_THAT(buffer.available_up_to(3).size(), Eq(expected_up_to)); + + buffer.SetLength(2); + expected_up_to = std::min(3, buffer.capacity() - 2); + EXPECT_THAT(buffer.available_up_to(3).data(), Eq(buffer.data() + 2)); + EXPECT_THAT(buffer.available_up_to(3).size(), Eq(expected_up_to)); +} + +// Returns the maximum capacity for a given block_size and requested size. +size_t MaxCapacityFor(size_t block_size, size_t requested) { + requested = (std::min)(requested, cord_internal::kMaxLargeFlatSize); + // Maximum returned size is always capped at block_size - kFlatOverhead. + return block_size - kFlatOverhead; +} + +TEST_P(CordBufferTest, CreateWithDefaultLimit) { + const size_t requested = GetParam(); + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(requested); + EXPECT_THAT(buffer.capacity(), Ge(requested)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(kMaxFlatSize, requested))); + EXPECT_THAT(buffer.length(), Eq(0)); + + memset(buffer.data(), 0xCD, buffer.capacity()); + + std::string data(requested - 1, 'x'); + memcpy(buffer.data(), data.c_str(), requested); + buffer.SetLength(requested); + + EXPECT_THAT(buffer.length(), Eq(requested)); + EXPECT_THAT(absl::string_view(buffer.data()), Eq(data)); +} + +TEST(CordBufferTest, CreateWithDefaultLimitAskingFor2GB) { + constexpr size_t k2GiB = 1U << 31; + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(k2GiB); + // Expect to never be awarded more than a reasonable memory size, even in + // cases where a (debug) memory allocator may grant us somewhat more memory + // than `kDefaultLimit` which should be no more than `2 * kDefaultLimit` + EXPECT_THAT(buffer.capacity(), Le(2 * CordBuffer::kDefaultLimit)); + EXPECT_THAT(buffer.length(), Eq(0)); + EXPECT_THAT(buffer.data(), Ne(nullptr)); + memset(buffer.data(), 0xCD, buffer.capacity()); +} + +TEST_P(CordBufferTest, MoveConstruct) { + const size_t requested = GetParam(); + CordBuffer from = CordBuffer::CreateWithDefaultLimit(requested); + const size_t capacity = from.capacity(); + memcpy(from.data(), "Abc", 4); + from.SetLength(4); + + CordBuffer to(std::move(from)); + EXPECT_THAT(to.capacity(), Eq(capacity)); + EXPECT_THAT(to.length(), Eq(4)); + EXPECT_THAT(absl::string_view(to.data()), Eq("Abc")); + + EXPECT_THAT(from.length(), Eq(0)); // NOLINT +} + +TEST_P(CordBufferTest, MoveAssign) { + const size_t requested = GetParam(); + CordBuffer from = CordBuffer::CreateWithDefaultLimit(requested); + const size_t capacity = from.capacity(); + memcpy(from.data(), "Abc", 4); + from.SetLength(4); + + CordBuffer to; + to = std::move(from); + EXPECT_THAT(to.capacity(), Eq(capacity)); + EXPECT_THAT(to.length(), Eq(4)); + EXPECT_THAT(absl::string_view(to.data()), Eq("Abc")); + + EXPECT_THAT(from.length(), Eq(0)); // NOLINT +} + +TEST_P(CordBufferTest, ConsumeValue) { + const size_t requested = GetParam(); + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(requested); + memcpy(buffer.data(), "Abc", 4); + buffer.SetLength(3); + + absl::string_view short_value; + if (cord_internal::CordRep* rep = + CordBufferTestPeer::ConsumeValue(buffer, short_value)) { + EXPECT_THAT(CordToString(rep), Eq("Abc")); + cord_internal::CordRep::Unref(rep); + } else { + EXPECT_THAT(short_value, Eq("Abc")); + } + EXPECT_THAT(buffer.length(), Eq(0)); +} + +TEST_P(CordBufferTest, CreateWithCustomLimitWithinDefaultLimit) { + const size_t requested = GetParam(); + CordBuffer buffer = + CordBuffer::CreateWithCustomLimit(kMaxFlatSize, requested); + EXPECT_THAT(buffer.capacity(), Ge(requested)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(kMaxFlatSize, requested))); + EXPECT_THAT(buffer.length(), Eq(0)); + + memset(buffer.data(), 0xCD, buffer.capacity()); + + std::string data(requested - 1, 'x'); + memcpy(buffer.data(), data.c_str(), requested); + buffer.SetLength(requested); + + EXPECT_THAT(buffer.length(), Eq(requested)); + EXPECT_THAT(absl::string_view(buffer.data()), Eq(data)); +} + +TEST(CordLargeBufferTest, CreateAtOrBelowDefaultLimit) { + CordBuffer buffer = CordBuffer::CreateWithCustomLimit(k64KiB, kDefaultLimit); + EXPECT_THAT(buffer.capacity(), Ge(kDefaultLimit)); + EXPECT_THAT(buffer.capacity(), + Le(MaxCapacityFor(kMaxFlatSize, kDefaultLimit))); + + buffer = CordBuffer::CreateWithCustomLimit(k64KiB, 3178); + EXPECT_THAT(buffer.capacity(), Ge(3178)); +} + +TEST(CordLargeBufferTest, CreateWithCustomLimit) { + ASSERT_THAT((kMaxFlatSize & (kMaxFlatSize - 1)) == 0, "Must be power of 2"); + + for (size_t size = kMaxFlatSize; size <= kCustomLimit; size *= 2) { + CordBuffer buffer = CordBuffer::CreateWithCustomLimit(size, size); + size_t expected = size - kFlatOverhead; + ASSERT_THAT(buffer.capacity(), Ge(expected)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(size, expected))); + } +} + +TEST(CordLargeBufferTest, CreateWithTooLargeLimit) { + CordBuffer buffer = CordBuffer::CreateWithCustomLimit(k64KiB, k1MB); + ASSERT_THAT(buffer.capacity(), Ge(k64KiB - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(k64KiB, k1MB))); +} + +TEST(CordLargeBufferTest, CreateWithHugeValueForOverFlowHardening) { + for (size_t dist_from_max = 0; dist_from_max <= 32; ++dist_from_max) { + size_t capacity = std::numeric_limits::max() - dist_from_max; + + CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(capacity); + ASSERT_THAT(buffer.capacity(), Ge(kDefaultLimit)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(kMaxFlatSize, capacity))); + + for (size_t limit = kMaxFlatSize; limit <= kCustomLimit; limit *= 2) { + CordBuffer buffer = CordBuffer::CreateWithCustomLimit(limit, capacity); + ASSERT_THAT(buffer.capacity(), Ge(limit - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(limit, capacity))); + } + } +} + +TEST(CordLargeBufferTest, CreateWithSmallLimit) { + CordBuffer buffer = CordBuffer::CreateWithCustomLimit(512, 1024); + ASSERT_THAT(buffer.capacity(), Ge(512 - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(512, 1024))); + + // Ask for precise block size, should return size - kOverhead + buffer = CordBuffer::CreateWithCustomLimit(512, 512); + ASSERT_THAT(buffer.capacity(), Ge(512 - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(512, 512))); + + // Corner case: 511 < block_size, but 511 + kOverhead is above + buffer = CordBuffer::CreateWithCustomLimit(512, 511); + ASSERT_THAT(buffer.capacity(), Ge(512 - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(512, 511))); + + // Corner case: 498 + kOverhead < block_size + buffer = CordBuffer::CreateWithCustomLimit(512, 498); + ASSERT_THAT(buffer.capacity(), Ge(512 - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(512, 498))); +} + +TEST(CordLargeBufferTest, CreateWasteFull) { + // 15 KiB gets rounded down to next pow2 value. + const size_t requested = (15 << 10); + CordBuffer buffer = CordBuffer::CreateWithCustomLimit(k16KiB, requested); + ASSERT_THAT(buffer.capacity(), Ge(k8KiB - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(k8KiB, requested))); +} + +TEST(CordLargeBufferTest, CreateSmallSlop) { + const size_t requested = k16KiB - 2 * kFlatOverhead; + CordBuffer buffer = CordBuffer::CreateWithCustomLimit(k16KiB, requested); + ASSERT_THAT(buffer.capacity(), Ge(k16KiB - kFlatOverhead)); + EXPECT_THAT(buffer.capacity(), Le(MaxCapacityFor(k16KiB, requested))); +} + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 4261c99a..0862f69a 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc @@ -597,6 +597,284 @@ TEST_P(CordTest, CopyToString) { "copying ", "to ", "a ", "string."}))); } +TEST_P(CordTest, AppendEmptyBuffer) { + absl::Cord cord; + cord.Append(absl::CordBuffer()); + cord.Append(absl::CordBuffer::CreateWithDefaultLimit(2000)); +} + +TEST_P(CordTest, AppendEmptyBufferToFlat) { + absl::Cord cord(std::string(2000, 'x')); + cord.Append(absl::CordBuffer()); + cord.Append(absl::CordBuffer::CreateWithDefaultLimit(2000)); +} + +TEST_P(CordTest, AppendEmptyBufferToTree) { + absl::Cord cord(std::string(2000, 'x')); + cord.Append(std::string(2000, 'y')); + cord.Append(absl::CordBuffer()); + cord.Append(absl::CordBuffer::CreateWithDefaultLimit(2000)); +} + +TEST_P(CordTest, AppendSmallBuffer) { + absl::Cord cord; + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3); + ASSERT_THAT(buffer.capacity(), ::testing::Le(15)); + memcpy(buffer.data(), "Abc", 3); + buffer.SetLength(3); + cord.Append(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + buffer = absl::CordBuffer::CreateWithDefaultLimit(3); + memcpy(buffer.data(), "defgh", 5); + buffer.SetLength(5); + cord.Append(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre("Abcdefgh")); +} + +TEST_P(CordTest, AppendAndPrependBufferArePrecise) { + // Create a cord large enough to force 40KB flats. + std::string test_data(absl::cord_internal::kMaxFlatLength * 10, 'x'); + absl::Cord cord1(test_data); + absl::Cord cord2(test_data); + const size_t size1 = cord1.EstimatedMemoryUsage(); + const size_t size2 = cord2.EstimatedMemoryUsage(); + + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3); + memcpy(buffer.data(), "Abc", 3); + buffer.SetLength(3); + cord1.Append(std::move(buffer)); + + buffer = absl::CordBuffer::CreateWithDefaultLimit(3); + memcpy(buffer.data(), "Abc", 3); + buffer.SetLength(3); + cord2.Prepend(std::move(buffer)); + +#ifndef NDEBUG + // Allow 32 bytes new CordRepFlat, and 128 bytes for 'glue nodes' + constexpr size_t kMaxDelta = 128 + 32; +#else + // Allow 256 bytes extra for 'allocation debug overhead' + constexpr size_t kMaxDelta = 128 + 32 + 256; +#endif + + EXPECT_LE(cord1.EstimatedMemoryUsage() - size1, kMaxDelta); + EXPECT_LE(cord2.EstimatedMemoryUsage() - size2, kMaxDelta); + + EXPECT_EQ(cord1, absl::StrCat(test_data, "Abc")); + EXPECT_EQ(cord2, absl::StrCat("Abc", test_data)); +} + +TEST_P(CordTest, PrependSmallBuffer) { + absl::Cord cord; + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(3); + ASSERT_THAT(buffer.capacity(), ::testing::Le(15)); + memcpy(buffer.data(), "Abc", 3); + buffer.SetLength(3); + cord.Prepend(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + buffer = absl::CordBuffer::CreateWithDefaultLimit(3); + memcpy(buffer.data(), "defgh", 5); + buffer.SetLength(5); + cord.Prepend(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre("defghAbc")); +} + +TEST_P(CordTest, AppendLargeBuffer) { + absl::Cord cord; + + std::string s1(700, '1'); + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(s1.size()); + memcpy(buffer.data(), s1.data(), s1.size()); + buffer.SetLength(s1.size()); + cord.Append(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + std::string s2(1000, '2'); + buffer = absl::CordBuffer::CreateWithDefaultLimit(s2.size()); + memcpy(buffer.data(), s2.data(), s2.size()); + buffer.SetLength(s2.size()); + cord.Append(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre(s1, s2)); +} + +TEST_P(CordTest, PrependLargeBuffer) { + absl::Cord cord; + + std::string s1(700, '1'); + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(s1.size()); + memcpy(buffer.data(), s1.data(), s1.size()); + buffer.SetLength(s1.size()); + cord.Prepend(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + std::string s2(1000, '2'); + buffer = absl::CordBuffer::CreateWithDefaultLimit(s2.size()); + memcpy(buffer.data(), s2.data(), s2.size()); + buffer.SetLength(s2.size()); + cord.Prepend(std::move(buffer)); + EXPECT_EQ(buffer.length(), 0); // NOLINT + EXPECT_GT(buffer.capacity(), 0); // NOLINT + + EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre(s2, s1)); +} + +TEST_P(CordTest, GetAppendBufferOnEmptyCord) { + absl::Cord cord; + absl::CordBuffer buffer = cord.GetAppendBuffer(1000); + EXPECT_GE(buffer.capacity(), 1000); + EXPECT_EQ(buffer.length(), 0); +} + +TEST_P(CordTest, GetAppendBufferOnInlinedCord) { + static constexpr int kInlinedSize = sizeof(absl::CordBuffer) - 1; + for (int size : {6, kInlinedSize - 3, kInlinedSize - 2, 1000}) { + absl::Cord cord("Abc"); + absl::CordBuffer buffer = cord.GetAppendBuffer(size, 1); + EXPECT_GE(buffer.capacity(), 3 + size); + EXPECT_EQ(buffer.length(), 3); + EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc"); + EXPECT_TRUE(cord.empty()); + } +} + +TEST_P(CordTest, GetAppendBufferOnInlinedCordWithCapacityCloseToMax) { + // Cover the use case where we have a non empty inlined cord with some size + // 'n', and ask for something like 'uint64_max - k', assuming internal logic + // could overflow on 'uint64_max - k + size', and return a valid, but + // inefficiently smaller buffer if it would provide is the max allowed size. + for (size_t dist_from_max = 0; dist_from_max <= 4; ++dist_from_max) { + absl::Cord cord("Abc"); + size_t size = std::numeric_limits::max() - dist_from_max; + absl::CordBuffer buffer = cord.GetAppendBuffer(size, 1); + EXPECT_EQ(buffer.capacity(), absl::CordBuffer::kDefaultLimit); + EXPECT_EQ(buffer.length(), 3); + EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc"); + EXPECT_TRUE(cord.empty()); + } +} + +TEST_P(CordTest, GetAppendBufferOnFlat) { + // Create a cord with a single flat and extra capacity + absl::Cord cord; + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + buffer.SetLength(3); + memcpy(buffer.data(), "Abc", 3); + cord.Append(std::move(buffer)); + + buffer = cord.GetAppendBuffer(6); + EXPECT_GE(buffer.capacity(), 500); + EXPECT_EQ(buffer.length(), 3); + EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc"); + EXPECT_TRUE(cord.empty()); +} + +TEST_P(CordTest, GetAppendBufferOnFlatWithoutMinCapacity) { + // Create a cord with a single flat and extra capacity + absl::Cord cord; + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + buffer.SetLength(30); + memset(buffer.data(), 'x', 30); + cord.Append(std::move(buffer)); + + buffer = cord.GetAppendBuffer(1000, 900); + EXPECT_GE(buffer.capacity(), 1000); + EXPECT_EQ(buffer.length(), 0); + EXPECT_EQ(cord, std::string(30, 'x')); +} + +TEST_P(CordTest, GetAppendBufferOnTree) { + RandomEngine rng; + for (int num_flats : {2, 3, 100}) { + // Create a cord with `num_flats` flats and extra capacity + absl::Cord cord; + std::string prefix; + std::string last; + for (int i = 0; i < num_flats - 1; ++i) { + prefix += last; + last = RandomLowercaseString(&rng, 10); + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + buffer.SetLength(10); + memcpy(buffer.data(), last.data(), 10); + cord.Append(std::move(buffer)); + } + absl::CordBuffer buffer = cord.GetAppendBuffer(6); + EXPECT_GE(buffer.capacity(), 500); + EXPECT_EQ(buffer.length(), 10); + EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), last); + EXPECT_EQ(cord, prefix); + } +} + +TEST_P(CordTest, GetAppendBufferOnTreeWithoutMinCapacity) { + absl::Cord cord; + for (int i = 0; i < 2; ++i) { + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + buffer.SetLength(3); + memcpy(buffer.data(), i ? "def" : "Abc", 3); + cord.Append(std::move(buffer)); + } + absl::CordBuffer buffer = cord.GetAppendBuffer(1000, 900); + EXPECT_GE(buffer.capacity(), 1000); + EXPECT_EQ(buffer.length(), 0); + EXPECT_EQ(cord, "Abcdef"); +} + +TEST_P(CordTest, GetAppendBufferOnSubstring) { + // Create a large cord with a single flat and some extra capacity + absl::Cord cord; + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + buffer.SetLength(450); + memset(buffer.data(), 'x', 450); + cord.Append(std::move(buffer)); + cord.RemovePrefix(1); + + // Deny on substring + buffer = cord.GetAppendBuffer(6); + EXPECT_EQ(buffer.length(), 0); + EXPECT_EQ(cord, std::string(449, 'x')); +} + +TEST_P(CordTest, GetAppendBufferOnSharedCord) { + // Create a shared cord with a single flat and extra capacity + absl::Cord cord; + absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + buffer.SetLength(3); + memcpy(buffer.data(), "Abc", 3); + cord.Append(std::move(buffer)); + absl::Cord shared_cord = cord; + + // Deny on flat + buffer = cord.GetAppendBuffer(6); + EXPECT_EQ(buffer.length(), 0); + EXPECT_EQ(cord, "Abc"); + + buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + buffer.SetLength(3); + memcpy(buffer.data(), "def", 3); + cord.Append(std::move(buffer)); + shared_cord = cord; + + // Deny on tree + buffer = cord.GetAppendBuffer(6); + EXPECT_EQ(buffer.length(), 0); + EXPECT_EQ(cord, "Abcdef"); +} + TEST_P(CordTest, TryFlatEmpty) { absl::Cord c; EXPECT_EQ(c.TryFlat(), ""); -- cgit v1.2.3 From 6481443560a92d0a3a55a31807de0cd712cd4f88 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 6 Jun 2022 09:28:31 -0700 Subject: Optimize SwissMap for ARM by 3-8% for all operations https://pastebin.com/CmnzwUFN The key idea is to avoid using 16 byte NEON and use 8 byte NEON which has lower latency for BitMask::Match. Even though 16 byte NEON achieves higher throughput, in SwissMap it's very important to catch these Matches with low latency as probing on average happens at most once. I also introduced NonIterableMask as ARM has really great cbnz instructions and additional AND on scalar mask had 1 extra latency cycle PiperOrigin-RevId: 453216147 Change-Id: I842c50d323954f8383ae156491232ced55aacb78 --- absl/base/config.h | 9 + absl/container/internal/raw_hash_set.h | 227 ++++++++++++++-------- absl/container/internal/raw_hash_set_benchmark.cc | 14 +- absl/container/internal/raw_hash_set_test.cc | 24 ++- 4 files changed, 176 insertions(+), 98 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 4223629e..802529fc 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -898,4 +898,13 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAVE_ARM_ACLE 1 #endif +// ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM +// SIMD). +#ifdef ABSL_INTERNAL_HAVE_ARM_NEON +#error ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set +#elif defined(__ARM_NEON) +#define ABSL_INTERNAL_HAVE_ARM_NEON 1 +#endif + + #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index d503bc00..2756ce1b 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -184,6 +184,14 @@ #include #endif +#ifdef __ARM_NEON +#include +#endif + +#ifdef __ARM_ACLE +#include +#endif + #include #include #include @@ -211,10 +219,6 @@ #include "absl/numeric/bits.h" #include "absl/utility/utility.h" -#ifdef ABSL_INTERNAL_HAVE_ARM_ACLE -#include -#endif - namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { @@ -323,36 +327,15 @@ uint32_t TrailingZeros(T x) { // controlled by `SignificantBits` and `Shift`. `SignificantBits` is the number // of abstract bits in the bitset, while `Shift` is the log-base-two of the // width of an abstract bit in the representation. -// -// For example, when `SignificantBits` is 16 and `Shift` is zero, this is just -// an ordinary 16-bit bitset occupying the low 16 bits of `mask`. When -// `SignificantBits` is 8 and `Shift` is 3, abstract bits are represented as -// the bytes `0x00` and `0x80`, and it occupies all 64 bits of the bitmask. -// -// For example: -// for (int i : BitMask(0b101)) -> yields 0, 2 -// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +// This mask provides operations for any number of real bits set in an abstract +// bit. To add iteration on top of that, implementation must guarantee no more +// than one real bit is set in an abstract bit. template -class BitMask { - static_assert(std::is_unsigned::value, ""); - static_assert(Shift == 0 || Shift == 3, ""); - +class NonIterableBitMask { public: - // BitMask is an iterator over the indices of its abstract bits. - using value_type = int; - using iterator = BitMask; - using const_iterator = BitMask; - - explicit BitMask(T mask) : mask_(mask) {} - BitMask& operator++() { - mask_ &= (mask_ - 1); - return *this; - } - explicit operator bool() const { return mask_ != 0; } - uint32_t operator*() const { return LowestBitSet(); } + explicit NonIterableBitMask(T mask) : mask_(mask) {} - BitMask begin() const { return *this; } - BitMask end() const { return BitMask(0); } + explicit operator bool() const { return this->mask_ != 0; } // Returns the index of the lowest *abstract* bit set in `self`. uint32_t LowestBitSet() const { @@ -376,6 +359,42 @@ class BitMask { return static_cast(countl_zero(mask_ << extra_bits)) >> Shift; } + T mask_; +}; + +// Mask that can be iterable +// +// For example, when `SignificantBits` is 16 and `Shift` is zero, this is just +// an ordinary 16-bit bitset occupying the low 16 bits of `mask`. When +// `SignificantBits` is 8 and `Shift` is 3, abstract bits are represented as +// the bytes `0x00` and `0x80`, and it occupies all 64 bits of the bitmask. +// +// For example: +// for (int i : BitMask(0b101)) -> yields 0, 2 +// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +template +class BitMask : public NonIterableBitMask { + using Base = NonIterableBitMask; + static_assert(std::is_unsigned::value, ""); + static_assert(Shift == 0 || Shift == 3, ""); + + public: + explicit BitMask(T mask) : Base(mask) {} + // BitMask is an iterator over the indices of its abstract bits. + using value_type = int; + using iterator = BitMask; + using const_iterator = BitMask; + + BitMask& operator++() { + this->mask_ &= (this->mask_ - 1); + return *this; + } + + uint32_t operator*() const { return Base::LowestBitSet(); } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + private: friend bool operator==(const BitMask& a, const BitMask& b) { return a.mask_ == b.mask_; @@ -383,8 +402,6 @@ class BitMask { friend bool operator!=(const BitMask& a, const BitMask& b) { return a.mask_ != b.mask_; } - - T mask_; }; using h2_t = uint8_t; @@ -433,7 +450,7 @@ static_assert( static_cast(ctrl_t::kSentinel) & 0x7F) != 0, "ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not " "shared by ctrl_t::kSentinel to make the scalar test for " - "MatchEmptyOrDeleted() efficient"); + "MaskEmptyOrDeleted() efficient"); static_assert(ctrl_t::kDeleted == static_cast(-2), "ctrl_t::kDeleted must be -2 to make the implementation of " "ConvertSpecialToEmptyAndFullToDeleted efficient"); @@ -538,20 +555,22 @@ struct GroupSse2Impl { } // Returns a bitmask representing the positions of empty slots. - BitMask MatchEmpty() const { + NonIterableBitMask MaskEmpty() const { #ifdef ABSL_INTERNAL_HAVE_SSSE3 // This only works because ctrl_t::kEmpty is -128. - return BitMask( + return NonIterableBitMask( static_cast(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl)))); #else - return Match(static_cast(ctrl_t::kEmpty)); + auto match = _mm_set1_epi8(static_cast(ctrl_t::kEmpty)); + return NonIterableBitMask( + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl)))); #endif } // Returns a bitmask representing the positions of empty or deleted slots. - BitMask MatchEmptyOrDeleted() const { + NonIterableBitMask MaskEmptyOrDeleted() const { auto special = _mm_set1_epi8(static_cast(ctrl_t::kSentinel)); - return BitMask(static_cast( + return NonIterableBitMask(static_cast( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)))); } @@ -579,6 +598,80 @@ struct GroupSse2Impl { }; #endif // ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 +#if defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN) +struct GroupAArch64Impl { + static constexpr size_t kWidth = 8; + + explicit GroupAArch64Impl(const ctrl_t* pos) { + ctrl = vld1_u8(reinterpret_cast(pos)); + } + + BitMask Match(h2_t hash) const { + uint8x8_t dup = vdup_n_u8(hash); + auto mask = vceq_u8(ctrl, dup); + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask( + vget_lane_u64(vreinterpret_u64_u8(mask), 0) & msbs); + } + + NonIterableBitMask MaskEmpty() const { + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8( + vceq_s8(vdup_n_s8(static_cast(ctrl_t::kEmpty)), + vreinterpret_s8_u8(ctrl))), + 0); + return NonIterableBitMask(mask); + } + + NonIterableBitMask MaskEmptyOrDeleted() const { + uint64_t mask = + vget_lane_u64(vreinterpret_u64_u8(vcgt_s8( + vdup_n_s8(static_cast(ctrl_t::kSentinel)), + vreinterpret_s8_u8(ctrl))), + 0); + return NonIterableBitMask(mask); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0); + assert(IsEmptyOrDeleted(static_cast(mask & 0xff))); + constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; +#if defined(ABSL_INTERNAL_HAVE_ARM_ACLE) + // cls: Count leading sign bits. + // clsll(1ull << 63) -> 0 + // clsll((1ull << 63) | (1ull << 62)) -> 1 + // clsll((1ull << 63) | (1ull << 61)) -> 0 + // clsll(~0ull) -> 63 + // clsll(1) -> 62 + // clsll(3) -> 61 + // clsll(5) -> 60 + // Note that CountLeadingEmptyOrDeleted is called when first control block + // is kDeleted or kEmpty. The implementation is similar to GroupPortableImpl + // but avoids +1 and __clsll returns result not including the high bit. Thus + // saves one cycle. + // kEmpty = -128, // 0b10000000 + // kDeleted = -2, // 0b11111110 + // ~ctrl & (ctrl >> 7) will have the lowest bit set to 1. After rbit, + // it will the highest one. + return (__clsll(__rbitll((~mask & (mask >> 7)) | gaps)) + 8) >> 3; +#else + return (TrailingZeros(((~mask & (mask >> 7)) | gaps) + 1) + 7) >> 3; +#endif // ABSL_INTERNAL_HAVE_ARM_ACLE + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0); + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = mask & msbs; + auto res = (~x + (x >> 7)) & ~lsbs; + little_endian::Store64(dst, res); + } + + uint8x8_t ctrl; +}; +#endif // ABSL_INTERNAL_HAVE_ARM_NEON && ABSL_IS_LITTLE_ENDIAN + struct GroupPortableImpl { static constexpr size_t kWidth = 8; @@ -605,14 +698,16 @@ struct GroupPortableImpl { return BitMask((x - lsbs) & ~x & msbs); } - BitMask MatchEmpty() const { + NonIterableBitMask MaskEmpty() const { constexpr uint64_t msbs = 0x8080808080808080ULL; - return BitMask((ctrl & (~ctrl << 6)) & msbs); + return NonIterableBitMask((ctrl & (~ctrl << 6)) & + msbs); } - BitMask MatchEmptyOrDeleted() const { + NonIterableBitMask MaskEmptyOrDeleted() const { constexpr uint64_t msbs = 0x8080808080808080ULL; - return BitMask((ctrl & (~ctrl << 7)) & msbs); + return NonIterableBitMask((ctrl & (~ctrl << 7)) & + msbs); } uint32_t CountLeadingEmptyOrDeleted() const { @@ -631,39 +726,9 @@ struct GroupPortableImpl { uint64_t ctrl; }; -#ifdef ABSL_INTERNAL_HAVE_ARM_ACLE -struct GroupAArch64Impl : public GroupPortableImpl { - static constexpr size_t kWidth = GroupPortableImpl::kWidth; - - using GroupPortableImpl::GroupPortableImpl; - - uint32_t CountLeadingEmptyOrDeleted() const { - assert(IsEmptyOrDeleted(static_cast(ctrl & 0xff))); - constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; - // cls: Count leading sign bits. - // clsll(1ull << 63) -> 0 - // clsll((1ull << 63) | (1ull << 62)) -> 1 - // clsll((1ull << 63) | (1ull << 61)) -> 0 - // clsll(~0ull) -> 63 - // clsll(1) -> 62 - // clsll(3) -> 61 - // clsll(5) -> 60 - // Note that CountLeadingEmptyOrDeleted is called when first control block - // is kDeleted or kEmpty. The implementation is similar to GroupPortableImpl - // but avoids +1 and __clsll returns result not including the high bit. Thus - // saves one cycle. - // kEmpty = -128, // 0b10000000 - // kDeleted = -2, // 0b11111110 - // ~ctrl & (ctrl >> 7) will have the lowest bit set to 1. After rbit, - // it will the highest one. - return (__clsll(__rbitll((~ctrl & (ctrl >> 7)) | gaps)) + 8) >> 3; - } -}; -#endif - #ifdef ABSL_INTERNAL_HAVE_SSE2 using Group = GroupSse2Impl; -#elif defined(ABSL_INTERNAL_HAVE_ARM_ACLE) +#elif defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN) using Group = GroupAArch64Impl; #else using Group = GroupPortableImpl; @@ -798,7 +863,7 @@ inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, auto seq = probe(ctrl, hash, capacity); while (true) { Group g{ctrl + seq.offset()}; - auto mask = g.MatchEmptyOrDeleted(); + auto mask = g.MaskEmptyOrDeleted(); if (mask) { #if !defined(NDEBUG) // We want to add entropy even when ASLR is not enabled. @@ -1700,7 +1765,7 @@ class raw_hash_set { PolicyTraits::element(slots_ + seq.offset(i))))) return iterator_at(seq.offset(i)); } - if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end(); + if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return end(); seq.next(); assert(seq.index() <= capacity_ && "full table!"); } @@ -1849,8 +1914,8 @@ class raw_hash_set { --size_; const size_t index = static_cast(it.inner_.ctrl_ - ctrl_); const size_t index_before = (index - Group::kWidth) & capacity_; - const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty(); - const auto empty_before = Group(ctrl_ + index_before).MatchEmpty(); + const auto empty_after = Group(it.inner_.ctrl_).MaskEmpty(); + const auto empty_before = Group(ctrl_ + index_before).MaskEmpty(); // We count how many consecutive non empties we have to the right and to the // left of `it`. If the sum is >= kWidth then there is at least one probe @@ -2091,7 +2156,7 @@ class raw_hash_set { elem)) return true; } - if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false; + if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return false; seq.next(); assert(seq.index() <= capacity_ && "full table!"); } @@ -2127,7 +2192,7 @@ class raw_hash_set { PolicyTraits::element(slots_ + seq.offset(i))))) return {seq.offset(i), false}; } - if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break; + if (ABSL_PREDICT_TRUE(g.MaskEmpty())) break; seq.next(); assert(seq.index() <= capacity_ && "full table!"); } @@ -2272,7 +2337,7 @@ struct HashtableDebugAccess> { return num_probes; ++num_probes; } - if (g.MatchEmpty()) return num_probes; + if (g.MaskEmpty()) return num_probes; seq.next(); ++num_probes; } diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc index 146ef433..47dc9048 100644 --- a/absl/container/internal/raw_hash_set_benchmark.cc +++ b/absl/container/internal/raw_hash_set_benchmark.cc @@ -336,27 +336,27 @@ void BM_Group_Match(benchmark::State& state) { } BENCHMARK(BM_Group_Match); -void BM_Group_MatchEmpty(benchmark::State& state) { +void BM_Group_MaskEmpty(benchmark::State& state) { std::array group; Iota(group.begin(), group.end(), -4); Group g{group.data()}; for (auto _ : state) { ::benchmark::DoNotOptimize(g); - ::benchmark::DoNotOptimize(g.MatchEmpty()); + ::benchmark::DoNotOptimize(g.MaskEmpty()); } } -BENCHMARK(BM_Group_MatchEmpty); +BENCHMARK(BM_Group_MaskEmpty); -void BM_Group_MatchEmptyOrDeleted(benchmark::State& state) { +void BM_Group_MaskEmptyOrDeleted(benchmark::State& state) { std::array group; Iota(group.begin(), group.end(), -4); Group g{group.data()}; for (auto _ : state) { ::benchmark::DoNotOptimize(g); - ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted()); + ::benchmark::DoNotOptimize(g.MaskEmptyOrDeleted()); } } -BENCHMARK(BM_Group_MatchEmptyOrDeleted); +BENCHMARK(BM_Group_MaskEmptyOrDeleted); void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) { std::array group; @@ -375,7 +375,7 @@ void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) { Group g{group.data()}; for (auto _ : state) { ::benchmark::DoNotOptimize(g); - ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted()); + ::benchmark::DoNotOptimize(g.MaskEmptyOrDeleted().LowestBitSet()); } } BENCHMARK(BM_Group_MatchFirstEmptyOrDeleted); diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index c79f8641..dc6bc3d2 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -195,35 +195,39 @@ TEST(Group, Match) { } } -TEST(Group, MatchEmpty) { +TEST(Group, MaskEmpty) { if (Group::kWidth == 16) { ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; - EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4)); + EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0); + EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 4); } else if (Group::kWidth == 8) { ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), ctrl_t::kDeleted, CtrlT(2), CtrlT(1), ctrl_t::kSentinel, CtrlT(1)}; - EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0)); + EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0); + EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 0); } else { FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; } } -TEST(Group, MatchEmptyOrDeleted) { +TEST(Group, MaskEmptyOrDeleted) { if (Group::kWidth == 16) { - ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), - ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), - CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), - CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; - EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4)); + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty, CtrlT(3), + ctrl_t::kDeleted, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), + CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), + CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; + EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0); + EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 4); } else if (Group::kWidth == 8) { ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), ctrl_t::kDeleted, CtrlT(2), CtrlT(1), ctrl_t::kSentinel, CtrlT(1)}; - EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3)); + EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0); + EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 3); } else { FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; } -- cgit v1.2.3 From ef034836d3bfcaff018593014f14a3ebc264cc81 Mon Sep 17 00:00:00 2001 From: Evan Brown Date: Mon, 6 Jun 2022 11:02:03 -0700 Subject: In b-tree, support unassignable value types. Avoid using value move/swap and delete those functions from slot_policy types. There was only one use of params_type::move in `erase`. PiperOrigin-RevId: 453237739 Change-Id: Ie81c6dba6c4db34e97a067d2c0defcded8044a5a --- absl/container/btree_set.h | 11 ------ absl/container/btree_test.cc | 60 ++++++++++++++++++++++++++++++ absl/container/internal/btree.h | 40 ++++++++++---------- absl/container/internal/container_memory.h | 27 -------------- 4 files changed, 80 insertions(+), 58 deletions(-) diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h index 32a7c506..695b09f5 100644 --- a/absl/container/btree_set.h +++ b/absl/container/btree_set.h @@ -766,17 +766,6 @@ struct set_slot_policy { construct(alloc, new_slot, old_slot); destroy(alloc, old_slot); } - - template - static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) { - using std::swap; - swap(*a, *b); - } - - template - static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { - *dest = std::move(*src); - } }; // A parameters structure for holding the type parameters for a btree_set. diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index b3fa98f4..f20f3430 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -3188,6 +3188,66 @@ TEST(Btree, OnlyConstructibleByAllocatorType) { } } +class NotAssignable { + public: + explicit NotAssignable(int i) : i_(i) {} + NotAssignable(const NotAssignable &other) : i_(other.i_) {} + NotAssignable &operator=(NotAssignable &&other) = delete; + int Get() const { return i_; } + bool operator==(int i) const { return i_ == i; } + friend bool operator<(NotAssignable a, NotAssignable b) { + return a.i_ < b.i_; + } + + private: + int i_; +}; + +TEST(Btree, NotAssignableType) { + { + absl::btree_set set; + set.emplace(1); + set.emplace_hint(set.end(), 2); + set.insert(NotAssignable(3)); + set.insert(set.end(), NotAssignable(4)); + EXPECT_THAT(set, ElementsAre(1, 2, 3, 4)); + set.erase(set.begin()); + EXPECT_THAT(set, ElementsAre(2, 3, 4)); + } + { + absl::btree_multiset set; + set.emplace(1); + set.emplace_hint(set.end(), 2); + set.insert(NotAssignable(2)); + set.insert(set.end(), NotAssignable(3)); + EXPECT_THAT(set, ElementsAre(1, 2, 2, 3)); + set.erase(set.begin()); + EXPECT_THAT(set, ElementsAre(2, 2, 3)); + } + { + absl::btree_map map; + map.emplace(NotAssignable(1), 1); + map.emplace_hint(map.end(), NotAssignable(2), 2); + map.insert({NotAssignable(3), 3}); + map.insert(map.end(), {NotAssignable(4), 4}); + EXPECT_THAT(map, + ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(4, 4))); + map.erase(map.begin()); + EXPECT_THAT(map, ElementsAre(Pair(2, 2), Pair(3, 3), Pair(4, 4))); + } + { + absl::btree_multimap map; + map.emplace(NotAssignable(1), 1); + map.emplace_hint(map.end(), NotAssignable(2), 2); + map.insert({NotAssignable(2), 3}); + map.insert(map.end(), {NotAssignable(3), 3}); + EXPECT_THAT(map, + ElementsAre(Pair(1, 1), Pair(2, 2), Pair(2, 3), Pair(3, 3))); + map.erase(map.begin()); + EXPECT_THAT(map, ElementsAre(Pair(2, 2), Pair(2, 3), Pair(3, 3))); + } +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index 1ff2e6e7..01f4e749 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -462,12 +462,6 @@ struct common_params { static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) { slot_policy::transfer(alloc, new_slot, old_slot); } - static void swap(Alloc *alloc, slot_type *a, slot_type *b) { - slot_policy::swap(alloc, a, b); - } - static void move(Alloc *alloc, slot_type *src, slot_type *dest) { - slot_policy::move(alloc, src, dest); - } }; // An adapter class that converts a lower-bound compare into an upper-bound @@ -2300,23 +2294,29 @@ auto btree

::operator=(btree &&other) noexcept -> btree & { template auto btree

::erase(iterator iter) -> iterator { - bool internal_delete = false; - if (iter.node_->is_internal()) { - // Deletion of a value on an internal node. First, move the largest value - // from our left child here, then delete that position (in remove_values() - // below). We can get to the largest value from our left child by - // decrementing iter. + iter.node_->value_destroy(iter.position_, mutable_allocator()); + iter.update_generation(); + + const bool internal_delete = iter.node_->is_internal(); + if (internal_delete) { + // Deletion of a value on an internal node. First, transfer the largest + // value from our left child here, then erase/rebalance from that position. + // We can get to the largest value from our left child by decrementing iter. iterator internal_iter(iter); --iter; assert(iter.node_->is_leaf()); - params_type::move(mutable_allocator(), iter.node_->slot(iter.position_), - internal_iter.node_->slot(internal_iter.position_)); - internal_delete = true; - } - - // Delete the key from the leaf. - iter.node_->remove_values(iter.position_, /*to_erase=*/1, - mutable_allocator()); + internal_iter.node_->transfer(internal_iter.position_, iter.position_, + iter.node_, mutable_allocator()); + } else { + // Shift values after erased position in leaf. In the internal case, we + // don't need to do this because the leaf position is the end of the node. + const field_type transfer_from = iter.position_ + 1; + const field_type num_to_transfer = iter.node_->finish() - transfer_from; + iter.node_->transfer_n(num_to_transfer, iter.position_, transfer_from, + iter.node_, mutable_allocator()); + } + // Update node finish and container size. + iter.node_->set_finish(iter.node_->finish() - 1); --size_; // We want to return the next value after the one we just erased. If we diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index 94185c6e..00e9f6d7 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h @@ -433,33 +433,6 @@ struct map_slot_policy { } destroy(alloc, old_slot); } - - template - static void swap(Allocator* alloc, slot_type* a, slot_type* b) { - if (kMutableKeys::value) { - using std::swap; - swap(a->mutable_value, b->mutable_value); - } else { - value_type tmp = std::move(a->value); - absl::allocator_traits::destroy(*alloc, &a->value); - absl::allocator_traits::construct(*alloc, &a->value, - std::move(b->value)); - absl::allocator_traits::destroy(*alloc, &b->value); - absl::allocator_traits::construct(*alloc, &b->value, - std::move(tmp)); - } - } - - template - static void move(Allocator* alloc, slot_type* src, slot_type* dest) { - if (kMutableKeys::value) { - dest->mutable_value = std::move(src->mutable_value); - } else { - absl::allocator_traits::destroy(*alloc, &dest->value); - absl::allocator_traits::construct(*alloc, &dest->value, - std::move(src->value)); - } - } }; } // namespace container_internal -- cgit v1.2.3 From 800a88de73b89926129c63276f133d0f3d375d4a Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Mon, 6 Jun 2022 11:17:43 -0700 Subject: Fix comment typo about absl::Status PiperOrigin-RevId: 453241556 Change-Id: Ia92d737b6a678e3a4eda965056503392af44486a --- absl/status/statusor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/absl/status/statusor.h b/absl/status/statusor.h index a6d29110..a76e7201 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h @@ -162,8 +162,8 @@ class ABSL_MUST_USE_RESULT StatusOr; // A `absl::StatusOr` can be constructed from a null pointer like any other // pointer value, and the result will be that `ok()` returns `true` and // `value()` returns `nullptr`. Checking the value of pointer in an -// `absl::StatusOr` generally requires a bit more care, to ensure both that a -// value is present and that value is not null: +// `absl::StatusOr` generally requires a bit more care, to ensure both that +// a value is present and that value is not null: // // StatusOr> result = FooFactory::MakeNewFoo(arg); // if (!result.ok()) { -- cgit v1.2.3 From 91b8bfab44baf5c89efabd6f6b8b33f6a42f39b6 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Mon, 6 Jun 2022 11:26:39 -0700 Subject: Add ABSL_HARDENING_ASSERTs to CordBuffer::SetLength() and CordBuffer::IncreaseLengthBy() PiperOrigin-RevId: 453243686 Change-Id: If109da6be651006d4d9820bcc10eddfb56deaea2 --- absl/strings/cord_buffer.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/absl/strings/cord_buffer.h b/absl/strings/cord_buffer.h index 4f4bdc0e..56a6ce6f 100644 --- a/absl/strings/cord_buffer.h +++ b/absl/strings/cord_buffer.h @@ -32,6 +32,7 @@ #include #include "absl/base/config.h" +#include "absl/base/macros.h" #include "absl/numeric/bits.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_flat.h" @@ -548,7 +549,7 @@ inline size_t CordBuffer::length() const { } inline void CordBuffer::SetLength(size_t length) { - assert(length <= capacity()); + ABSL_HARDENING_ASSERT(length <= capacity()); if (rep_.is_short()) { rep_.set_short_length(length); } else { @@ -557,7 +558,7 @@ inline void CordBuffer::SetLength(size_t length) { } inline void CordBuffer::IncreaseLengthBy(size_t n) { - assert(n <= capacity() && length() + n <= capacity()); + ABSL_HARDENING_ASSERT(n <= capacity() && length() + n <= capacity()); if (rep_.is_short()) { rep_.add_short_length(n); } else { -- cgit v1.2.3 From ba9f2f66095702db6b9580b40e8602ef64ec922d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 6 Jun 2022 12:28:04 -0700 Subject: Ignore invalid TZ settings in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For portability, absl_time_test builds a small, incomplete tzdata database into the test binary and uses that instead of the system tzdata database. (absl_time_test needs to run on some platforms that lack a system tzdata database.) However, this causes issues if TZ is set to something that isn’t in the test database. To address them, fall back to America/Los_Angeles if TZ is set to an unknown value during testing. Bug: https://bugs.debian.org/1012194 PiperOrigin-RevId: 453257912 Change-Id: I293d0f96876b31c32a2847468a3377bb49f3aa15 --- absl/time/internal/test_util.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc index 9a485a07..24cff138 100644 --- a/absl/time/internal/test_util.cc +++ b/absl/time/internal/test_util.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" @@ -103,6 +104,12 @@ class TestZoneInfoSource : public cctz::ZoneInfoSource { const char* const end_; }; +std::unique_ptr EmbeddedTestZone(const char* data, + std::size_t size) { + return std::unique_ptr( + new TestZoneInfoSource(data, size)); +} + std::unique_ptr TestFactory( const std::string& name, const std::function( @@ -110,12 +117,14 @@ std::unique_ptr TestFactory( for (const ZoneInfo& zoneinfo : kZoneInfo) { if (name == zoneinfo.name) { if (zoneinfo.data == nullptr) return nullptr; - return std::unique_ptr( - new TestZoneInfoSource(zoneinfo.data, zoneinfo.length)); + return EmbeddedTestZone(zoneinfo.data, zoneinfo.length); } } - ABSL_RAW_LOG(FATAL, "Unexpected time zone \"%s\" in test", name.c_str()); - return nullptr; + + // The embedded tzdata database knows nothing about this zone. Return + // something so the tests can proceed. + return EmbeddedTestZone(reinterpret_cast(America_Los_Angeles), + America_Los_Angeles_len); } } // namespace -- cgit v1.2.3 From c588201b688ffce93758ff4ca17aecc24b5ab460 Mon Sep 17 00:00:00 2001 From: Jeremy Nimmer Date: Mon, 6 Jun 2022 13:13:36 -0700 Subject: Obey ABSL_DEFAULT_LINKOPTS for all cc_library targets A few targets were missing `linkopts = ...` and so were not obeying the project-wide default settings. Omit any changes to cctz for now, because it's vendored from another project. --- absl/debugging/BUILD.bazel | 1 + absl/random/BUILD.bazel | 1 + absl/random/internal/BUILD.bazel | 2 ++ absl/status/BUILD.bazel | 3 +++ absl/strings/BUILD.bazel | 18 ++++++++++++++++++ absl/types/BUILD.bazel | 1 + 6 files changed, 26 insertions(+) diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index dce59e1d..932a8e9f 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -196,6 +196,7 @@ cc_library( srcs = ["internal/demangle.cc"], hdrs = ["internal/demangle.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ "//absl/base", diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index 27f3e334..d983685c 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -128,6 +128,7 @@ cc_library( name = "mock_distributions", testonly = 1, hdrs = ["mock_distributions.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":distributions", ":mocking_bit_gen", diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 69f9f80c..fd5b6195 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -502,6 +502,7 @@ cc_test( cc_library( name = "mock_helpers", hdrs = ["mock_helpers.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:fast_type_id", "//absl/types:optional", @@ -512,6 +513,7 @@ cc_library( name = "mock_overload_set", testonly = 1, hdrs = ["mock_overload_set.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":mock_helpers", "//absl/random:mocking_bit_gen", diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index b334f31d..ce0ea70c 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel @@ -20,6 +20,7 @@ load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -39,6 +40,7 @@ cc_library( "status_payload_printer.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:atomic_hook", "//absl/base:core_headers", @@ -76,6 +78,7 @@ cc_library( "statusor.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":status", "//absl/base", diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 81f5a589..3f51252f 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -16,6 +16,7 @@ load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -65,6 +66,7 @@ cc_library( "substitute.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":internal", "//absl/base", @@ -95,6 +97,7 @@ cc_library( "internal/utf8.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -288,6 +291,7 @@ cc_library( "internal/cord_rep_ring_reader.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//visibility:private", ], @@ -391,6 +395,7 @@ cc_library( name = "cordz_update_tracker", hdrs = ["internal/cordz_update_tracker.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -422,6 +427,7 @@ cc_library( "cord_buffer.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":cord_internal", ":cordz_functions", @@ -452,6 +458,7 @@ cc_library( srcs = ["internal/cordz_handle.cc"], hdrs = ["internal/cordz_handle.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -468,6 +475,7 @@ cc_library( srcs = ["internal/cordz_info.cc"], hdrs = ["internal/cordz_info.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -492,6 +500,7 @@ cc_library( name = "cordz_update_scope", hdrs = ["internal/cordz_update_scope.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -524,6 +533,7 @@ cc_library( srcs = ["internal/cordz_sample_token.cc"], hdrs = ["internal/cordz_sample_token.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -539,6 +549,7 @@ cc_library( srcs = ["internal/cordz_functions.cc"], hdrs = ["internal/cordz_functions.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -554,6 +565,7 @@ cc_library( name = "cordz_statistics", hdrs = ["internal/cordz_statistics.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -663,6 +675,7 @@ cc_library( "cord_test_helpers.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":cord", ":cord_internal", @@ -676,6 +689,7 @@ cc_library( testonly = 1, hdrs = ["internal/cord_rep_test_util.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":cord_internal", ":strings", @@ -689,6 +703,7 @@ cc_library( testonly = 1, hdrs = ["cordz_test_helpers.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":cord", ":cord_internal", @@ -1097,6 +1112,7 @@ cc_library( "str_format.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":str_format_internal", ], @@ -1122,6 +1138,7 @@ cc_library( "internal/str_format/parser.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":strings", @@ -1246,6 +1263,7 @@ cc_library( testonly = True, srcs = ["internal/pow10_helper.cc"], hdrs = ["internal/pow10_helper.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = ["//absl/base:config"], ) diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 38ed2286..bb801012 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -314,6 +314,7 @@ cc_library( name = "compare", hdrs = ["compare.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:core_headers", "//absl/meta:type_traits", -- cgit v1.2.3 From ef615a8f5dba98457a1e949d20f734b660633a9d Mon Sep 17 00:00:00 2001 From: James Y Knight Date: Tue, 7 Jun 2022 07:13:36 -0700 Subject: Remove unintended defines from config.h PiperOrigin-RevId: 453429588 Change-Id: Id377cd89dc807da80a33a8549f4e59bd935aff93 --- absl/base/config.h | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 802529fc..cf940f32 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -183,12 +183,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_BUILTIN(x) 0 #endif -#if defined(__is_identifier) -#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x)) -#else -#define ABSL_INTERNAL_HAS_KEYWORD(x) 0 -#endif - #ifdef __has_feature #define ABSL_HAVE_FEATURE(f) __has_feature(f) #else @@ -260,21 +254,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif -// ABSL_HAVE_SOURCE_LOCATION_CURRENT -// -// Indicates whether `absl::SourceLocation::current()` will return useful -// information in some contexts. -#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT -#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ - ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) -#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 -#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) -#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 -#elif defined(_MSC_VER) && _MSC_VER >= 1926 -#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 -#endif -#endif - // ABSL_HAVE_THREAD_LOCAL // // Checks whether C++11's `thread_local` storage duration specifier is @@ -729,8 +708,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #endif -#undef ABSL_INTERNAL_HAS_KEYWORD - // ABSL_DLL // // When building Abseil as a DLL, this macro expands to `__declspec(dllexport)` -- cgit v1.2.3 From 7a8d20b7841b43cc685230058130bd0f019e4004 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 8 Jun 2022 12:42:23 -0700 Subject: absl: correct the stack trace path on RISCV When we would perform a stacktrace using the frame pointer walking, because we did the adjustment for the return address separately, we were misaligning the stack size and frame. Simplify the logic and correct the offset. The recovered frame pointer provides us with the return address of the current frame and the previous frame's frame pointer. Subsequently, we decide if we want to record this frame or not. The value in `next_frame_pointer` already points to the value from the previous stack frame (that is the next frame pointer to iterate). As such, the value computed by `ComputeStackFrameSize` is the value for the current frame. This was offset by one previously. Take the opportunity to clean up some of the local comments, fixing typos and splitting up the comments to reflect the lines that they are associated with. PiperOrigin-RevId: 453744059 Change-Id: If14813e0ac36f327f4b7594472f2222d05c478aa --- absl/debugging/internal/stacktrace_riscv-inl.inc | 25 +++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc index 98b09a22..7123b71b 100644 --- a/absl/debugging/internal/stacktrace_riscv-inl.inc +++ b/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -171,26 +171,21 @@ ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { + // The `frame_pointer` that is computed here points to the top of the frame. + // The two words preceding the address are the return address and the previous + // frame pointer. #if defined(__GNUC__) void **frame_pointer = reinterpret_cast(__builtin_frame_address(0)); #else #error reading stack pointer not yet supported on this platform #endif - skip_count++; // Skip the frame for this function. int n = 0; - - // The `frame_pointer` that is computed here points to the top of the frame. - // The two words preceding the address are the return address and the previous - // frame pointer. To find a PC value associated with the current frame, we - // need to go down a level in the call chain. So we remember the return - // address of the last frame seen. This does not work for the first stack - // frame, which belongs to `UnwindImp()` but we skip the frame for - // `UnwindImp()` anyway. - void *prev_return_address = nullptr; - + void *return_address = nullptr; while (frame_pointer && n < max_depth) { - // The absl::GetStackFrames routine si called when we are in some + return_address = frame_pointer[-1]; + + // The absl::GetStackFrames routine is called when we are in some // informational context (the failure signal handler for example). Use the // non-strict unwinding rules to produce a stack trace that is as complete // as possible (even if it contains a few bogus entries in some rare cases). @@ -200,15 +195,16 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, if (skip_count > 0) { skip_count--; } else { - result[n] = prev_return_address; + result[n] = return_address; if (IS_STACK_FRAMES) { sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); } n++; } - prev_return_address = frame_pointer[-1]; + frame_pointer = next_frame_pointer; } + if (min_dropped_frames != nullptr) { // Implementation detail: we clamp the max of frames we are willing to // count, so as not to spend too much time in the loop below. @@ -225,6 +221,7 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, } *min_dropped_frames = num_dropped_frames; } + return n; } -- cgit v1.2.3 From e0a32c2aee27aadce321471be734d7643d7d9439 Mon Sep 17 00:00:00 2001 From: Tom Manshreck Date: Wed, 8 Jun 2022 14:22:40 -0700 Subject: Add documentation on optional flags to the flags library overview. PiperOrigin-RevId: 453766125 Change-Id: Id4d88ae20bdadc960a65bc1010eea746f1eba051 --- absl/flags/flag.h | 4 ++++ absl/flags/marshalling.h | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/absl/flags/flag.h b/absl/flags/flag.h index d2750b31..b7f94be7 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -67,6 +67,10 @@ ABSL_NAMESPACE_BEGIN // ABSL_FLAG(int, count, 0, "Count of items to process"); // // No public methods of `absl::Flag` are part of the Abseil Flags API. +// +// For type support of Abseil Flags, see the marshalling.h header file, which +// discusses supported standard types, optional flags, and additional Abseil +// type support. #if !defined(_MSC_VER) || defined(__clang__) template using Flag = flags_internal::Flag; diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h index 0f63cdc5..b1e2ffa5 100644 --- a/absl/flags/marshalling.h +++ b/absl/flags/marshalling.h @@ -33,6 +33,7 @@ // * `double` // * `std::string` // * `std::vector` +// * `std::optional` // * `absl::LogSeverity` (provided natively for layering reasons) // // Note that support for integral types is implemented using overloads for @@ -65,6 +66,42 @@ // below.) // // ----------------------------------------------------------------------------- +// Optional Flags +// ----------------------------------------------------------------------------- +// +// The Abseil flags library supports flags of type `std::optional` where +// `T` is a type of one of the supported flags. We refer to this flag type as +// an "optional flag." An optional flag is either "valueless", holding no value +// of type `T` (indicating that the flag has not been set) or a value of type +// `T`. The valueless state in C++ code is represented by a value of +// `std::nullopt` for the optional flag. +// +// Using `std::nullopt` as an optional flag's default value allows you to check +// whether such a flag was ever specified on the command line: +// +// if (absl::GetFlag(FLAGS_foo).has_value()) { +// // flag was set on command line +// } else { +// // flag was not passed on command line +// } +// +// Using an optional flag in this manner avoids common workarounds for +// indicating such an unset flag (such as using sentinal values to indicate this +// state). +// +// An optional flag also allows a developer to pass a flag in an "unset" +// valueless state on the command line, allowing the flag to later be set in +// binary logic. An optional flag's valueless state is indicated by the special +// notation of passing the value as an empty string through the syntax `--flag=` +// or `--flag ""`. +// +// $ binary_with_optional --flag_in_unset_state= +// $ binary_with_optional --flag_in_unset_state "" +// +// Note: as a result of the above syntax requirements, an optional flag cannot +// be set to a `T` of any value which unparses to the empty string. +// +// ----------------------------------------------------------------------------- // Adding Type Support for Abseil Flags // ----------------------------------------------------------------------------- // -- cgit v1.2.3 From 7383f346c9e33a08ed2132f117b3de6b13eac173 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 9 Jun 2022 03:12:43 -0700 Subject: Optimize SwissMap iteration by another 5-10% for ARM https://pastebin.com/fDvgWgHe After having a chat with Dougall Johnson (https://twitter.com/dougallj/status/1534213050944802816), we realized that __clzll works with zero arguments per documentation: https://developer.arm.com/documentation/101028/0009/Data-processing-intrinsics ``` Returns the number of leading zero bits in x. When x is zero it returns the argument width, i.e. 32 or 64. ``` Codegen improves https://godbolt.org/z/ebadf717Y Thus we can use a little bit different construction not involving CLS but using more understandable CLZ and removing some operations. PiperOrigin-RevId: 453879080 Change-Id: Ie2d7f834f63364d7bd50dd6a682c107985f21942 --- absl/base/config.h | 15 ----------- absl/container/internal/raw_hash_set.h | 39 +++++++--------------------- absl/container/internal/raw_hash_set_test.cc | 6 +---- 3 files changed, 11 insertions(+), 49 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index cf940f32..58097b0d 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -860,21 +860,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAVE_SSSE3 1 #endif -// ABSL_INTERNAL_HAVE_ARM_ACLE is used for compile-time detection of ACLE (ARM -// C language extensions). -#ifdef ABSL_INTERNAL_HAVE_ARM_ACLE -#error ABSL_INTERNAL_HAVE_ARM_ACLE cannot be directly set -// __cls, __rbit were added quite late in clang. They are not supported -// by GCC as well. __cls can be replaced with __builtin_clrsb but clang does -// not recognize cls instruction in latest versions. -// TODO(b/233604649): Relax to __builtin_clrsb and __builtin_bitreverse64 (note -// that the latter is not supported by GCC). -#elif defined(__ARM_ACLE) && defined(__clang__) && \ - ABSL_HAVE_BUILTIN(__builtin_arm_cls64) && \ - ABSL_HAVE_BUILTIN(__builtin_arm_rbit64) -#define ABSL_INTERNAL_HAVE_ARM_ACLE 1 -#endif - // ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM // SIMD). #ifdef ABSL_INTERNAL_HAVE_ARM_NEON diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 2756ce1b..cd31b870 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -188,10 +188,6 @@ #include #endif -#ifdef __ARM_ACLE -#include -#endif - #include #include #include @@ -634,29 +630,12 @@ struct GroupAArch64Impl { uint32_t CountLeadingEmptyOrDeleted() const { uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0); - assert(IsEmptyOrDeleted(static_cast(mask & 0xff))); - constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; -#if defined(ABSL_INTERNAL_HAVE_ARM_ACLE) - // cls: Count leading sign bits. - // clsll(1ull << 63) -> 0 - // clsll((1ull << 63) | (1ull << 62)) -> 1 - // clsll((1ull << 63) | (1ull << 61)) -> 0 - // clsll(~0ull) -> 63 - // clsll(1) -> 62 - // clsll(3) -> 61 - // clsll(5) -> 60 - // Note that CountLeadingEmptyOrDeleted is called when first control block - // is kDeleted or kEmpty. The implementation is similar to GroupPortableImpl - // but avoids +1 and __clsll returns result not including the high bit. Thus - // saves one cycle. - // kEmpty = -128, // 0b10000000 - // kDeleted = -2, // 0b11111110 - // ~ctrl & (ctrl >> 7) will have the lowest bit set to 1. After rbit, - // it will the highest one. - return (__clsll(__rbitll((~mask & (mask >> 7)) | gaps)) + 8) >> 3; -#else - return (TrailingZeros(((~mask & (mask >> 7)) | gaps) + 1) + 7) >> 3; -#endif // ABSL_INTERNAL_HAVE_ARM_ACLE + // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and + // kDeleted. We lower all other bits and count number of trailing zeros. + // Clang and GCC optimize countr_zero to rbit+clz without any check for 0, + // so we should be fine. + constexpr uint64_t bits = 0x0101010101010101ULL; + return countr_zero((mask | ~(mask >> 7)) & bits) >> 3; } void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { @@ -711,8 +690,10 @@ struct GroupPortableImpl { } uint32_t CountLeadingEmptyOrDeleted() const { - constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; - return (TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3; + // ctrl | ~(ctrl >> 7) will have the lowest bit set to zero for kEmpty and + // kDeleted. We lower all other bits and count number of trailing zeros. + constexpr uint64_t bits = 0x0101010101010101ULL; + return countr_zero((ctrl | ~(ctrl >> 7)) & bits) >> 3; } void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index dc6bc3d2..f77ffbc1 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -266,20 +266,16 @@ TEST(Group, CountLeadingEmptyOrDeleted) { for (ctrl_t empty : empty_examples) { std::vector e(Group::kWidth, empty); - EXPECT_TRUE(IsEmptyOrDeleted(e[0])); EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted()); for (ctrl_t full : full_examples) { - // First is always kEmpty or kDeleted. - for (size_t i = 1; i != Group::kWidth; ++i) { + for (size_t i = 0; i != Group::kWidth; ++i) { std::vector f(Group::kWidth, empty); f[i] = full; - EXPECT_TRUE(IsEmptyOrDeleted(f[0])); EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted()); } std::vector f(Group::kWidth, empty); f[Group::kWidth * 2 / 3] = full; f[Group::kWidth / 2] = full; - EXPECT_TRUE(IsEmptyOrDeleted(f[0])); EXPECT_EQ( Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted()); } -- cgit v1.2.3 From 9eff97861b88999428d1254f95c83d94a2e95944 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Thu, 9 Jun 2022 07:49:38 -0700 Subject: Fix C++17 constexpr storage deprecation warnings This change introduces the symbol ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL to guard redundant declarations of static constexpr data members that are needed prior to C++17. This change also introduces the symbol ABSL_INTERNAL_CPLUSPLUS_LANG, which is supposed to be set to the same value as __cplusplus, except it uses _MSVC_LANG on MSVC so that the value is correct on MSVC. Neither of these new symbols should be used outside of Abseil. Fixes #1191 PiperOrigin-RevId: 453923908 Change-Id: I1316c52c19fa0c168b93cced0c817e4cb7c9c862 --- absl/base/config.h | 43 ++++++++++++++++++++++++++- absl/base/exception_safety_testing_test.cc | 3 ++ absl/base/internal/cycleclock.cc | 3 ++ absl/base/internal/fast_type_id.h | 2 ++ absl/base/internal/spinlock.cc | 3 ++ absl/container/fixed_array.h | 2 ++ absl/container/internal/hashtablez_sampler.cc | 3 ++ absl/container/internal/raw_hash_set.cc | 2 ++ absl/numeric/int128.cc | 2 ++ absl/status/status.cc | 2 ++ absl/strings/cord.cc | 2 ++ absl/strings/cord_buffer.cc | 2 ++ absl/strings/internal/cord_rep_btree.cc | 4 ++- absl/strings/internal/cord_rep_ring.cc | 4 ++- absl/strings/internal/cordz_info.cc | 2 ++ absl/strings/internal/str_format/extension.cc | 9 +++--- absl/strings/internal/string_constant.h | 4 ++- absl/strings/string_view.cc | 15 ++-------- absl/types/internal/conformance_profile.h | 2 ++ 19 files changed, 87 insertions(+), 22 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 58097b0d..94f7fcb5 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -56,6 +56,25 @@ #include #endif // __cplusplus +// ABSL_INTERNAL_CPLUSPLUS_LANG +// +// MSVC does not set the value of __cplusplus correctly, but instead uses +// _MSVC_LANG as a stand-in. +// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +// +// However, there are reports that MSVC even sets _MSVC_LANG incorrectly at +// times, for example: +// https://github.com/microsoft/vscode-cpptools/issues/1770 +// https://reviews.llvm.org/D70996 +// +// For this reason, this symbol is considered INTERNAL and code outside of +// Abseil must not use it. +#if defined(_MSVC_LANG) +#define ABSL_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG +#elif defined(__cplusplus) +#define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus +#endif + #if defined(__APPLE__) // Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED, // __IPHONE_8_0. @@ -807,6 +826,29 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 #endif +// ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// +// Prior to C++17, static constexpr variables defined in classes required a +// separate definition outside of the class body, for example: +// +// class Foo { +// static constexpr int kBar = 0; +// }; +// constexpr int Foo::kBar; +// +// In C++17, these variables defined in classes are considered inline variables, +// and the extra declaration is redundant. Since some compilers warn on the +// extra declarations, ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL can be used +// conditionally ignore them: +// +// #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +// constexpr int Foo::kBar; +// #endif +#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L +#define ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1 +#endif + // `ABSL_INTERNAL_HAS_RTTI` determines whether abseil is being compiled with // RTTI support. #ifdef ABSL_INTERNAL_HAS_RTTI @@ -868,5 +910,4 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAVE_ARM_NEON 1 #endif - #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index a59be29e..a87fd6a9 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc @@ -701,7 +701,10 @@ struct BasicGuaranteeWithExtraContracts : public NonNegative { static constexpr int kExceptionSentinel = 9999; }; + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr int BasicGuaranteeWithExtraContracts::kExceptionSentinel; +#endif TEST(ExceptionCheckTest, BasicGuaranteeWithExtraContracts) { auto tester_with_val = diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc index f6e64242..902e3f5e 100644 --- a/absl/base/internal/cycleclock.cc +++ b/absl/base/internal/cycleclock.cc @@ -26,6 +26,7 @@ #include // NOLINT(build/c++11) #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/unscaledcycleclock.h" namespace absl { @@ -34,8 +35,10 @@ namespace base_internal { #if ABSL_USE_UNSCALED_CYCLECLOCK +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr int32_t CycleClock::kShift; constexpr double CycleClock::kFrequencyScale; +#endif ABSL_CONST_INIT std::atomic CycleClock::cycle_clock_source_{nullptr}; diff --git a/absl/base/internal/fast_type_id.h b/absl/base/internal/fast_type_id.h index 3db59e83..a547b3a8 100644 --- a/absl/base/internal/fast_type_id.h +++ b/absl/base/internal/fast_type_id.h @@ -28,8 +28,10 @@ struct FastTypeTag { constexpr static char dummy_var = 0; }; +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL template constexpr char FastTypeTag::dummy_var; +#endif // FastTypeId() evaluates at compile/link-time to a unique pointer for the // passed-in type. These are meant to be good match for keys into maps or diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 35c0696a..9b5ed6e4 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc @@ -19,6 +19,7 @@ #include #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/atomic_hook.h" #include "absl/base/internal/cycleclock.h" #include "absl/base/internal/spinlock_wait.h" @@ -66,12 +67,14 @@ void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock, submit_profile_data.Store(fn); } +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL // Static member variable definitions. constexpr uint32_t SpinLock::kSpinLockHeld; constexpr uint32_t SpinLock::kSpinLockCooperative; constexpr uint32_t SpinLock::kSpinLockDisabledScheduling; constexpr uint32_t SpinLock::kSpinLockSleeper; constexpr uint32_t SpinLock::kWaitTimeMask; +#endif // Uncommon constructors. SpinLock::SpinLock(base_internal::SchedulingMode mode) diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index 839ba0bc..2aefae3b 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -489,12 +489,14 @@ class FixedArray { Storage storage_; }; +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL template constexpr size_t FixedArray::kInlineBytesDefault; template constexpr typename FixedArray::size_type FixedArray::inline_elements; +#endif template void FixedArray::NonEmptyInlinedStorage::AnnotateConstruct( diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index c6036d51..efc1be58 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -32,7 +32,10 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr int HashtablezInfo::kMaxStackDepth; +#endif namespace { ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index 61bdb773..c63a2e02 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -31,7 +31,9 @@ alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[16] = { ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr size_t Group::kWidth; +#endif // Returns "random" seed. inline size_t RandomSeed() { diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 2f4e081f..8cdcbf05 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -332,6 +332,7 @@ std::ostream& operator<<(std::ostream& os, int128 v) { ABSL_NAMESPACE_END } // namespace absl +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL namespace std { constexpr bool numeric_limits::is_specialized; constexpr bool numeric_limits::is_signed; @@ -381,3 +382,4 @@ constexpr int numeric_limits::max_exponent10; constexpr bool numeric_limits::traps; constexpr bool numeric_limits::tinyness_before; } // namespace std +#endif diff --git a/absl/status/status.cc b/absl/status/status.cc index c66009d6..89749a52 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -195,7 +195,9 @@ const std::string* Status::EmptyString() { return &empty.str; } +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr const char Status::kMovedFromString[]; +#endif const std::string* Status::MovedFromString() { static std::string* moved_from_string = new std::string(kMovedFromString); diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 04dc7b90..85a67a08 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc @@ -160,7 +160,9 @@ static CordRep* CordRepFromString(std::string&& src) { // -------------------------------------------------------------------- // Cord::InlineRep functions +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr unsigned char Cord::InlineRep::kMaxInline; +#endif inline void Cord::InlineRep::set_data(const char* data, size_t n) { static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15"); diff --git a/absl/strings/cord_buffer.cc b/absl/strings/cord_buffer.cc index fd4045bd..fad6269c 100644 --- a/absl/strings/cord_buffer.cc +++ b/absl/strings/cord_buffer.cc @@ -21,8 +21,10 @@ namespace absl { ABSL_NAMESPACE_BEGIN +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr size_t CordBuffer::kDefaultLimit; constexpr size_t CordBuffer::kCustomLimit; +#endif ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/strings/internal/cord_rep_btree.cc b/absl/strings/internal/cord_rep_btree.cc index 2b592b47..cacbf3da 100644 --- a/absl/strings/internal/cord_rep_btree.cc +++ b/absl/strings/internal/cord_rep_btree.cc @@ -33,7 +33,9 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -constexpr size_t CordRepBtree::kMaxCapacity; // NOLINT: needed for c++ < c++17 +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +constexpr size_t CordRepBtree::kMaxCapacity; +#endif namespace { diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc index db1f63fa..af2fc768 100644 --- a/absl/strings/internal/cord_rep_ring.cc +++ b/absl/strings/internal/cord_rep_ring.cc @@ -129,7 +129,9 @@ class CordRepRing::Filler { index_type pos_; }; -constexpr size_t CordRepRing::kMaxCapacity; // NOLINT: needed for c++11 +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +constexpr size_t CordRepRing::kMaxCapacity; +#endif bool CordRepRing::IsValid(std::ostream& output) const { if (capacity_ == 0) { diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc index c891d0ed..dac3fd8b 100644 --- a/absl/strings/internal/cordz_info.cc +++ b/absl/strings/internal/cordz_info.cc @@ -34,7 +34,9 @@ namespace cord_internal { using ::absl::base_internal::SpinLockHolder; +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr int CordzInfo::kMaxStackDepth; +#endif ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{absl::kConstInit}; diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc index 484f6ebf..f93153d5 100644 --- a/absl/strings/internal/str_format/extension.cc +++ b/absl/strings/internal/str_format/extension.cc @@ -33,6 +33,8 @@ std::string FlagsToString(Flags v) { return s; } +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL + #define ABSL_INTERNAL_X_VAL(id) \ constexpr absl::FormatConversionChar FormatConversionCharInternal::id; ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) @@ -45,17 +47,14 @@ constexpr absl::FormatConversionChar FormatConversionCharInternal::kNone; ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) #undef ABSL_INTERNAL_CHAR_SET_CASE -// NOLINTNEXTLINE(readability-redundant-declaration) constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar; -// NOLINTNEXTLINE(readability-redundant-declaration) constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral; -// NOLINTNEXTLINE(readability-redundant-declaration) constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating; -// NOLINTNEXTLINE(readability-redundant-declaration) constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric; -// NOLINTNEXTLINE(readability-redundant-declaration) constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer; +#endif // ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL + bool FormatSinkImpl::PutPaddedString(string_view value, int width, int precision, bool left) { size_t space_remaining = 0; diff --git a/absl/strings/internal/string_constant.h b/absl/strings/internal/string_constant.h index b358efdd..f68b17d7 100644 --- a/absl/strings/internal/string_constant.h +++ b/absl/strings/internal/string_constant.h @@ -50,8 +50,10 @@ struct StringConstant { "The input string_view must point to constant data."); }; +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL template -constexpr absl::string_view StringConstant::value; // NOLINT +constexpr absl::string_view StringConstant::value; +#endif // Factory function for `StringConstant` instances. // It supports callables that have a constexpr default constructor and a diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc index d596e08c..adce3be9 100644 --- a/absl/strings/string_view.cc +++ b/absl/strings/string_view.cc @@ -207,22 +207,11 @@ string_view::size_type string_view::find_last_not_of( return npos; } -// MSVC has non-standard behavior that implicitly creates definitions for static -// const members. These implicit definitions conflict with explicit out-of-class -// member definitions that are required by the C++ standard, resulting in -// LNK1169 "multiply defined" errors at link time. __declspec(selectany) asks -// MSVC to choose only one definition for the symbol it decorates. See details -// at https://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx -#ifdef _MSC_VER -#define ABSL_STRING_VIEW_SELECTANY __declspec(selectany) -#else -#define ABSL_STRING_VIEW_SELECTANY -#endif -ABSL_STRING_VIEW_SELECTANY +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL constexpr string_view::size_type string_view::npos; -ABSL_STRING_VIEW_SELECTANY constexpr string_view::size_type string_view::kMaxSize; +#endif ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/types/internal/conformance_profile.h b/absl/types/internal/conformance_profile.h index cf64ff4f..37b017db 100644 --- a/absl/types/internal/conformance_profile.h +++ b/absl/types/internal/conformance_profile.h @@ -719,6 +719,7 @@ struct SyntacticConformanceProfileOf { type##_support); \ ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(bool, is_##type) +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(default_constructible); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_constructible); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_constructible); @@ -733,6 +734,7 @@ ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_equal_comparable); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_than_comparable); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(swappable); ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable); +#endif #undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF #undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL -- cgit v1.2.3 From db609075875320c32a197ae633302c50bead6e39 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Thu, 9 Jun 2022 11:20:41 -0700 Subject: absl/base/internal/invoke.h: Use ABSL_INTERNAL_CPLUSPLUS_LANG for language version guard PiperOrigin-RevId: 453970585 Change-Id: Iac23eb88ea676efc822f001020b1cc2c255dbbc1 --- absl/base/internal/invoke.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index 4a644c68..018b1af3 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -39,7 +39,7 @@ #include "absl/base/config.h" -#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L #include @@ -55,7 +55,7 @@ using std::is_invocable_r; ABSL_NAMESPACE_END } // namespace absl -#else // __cplusplus >= 201703L +#else // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L #include #include @@ -226,6 +226,6 @@ using is_invocable_r = IsInvocableRImpl; ABSL_NAMESPACE_END } // namespace absl -#endif // __cplusplus >= 201703L +#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L #endif // ABSL_BASE_INTERNAL_INVOKE_H_ -- cgit v1.2.3 From 45eba9c16a513cda7fe53b44889c7300f158cf3d Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 10 Jun 2022 07:18:20 -0700 Subject: Reformulate documentation of ABSL_LOCKS_EXCLUDED. The intent of the macro is to say what locks cannot be held when calling the method, not making a promise that they will be acquired. PiperOrigin-RevId: 454158686 Change-Id: I71087460c3df27c7d6e0571156f19f525024f1de --- absl/base/thread_annotations.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h index 9695f6de..bc8a6203 100644 --- a/absl/base/thread_annotations.h +++ b/absl/base/thread_annotations.h @@ -154,8 +154,8 @@ // ABSL_LOCKS_EXCLUDED() // -// Documents the locks acquired in the body of the function. These locks -// cannot be held when calling this function (as Abseil's `Mutex` locks are +// Documents the locks that cannot be held by callers of this function, as they +// might be acquired by this function (Abseil's `Mutex` locks are // non-reentrant). #if ABSL_HAVE_ATTRIBUTE(locks_excluded) #define ABSL_LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) -- cgit v1.2.3 From 9431a837919ba242eb7560a3ec74c55db3a027d2 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 10 Jun 2022 09:53:15 -0700 Subject: Fix several typos in comments. PiperOrigin-RevId: 454185620 Change-Id: Ifdff33cec4bdd63f160a8d3c18f959ac2a3b6f25 --- absl/container/internal/inlined_vector.h | 2 +- absl/strings/cord.h | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index bc1b8c1b..54c92a01 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -610,7 +610,7 @@ auto Storage::Resize(ValueAdapter values, SizeType new_size) // Steps: // a. Allocate new backing store. // b. Construct new elements in new backing store. - // c. Move existing elements from old backing store to now. + // c. Move existing elements from old backing store to new backing store. // d. Destroy all elements in old backing store. // Use transactional wrappers for the first two steps so we can roll // back if necessary due to exceptions. diff --git a/absl/strings/cord.h b/absl/strings/cord.h index f378dc07..18d6ab85 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h @@ -467,7 +467,7 @@ class Cord { CordRepBtreeReader btree_reader_; }; - // Cord::ChunkIterator::chunk_begin() + // Cord::chunk_begin() // // Returns an iterator to the first chunk of the `Cord`. // @@ -483,7 +483,7 @@ class Cord { // } ChunkIterator chunk_begin() const; - // Cord::ChunkItertator::chunk_end() + // Cord::chunk_end() // // Returns an iterator one increment past the last chunk of the `Cord`. // @@ -493,7 +493,7 @@ class Cord { ChunkIterator chunk_end() const; //---------------------------------------------------------------------------- - // Cord::ChunkIterator::ChunkRange + // Cord::ChunkRange //---------------------------------------------------------------------------- // // `ChunkRange` is a helper class for iterating over the chunks of the `Cord`, @@ -527,9 +527,9 @@ class Cord { // Cord::Chunks() // - // Returns a `Cord::ChunkIterator::ChunkRange` for iterating over the chunks - // of a `Cord` with a range-based for-loop. For most iteration tasks on a - // Cord, use `Cord::Chunks()` to retrieve this iterator. + // Returns a `Cord::ChunkRange` for iterating over the chunks of a `Cord` with + // a range-based for-loop. For most iteration tasks on a Cord, use + // `Cord::Chunks()` to retrieve this iterator. // // Example: // @@ -595,7 +595,7 @@ class Cord { ChunkIterator chunk_iterator_; }; - // Cord::CharIterator::AdvanceAndRead() + // Cord::AdvanceAndRead() // // Advances the `Cord::CharIterator` by `n_bytes` and returns the bytes // advanced as a separate `Cord`. `n_bytes` must be less than or equal to the @@ -603,21 +603,21 @@ class Cord { // valid to pass `char_end()` and `0`. static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes); - // Cord::CharIterator::Advance() + // Cord::Advance() // // Advances the `Cord::CharIterator` by `n_bytes`. `n_bytes` must be less than // or equal to the number of bytes remaining within the Cord; otherwise, // behavior is undefined. It is valid to pass `char_end()` and `0`. static void Advance(CharIterator* it, size_t n_bytes); - // Cord::CharIterator::ChunkRemaining() + // Cord::ChunkRemaining() // // Returns the longest contiguous view starting at the iterator's position. // // `it` must be dereferenceable. static absl::string_view ChunkRemaining(const CharIterator& it); - // Cord::CharIterator::char_begin() + // Cord::char_begin() // // Returns an iterator to the first character of the `Cord`. // @@ -626,7 +626,7 @@ class Cord { // a `CharIterator` where range-based for-loops may not be available. CharIterator char_begin() const; - // Cord::CharIterator::char_end() + // Cord::char_end() // // Returns an iterator to one past the last character of the `Cord`. // @@ -635,7 +635,7 @@ class Cord { // a `CharIterator` where range-based for-loops are not useful. CharIterator char_end() const; - // Cord::CharIterator::CharRange + // Cord::CharRange // // `CharRange` is a helper class for iterating over the characters of a // producing an iterator which can be used within a range-based for loop. @@ -666,11 +666,11 @@ class Cord { const Cord* cord_; }; - // Cord::CharIterator::Chars() + // Cord::Chars() // - // Returns a `Cord::CharIterator` for iterating over the characters of a - // `Cord` with a range-based for-loop. For most character-based iteration - // tasks on a Cord, use `Cord::Chars()` to retrieve this iterator. + // Returns a `Cord::CharRange` for iterating over the characters of a `Cord` + // with a range-based for-loop. For most character-based iteration tasks on a + // Cord, use `Cord::Chars()` to retrieve this iterator. // // Example: // -- cgit v1.2.3 From eda52d053e6400d9411b1f46a1a17a959d4db11f Mon Sep 17 00:00:00 2001 From: Andy Getzendanner Date: Fri, 10 Jun 2022 15:12:31 -0700 Subject: Tweak the signature of status_internal::MakeCheckFailString as part of an upcoming change PiperOrigin-RevId: 454251164 Change-Id: I256b7a662478f9317a4133ec209fa5488a942886 --- absl/status/internal/status_internal.h | 8 ++++---- absl/status/status.cc | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index fc1e78bc..19a4a7aa 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h @@ -15,6 +15,7 @@ #define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ #include +#include #include "absl/base/attributes.h" #include "absl/container/inlined_vector.h" @@ -70,12 +71,11 @@ struct StatusRep { absl::StatusCode MapToLocalCode(int value); -// If `status` is not OK, returns a pointer to a newly-allocated string with the -// given `prefix`, suitable for output as an error message in assertion/CHECK() -// failures. Otherwise returns nullptr. +// Returns a pointer to a newly-allocated string with the given `prefix`, +// suitable for output as an error message in assertion/`CHECK()` failures. // // This is an internal implementation detail for Abseil logging. -std::string* MakeCheckFailString(const absl::Status& status, +std::string* MakeCheckFailString(const absl::Status* status, const char* prefix); } // namespace status_internal diff --git a/absl/status/status.cc b/absl/status/status.cc index 89749a52..88e8eda9 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -603,12 +603,11 @@ Status ErrnoToStatus(int error_number, absl::string_view message) { namespace status_internal { -std::string* MakeCheckFailString(const absl::Status& status, +std::string* MakeCheckFailString(const absl::Status* status, const char* prefix) { - if (status.ok()) { return nullptr; } return new std::string( absl::StrCat(prefix, " (", - status.ToString(StatusToStringMode::kWithEverything), ")")); + status->ToString(StatusToStringMode::kWithEverything), ")")); } } // namespace status_internal -- cgit v1.2.3 From 02b0216656ff68784b4864557cb15b9a554792a4 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 10 Jun 2022 23:20:25 -0700 Subject: Minor wording fix in the comment for ConsumeSuffix() PiperOrigin-RevId: 454305599 Change-Id: I528dfe0223280c379d8791373dc2871ad5812f63 --- absl/strings/strip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/strings/strip.h b/absl/strings/strip.h index d801774d..341e66fc 100644 --- a/absl/strings/strip.h +++ b/absl/strings/strip.h @@ -50,7 +50,7 @@ inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) { } // ConsumeSuffix() // -// Strips the `expected` prefix, if found, from the end of `str`. +// Strips the `expected` suffix, if found, from the end of `str`. // If the operation succeeded, `true` is returned. If not, `false` // is returned and `str` is not modified. // -- cgit v1.2.3 From 76c7ad82415813cc0354647e4b9b73eaf68a9da0 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 14 Jun 2022 10:23:06 -0700 Subject: Prefer to fallback to UTC when the embedded zoneinfo data does not contain the requested zone. And now that we have a fallback at all, remove the special case that allowed for testing absl::LocalTimeZone() under TZ=US/Pacific. And we might as well update the existing embedded data while we're here. This is modifying the test framework only. PiperOrigin-RevId: 454896078 Change-Id: I3ec8391a5a51fe1e86a14f39d57ed6dac89d5905 --- absl/time/internal/test_util.cc | 21 +- absl/time/internal/zoneinfo.inc | 943 ++++++++++++++++++++-------------------- 2 files changed, 475 insertions(+), 489 deletions(-) diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc index 24cff138..454b33a1 100644 --- a/absl/time/internal/test_util.cc +++ b/absl/time/internal/test_util.cc @@ -68,10 +68,6 @@ const struct ZoneInfo { {"Invalid/TimeZone", nullptr, 0}, {"", nullptr, 0}, - // Also allow for loading the local time zone under TZ=US/Pacific. - {"US/Pacific", // - reinterpret_cast(America_Los_Angeles), America_Los_Angeles_len}, - // Allows use of the local time zone from a system-specific location. #ifdef _MSC_VER {"localtime", // @@ -104,12 +100,6 @@ class TestZoneInfoSource : public cctz::ZoneInfoSource { const char* const end_; }; -std::unique_ptr EmbeddedTestZone(const char* data, - std::size_t size) { - return std::unique_ptr( - new TestZoneInfoSource(data, size)); -} - std::unique_ptr TestFactory( const std::string& name, const std::function( @@ -117,14 +107,15 @@ std::unique_ptr TestFactory( for (const ZoneInfo& zoneinfo : kZoneInfo) { if (name == zoneinfo.name) { if (zoneinfo.data == nullptr) return nullptr; - return EmbeddedTestZone(zoneinfo.data, zoneinfo.length); + return std::unique_ptr( + new TestZoneInfoSource(zoneinfo.data, zoneinfo.length)); } } - // The embedded tzdata database knows nothing about this zone. Return - // something so the tests can proceed. - return EmbeddedTestZone(reinterpret_cast(America_Los_Angeles), - America_Los_Angeles_len); + // The embedded zoneinfo data does not include the zone, so fallback to + // built-in UTC. The tests have been crafted so that this should only + // happen when testing absl::LocalTimeZone() with an unconstrained ${TZ}. + return nullptr; } } // namespace diff --git a/absl/time/internal/zoneinfo.inc b/absl/time/internal/zoneinfo.inc index bfed8299..7d8b3ff2 100644 --- a/absl/time/internal/zoneinfo.inc +++ b/absl/time/internal/zoneinfo.inc @@ -88,157 +88,156 @@ unsigned char America_Los_Angeles[] = { 0x00, 0x01, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbb, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x04, - 0x1a, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6, 0x48, 0xa0, 0xff, 0xff, - 0xff, 0xff, 0x9f, 0xbb, 0x15, 0x90, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x86, - 0x2a, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a, 0xf7, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xcb, 0x89, 0x1a, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x23, - 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x61, 0x26, 0x10, 0xff, 0xff, - 0xff, 0xff, 0xd6, 0xfe, 0x74, 0x5c, 0xff, 0xff, 0xff, 0xff, 0xd8, 0x80, - 0xad, 0x90, 0xff, 0xff, 0xff, 0xff, 0xda, 0xfe, 0xc3, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xdb, 0xc0, 0x90, 0x10, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xde, - 0xa5, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xa9, 0xac, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xde, 0xbe, 0x87, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdf, 0x89, - 0x8e, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x9e, 0x69, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xe1, 0x69, 0x70, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe2, 0x7e, - 0x4b, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x49, 0x52, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xe4, 0x5e, 0x2d, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe5, 0x29, - 0x34, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x47, 0x4a, 0x10, 0xff, 0xff, - 0xff, 0xff, 0xe7, 0x12, 0x51, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x27, - 0x2c, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe8, 0xf2, 0x33, 0x10, 0xff, 0xff, - 0xff, 0xff, 0xea, 0x07, 0x0e, 0x10, 0xff, 0xff, 0xff, 0xff, 0xea, 0xd2, - 0x15, 0x10, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6, 0xf0, 0x10, 0xff, 0xff, - 0xff, 0xff, 0xec, 0xb1, 0xf7, 0x10, 0xff, 0xff, 0xff, 0xff, 0xed, 0xc6, - 0xd2, 0x10, 0xff, 0xff, 0xff, 0xff, 0xee, 0x91, 0xd9, 0x10, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xaf, 0xee, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x71, - 0xbb, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, 0xd0, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xf2, 0x7f, 0xc1, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x6f, - 0xb2, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x5f, 0xa3, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xf5, 0x4f, 0x94, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x3f, - 0x85, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x2f, 0x76, 0x90, 0xff, 0xff, - 0xff, 0xff, 0xf8, 0x28, 0xa2, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x0f, - 0x58, 0x90, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x08, 0x84, 0x10, 0xff, 0xff, - 0xff, 0xff, 0xfa, 0xf8, 0x83, 0x20, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xe8, - 0x66, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xd8, 0x65, 0x20, 0xff, 0xff, - 0xff, 0xff, 0xfd, 0xc8, 0x48, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xb8, - 0x47, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0x2a, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x98, 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, - 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x0b, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00, 0x00, 0x00, 0x04, 0x61, - 0x27, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x51, 0x0a, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x41, 0x09, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, - 0xec, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d, 0x43, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x09, 0x10, 0xce, 0x90, 0x00, 0x00, 0x00, 0x00, 0x09, 0xad, - 0xbf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0xb0, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0xe0, 0xaf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xd9, - 0xcd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc0, 0x91, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x0e, 0xb9, 0xaf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xa9, - 0xae, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x91, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x89, 0x90, 0x20, 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, - 0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x72, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, - 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x37, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x17, 0x29, 0x36, 0x20, 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, - 0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, 0x18, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x1a, 0x02, 0x35, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xf2, - 0x34, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe2, 0x17, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x1c, 0xd2, 0x16, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xc1, - 0xf9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb1, 0xf8, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x1f, 0xa1, 0xdb, 0x90, 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, - 0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0xbd, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x22, 0x56, 0x0d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x23, 0x6a, - 0xda, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xef, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x25, 0x4a, 0xbc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, - 0xd1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2a, 0x9e, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x27, 0xfe, 0xed, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0a, - 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xde, 0xcf, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x2a, 0xea, 0x62, 0x10, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xbe, - 0xb1, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd3, 0x7e, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x2d, 0x9e, 0x93, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xb3, - 0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7e, 0x75, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, - 0x92, 0x20, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, 0x24, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x33, 0x47, 0x74, 0x20, 0x00, 0x00, 0x00, 0x00, 0x34, 0x53, - 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x56, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x36, 0x32, 0xe8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, - 0x38, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x05, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x38, 0xe7, 0x1a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x39, 0xfb, - 0xe7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc6, 0xfc, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x3b, 0xdb, 0xc9, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb0, - 0x18, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0xab, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x3e, 0x8f, 0xfa, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x9b, - 0x8d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6f, 0xdc, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x41, 0x84, 0xa9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0x4f, - 0xbe, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x8b, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x44, 0x2f, 0xa0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, - 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xf3, 0xd3, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x47, 0x2d, 0x8a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x47, 0xd3, - 0xb5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0d, 0x6c, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x49, 0xb3, 0x97, 0x20, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xed, - 0x4e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x9c, 0xb3, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x4c, 0xd6, 0x6a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x7c, - 0x95, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb6, 0x4c, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x4f, 0x5c, 0x77, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, - 0x2e, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3c, 0x59, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00, 0x00, 0x00, 0x53, 0x1c, - 0x3b, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xf2, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x54, 0xfc, 0x1d, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, - 0xd4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xe5, 0x3a, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x58, 0x1e, 0xf1, 0x10, 0x00, 0x00, 0x00, 0x00, 0x58, 0xc5, - 0x1c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xfe, 0xd3, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x5a, 0xa4, 0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xde, - 0xb5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x84, 0xe0, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x5d, 0xbe, 0x97, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x64, - 0xc2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9e, 0x79, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x4d, 0xde, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, - 0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2d, 0xc0, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0d, - 0xa2, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x59, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x65, 0xed, 0x84, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, - 0x3b, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xcd, 0x66, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x69, 0x07, 0x1d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x69, 0xad, - 0x48, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe6, 0xff, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x6b, 0x96, 0x65, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xd0, - 0x1c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x76, 0x47, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x6e, 0xaf, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x56, - 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8f, 0xe0, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x71, 0x36, 0x0b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, - 0xc2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xed, 0x20, 0x00, 0x00, - 0x00, 0x00, 0x74, 0x4f, 0xa4, 0x10, 0x00, 0x00, 0x00, 0x00, 0x74, 0xff, - 0x09, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, 0xc0, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x76, 0xde, 0xeb, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, - 0xa2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0xbe, 0xcd, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x79, 0xf8, 0x84, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x9e, - 0xaf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd8, 0x66, 0x90, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0x7e, 0x91, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xb8, - 0x48, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x5e, 0x73, 0xa0, 0x00, 0x00, - 0x00, 0x00, 0x7f, 0x98, 0x2a, 0x90, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, - 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0xff, 0xff, 0x91, 0x26, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x90, 0x01, - 0x04, 0xff, 0xff, 0x8f, 0x80, 0x00, 0x08, 0xff, 0xff, 0x9d, 0x90, 0x01, - 0x0c, 0xff, 0xff, 0x9d, 0x90, 0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00, 0x50, - 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, 0x54, 0x00, 0x50, - 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x0a, 0x50, 0x53, 0x54, 0x38, 0x50, 0x44, 0x54, 0x2c, 0x4d, 0x33, - 0x2e, 0x32, 0x2e, 0x30, 0x2c, 0x4d, 0x31, 0x31, 0x2e, 0x31, 0x2e, 0x30, - 0x0a + 0x00, 0xba, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, + 0xff, 0xff, 0x5e, 0x04, 0x1a, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6, + 0x48, 0xa0, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xbb, 0x15, 0x90, 0xff, 0xff, + 0xff, 0xff, 0xa0, 0x86, 0x2a, 0xa0, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a, + 0xf7, 0x90, 0xff, 0xff, 0xff, 0xff, 0xcb, 0x89, 0x1a, 0xa0, 0xff, 0xff, + 0xff, 0xff, 0xd2, 0x23, 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x61, + 0x26, 0x10, 0xff, 0xff, 0xff, 0xff, 0xd6, 0xfe, 0x74, 0x5c, 0xff, 0xff, + 0xff, 0xff, 0xd8, 0x80, 0xad, 0x90, 0xff, 0xff, 0xff, 0xff, 0xda, 0xfe, + 0xc3, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xc0, 0x90, 0x10, 0xff, 0xff, + 0xff, 0xff, 0xdc, 0xde, 0xa5, 0x90, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xa9, + 0xac, 0x90, 0xff, 0xff, 0xff, 0xff, 0xde, 0xbe, 0x87, 0x90, 0xff, 0xff, + 0xff, 0xff, 0xdf, 0x89, 0x8e, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x9e, + 0x69, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x69, 0x70, 0x90, 0xff, 0xff, + 0xff, 0xff, 0xe2, 0x7e, 0x4b, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x49, + 0x52, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0x2d, 0x90, 0xff, 0xff, + 0xff, 0xff, 0xe5, 0x29, 0x34, 0x90, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x47, + 0x4a, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x12, 0x51, 0x10, 0xff, 0xff, + 0xff, 0xff, 0xe8, 0x27, 0x2c, 0x10, 0xff, 0xff, 0xff, 0xff, 0xe8, 0xf2, + 0x33, 0x10, 0xff, 0xff, 0xff, 0xff, 0xea, 0x07, 0x0e, 0x10, 0xff, 0xff, + 0xff, 0xff, 0xea, 0xd2, 0x15, 0x10, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6, + 0xf0, 0x10, 0xff, 0xff, 0xff, 0xff, 0xec, 0xb1, 0xf7, 0x10, 0xff, 0xff, + 0xff, 0xff, 0xed, 0xc6, 0xd2, 0x10, 0xff, 0xff, 0xff, 0xff, 0xee, 0x91, + 0xd9, 0x10, 0xff, 0xff, 0xff, 0xff, 0xef, 0xaf, 0xee, 0x90, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0x71, 0xbb, 0x10, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, + 0xd0, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf2, 0x7f, 0xc1, 0x90, 0xff, 0xff, + 0xff, 0xff, 0xf3, 0x6f, 0xb2, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x5f, + 0xa3, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x4f, 0x94, 0x90, 0xff, 0xff, + 0xff, 0xff, 0xf6, 0x3f, 0x85, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x2f, + 0x76, 0x90, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x28, 0xa2, 0x10, 0xff, 0xff, + 0xff, 0xff, 0xf9, 0x0f, 0x58, 0x90, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x08, + 0x84, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf8, 0x83, 0x20, 0xff, 0xff, + 0xff, 0xff, 0xfb, 0xe8, 0x66, 0x10, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xd8, + 0x65, 0x20, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xc8, 0x48, 0x10, 0xff, 0xff, + 0xff, 0xff, 0xfe, 0xb8, 0x47, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, + 0x2a, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x29, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x88, 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, + 0x0b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03, 0x71, 0x28, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x61, 0x27, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x05, 0x51, + 0x0a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x06, 0x41, 0x09, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x30, 0xec, 0x90, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d, + 0x43, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xce, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x09, 0xad, 0xbf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, + 0xb0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xe0, 0xaf, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0xd9, 0xcd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc0, + 0x91, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xb9, 0xaf, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xa9, 0xae, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, + 0x91, 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x90, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x12, 0x79, 0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, + 0x72, 0x20, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x55, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x49, 0x54, 0x20, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, + 0x37, 0x10, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x36, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x22, 0x53, 0x90, 0x00, 0x00, 0x00, 0x00, 0x19, 0x09, + 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x02, 0x35, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x1a, 0xf2, 0x34, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe2, + 0x17, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xd2, 0x16, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x1d, 0xc1, 0xf9, 0x90, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb1, + 0xf8, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xa1, 0xdb, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x76, 0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, + 0xbd, 0x90, 0x00, 0x00, 0x00, 0x00, 0x22, 0x56, 0x0d, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x23, 0x6a, 0xda, 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, + 0xef, 0x20, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4a, 0xbc, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x26, 0x15, 0xd1, 0x20, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2a, + 0x9e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x27, 0xfe, 0xed, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x29, 0x0a, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x29, 0xde, + 0xcf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xea, 0x62, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x2b, 0xbe, 0xb1, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd3, + 0x7e, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x9e, 0x93, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x2e, 0xb3, 0x60, 0x90, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7e, + 0x75, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x42, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x31, 0x67, 0x92, 0x20, 0x00, 0x00, 0x00, 0x00, 0x32, 0x73, + 0x24, 0x90, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x74, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x53, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, + 0x56, 0x20, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xe8, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x37, 0x07, 0x38, 0x20, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, + 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x38, 0xe7, 0x1a, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x39, 0xfb, 0xe7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc6, + 0xfc, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xdb, 0xc9, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0xb0, 0x18, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbb, + 0xab, 0x10, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x8f, 0xfa, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x9b, 0x8d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6f, + 0xdc, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0xa9, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x4f, 0xbe, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, + 0x8b, 0x90, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2f, 0xa0, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x45, 0x44, 0x6d, 0x90, 0x00, 0x00, 0x00, 0x00, 0x45, 0xf3, + 0xd3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2d, 0x8a, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x47, 0xd3, 0xb5, 0x20, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0d, + 0x6c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0xb3, 0x97, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x4a, 0xed, 0x4e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x9c, + 0xb3, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xd6, 0x6a, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x4d, 0x7c, 0x95, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb6, + 0x4c, 0x90, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x5c, 0x77, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x50, 0x96, 0x2e, 0x90, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3c, + 0x59, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x52, 0x76, 0x10, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x53, 0x1c, 0x3b, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, + 0xf2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfc, 0x1d, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x56, 0x35, 0xd4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x56, 0xe5, + 0x3a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1e, 0xf1, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x58, 0xc5, 0x1c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x59, 0xfe, + 0xd3, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xa4, 0xfe, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x5b, 0xde, 0xb5, 0x10, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x84, + 0xe0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbe, 0x97, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x5e, 0x64, 0xc2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9e, + 0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4d, 0xde, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x61, 0x87, 0x95, 0x90, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2d, + 0xc0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x77, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x64, 0x0d, 0xa2, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, + 0x59, 0x90, 0x00, 0x00, 0x00, 0x00, 0x65, 0xed, 0x84, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x67, 0x27, 0x3b, 0x90, 0x00, 0x00, 0x00, 0x00, 0x67, 0xcd, + 0x66, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x69, 0x07, 0x1d, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x69, 0xad, 0x48, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe6, + 0xff, 0x90, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x96, 0x65, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0xd0, 0x1c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x76, + 0x47, 0x20, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xaf, 0xfe, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x6f, 0x56, 0x29, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8f, + 0xe0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x71, 0x36, 0x0b, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x72, 0x6f, 0xc2, 0x10, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, + 0xed, 0x20, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4f, 0xa4, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x74, 0xff, 0x09, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, + 0xc0, 0x90, 0x00, 0x00, 0x00, 0x00, 0x76, 0xde, 0xeb, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x18, 0xa2, 0x90, 0x00, 0x00, 0x00, 0x00, 0x78, 0xbe, + 0xcd, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x79, 0xf8, 0x84, 0x90, 0x00, 0x00, + 0x00, 0x00, 0x7a, 0x9e, 0xaf, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd8, + 0x66, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0x91, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x7d, 0xb8, 0x48, 0x90, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x5e, + 0x73, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x98, 0x2a, 0x90, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0xff, 0xff, 0x91, 0x26, 0x00, 0x00, 0xff, 0xff, + 0x9d, 0x90, 0x01, 0x04, 0xff, 0xff, 0x8f, 0x80, 0x00, 0x08, 0xff, 0xff, + 0x9d, 0x90, 0x01, 0x0c, 0xff, 0xff, 0x9d, 0x90, 0x01, 0x10, 0x4c, 0x4d, + 0x54, 0x00, 0x50, 0x44, 0x54, 0x00, 0x50, 0x53, 0x54, 0x00, 0x50, 0x57, + 0x54, 0x00, 0x50, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x0a, 0x50, 0x53, 0x54, 0x38, 0x50, 0x44, 0x54, + 0x2c, 0x4d, 0x33, 0x2e, 0x32, 0x2e, 0x30, 0x2c, 0x4d, 0x31, 0x31, 0x2e, + 0x31, 0x2e, 0x30, 0x0a }; -unsigned int America_Los_Angeles_len = 2845; +unsigned int America_Los_Angeles_len = 2836; unsigned char America_New_York[] = { 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, @@ -349,203 +348,202 @@ unsigned char America_New_York[] = { 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x03, 0xf0, 0x90, - 0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6, 0x1e, 0x70, 0xff, 0xff, 0xff, 0xff, - 0x9f, 0xba, 0xeb, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x86, 0x00, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a, 0xcd, 0x60, 0xff, 0xff, 0xff, 0xff, - 0xa2, 0x65, 0xe2, 0x70, 0xff, 0xff, 0xff, 0xff, 0xa3, 0x83, 0xe9, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xa4, 0x6a, 0xae, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xa5, 0x35, 0xa7, 0x60, 0xff, 0xff, 0xff, 0xff, 0xa6, 0x53, 0xca, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xa7, 0x15, 0x89, 0x60, 0xff, 0xff, 0xff, 0xff, - 0xa8, 0x33, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xfe, 0xa5, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xaa, 0x13, 0x8e, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xaa, 0xde, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xab, 0xf3, 0x70, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xac, 0xbe, 0x69, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xad, 0xd3, 0x52, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xae, 0x9e, 0x4b, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xaf, 0xb3, 0x34, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xb0, 0x7e, 0x2d, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x9c, 0x51, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xb2, 0x67, 0x4a, 0x60, 0xff, 0xff, 0xff, 0xff, - 0xb3, 0x7c, 0x33, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb4, 0x47, 0x2c, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xb5, 0x5c, 0x15, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xb6, 0x27, 0x0e, 0x60, 0xff, 0xff, 0xff, 0xff, 0xb7, 0x3b, 0xf7, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xb8, 0x06, 0xf0, 0x60, 0xff, 0xff, 0xff, 0xff, - 0xb9, 0x1b, 0xd9, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb9, 0xe6, 0xd2, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xbb, 0x04, 0xf5, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xbb, 0xc6, 0xb4, 0x60, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe4, 0xd7, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xbd, 0xaf, 0xd0, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xbe, 0xc4, 0xb9, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x8f, 0xb2, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xc0, 0xa4, 0x9b, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xc1, 0x6f, 0x94, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc2, 0x84, 0x7d, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xc3, 0x4f, 0x76, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xc4, 0x64, 0x5f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xc5, 0x2f, 0x58, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xc6, 0x4d, 0x7c, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xc7, 0x0f, 0x3a, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x2d, 0x5e, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xc8, 0xf8, 0x57, 0x60, 0xff, 0xff, 0xff, 0xff, - 0xca, 0x0d, 0x40, 0x70, 0xff, 0xff, 0xff, 0xff, 0xca, 0xd8, 0x39, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xcb, 0x88, 0xf0, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xd2, 0x23, 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xd2, 0x60, 0xfb, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xd3, 0x75, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xd4, 0x40, 0xdd, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x55, 0xc6, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xd6, 0x20, 0xbf, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xd7, 0x35, 0xa8, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xd8, 0x00, 0xa1, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xd9, 0x15, 0x8a, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xd9, 0xe0, 0x83, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xda, 0xfe, 0xa7, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xdb, 0xc0, 0x65, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xdc, 0xde, 0x89, 0x70, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xa9, 0x82, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xde, 0xbe, 0x6b, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xdf, 0x89, 0x64, 0x60, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x9e, 0x4d, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xe1, 0x69, 0x46, 0x60, 0xff, 0xff, 0xff, 0xff, - 0xe2, 0x7e, 0x2f, 0x70, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x49, 0x28, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0x11, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xe5, 0x57, 0x2e, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xe6, 0x47, 0x2d, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xe7, 0x37, 0x10, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xe8, 0x27, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xe9, 0x16, 0xf2, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xea, 0x06, 0xf1, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xea, 0xf6, 0xd4, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6, 0xd3, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xec, 0xd6, 0xb6, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xed, 0xc6, 0xb5, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xee, 0xbf, 0xd3, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xef, 0xaf, 0xd2, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xf0, 0x9f, 0xb5, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x8f, 0xb4, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xf2, 0x7f, 0x97, 0x60, 0xff, 0xff, 0xff, 0xff, - 0xf3, 0x6f, 0x96, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x5f, 0x79, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xf5, 0x4f, 0x78, 0x70, 0xff, 0xff, 0xff, 0xff, - 0xf6, 0x3f, 0x5b, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x2f, 0x5a, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xf8, 0x28, 0x77, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xf9, 0x0f, 0x3c, 0x70, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x08, 0x59, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf8, 0x58, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xfb, 0xe8, 0x3b, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xd8, 0x3a, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xfd, 0xc8, 0x1d, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xb8, 0x1c, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x87, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x77, 0xe0, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0xfe, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x60, 0xfd, 0x70, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0xe0, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x30, 0xc2, 0x60, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d, 0x19, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xa4, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x09, 0xad, 0x94, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x86, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0xe0, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0xd9, 0xa2, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc0, 0x67, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0xb9, 0x84, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0xa9, 0x83, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x99, 0x66, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x65, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x12, 0x79, 0x48, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x13, 0x69, 0x47, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x2a, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x49, 0x29, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x16, 0x39, 0x0c, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x0b, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x22, 0x29, 0x60, 0x00, 0x00, 0x00, 0x00, 0x19, 0x08, 0xed, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x1a, 0x02, 0x0b, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0xf2, 0x0a, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe1, 0xed, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x1c, 0xd1, 0xec, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0xc1, 0xcf, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb1, 0xce, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0xa1, 0xb1, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x76, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x21, 0x81, 0x93, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xe2, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x6a, 0xaf, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x24, 0x35, 0xc4, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x25, 0x4a, 0x91, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x15, 0xa6, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x27, 0x2a, 0x73, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x27, 0xfe, 0xc3, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x29, 0x0a, 0x55, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x29, 0xde, 0xa5, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x2a, 0xea, 0x37, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0xbe, 0x87, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd3, 0x54, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x2d, 0x9e, 0x69, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x2e, 0xb3, 0x36, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7e, 0x4b, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x31, 0x67, 0x67, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0xfa, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x49, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x52, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x35, 0x27, 0x2b, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xbe, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x37, 0x07, 0x0d, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1b, 0xda, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x38, 0xe6, 0xef, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x39, 0xfb, 0xbc, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xc6, 0xd1, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x3b, 0xdb, 0x9e, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0xaf, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xbb, 0x80, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x8f, 0xd0, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x9b, 0x62, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6f, 0xb2, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x7f, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x4f, 0x94, 0x70, 0x00, 0x00, 0x00, 0x00, 0x43, 0x64, 0x61, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x2f, 0x76, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x44, 0x43, 0x60, 0x00, 0x00, 0x00, 0x00, 0x45, 0xf3, 0xa8, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x2d, 0x5f, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x47, 0xd3, 0x8a, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x0d, 0x41, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x49, 0xb3, 0x6c, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x4a, 0xed, 0x23, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x9c, 0x89, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0xd6, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x4d, 0x7c, 0x6b, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb6, 0x22, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x5c, 0x4d, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x50, 0x96, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3c, 0x2f, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x52, 0x75, 0xe6, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x53, 0x1c, 0x11, 0x70, 0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0xc8, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x54, 0xfb, 0xf3, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x56, 0x35, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x00, 0x56, 0xe5, 0x0f, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x58, 0x1e, 0xc6, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x58, 0xc4, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x59, 0xfe, 0xa8, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x5a, 0xa4, 0xd3, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x5b, 0xde, 0x8a, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x84, 0xb5, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbe, 0x6c, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x64, 0x97, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9e, 0x4e, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x60, 0x4d, 0xb4, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x87, 0x6b, 0x60, 0x00, 0x00, 0x00, 0x00, 0x62, 0x2d, 0x96, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x4d, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x64, 0x0d, 0x78, 0x70, 0x00, 0x00, 0x00, 0x00, 0x65, 0x47, 0x2f, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x65, 0xed, 0x5a, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x27, 0x11, 0x60, 0x00, 0x00, 0x00, 0x00, 0x67, 0xcd, 0x3c, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x69, 0x06, 0xf3, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x69, 0xad, 0x1e, 0x70, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe6, 0xd5, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x6b, 0x96, 0x3a, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x6c, 0xcf, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x76, 0x1c, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x6e, 0xaf, 0xd3, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x6f, 0x55, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x8f, 0xb5, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x71, 0x35, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x72, 0x6f, 0x97, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x73, 0x15, 0xc2, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x74, 0x4f, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x74, 0xfe, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00, 0x76, 0x38, 0x96, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x76, 0xde, 0xc1, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x18, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x78, 0xbe, 0xa3, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x79, 0xf8, 0x5a, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x7a, 0x9e, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xd8, 0x3c, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0x67, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0xb8, 0x1e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x5e, 0x49, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x98, 0x00, 0x60, 0x00, 0x02, 0x01, 0x02, - 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, - 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, - 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, - 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0xff, 0xff, 0xff, 0xff, + 0x5e, 0x03, 0xf0, 0x90, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xa6, 0x1e, 0x70, + 0xff, 0xff, 0xff, 0xff, 0x9f, 0xba, 0xeb, 0x60, 0xff, 0xff, 0xff, 0xff, + 0xa0, 0x86, 0x00, 0x70, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x9a, 0xcd, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xa2, 0x65, 0xe2, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xa3, 0x83, 0xe9, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xa4, 0x6a, 0xae, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xa5, 0x35, 0xa7, 0x60, 0xff, 0xff, 0xff, 0xff, + 0xa6, 0x53, 0xca, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x15, 0x89, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xa8, 0x33, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xa8, 0xfe, 0xa5, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xaa, 0x13, 0x8e, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xaa, 0xde, 0x87, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xab, 0xf3, 0x70, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xac, 0xbe, 0x69, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xad, 0xd3, 0x52, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xae, 0x9e, 0x4b, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xaf, 0xb3, 0x34, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xb0, 0x7e, 0x2d, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xb1, 0x9c, 0x51, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb2, 0x67, 0x4a, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xb3, 0x7c, 0x33, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xb4, 0x47, 0x2c, 0x60, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x5c, 0x15, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xb6, 0x27, 0x0e, 0x60, 0xff, 0xff, 0xff, 0xff, + 0xb7, 0x3b, 0xf7, 0x70, 0xff, 0xff, 0xff, 0xff, 0xb8, 0x06, 0xf0, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xb9, 0x1b, 0xd9, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xb9, 0xe6, 0xd2, 0x60, 0xff, 0xff, 0xff, 0xff, 0xbb, 0x04, 0xf5, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xbb, 0xc6, 0xb4, 0x60, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xe4, 0xd7, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xbd, 0xaf, 0xd0, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xbe, 0xc4, 0xb9, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xbf, 0x8f, 0xb2, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xa4, 0x9b, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xc1, 0x6f, 0x94, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xc2, 0x84, 0x7d, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x4f, 0x76, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xc4, 0x64, 0x5f, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xc5, 0x2f, 0x58, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xc6, 0x4d, 0x7c, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xc7, 0x0f, 0x3a, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0x2d, 0x5e, 0x70, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xf8, 0x57, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xca, 0x0d, 0x40, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xca, 0xd8, 0x39, 0x60, 0xff, 0xff, 0xff, 0xff, 0xcb, 0x88, 0xf0, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xd2, 0x23, 0xf4, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xd2, 0x60, 0xfb, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd3, 0x75, 0xe4, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xd4, 0x40, 0xdd, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xd5, 0x55, 0xc6, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xd6, 0x20, 0xbf, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xd7, 0x35, 0xa8, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xd8, 0x00, 0xa1, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xd9, 0x15, 0x8a, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xd9, 0xe0, 0x83, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xda, 0xfe, 0xa7, 0x70, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xc0, 0x65, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xdc, 0xde, 0x89, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xdd, 0xa9, 0x82, 0x60, 0xff, 0xff, 0xff, 0xff, 0xde, 0xbe, 0x6b, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xdf, 0x89, 0x64, 0x60, 0xff, 0xff, 0xff, 0xff, + 0xe0, 0x9e, 0x4d, 0x70, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x69, 0x46, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xe2, 0x7e, 0x2f, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xe3, 0x49, 0x28, 0x60, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x5e, 0x11, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xe5, 0x57, 0x2e, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xe6, 0x47, 0x2d, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x37, 0x10, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xe8, 0x27, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xe9, 0x16, 0xf2, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xea, 0x06, 0xf1, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xea, 0xf6, 0xd4, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xeb, 0xe6, 0xd3, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xec, 0xd6, 0xb6, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xed, 0xc6, 0xb5, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xee, 0xbf, 0xd3, 0x60, 0xff, 0xff, 0xff, 0xff, 0xef, 0xaf, 0xd2, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x9f, 0xb5, 0x60, 0xff, 0xff, 0xff, 0xff, + 0xf1, 0x8f, 0xb4, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf2, 0x7f, 0x97, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xf3, 0x6f, 0x96, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xf4, 0x5f, 0x79, 0x60, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x4f, 0x78, 0x70, + 0xff, 0xff, 0xff, 0xff, 0xf6, 0x3f, 0x5b, 0x60, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0x2f, 0x5a, 0x70, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x28, 0x77, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xf9, 0x0f, 0x3c, 0x70, 0xff, 0xff, 0xff, 0xff, + 0xfa, 0x08, 0x59, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xf8, 0x58, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xfb, 0xe8, 0x3b, 0xe0, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xd8, 0x3a, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xc8, 0x1d, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xb8, 0x1c, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xa7, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xfe, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x87, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x77, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0xfe, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0xfd, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x50, 0xe0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x06, 0x40, 0xdf, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x30, 0xc2, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x8d, 0x19, 0x70, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0xa4, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x09, 0xad, 0x94, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0xf0, 0x86, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xe0, 0x85, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0xd9, 0xa2, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x0d, 0xc0, 0x67, 0x70, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xb9, 0x84, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xa9, 0x83, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x99, 0x66, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x11, 0x89, 0x65, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x79, 0x48, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x69, 0x47, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x14, 0x59, 0x2a, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x49, 0x29, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x39, 0x0c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x17, 0x29, 0x0b, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x22, 0x29, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x08, 0xed, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x02, 0x0b, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0xf2, 0x0a, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0xe1, 0xed, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xd1, 0xec, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0xc1, 0xcf, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0xb1, 0xce, 0x70, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xa1, 0xb1, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x76, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x81, 0x93, 0x60, 0x00, 0x00, 0x00, 0x00, 0x22, 0x55, 0xe2, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x6a, 0xaf, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x35, 0xc4, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x25, 0x4a, 0x91, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x26, 0x15, 0xa6, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x27, 0x2a, 0x73, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x27, 0xfe, 0xc3, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x29, 0x0a, 0x55, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x29, 0xde, 0xa5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xea, 0x37, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x2b, 0xbe, 0x87, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0xd3, 0x54, 0x60, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x9e, 0x69, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0xb3, 0x36, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x2f, 0x7e, 0x4b, 0x70, 0x00, 0x00, 0x00, 0x00, 0x30, 0x93, 0x18, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x31, 0x67, 0x67, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x72, 0xfa, 0x60, 0x00, 0x00, 0x00, 0x00, 0x33, 0x47, 0x49, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x35, 0x27, 0x2b, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x36, 0x32, 0xbe, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x37, 0x07, 0x0d, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x1b, 0xda, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x38, 0xe6, 0xef, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x39, 0xfb, 0xbc, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0xc6, 0xd1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xdb, 0x9e, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0xaf, 0xee, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0xbb, 0x80, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x8f, 0xd0, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x9b, 0x62, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x6f, 0xb2, 0x70, 0x00, 0x00, 0x00, 0x00, 0x41, 0x84, 0x7f, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x4f, 0x94, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x64, 0x61, 0x60, 0x00, 0x00, 0x00, 0x00, 0x44, 0x2f, 0x76, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x44, 0x43, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x45, 0xf3, 0xa8, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x47, 0x2d, 0x5f, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x47, 0xd3, 0x8a, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x0d, 0x41, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x49, 0xb3, 0x6c, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x4a, 0xed, 0x23, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x4b, 0x9c, 0x89, 0x70, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xd6, 0x40, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x4d, 0x7c, 0x6b, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x4e, 0xb6, 0x22, 0x60, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x5c, 0x4d, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x96, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x3c, 0x2f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x52, 0x75, 0xe6, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x53, 0x1c, 0x11, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x55, 0xc8, 0x60, 0x00, 0x00, 0x00, 0x00, 0x54, 0xfb, 0xf3, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x56, 0x35, 0xaa, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x56, 0xe5, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x58, 0x1e, 0xc6, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x58, 0xc4, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x59, 0xfe, 0xa8, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xa4, 0xd3, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x5b, 0xde, 0x8a, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x84, 0xb5, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbe, 0x6c, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x64, 0x97, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x9e, 0x4e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x60, 0x4d, 0xb4, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x87, 0x6b, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x2d, 0x96, 0x70, 0x00, 0x00, 0x00, 0x00, 0x63, 0x67, 0x4d, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x0d, 0x78, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x65, 0x47, 0x2f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x65, 0xed, 0x5a, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x67, 0x27, 0x11, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x67, 0xcd, 0x3c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x69, 0x06, 0xf3, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x69, 0xad, 0x1e, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x6a, 0xe6, 0xd5, 0x60, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x96, 0x3a, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xcf, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x76, 0x1c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xaf, 0xd3, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0x55, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x8f, 0xb5, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x71, 0x35, 0xe0, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x97, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x15, 0xc2, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x74, 0x4f, 0x79, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x74, 0xfe, 0xdf, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x76, 0x38, 0x96, 0x60, 0x00, 0x00, 0x00, 0x00, 0x76, 0xde, 0xc1, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x78, 0xbe, 0xa3, 0x70, 0x00, 0x00, 0x00, 0x00, 0x79, 0xf8, 0x5a, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x9e, 0x85, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x7b, 0xd8, 0x3c, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0x67, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0xb8, 0x1e, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x5e, 0x49, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x98, 0x00, 0x60, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, - 0x02, 0x01, 0x02, 0x01, 0x02, 0xff, 0xff, 0xba, 0x9e, 0x00, 0x00, 0xff, - 0xff, 0xc7, 0xc0, 0x01, 0x04, 0xff, 0xff, 0xb9, 0xb0, 0x00, 0x08, 0xff, - 0xff, 0xc7, 0xc0, 0x01, 0x0c, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x10, 0x4c, - 0x4d, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, - 0x57, 0x54, 0x00, 0x45, 0x50, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x45, 0x53, 0x54, 0x35, 0x45, 0x44, - 0x54, 0x2c, 0x4d, 0x33, 0x2e, 0x32, 0x2e, 0x30, 0x2c, 0x4d, 0x31, 0x31, - 0x2e, 0x31, 0x2e, 0x30, 0x0a + 0x02, 0x03, 0x04, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xff, 0xff, 0xba, 0x9e, + 0x00, 0x00, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x04, 0xff, 0xff, 0xb9, 0xb0, + 0x00, 0x08, 0xff, 0xff, 0xc7, 0xc0, 0x01, 0x0c, 0xff, 0xff, 0xc7, 0xc0, + 0x01, 0x10, 0x4c, 0x4d, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53, + 0x54, 0x00, 0x45, 0x57, 0x54, 0x00, 0x45, 0x50, 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x45, 0x53, 0x54, + 0x35, 0x45, 0x44, 0x54, 0x2c, 0x4d, 0x33, 0x2e, 0x32, 0x2e, 0x30, 0x2c, + 0x4d, 0x31, 0x31, 0x2e, 0x31, 0x2e, 0x30, 0x0a }; -unsigned int America_New_York_len = 3545; +unsigned int America_New_York_len = 3536; unsigned char Australia_Sydney[] = { 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00, 0x00, - 0x9c, 0x4e, 0xa6, 0x9c, 0x9c, 0xbc, 0x20, 0xf0, 0xcb, 0x54, 0xb3, 0x00, - 0xcb, 0xc7, 0x57, 0x70, 0xcc, 0xb7, 0x56, 0x80, 0xcd, 0xa7, 0x39, 0x70, - 0xce, 0xa0, 0x73, 0x00, 0xcf, 0x87, 0x1b, 0x70, 0x03, 0x70, 0x39, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00, 0x00, + 0x9c, 0x4e, 0xc2, 0x80, 0x9c, 0xbc, 0x2f, 0x00, 0xcb, 0x54, 0xb3, 0x00, + 0xcb, 0xc7, 0x65, 0x80, 0xcc, 0xb7, 0x56, 0x80, 0xcd, 0xa7, 0x47, 0x80, + 0xce, 0xa0, 0x73, 0x00, 0xcf, 0x87, 0x29, 0x80, 0x03, 0x70, 0x39, 0x80, 0x04, 0x0d, 0x1c, 0x00, 0x05, 0x50, 0x1b, 0x80, 0x05, 0xf6, 0x38, 0x80, 0x07, 0x2f, 0xfd, 0x80, 0x07, 0xd6, 0x1a, 0x80, 0x09, 0x0f, 0xdf, 0x80, 0x09, 0xb5, 0xfc, 0x80, 0x0a, 0xef, 0xc1, 0x80, 0x0b, 0x9f, 0x19, 0x00, @@ -590,140 +588,137 @@ unsigned char Australia_Sydney[] = { 0x77, 0xe9, 0x8f, 0x00, 0x78, 0xd9, 0x80, 0x00, 0x79, 0xc9, 0x71, 0x00, 0x7a, 0xb9, 0x62, 0x00, 0x7b, 0xb2, 0x8d, 0x80, 0x7c, 0xa2, 0x7e, 0x80, 0x7d, 0x92, 0x6f, 0x80, 0x7e, 0x82, 0x60, 0x80, 0x7f, 0x72, 0x51, 0x80, - 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, - 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x00, 0x00, + 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x00, 0x8d, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xb0, 0x01, 0x04, 0x00, 0x00, - 0x8c, 0xa0, 0x00, 0x09, 0x00, 0x00, 0x9a, 0xb0, 0x01, 0x04, 0x00, 0x00, - 0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d, 0x54, 0x00, 0x41, 0x45, 0x44, 0x54, - 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, + 0x8c, 0xa0, 0x00, 0x09, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d, + 0x54, 0x00, 0x41, 0x45, 0x44, 0x54, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0e, - 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x73, 0x16, 0x7f, 0x3c, 0xff, 0xff, 0xff, 0xff, 0x9c, 0x4e, 0xa6, 0x9c, - 0xff, 0xff, 0xff, 0xff, 0x9c, 0xbc, 0x20, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xcb, 0x54, 0xb3, 0x00, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xc7, 0x57, 0x70, - 0xff, 0xff, 0xff, 0xff, 0xcc, 0xb7, 0x56, 0x80, 0xff, 0xff, 0xff, 0xff, - 0xcd, 0xa7, 0x39, 0x70, 0xff, 0xff, 0xff, 0xff, 0xce, 0xa0, 0x73, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xcf, 0x87, 0x1b, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x70, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0d, 0x1c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0x1b, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x05, 0xf6, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x2f, 0xfd, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x07, 0xd6, 0x1a, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x0f, 0xdf, 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0xb5, 0xfc, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0xef, 0xc1, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x9f, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xd8, 0xde, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0d, 0x7e, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0xb8, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x5e, 0xdd, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x98, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x3e, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x78, 0x84, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x1e, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x58, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xfe, 0x83, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x16, 0x38, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x0c, 0x89, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0x21, 0x64, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x18, 0xc7, 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0x01, 0x46, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1a, 0xa7, 0x63, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe1, 0x28, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x87, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xc1, 0x0a, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x79, 0x9c, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x97, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x59, 0x7e, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0xce, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x42, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x69, 0xeb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x24, 0x22, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x49, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xef, 0xea, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x27, 0x29, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x27, 0xcf, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x09, 0x91, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x29, 0xaf, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2a, 0xe9, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x98, 0xca, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd2, 0x8f, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x2d, 0x78, 0xac, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xb2, 0x71, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x2f, 0x58, 0x8e, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x92, 0x53, 0x80, 0x00, 0x00, 0x00, 0x00, 0x31, 0x5d, 0x5a, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x35, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x33, 0x3d, 0x3c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x34, 0x52, 0x17, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x35, 0x1d, 0x1e, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x31, 0xf9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x36, 0xfd, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x38, 0x1b, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0xdc, 0xe2, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0xa7, 0xe9, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0xbc, 0xc4, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0xda, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xa5, 0xe1, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0xba, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x85, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x9a, 0x9e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x83, 0xba, 0x80, 0x00, 0x00, 0x00, 0x00, 0x42, 0x45, 0x87, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x9c, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x44, 0x2e, 0xa3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x45, 0x43, 0x7e, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x23, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x47, 0xf7, 0xa2, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0xe7, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x49, 0xd7, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xc7, 0x75, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4b, 0xb7, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4c, 0xa7, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x97, 0x48, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4e, 0x87, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4f, 0x77, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x70, 0x55, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x60, 0x46, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x50, 0x37, 0x80, 0x00, 0x00, 0x00, 0x00, 0x53, 0x40, 0x28, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x30, 0x19, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x20, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0x0f, 0xfb, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x56, 0xff, 0xec, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x57, 0xef, 0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x58, 0xdf, 0xce, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x59, 0xcf, 0xbf, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x5a, 0xbf, 0xb0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5b, 0xb8, 0xdc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5c, 0xa8, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5d, 0x98, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x88, 0xaf, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x78, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x60, 0x68, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x58, 0x82, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x62, 0x48, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x63, 0x38, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x28, 0x55, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x65, 0x18, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x11, 0x71, 0x80, 0x00, 0x00, 0x00, 0x00, 0x67, 0x01, 0x62, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x67, 0xf1, 0x53, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x68, 0xe1, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00, 0x69, 0xd1, 0x35, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x6a, 0xc1, 0x26, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x6b, 0xb1, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xa1, 0x08, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x6d, 0x90, 0xf9, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x6e, 0x80, 0xea, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x70, 0xdb, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x70, 0x6a, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x71, 0x59, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x49, 0xe9, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x73, 0x39, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x74, 0x29, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x19, 0xbc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x09, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x76, 0xf9, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0xe9, 0x8f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0xd9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x79, 0xc9, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0xb9, 0x62, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7b, 0xb2, 0x8d, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x7c, 0xa2, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x92, 0x6f, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x82, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x72, 0x51, 0x80, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, - 0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, - 0x03, 0x04, 0x03, 0x00, 0x00, 0x8d, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x9a, - 0xb0, 0x01, 0x04, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x00, 0x00, 0x9a, - 0xb0, 0x01, 0x04, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d, 0x54, - 0x00, 0x41, 0x45, 0x44, 0x54, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x41, 0x45, - 0x53, 0x54, 0x2d, 0x31, 0x30, 0x41, 0x45, 0x44, 0x54, 0x2c, 0x4d, 0x31, - 0x30, 0x2e, 0x31, 0x2e, 0x30, 0x2c, 0x4d, 0x34, 0x2e, 0x31, 0x2e, 0x30, - 0x2f, 0x33, 0x0a + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, + 0xff, 0xff, 0xff, 0xff, 0x73, 0x16, 0x7f, 0x3c, 0xff, 0xff, 0xff, 0xff, + 0x9c, 0x4e, 0xc2, 0x80, 0xff, 0xff, 0xff, 0xff, 0x9c, 0xbc, 0x2f, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xcb, 0x54, 0xb3, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xcb, 0xc7, 0x65, 0x80, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xb7, 0x56, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xcd, 0xa7, 0x47, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xce, 0xa0, 0x73, 0x00, 0xff, 0xff, 0xff, 0xff, 0xcf, 0x87, 0x29, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x70, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x0d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0x1b, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x05, 0xf6, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x2f, 0xfd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd6, 0x1a, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x0f, 0xdf, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x09, 0xb5, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xef, 0xc1, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x9f, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0xd8, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x7e, 0xfb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0xb8, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x5e, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x98, 0xa2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x3e, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x78, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x1e, 0xa1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x58, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0xfe, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x38, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x0c, 0x89, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x21, 0x64, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0xc7, 0x81, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x46, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x1a, 0xa7, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xe1, 0x28, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x87, 0x45, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0xc1, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x79, 0x9c, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x97, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x59, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00, 0x21, 0x80, 0xce, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x42, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x69, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x22, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x25, 0x49, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x25, 0xef, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x29, 0xaf, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x27, 0xcf, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x29, 0x09, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xaf, 0xae, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2a, 0xe9, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x98, 0xca, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xd2, 0x8f, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x78, 0xac, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0xb2, 0x71, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x58, 0x8e, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x92, 0x53, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x5d, 0x5a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x32, 0x72, 0x35, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x33, 0x3d, 0x3c, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x52, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00, 0x35, 0x1d, 0x1e, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x31, 0xf9, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x36, 0xfd, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1b, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0xdc, 0xe2, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x39, 0xa7, 0xe9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xbc, 0xc4, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0xda, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0xa5, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xba, 0xbc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x85, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x9a, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x65, 0xa5, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x83, 0xba, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x45, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x9c, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x2e, 0xa3, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x43, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00, 0x46, 0x05, 0x4b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x23, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x47, 0xf7, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0xe7, 0x93, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0xd7, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4a, 0xc7, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0xb7, 0x66, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0xa7, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, 0x97, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x87, 0x39, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4f, 0x77, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x70, 0x55, 0x80, 0x00, 0x00, 0x00, 0x00, 0x51, 0x60, 0x46, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x52, 0x50, 0x37, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x40, 0x28, 0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0x30, 0x19, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x20, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x0f, 0xfb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x56, 0xff, 0xec, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x57, 0xef, 0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x58, 0xdf, 0xce, 0x80, 0x00, 0x00, 0x00, 0x00, 0x59, 0xcf, 0xbf, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x5a, 0xbf, 0xb0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x5b, 0xb8, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xa8, 0xcd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x98, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x88, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x78, 0xa0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x68, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x58, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x48, 0x73, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x38, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x28, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x18, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x11, 0x71, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x01, 0x62, 0x80, 0x00, 0x00, 0x00, 0x00, 0x67, 0xf1, 0x53, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x68, 0xe1, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x69, 0xd1, 0x35, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xc1, 0x26, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x6b, 0xb1, 0x17, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0xa1, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x90, 0xf9, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x80, 0xea, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0x70, 0xdb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6a, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x71, 0x59, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x49, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x39, 0xda, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x29, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x75, 0x19, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x09, 0xad, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xf9, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0xe9, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xd9, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x79, 0xc9, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0xb9, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xb2, 0x8d, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xa2, 0x7e, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x92, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x82, 0x60, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x72, 0x51, 0x80, 0x03, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x00, 0x8d, 0xc4, 0x00, 0x00, + 0x00, 0x00, 0x9a, 0xb0, 0x01, 0x04, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, + 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x09, 0x4c, 0x4d, 0x54, 0x00, 0x41, 0x45, + 0x44, 0x54, 0x00, 0x41, 0x45, 0x53, 0x54, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x0a, 0x41, 0x45, 0x53, 0x54, 0x2d, 0x31, 0x30, 0x41, 0x45, 0x44, 0x54, + 0x2c, 0x4d, 0x31, 0x30, 0x2e, 0x31, 0x2e, 0x30, 0x2c, 0x4d, 0x34, 0x2e, + 0x31, 0x2e, 0x30, 0x2f, 0x33, 0x0a }; -unsigned int Australia_Sydney_len = 2223; +unsigned int Australia_Sydney_len = 2190; -- cgit v1.2.3 From bf93dba8e2058c64fc1140c9035bbf9bbe20bfdf Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 14 Jun 2022 12:48:22 -0700 Subject: counting_allocator: suppress bogus -Wuse-after-free warning in GCC 12 PiperOrigin-RevId: 454932630 Change-Id: Ifc716552bb0cd7babcaf416fe28462c15b4c7f23 --- absl/container/internal/counting_allocator.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h index 927cf082..66068a5a 100644 --- a/absl/container/internal/counting_allocator.h +++ b/absl/container/internal/counting_allocator.h @@ -80,7 +80,15 @@ class CountingAllocator { template void destroy(U* p) { Allocator allocator; + // Ignore GCC warning bug. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuse-after-free" +#endif AllocatorTraits::destroy(allocator, p); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic pop +#endif if (instance_count_ != nullptr) { *instance_count_ -= 1; } -- cgit v1.2.3 From cfd9476bffc36ebb14d09d17424c0878626b3bb9 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 14 Jun 2022 12:58:49 -0700 Subject: flags/marshalling_test: work around bogus GCC 12 -Wmaybe-uninitialized warning PiperOrigin-RevId: 454934969 Change-Id: Ia4c157133e73ff16390179b37f7a789fc03fa92e --- absl/flags/marshalling_test.cc | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc index 691cd2f1..7b6d2ad5 100644 --- a/absl/flags/marshalling_test.cc +++ b/absl/flags/marshalling_test.cc @@ -933,17 +933,17 @@ TEST(MarshallingTest, TestOptionalDoubleUnparsing) { // -------------------------------------------------------------------- TEST(MarshallingTest, TestOptionalStringUnparsing) { - absl::optional value; + absl::optional strvalue; + EXPECT_EQ(absl::UnparseFlag(strvalue), ""); - EXPECT_EQ(absl::UnparseFlag(value), ""); - value = "asdfg"; - EXPECT_EQ(absl::UnparseFlag(value), "asdfg"); - value = " "; - EXPECT_EQ(absl::UnparseFlag(value), " "); - value = ""; // This is UB to set optional string flag to "" - EXPECT_EQ(absl::UnparseFlag(value), ""); - value = absl::nullopt; - EXPECT_EQ(absl::UnparseFlag(value), ""); + strvalue = "asdfg"; + EXPECT_EQ(absl::UnparseFlag(strvalue), "asdfg"); + + strvalue = " "; + EXPECT_EQ(absl::UnparseFlag(strvalue), " "); + + strvalue = ""; // It is UB to set an optional string flag to "" + EXPECT_EQ(absl::UnparseFlag(strvalue), ""); } // -------------------------------------------------------------------- @@ -952,18 +952,22 @@ TEST(MarshallingTest, TestOptionalStringUnparsing) { TEST(MarshallingTest, TestStdOptionalUnparsing) { std::optional strvalue; - EXPECT_EQ(absl::UnparseFlag(strvalue), ""); + strvalue = "asdfg"; EXPECT_EQ(absl::UnparseFlag(strvalue), "asdfg"); - strvalue = std::nullopt; + + strvalue = " "; + EXPECT_EQ(absl::UnparseFlag(strvalue), " "); + + strvalue = ""; // It is UB to set an optional string flag to "" EXPECT_EQ(absl::UnparseFlag(strvalue), ""); - std::optional intvalue(10); + std::optional intvalue; + EXPECT_EQ(absl::UnparseFlag(intvalue), ""); + intvalue = 10; EXPECT_EQ(absl::UnparseFlag(intvalue), "10"); - intvalue = std::nullopt; - EXPECT_EQ(absl::UnparseFlag(intvalue), ""); } // -------------------------------------------------------------------- -- cgit v1.2.3 From 10ec11de9c169c5a8ae6d29a09beae6b0d624e91 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 14 Jun 2022 13:10:59 -0700 Subject: algorithm_test: suppress bogus -Wnonnull warning in GCC 12 Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105705 PiperOrigin-RevId: 454937873 Change-Id: I44f940caf36d83714af765f01cc43c5143fe21c3 --- absl/algorithm/BUILD.bazel | 1 + absl/algorithm/CMakeLists.txt | 1 + absl/algorithm/algorithm_test.cc | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index afc52639..f6d74714 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel @@ -43,6 +43,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":algorithm", + "//absl/base:config", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index 609d8589..181b49ca 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt @@ -35,6 +35,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::algorithm + absl::config GTest::gmock_main ) diff --git a/absl/algorithm/algorithm_test.cc b/absl/algorithm/algorithm_test.cc index 81fccb61..d18df024 100644 --- a/absl/algorithm/algorithm_test.cc +++ b/absl/algorithm/algorithm_test.cc @@ -20,6 +20,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" namespace { @@ -50,7 +51,15 @@ TEST(EqualTest, EmptyRange) { std::vector empty1; std::vector empty2; + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105705 +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull" +#endif EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), empty1.begin(), empty1.end())); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic pop +#endif EXPECT_FALSE(absl::equal(empty1.begin(), empty1.end(), v1.begin(), v1.end())); EXPECT_TRUE( absl::equal(empty1.begin(), empty1.end(), empty2.begin(), empty2.end())); -- cgit v1.2.3 From d66e0dc6cc3c0ce647a9c238aa30d7b867f87317 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 14 Jun 2022 13:51:01 -0700 Subject: absl::Optional: suppress bogus -Wmaybe-uninitialized GCC 12 warning PiperOrigin-RevId: 454947125 Change-Id: Idbe6c8b4953c3d6147326bebc915d24dff83e7d5 --- absl/types/internal/optional.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/absl/types/internal/optional.h b/absl/types/internal/optional.h index 92932b60..6ed0c669 100644 --- a/absl/types/internal/optional.h +++ b/absl/types/internal/optional.h @@ -91,7 +91,15 @@ class optional_data_dtor_base { void destruct() noexcept { if (engaged_) { + // `data_` must be initialized if `engaged_` is true. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif data_.~T(); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic pop +#endif engaged_ = false; } } -- cgit v1.2.3 From ae228edc168d9300bb49f72b1388166691fbf20a Mon Sep 17 00:00:00 2001 From: Laramie Leavitt Date: Tue, 14 Jun 2022 15:24:13 -0700 Subject: Make absl::StdSeedSeq an alias for std::seed_seq PiperOrigin-RevId: 454969441 Change-Id: Ic18d91243ed3aa661ebf47a4c0ea96057aa4092d --- absl/random/BUILD.bazel | 3 +-- absl/random/CMakeLists.txt | 2 +- absl/random/seed_sequences.h | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index d983685c..08ecd197 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -100,8 +100,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":seed_gen_exception", - "//absl/container:inlined_vector", - "//absl/random/internal:nonsecure_base", + "//absl/base:config", "//absl/random/internal:pool_urbg", "//absl/random/internal:salted_seed_seq", "//absl/random/internal:seed_material", diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index c3f0b450..d04c7081 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -223,8 +223,8 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config absl::inlined_vector - absl::random_internal_nonsecure_base absl::random_internal_pool_urbg absl::random_internal_salted_seed_seq absl::random_internal_seed_material diff --git a/absl/random/seed_sequences.h b/absl/random/seed_sequences.h index ff1340cc..c3af4b00 100644 --- a/absl/random/seed_sequences.h +++ b/absl/random/seed_sequences.h @@ -28,6 +28,7 @@ #include #include +#include "absl/base/config.h" #include "absl/random/internal/salted_seed_seq.h" #include "absl/random/internal/seed_material.h" #include "absl/random/seed_gen_exception.h" -- cgit v1.2.3 From 2e36d96669b0f56eaf865245c7bcf8060963a088 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 14 Jun 2022 18:04:19 -0700 Subject: absl::Time: work around bogus GCC 12 -Wrestrict warning Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104336 PiperOrigin-RevId: 455001261 Change-Id: If0c72766a2a1d1e87c19c93c07cf62917031415b --- absl/time/duration.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/absl/time/duration.cc b/absl/time/duration.cc index 4443109a..2bba62da 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -766,13 +766,14 @@ void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) { // is non-zero. // Unlike Go, we format the zero duration as 0, with no unit. std::string FormatDuration(Duration d) { - const Duration min_duration = Seconds(kint64min); - if (d == min_duration) { + constexpr Duration kMinDuration = Seconds(kint64min); + std::string s; + if (d == kMinDuration) { // Avoid needing to negate kint64min by directly returning what the // following code should produce in that case. - return "-2562047788015215h30m8s"; + s = "-2562047788015215h30m8s"; + return s; } - std::string s; if (d < ZeroDuration()) { s.append("-"); d = -d; -- cgit v1.2.3 From a184bab83ffcffc2aaac49a3900361158ab3890f Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 15 Jun 2022 07:42:49 -0700 Subject: any_test: expand the any emplace bug suppression, since it has gotten worse in GCC 12 PiperOrigin-RevId: 455128070 Change-Id: Ia866e59d4e2e810aea16afe492d58013c5661a2b --- absl/types/any_test.cc | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/absl/types/any_test.cc b/absl/types/any_test.cc index 70e4ba22..d382b927 100644 --- a/absl/types/any_test.cc +++ b/absl/types/any_test.cc @@ -754,26 +754,23 @@ TEST(AnyTest, FailedCopy) { // Test the guarantees regarding exceptions in emplace. TEST(AnyTest, FailedEmplace) { - { - BadCopyable bad; - absl::any target; - ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace(bad)); - } + BadCopyable bad; + absl::any target; + ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace(bad)); +} - { - BadCopyable bad; - absl::any target(absl::in_place_type); - ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace(bad)); -#if defined(ABSL_USES_STD_ANY) && defined(__GLIBCXX__) - // libstdc++ std::any::emplace() implementation (as of 7.2) has a bug: if an - // exception is thrown, *this contains a value. -#define ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG 1 -#endif -#if defined(ABSL_HAVE_EXCEPTIONS) && \ - !defined(ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG) - EXPECT_FALSE(target.has_value()); +// GCC and Clang have a bug here. +// Ine some cases, the exception seems to be thrown at the wrong time, and +// target may contain a value. +#ifdef __GNUC__ +TEST(AnyTest, DISABLED_FailedEmplaceInPlace) { +#else +TEST(AnyTest, FailedEmplaceInPlace) { #endif - } + BadCopyable bad; + absl::any target(absl::in_place_type); + ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace(bad)); + EXPECT_FALSE(target.has_value()); } } // namespace -- cgit v1.2.3 From 509f275822e8158bf76a9b24830a8de9d2fdfef2 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 15 Jun 2022 07:53:43 -0700 Subject: explicit_seed_seq_test: work around/disable bogus warnings in GCC 12 PiperOrigin-RevId: 455129922 Change-Id: I3b2a62cbf50057b3ea9b73c2edb44271dc46986c --- absl/random/internal/explicit_seed_seq_test.cc | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/absl/random/internal/explicit_seed_seq_test.cc b/absl/random/internal/explicit_seed_seq_test.cc index f867f610..e36d5fa0 100644 --- a/absl/random/internal/explicit_seed_seq_test.cc +++ b/absl/random/internal/explicit_seed_seq_test.cc @@ -140,10 +140,8 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { ExplicitSeedSeq seq_copy(seq_from_entropy); EXPECT_EQ(seq_copy.size(), seq_from_entropy.size()); - std::vector seeds_1; - seeds_1.resize(1000, 0); - std::vector seeds_2; - seeds_2.resize(1000, 1); + std::vector seeds_1(1000, 0); + std::vector seeds_2(1000, 1); seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); seq_copy.generate(seeds_2.begin(), seeds_2.end()); @@ -157,10 +155,8 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { } ExplicitSeedSeq another_seq(std::begin(entropy), std::end(entropy)); - std::vector seeds_1; - seeds_1.resize(1000, 0); - std::vector seeds_2; - seeds_2.resize(1000, 0); + std::vector seeds_1(1000, 0); + std::vector seeds_2(1000, 0); seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); another_seq.generate(seeds_2.begin(), seeds_2.end()); @@ -169,7 +165,15 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { EXPECT_THAT(seeds_1, Not(Pointwise(Eq(), seeds_2))); // Apply the assignment-operator. + // GCC 12 has a false-positive -Wstringop-overflow warning here. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif another_seq = seq_from_entropy; +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) +#pragma GCC diagnostic pop +#endif // Re-generate seeds. seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); @@ -181,15 +185,13 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { // Move constructor. { // Get seeds from seed-sequence constructed from entropy. - std::vector seeds_1; - seeds_1.resize(1000, 0); + std::vector seeds_1(1000, 0); seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); // Apply move-constructor move the sequence to another instance. absl::random_internal::ExplicitSeedSeq moved_seq( std::move(seq_from_entropy)); - std::vector seeds_2; - seeds_2.resize(1000, 1); + std::vector seeds_2(1000, 1); moved_seq.generate(seeds_2.begin(), seeds_2.end()); // Verify that seeds produced by moved-instance are the same as original. EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2)); -- cgit v1.2.3 From 6b6d40f999f09b1bbc0150e8f57a2c39a93d25e5 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 15 Jun 2022 17:27:41 -0700 Subject: Update GoogleTest version used by Abseil As part of this update, GoogleTest is now using the Abseil flags implementation, and the flags usage_test needs to be modified to pass. If building with bazel and --define=absl=1 to force GoogleTest to use Abseil, a WORKSPACE dependency on the abseil branch of the RE2 project is now required. PiperOrigin-RevId: 455257879 Change-Id: Id1548ce7d6a95b747b72a4f255d31ced98e36006 --- WORKSPACE | 19 ++++++++++++++----- absl/flags/CMakeLists.txt | 2 +- absl/flags/internal/usage_test.cc | 18 +++++++++++++----- ci/cmake_common.sh | 2 +- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 1a1753a8..3c4d397b 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -20,11 +20,20 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( - name = "com_google_googletest", # 2022-01-28T15:27:11Z - sha256 = "eb70a6d4520f940956a6b3e37d205d92736bb104c6a1b2b9f82bfc41bd7a2b34", - strip_prefix = "googletest-28e1da21d8d677bc98f12ccc7fc159ff19e8e817", - # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. - urls = ["https://github.com/google/googletest/archive/28e1da21d8d677bc98f12ccc7fc159ff19e8e817.zip"], + name = "com_google_googletest", # 2022-06-01T21:08:04Z + sha256 = "df6cad4bf17df72d8d86306628701c01a45b9e001c7f2a3b28971c7e24b1035b", + strip_prefix = "googletest-0320f517fd920866d918e564105d68fd4362040a", + urls = ["https://github.com/google/googletest/archive/0320f517fd920866d918e564105d68fd4362040a.zip"], +) + +# RE2 (the regular expression library used by GoogleTest) +# Note this must use a commit from the `abseil` branch of the RE2 project. +# https://github.com/google/re2/tree/abseil +http_archive( + name = "com_googlesource_code_re2", # 2022-04-08 + sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9", + strip_prefix = "re2-a276a8c738735a0fe45a6ee590fe2df69bcf4502", + urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"], ) # Google benchmark. diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 79e51a11..3e9d5adf 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -466,5 +466,5 @@ absl_cc_test( absl::flags_reflection absl::flags_usage absl::strings - GTest::gtest + GTest::gmock ) diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 6a65a1a3..209a7be9 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -20,6 +20,7 @@ #include #include +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/flags/flag.h" #include "absl/flags/internal/parse.h" @@ -105,14 +106,19 @@ class UsageReportingTest : public testing::Test { using UsageReportingDeathTest = UsageReportingTest; TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) { +#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL + // Check for kTestUsageMessage set in main() below. EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage); +#else + // Check for part of the usage message set by GoogleTest. + EXPECT_THAT(absl::ProgramUsageMessage(), + ::testing::HasSubstr( + "This program contains tests written using Google Test")); +#endif -#ifndef _WIN32 - // TODO(rogeeff): figure out why this does not work on Windows. EXPECT_DEATH_IF_SUPPORTED( absl::SetProgramUsageMessage("custom usage message"), - ".*SetProgramUsageMessage\\(\\) called twice.*"); -#endif + ::testing::HasSubstr("SetProgramUsageMessage() called twice")); } // -------------------------------------------------------------------- @@ -489,8 +495,10 @@ path. int main(int argc, char* argv[]) { (void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc flags::SetProgramInvocationName("usage_test"); +#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL + // GoogleTest calls absl::SetProgramUsageMessage() already. absl::SetProgramUsageMessage(kTestUsageMessage); +#endif ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); } diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index 8a93389b..de0e9092 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="28e1da21d8d677bc98f12ccc7fc159ff19e8e817" +readonly ABSL_GOOGLETEST_COMMIT="0320f517fd920866d918e564105d68fd4362040a" # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then -- cgit v1.2.3 From 44050f0d60e8196773f9af693932b668d1877c1b Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Thu, 16 Jun 2022 13:17:21 -0700 Subject: Revert GoogleTest version used by Abseil to commit 28e1da21d8d677bc98f12ccc7fc159ff19e8e817 Using GoogleTest at 0320f517fd920866d918e564105d68fd4362040a breaks Windows DLLs. PiperOrigin-RevId: 455452411 Change-Id: Iff89a01351c01487786a22701efedf25860fadf9 --- WORKSPACE | 19 +++++-------------- absl/flags/CMakeLists.txt | 2 +- absl/flags/internal/usage_test.cc | 18 +++++------------- ci/cmake_common.sh | 2 +- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 3c4d397b..1a1753a8 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -20,20 +20,11 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( - name = "com_google_googletest", # 2022-06-01T21:08:04Z - sha256 = "df6cad4bf17df72d8d86306628701c01a45b9e001c7f2a3b28971c7e24b1035b", - strip_prefix = "googletest-0320f517fd920866d918e564105d68fd4362040a", - urls = ["https://github.com/google/googletest/archive/0320f517fd920866d918e564105d68fd4362040a.zip"], -) - -# RE2 (the regular expression library used by GoogleTest) -# Note this must use a commit from the `abseil` branch of the RE2 project. -# https://github.com/google/re2/tree/abseil -http_archive( - name = "com_googlesource_code_re2", # 2022-04-08 - sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9", - strip_prefix = "re2-a276a8c738735a0fe45a6ee590fe2df69bcf4502", - urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"], + name = "com_google_googletest", # 2022-01-28T15:27:11Z + sha256 = "eb70a6d4520f940956a6b3e37d205d92736bb104c6a1b2b9f82bfc41bd7a2b34", + strip_prefix = "googletest-28e1da21d8d677bc98f12ccc7fc159ff19e8e817", + # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. + urls = ["https://github.com/google/googletest/archive/28e1da21d8d677bc98f12ccc7fc159ff19e8e817.zip"], ) # Google benchmark. diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 3e9d5adf..79e51a11 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -466,5 +466,5 @@ absl_cc_test( absl::flags_reflection absl::flags_usage absl::strings - GTest::gmock + GTest::gtest ) diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 209a7be9..6a65a1a3 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -20,7 +20,6 @@ #include #include -#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/flags/flag.h" #include "absl/flags/internal/parse.h" @@ -106,19 +105,14 @@ class UsageReportingTest : public testing::Test { using UsageReportingDeathTest = UsageReportingTest; TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) { -#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL - // Check for kTestUsageMessage set in main() below. EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage); -#else - // Check for part of the usage message set by GoogleTest. - EXPECT_THAT(absl::ProgramUsageMessage(), - ::testing::HasSubstr( - "This program contains tests written using Google Test")); -#endif +#ifndef _WIN32 + // TODO(rogeeff): figure out why this does not work on Windows. EXPECT_DEATH_IF_SUPPORTED( absl::SetProgramUsageMessage("custom usage message"), - ::testing::HasSubstr("SetProgramUsageMessage() called twice")); + ".*SetProgramUsageMessage\\(\\) called twice.*"); +#endif } // -------------------------------------------------------------------- @@ -495,10 +489,8 @@ path. int main(int argc, char* argv[]) { (void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc flags::SetProgramInvocationName("usage_test"); -#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL - // GoogleTest calls absl::SetProgramUsageMessage() already. absl::SetProgramUsageMessage(kTestUsageMessage); -#endif ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index de0e9092..8a93389b 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="0320f517fd920866d918e564105d68fd4362040a" +readonly ABSL_GOOGLETEST_COMMIT="28e1da21d8d677bc98f12ccc7fc159ff19e8e817" # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then -- cgit v1.2.3 From c3c49150cc9711371c4402ceaa6143e4cc91d9a8 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Thu, 16 Jun 2022 14:09:05 -0700 Subject: absl/base/internal/invoke: Ignore bogus warnings on GCC >= 11 PiperOrigin-RevId: 455463553 Change-Id: Ifa6c238556339dd4e36715ac040ca048f2f84a9a --- absl/base/internal/invoke.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index 018b1af3..643c2a42 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -102,8 +102,18 @@ struct MemFunAndRef : StrippedAccept { static decltype((std::declval().* std::declval())(std::declval()...)) Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) { +// Ignore bogus GCC warnings on this line. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101436 for similar example. +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif return (std::forward(obj).* std::forward(mem_fun))(std::forward(args)...); +#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) +#pragma GCC diagnostic pop +#endif } }; -- cgit v1.2.3 From 53a90f079af7ab491530d432bb318a95371ba877 Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Thu, 16 Jun 2022 14:28:53 -0700 Subject: PR #1197: absl/base/internal/direct_mmap.h: fix musl build on mips Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/1197 Fix the following musl build failure on mips: ``` In file included from /nvmedata/autobuild/instance-15/output-1/build/libabseil-cpp-20211102.0/absl/base/internal/low_level_alloc.cc:26: /nvmedata/autobuild/instance-15/output-1/build/libabseil-cpp-20211102.0/absl/base/internal/direct_mmap.h:49:10: fatal error: sgidefs.h: No such file or directory 49 | #include | ^~~~~~~~~~~ ``` Fixes: - http://autobuild.buildroot.org/results/3fa027e602bacb22316fb5d9b233baa0b0f0e845 Signed-off-by: Fabrice Fontaine Merge c9b5b5c5471213a871f7d6d1d2fc8f6899effbac into a184bab83ffcffc2aaac49a3900361158ab3890f Merging this change closes #1197 COPYBARA_INTEGRATE_REVIEW=https://github.com/abseil/abseil-cpp/pull/1197 from ffontaine:master c9b5b5c5471213a871f7d6d1d2fc8f6899effbac PiperOrigin-RevId: 455467767 Change-Id: I1905f7d70e914288bc1524a52adce3476a779fd8 --- absl/base/internal/direct_mmap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h index a01d6122..e492bb00 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -41,13 +41,13 @@ #ifdef __mips__ // Include definitions of the ABI currently in use. -#ifdef __BIONIC__ +#if defined(__BIONIC__) || !defined(__GLIBC__) // Android doesn't have sgidefs.h, but does have asm/sgidefs.h, which has the // definitions we need. #include #else #include -#endif // __BIONIC__ +#endif // __BIONIC__ || !__GLIBC__ #endif // __mips__ // SYS_mmap and SYS_munmap are not defined in Android. -- cgit v1.2.3 From b7ceff06d2358d09ba967c00f526b6143f207d56 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Thu, 16 Jun 2022 16:42:34 -0700 Subject: Release absl::AnyInvocable AnyInvocable is a C++11 compatible equivalent of the C++23 [std::move_only_function](https://en.cppreference.com/w/cpp/utility/functional/move_only_function/move_only_function). Although this implementation matches an intermediate draft revision of the standard (http://wg21.link/p0288r5), it is neither a standard tracking type nor a seamless backfill type. PiperOrigin-RevId: 455494585 Change-Id: If01565f8eecc78eee38fb794ef142b32b31abc7c --- CMake/AbseilDll.cmake | 3 + absl/functional/BUILD.bazel | 35 + absl/functional/CMakeLists.txt | 36 + absl/functional/any_invocable.h | 313 +++++ absl/functional/any_invocable_test.cc | 1696 ++++++++++++++++++++++++++++ absl/functional/function_type_benchmark.cc | 33 + absl/functional/internal/any_invocable.h | 857 ++++++++++++++ 7 files changed, 2973 insertions(+) create mode 100644 absl/functional/any_invocable.h create mode 100644 absl/functional/any_invocable_test.cc create mode 100644 absl/functional/internal/any_invocable.h diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index f81cb0ab..00cddb84 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -109,9 +109,11 @@ set(ABSL_INTERNAL_DLL_FILES "debugging/internal/symbolize.h" "debugging/internal/vdso_support.cc" "debugging/internal/vdso_support.h" + "functional/any_invocable.h" "functional/internal/front_binder.h" "functional/bind_front.h" "functional/function_ref.h" + "functional/internal/any_invocable.h" "functional/internal/function_ref.h" "hash/hash.h" "hash/internal/city.h" @@ -388,6 +390,7 @@ set(ABSL_INTERNAL_DLL_TARGETS "kernel_timeout_internal" "synchronization" "thread_pool" + "any_invocable" "bind_front" "function_ref" "atomic_hook" diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel index dbfa81f3..c4fbce98 100644 --- a/absl/functional/BUILD.bazel +++ b/absl/functional/BUILD.bazel @@ -25,6 +25,40 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) +cc_library( + name = "any_invocable", + srcs = ["internal/any_invocable.h"], + hdrs = ["any_invocable.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:base_internal", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/utility", + ], +) + +cc_test( + name = "any_invocable_test", + srcs = [ + "any_invocable_test.cc", + "internal/any_invocable.h", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":any_invocable", + "//absl/base:base_internal", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/utility", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "bind_front", srcs = ["internal/front_binder.h"], @@ -86,6 +120,7 @@ cc_test( tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ + ":any_invocable", ":function_ref", "//absl/base:core_headers", "@com_github_google_benchmark//:benchmark_main", diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt index 338ddc6c..c0f6eaaa 100644 --- a/absl/functional/CMakeLists.txt +++ b/absl/functional/CMakeLists.txt @@ -14,6 +14,42 @@ # limitations under the License. # +absl_cc_library( + NAME + any_invocable + SRCS + "internal/any_invocable.h" + HDRS + "any_invocable.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base_internal + absl::config + absl::core_headers + absl::type_traits + absl::utility + PUBLIC +) + +absl_cc_test( + NAME + any_invocable_test + SRCS + "any_invocable_test.cc" + "internal/any_invocable.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::any_invocable + absl::base_internal + absl::config + absl::core_headers + absl::type_traits + absl::utility + GTest::gmock_main +) + absl_cc_library( NAME bind_front diff --git a/absl/functional/any_invocable.h b/absl/functional/any_invocable.h new file mode 100644 index 00000000..0c5faca0 --- /dev/null +++ b/absl/functional/any_invocable.h @@ -0,0 +1,313 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: any_invocable.h +// ----------------------------------------------------------------------------- +// +// This header file defines an `absl::AnyInvocable` type that assumes ownership +// and wraps an object of an invocable type. (Invocable types adhere to the +// concept specified in https://en.cppreference.com/w/cpp/concepts/invocable.) +// +// In general, prefer `absl::AnyInvocable` when you need a type-erased +// function parameter that needs to take ownership of the type. +// +// NOTE: `absl::AnyInvocable` is similar to the C++23 `std::move_only_function` +// abstraction, but has a slightly different API and is not designed to be a +// drop-in replacement or C++11-compatible backfill of that type. + +#ifndef ABSL_FUNCTIONAL_ANY_INVOCABLE_H_ +#define ABSL_FUNCTIONAL_ANY_INVOCABLE_H_ + +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/functional/internal/any_invocable.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// absl::AnyInvocable +// +// `absl::AnyInvocable` is a functional wrapper type, like `std::function`, that +// assumes ownership of an invocable object. Unlike `std::function`, an +// `absl::AnyInvocable` is more type-safe and provides the following additional +// benefits: +// +// * Properly adheres to const correctness of the underlying type +// * Is move-only so avoids concurrency problems with copied invocables and +// unnecessary copies in general. +// * Supports reference qualifiers allowing it to perform unique actions (noted +// below). +// +// `absl::AnyInvocable` is a template, and an `absl::AnyInvocable` instantiation +// may wrap any invocable object with a compatible function signature, e.g. +// having arguments and return types convertible to types matching the +// `absl::AnyInvocable` signature, and also matching any stated reference +// qualifiers, as long as that type is moveable. It therefore provides broad +// type erasure for functional objects. +// +// An `absl::AnyInvocable` is typically used as a type-erased function parameter +// for accepting various functional objects: +// +// // Define a function taking an AnyInvocable parameter. +// void my_func(absl::AnyInvocable f) { +// ... +// }; +// +// // That function can accept any invocable type: +// +// // Accept a function reference. We don't need to move a reference. +// int func1() { return 0; }; +// my_func(func1); +// +// // Accept a lambda. We use std::move here because otherwise my_func would +// // copy the lambda. +// auto lambda = []() { return 0; }; +// my_func(std::move(lambda)); +// +// // Accept a function pointer. We don't need to move a function pointer. +// func2 = &func1; +// my_func(func2); +// +// // Accept an std::function by moving it. Note that the lambda is copyable +// // (satisfying std::function requirements) and moveable (satisfying +// // absl::AnyInvocable requirements). +// std::function func6 = []() { return 0; }; +// my_func(std::move(func6)); +// +// `AnyInvocable` also properly respects `const` qualifiers, reference +// qualifiers, and the `noexcept` specification (only in C++ 17 and beyond) as +// part of the user-specified function type (e.g. +// `AnyInvocable`). These qualifiers will be applied to +// the `AnyInvocable` object's `operator()`, and the underlying invocable must +// be compatible with those qualifiers. +// +// Comparison of const and non-const function types: +// +// // Store a closure inside of `func` with the function type `int()`. +// // Note that we have made `func` itself `const`. +// const AnyInvocable func = [](){ return 0; }; +// +// func(); // Compile-error: the passed type `int()` isn't `const`. +// +// // Store a closure inside of `const_func` with the function type +// // `int() const`. +// // Note that we have also made `const_func` itself `const`. +// const AnyInvocable const_func = [](){ return 0; }; +// +// const_func(); // Fine: `int() const` is `const`. +// +// In the above example, the call `func()` would have compiled if +// `std::function` were used even though the types are not const compatible. +// This is a bug, and using `absl::AnyInvocable` properly detects that bug. +// +// In addition to affecting the signature of `operator()`, the `const` and +// reference qualifiers of the function type also appropriately constrain which +// kinds of invocable objects you are allowed to place into the `AnyInvocable` +// instance. If you specify a function type that is const-qualified, then +// anything that you attempt to put into the `AnyInvocable` must be callable on +// a `const` instance of that type. +// +// Constraint example: +// +// // Fine because the lambda is callable when `const`. +// AnyInvocable func = [=](){ return 0; }; +// +// // This is a compile-error because the lambda isn't callable when `const`. +// AnyInvocable error = [=]() mutable { return 0; }; +// +// An `&&` qualifier can be used to express that an `absl::AnyInvocable` +// instance should be invoked at most once: +// +// // Invokes `continuation` with the logical result of an operation when +// // that operation completes (common in asynchronous code). +// void CallOnCompletion(AnyInvocable continuation) { +// int result_of_foo = foo(); +// +// // `std::move` is required because the `operator()` of `continuation` is +// // rvalue-reference qualified. +// std::move(continuation)(result_of_foo); +// } +// +// Credits to Matt Calabrese (https://github.com/mattcalabrese) for the original +// implementation. +template +class AnyInvocable : private internal_any_invocable::Impl { + private: + static_assert( + std::is_function::value, + "The template argument of AnyInvocable must be a function type."); + + using Impl = internal_any_invocable::Impl; + + public: + // The return type of Sig + using result_type = typename Impl::result_type; + + // Constructors + + // Constructs the `AnyInvocable` in an empty state. + AnyInvocable() noexcept = default; + AnyInvocable(std::nullptr_t) noexcept {} // NOLINT + + // Constructs the `AnyInvocable` from an existing `AnyInvocable` by a move. + // Note that `f` is not guaranteed to be empty after move-construction, + // although it may be. + AnyInvocable(AnyInvocable&& /*f*/) noexcept = default; + + // Constructs an `AnyInvocable` from an invocable object. + // + // Upon construction, `*this` is only empty if `f` is a function pointer or + // member pointer type and is null, or if `f` is an `AnyInvocable` that is + // empty. + template ::value>> + AnyInvocable(F&& f) // NOLINT + : Impl(internal_any_invocable::ConversionConstruct(), + std::forward(f)) {} + + // Constructs an `AnyInvocable` that holds an invocable object of type `T`, + // which is constructed in-place from the given arguments. + // + // Example: + // + // AnyInvocable func( + // absl::in_place_type, arg1, arg2); + // + template ::value>> + explicit AnyInvocable(absl::in_place_type_t, Args&&... args) + : Impl(absl::in_place_type>, + std::forward(args)...) { + static_assert(std::is_same>::value, + "The explicit template argument of in_place_type is required " + "to be an unqualified object type."); + } + + // Overload of the above constructor to support list-initialization. + template &, Args...>::value>> + explicit AnyInvocable(absl::in_place_type_t, + std::initializer_list ilist, Args&&... args) + : Impl(absl::in_place_type>, ilist, + std::forward(args)...) { + static_assert(std::is_same>::value, + "The explicit template argument of in_place_type is required " + "to be an unqualified object type."); + } + + // Assignment Operators + + // Assigns an `AnyInvocable` through move-assignment. + // Note that `f` is not guaranteed to be empty after move-assignment + // although it may be. + AnyInvocable& operator=(AnyInvocable&& /*f*/) noexcept = default; + + // Assigns an `AnyInvocable` from a nullptr, clearing the `AnyInvocable`. If + // not empty, destroys the target, putting `*this` into an empty state. + AnyInvocable& operator=(std::nullptr_t) noexcept { + this->Clear(); + return *this; + } + + // Assigns an `AnyInvocable` from an existing `AnyInvocable` instance. + // + // Upon assignment, `*this` is only empty if `f` is a function pointer or + // member pointer type and is null, or if `f` is an `AnyInvocable` that is + // empty. + template ::value>> + AnyInvocable& operator=(F&& f) { + *this = AnyInvocable(std::forward(f)); + return *this; + } + + // Assigns an `AnyInvocable` from a reference to an invocable object. + // Upon assignment, stores a reference to the invocable object in the + // `AnyInvocable` instance. + template < + class F, + typename = absl::enable_if_t< + internal_any_invocable::CanAssignReferenceWrapper::value>> + AnyInvocable& operator=(std::reference_wrapper f) noexcept { + *this = AnyInvocable(f); + return *this; + } + + // Destructor + + // If not empty, destroys the target. + ~AnyInvocable() = default; + + // absl::AnyInvocable::swap() + // + // Exchanges the targets of `*this` and `other`. + void swap(AnyInvocable& other) noexcept { std::swap(*this, other); } + + // abl::AnyInvocable::operator bool() + // + // Returns `true` if `*this` is not empty. + explicit operator bool() const noexcept { return this->HasValue(); } + + // Invokes the target object of `*this`. `*this` must not be empty. + // + // Note: The signature of this function call operator is the same as the + // template parameter `Sig`. + using Impl::operator(); + + // Equality operators + + // Returns `true` if `*this` is empty. + friend bool operator==(const AnyInvocable& f, std::nullptr_t) noexcept { + return !f.HasValue(); + } + + // Returns `true` if `*this` is empty. + friend bool operator==(std::nullptr_t, const AnyInvocable& f) noexcept { + return !f.HasValue(); + } + + // Returns `false` if `*this` is empty. + friend bool operator!=(const AnyInvocable& f, std::nullptr_t) noexcept { + return f.HasValue(); + } + + // Returns `false` if `*this` is empty. + friend bool operator!=(std::nullptr_t, const AnyInvocable& f) noexcept { + return f.HasValue(); + } + + // swap() + // + // Exchanges the targets of `f1` and `f2`. + friend void swap(AnyInvocable& f1, AnyInvocable& f2) noexcept { f1.swap(f2); } + + private: + // Friending other instantiations is necessary for conversions. + template + friend class internal_any_invocable::CoreImpl; +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_ANY_INVOCABLE_H_ diff --git a/absl/functional/any_invocable_test.cc b/absl/functional/any_invocable_test.cc new file mode 100644 index 00000000..fb5e7792 --- /dev/null +++ b/absl/functional/any_invocable_test.cc @@ -0,0 +1,1696 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/functional/any_invocable.h" + +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +static_assert(absl::internal_any_invocable::kStorageSize >= sizeof(void*), + "These tests assume that the small object storage is at least " + "the size of a pointer."); + +namespace { + +// Helper macro used to avoid spelling `noexcept` in language versions older +// than C++17, where it is not part of the type system, in order to avoid +// compilation failures and internal compiler errors. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) noexcept(noex) +#else +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) +#endif + +// A dummy type we use when passing qualifiers to metafunctions +struct _ {}; + +template +struct Wrapper { + template ::value>> + Wrapper(U&&); // NOLINT +}; + +// This will cause a recursive trait instantiation if the SFINAE checks are +// not ordered correctly for constructibility. +static_assert(std::is_constructible>, + Wrapper>>::value, + ""); + +// A metafunction that takes the cv and l-value reference qualifiers that were +// associated with a function type (here passed via qualifiers of an object +// type), and . +template +struct QualifiersForThisImpl { + static_assert(std::is_object::value, ""); + using type = + absl::conditional_t::value, const This, This>&; +}; + +template +struct QualifiersForThisImpl + : QualifiersForThisImpl {}; + +template +struct QualifiersForThisImpl { + static_assert(std::is_object::value, ""); + using type = + absl::conditional_t::value, const This, This>&&; +}; + +template +using QualifiersForThis = + typename QualifiersForThisImpl::type; + +// A metafunction that takes the cv and l-value reference qualifier of T and +// applies them to U's function type qualifiers. +template +struct GiveQualifiersToFunImpl; + +template +struct GiveQualifiersToFunImpl { + using type = + absl::conditional_t::value, R(P...) const, R(P...)>; +}; + +template +struct GiveQualifiersToFunImpl { + using type = + absl::conditional_t::value, R(P...) const&, R(P...)&>; +}; + +template +struct GiveQualifiersToFunImpl { + using type = + absl::conditional_t::value, R(P...) const&&, R(P...) &&>; +}; + +// If noexcept is a part of the type system, then provide the noexcept forms. +#if defined(__cpp_noexcept_function_type) + +template +struct GiveQualifiersToFunImpl { + using type = absl::conditional_t::value, + R(P...) const noexcept, R(P...) noexcept>; +}; + +template +struct GiveQualifiersToFunImpl { + using type = + absl::conditional_t::value, R(P...) const & noexcept, + R(P...) & noexcept>; +}; + +template +struct GiveQualifiersToFunImpl { + using type = + absl::conditional_t::value, R(P...) const && noexcept, + R(P...) && noexcept>; +}; + +#endif // defined(__cpp_noexcept_function_type) + +template +using GiveQualifiersToFun = typename GiveQualifiersToFunImpl::type; + +// This is used in template parameters to decide whether or not to use a type +// that fits in the small object optimization storage. +enum class ObjSize { small, large }; + +// A base type that is used with classes as a means to insert an +// appropriately-sized dummy datamember when Size is ObjSize::large so that the +// user's class type is guaranteed to not fit in small object storage. +template +struct TypeErasedPadding; + +template <> +struct TypeErasedPadding {}; + +template <> +struct TypeErasedPadding { + char dummy_data[absl::internal_any_invocable::kStorageSize + 1] = {}; +}; + +struct Int { + Int(int v) noexcept : value(v) {} // NOLINT +#ifndef _MSC_VER + Int(Int&&) noexcept { + // NOTE: Prior to C++17, this not being called requires optimizations to + // take place when performing the top-level invocation. In practice, + // most supported compilers perform this optimization prior to C++17. + std::abort(); + } +#else + Int(Int&& v) noexcept = default; +#endif + operator int() && noexcept { return value; } // NOLINT + + int MemberFunctionAdd(int const& b, int c) noexcept { // NOLINT + return value + b + c; + } + + int value; +}; + +enum class Movable { no, yes, nothrow, trivial }; + +enum class NothrowCall { no, yes }; + +enum class Destructible { nothrow, trivial }; + +enum class ObjAlign : std::size_t { + normal = absl::internal_any_invocable::kAlignment, + large = absl::internal_any_invocable::kAlignment * 2, +}; + +// A function-object template that has knobs for each property that can affect +// how the object is stored in AnyInvocable. +template +struct add; + +#define ABSL_INTERNALS_ADD(qual) \ + template \ + struct alignas(static_cast(Alignment)) \ + add : TypeErasedPadding { \ + explicit add(int state_init) : state(state_init) {} \ + explicit add(std::initializer_list state_init, int tail) \ + : state(std::accumulate(std::begin(state_init), std::end(state_init), \ + 0) + \ + tail) {} \ + add(add&& other) = default; /*NOLINT*/ \ + Int operator()(int a, int b, int c) qual \ + ABSL_INTERNAL_NOEXCEPT_SPEC(CallExceptionSpec == NothrowCall::yes) { \ + return state + a + b + c; \ + } \ + int state; \ + }; \ + \ + template \ + struct alignas(static_cast(Alignment)) \ + add : TypeErasedPadding { \ + explicit add(int state_init) : state(state_init) {} \ + explicit add(std::initializer_list state_init, int tail) \ + : state(std::accumulate(std::begin(state_init), std::end(state_init), \ + 0) + \ + tail) {} \ + ~add() noexcept {} \ + add(add&& other) = default; /*NOLINT*/ \ + Int operator()(int a, int b, int c) qual \ + ABSL_INTERNAL_NOEXCEPT_SPEC(CallExceptionSpec == NothrowCall::yes) { \ + return state + a + b + c; \ + } \ + int state; \ + } + +// Explicitly specify an empty argument. +// MSVC (at least up to _MSC_VER 1931, if not beyond) warns that +// ABSL_INTERNALS_ADD() is an undefined zero-arg overload. +#define ABSL_INTERNALS_NOARG +ABSL_INTERNALS_ADD(ABSL_INTERNALS_NOARG); +#undef ABSL_INTERNALS_NOARG + +ABSL_INTERNALS_ADD(const); +ABSL_INTERNALS_ADD(&); +ABSL_INTERNALS_ADD(const&); +ABSL_INTERNALS_ADD(&&); // NOLINT +ABSL_INTERNALS_ADD(const&&); // NOLINT + +#undef ABSL_INTERNALS_ADD + +template +struct add : private add { + using Base = add; + + explicit add(int state_init) : Base(state_init) {} + + explicit add(std::initializer_list state_init, int tail) + : Base(state_init, tail) {} + + add(add&&) = delete; + + using Base::operator(); + using Base::state; +}; + +template +struct add : private add { + using Base = add; + + explicit add(int state_init) : Base(state_init) {} + + explicit add(std::initializer_list state_init, int tail) + : Base(state_init, tail) {} + + add(add&& other) noexcept(false) : Base(other.state) {} // NOLINT + + using Base::operator(); + using Base::state; +}; + +template +struct add : private add { + using Base = add; + + explicit add(int state_init) : Base(state_init) {} + + explicit add(std::initializer_list state_init, int tail) + : Base(state_init, tail) {} + + add(add&& other) noexcept : Base(other.state) {} + + using Base::operator(); + using Base::state; +}; + +// Actual non-member functions rather than function objects +Int add_function(Int&& a, int b, int c) noexcept { return a.value + b + c; } + +Int mult_function(Int&& a, int b, int c) noexcept { return a.value * b * c; } + +Int square_function(Int const&& a) noexcept { return a.value * a.value; } + +template +using AnyInvocable = absl::AnyInvocable; + +// Instantiations of this template contains all of the compile-time parameters +// for a given instantiation of the AnyInvocable test suite. +template +struct TestParams { + static constexpr Movable kMovability = Movability; + static constexpr Destructible kDestructibility = Destructibility; + using Qualifiers = Qual; + static constexpr NothrowCall kCallExceptionSpec = CallExceptionSpec; + static constexpr bool kIsNoexcept = kCallExceptionSpec == NothrowCall::yes; + static constexpr bool kIsRvalueQualified = + std::is_rvalue_reference::value; + static constexpr ObjSize kSize = Size; + static constexpr ObjAlign kAlignment = Alignment; + + // These types are used when testing with member object pointer Invocables + using UnqualifiedUnaryFunType = int(Int const&&) + ABSL_INTERNAL_NOEXCEPT_SPEC(CallExceptionSpec == NothrowCall::yes); + using UnaryFunType = GiveQualifiersToFun; + using MemObjPtrType = int(Int::*); + using UnaryAnyInvType = AnyInvocable; + using UnaryThisParamType = QualifiersForThis; + + template + static UnaryThisParamType ToUnaryThisParam(T&& fun) { + return static_cast(fun); + } + + // This function type intentionally uses 3 "kinds" of parameter types. + // - A user-defined type + // - A reference type + // - A scalar type + // + // These were chosen because internal forwarding takes place on parameters + // differently depending based on type properties (scalars are forwarded by + // value). + using ResultType = Int; + using AnyInvocableFunTypeNotNoexcept = Int(Int, const int&, int); + using UnqualifiedFunType = + typename std::conditional::type; + using FunType = GiveQualifiersToFun; + using MemFunPtrType = + typename std::conditional::type; + using AnyInvType = AnyInvocable; + using AddType = add; + using ThisParamType = QualifiersForThis; + + template + static ThisParamType ToThisParam(T&& fun) { + return static_cast(fun); + } + + // These typedefs are used when testing void return type covariance. + using UnqualifiedVoidFunType = + typename std::conditional::type; + using VoidFunType = GiveQualifiersToFun; + using VoidAnyInvType = AnyInvocable; + using VoidThisParamType = QualifiersForThis; + + template + static VoidThisParamType ToVoidThisParam(T&& fun) { + return static_cast(fun); + } + + using CompatibleAnyInvocableFunType = + absl::conditional_t::value, + GiveQualifiersToFun, + GiveQualifiersToFun>; + + using CompatibleAnyInvType = AnyInvocable; + + using IncompatibleInvocable = + absl::conditional_t::value, + GiveQualifiersToFun<_&, UnqualifiedFunType>(_::*), + GiveQualifiersToFun<_&&, UnqualifiedFunType>(_::*)>; +}; + +// Given a member-pointer type, this metafunction yields the target type of the +// pointer, not including the class-type. It is used to verify that the function +// call operator of AnyInvocable has the proper signature, corresponding to the +// function type that the user provided. +template +struct MemberTypeOfImpl; + +template +struct MemberTypeOfImpl { + using type = T; +}; + +template +using MemberTypeOf = typename MemberTypeOfImpl::type; + +template +struct IsMemberSwappableImpl : std::false_type { + static constexpr bool kIsNothrow = false; +}; + +template +struct IsMemberSwappableImpl< + T, absl::void_t().swap(std::declval()))>> + : std::true_type { + static constexpr bool kIsNothrow = + noexcept(std::declval().swap(std::declval())); +}; + +template +using IsMemberSwappable = IsMemberSwappableImpl; + +template +using IsNothrowMemberSwappable = + std::integral_constant::kIsNothrow>; + +template +class AnyInvTestBasic : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestBasic); + +TYPED_TEST_P(AnyInvTestBasic, DefaultConstruction) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun; + + EXPECT_FALSE(static_cast(fun)); + + EXPECT_TRUE(std::is_nothrow_default_constructible::value); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionNullptr) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun = nullptr; + + EXPECT_FALSE(static_cast(fun)); + + EXPECT_TRUE( + (std::is_nothrow_constructible::value)); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionNullFunctionPtr) { + using AnyInvType = typename TypeParam::AnyInvType; + using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; + + UnqualifiedFunType* const null_fun_ptr = nullptr; + AnyInvType fun = null_fun_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionNullMemberFunctionPtr) { + using AnyInvType = typename TypeParam::AnyInvType; + using MemFunPtrType = typename TypeParam::MemFunPtrType; + + const MemFunPtrType null_mem_fun_ptr = nullptr; + AnyInvType fun = null_mem_fun_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionNullMemberObjectPtr) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + using MemObjPtrType = typename TypeParam::MemObjPtrType; + + const MemObjPtrType null_mem_obj_ptr = nullptr; + UnaryAnyInvType fun = null_mem_obj_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionMemberFunctionPtr) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun = &Int::MemberFunctionAdd; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionMemberObjectPtr) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + + UnaryAnyInvType fun = &Int::value; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13)); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionFunctionReferenceDecay) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun = add_function; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionCompatibleAnyInvocableEmpty) { + using AnyInvType = typename TypeParam::AnyInvType; + using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; + + CompatibleAnyInvType other; + AnyInvType fun = std::move(other); + + EXPECT_FALSE(static_cast(other)); // NOLINT + EXPECT_EQ(other, nullptr); // NOLINT + EXPECT_EQ(nullptr, other); // NOLINT + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, ConstructionCompatibleAnyInvocableNonempty) { + using AnyInvType = typename TypeParam::AnyInvType; + using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; + + CompatibleAnyInvType other = &add_function; + AnyInvType fun = std::move(other); + + EXPECT_FALSE(static_cast(other)); // NOLINT + EXPECT_EQ(other, nullptr); // NOLINT + EXPECT_EQ(nullptr, other); // NOLINT + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestBasic, ConversionToBool) { + using AnyInvType = typename TypeParam::AnyInvType; + + { + AnyInvType fun; + + // This tests contextually-convertible-to-bool. + EXPECT_FALSE(fun ? true : false); // NOLINT + + // Make sure that the conversion is not implicit. + EXPECT_TRUE( + (std::is_nothrow_constructible::value)); + EXPECT_FALSE((std::is_convertible::value)); + } + + { + AnyInvType fun = &add_function; + + // This tests contextually-convertible-to-bool. + EXPECT_TRUE(fun ? true : false); // NOLINT + } +} + +TYPED_TEST_P(AnyInvTestBasic, Invocation) { + using AnyInvType = typename TypeParam::AnyInvType; + + using FunType = typename TypeParam::FunType; + using AnyInvCallType = MemberTypeOf; + + // Make sure the function call operator of AnyInvocable always has the + // type that was specified via the template argument. + EXPECT_TRUE((std::is_same::value)); + + AnyInvType fun = &add_function; + + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceConstruction) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType fun(absl::in_place_type, 5); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceConstructionInitializerList) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType fun(absl::in_place_type, {1, 2, 3, 4}, 5); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(39, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceNullFunPtrConstruction) { + using AnyInvType = typename TypeParam::AnyInvType; + using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; + + AnyInvType fun(absl::in_place_type, nullptr); + + // In-place construction does not lead to empty. + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceNullFunPtrConstructionValueInit) { + using AnyInvType = typename TypeParam::AnyInvType; + using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; + + AnyInvType fun(absl::in_place_type); + + // In-place construction does not lead to empty. + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemFunPtrConstruction) { + using AnyInvType = typename TypeParam::AnyInvType; + using MemFunPtrType = typename TypeParam::MemFunPtrType; + + AnyInvType fun(absl::in_place_type, nullptr); + + // In-place construction does not lead to empty. + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemFunPtrConstructionValueInit) { + using AnyInvType = typename TypeParam::AnyInvType; + using MemFunPtrType = typename TypeParam::MemFunPtrType; + + AnyInvType fun(absl::in_place_type); + + // In-place construction does not lead to empty. + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemObjPtrConstruction) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + using MemObjPtrType = typename TypeParam::MemObjPtrType; + + UnaryAnyInvType fun(absl::in_place_type, nullptr); + + // In-place construction does not lead to empty. + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemObjPtrConstructionValueInit) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + using MemObjPtrType = typename TypeParam::MemObjPtrType; + + UnaryAnyInvType fun(absl::in_place_type); + + // In-place construction does not lead to empty. + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, InPlaceVoidCovarianceConstruction) { + using VoidAnyInvType = typename TypeParam::VoidAnyInvType; + using AddType = typename TypeParam::AddType; + + VoidAnyInvType fun(absl::in_place_type, 5); + + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromEmpty) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType source_fun; + AnyInvType fun(std::move(source_fun)); + + EXPECT_FALSE(static_cast(fun)); + + EXPECT_TRUE(std::is_nothrow_move_constructible::value); +} + +TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromNonEmpty) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType source_fun(absl::in_place_type, 5); + AnyInvType fun(std::move(source_fun)); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(std::is_nothrow_move_constructible::value); +} + +TYPED_TEST_P(AnyInvTestBasic, ComparisonWithNullptrEmpty) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun; + + EXPECT_TRUE(fun == nullptr); + EXPECT_TRUE(nullptr == fun); + + EXPECT_FALSE(fun != nullptr); + EXPECT_FALSE(nullptr != fun); +} + +TYPED_TEST_P(AnyInvTestBasic, ComparisonWithNullptrNonempty) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType fun(absl::in_place_type, 5); + + EXPECT_FALSE(fun == nullptr); + EXPECT_FALSE(nullptr == fun); + + EXPECT_TRUE(fun != nullptr); + EXPECT_TRUE(nullptr != fun); +} + +TYPED_TEST_P(AnyInvTestBasic, ResultType) { + using AnyInvType = typename TypeParam::AnyInvType; + using ExpectedResultType = typename TypeParam::ResultType; + + EXPECT_TRUE((std::is_same::value)); +} + +template +class AnyInvTestCombinatoric : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestCombinatoric); + +TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignEmptyEmptyLhsRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType source_fun; + AnyInvType fun; + + fun = std::move(source_fun); + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignEmptyLhsNonemptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType source_fun(absl::in_place_type, 5); + AnyInvType fun; + + fun = std::move(source_fun); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignNonemptyEmptyLhsRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType source_fun; + AnyInvType fun(absl::in_place_type, 5); + + fun = std::move(source_fun); + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignNonemptyLhsNonemptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType source_fun(absl::in_place_type, 5); + AnyInvType fun(absl::in_place_type, 20); + + fun = std::move(source_fun); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, SelfMoveAssignEmpty) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType source_fun; + source_fun = std::move(source_fun); + + // This space intentionally left blank. +} + +TYPED_TEST_P(AnyInvTestCombinatoric, SelfMoveAssignNonempty) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType source_fun(absl::in_place_type, 5); + source_fun = std::move(source_fun); + + // This space intentionally left blank. +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullptrEmptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun; + fun = nullptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullFunctionPtrEmptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; + + UnqualifiedFunType* const null_fun_ptr = nullptr; + AnyInvType fun; + fun = null_fun_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberFunctionPtrEmptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using MemFunPtrType = typename TypeParam::MemFunPtrType; + + const MemFunPtrType null_mem_fun_ptr = nullptr; + AnyInvType fun; + fun = null_mem_fun_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberObjectPtrEmptyLhs) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + using MemObjPtrType = typename TypeParam::MemObjPtrType; + + const MemObjPtrType null_mem_obj_ptr = nullptr; + UnaryAnyInvType fun; + fun = null_mem_obj_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberFunctionPtrEmptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun; + fun = &Int::MemberFunctionAdd; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberObjectPtrEmptyLhs) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + + UnaryAnyInvType fun; + fun = &Int::value; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignFunctionReferenceDecayEmptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun; + fun = add_function; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, + AssignCompatibleAnyInvocableEmptyLhsEmptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; + + CompatibleAnyInvType other; + AnyInvType fun; + fun = std::move(other); + + EXPECT_FALSE(static_cast(other)); // NOLINT + EXPECT_EQ(other, nullptr); // NOLINT + EXPECT_EQ(nullptr, other); // NOLINT + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, + AssignCompatibleAnyInvocableEmptyLhsNonemptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; + + CompatibleAnyInvType other = &add_function; + AnyInvType fun; + fun = std::move(other); + + EXPECT_FALSE(static_cast(other)); // NOLINT + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullptrNonemptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun = &mult_function; + fun = nullptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullFunctionPtrNonemptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; + + UnqualifiedFunType* const null_fun_ptr = nullptr; + AnyInvType fun = &mult_function; + fun = null_fun_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberFunctionPtrNonemptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using MemFunPtrType = typename TypeParam::MemFunPtrType; + + const MemFunPtrType null_mem_fun_ptr = nullptr; + AnyInvType fun = &mult_function; + fun = null_mem_fun_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberObjectPtrNonemptyLhs) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + using MemObjPtrType = typename TypeParam::MemObjPtrType; + + const MemObjPtrType null_mem_obj_ptr = nullptr; + UnaryAnyInvType fun = &square_function; + fun = null_mem_obj_ptr; + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberFunctionPtrNonemptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun = &mult_function; + fun = &Int::MemberFunctionAdd; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberObjectPtrNonemptyLhs) { + using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; + + UnaryAnyInvType fun = &square_function; + fun = &Int::value; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, AssignFunctionReferenceDecayNonemptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType fun = &mult_function; + fun = add_function; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, + AssignCompatibleAnyInvocableNonemptyLhsEmptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; + + CompatibleAnyInvType other; + AnyInvType fun = &mult_function; + fun = std::move(other); + + EXPECT_FALSE(static_cast(other)); // NOLINT + EXPECT_EQ(other, nullptr); // NOLINT + EXPECT_EQ(nullptr, other); // NOLINT + + EXPECT_FALSE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, + AssignCompatibleAnyInvocableNonemptyLhsNonemptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; + + CompatibleAnyInvType other = &add_function; + AnyInvType fun = &mult_function; + fun = std::move(other); + + EXPECT_FALSE(static_cast(other)); // NOLINT + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); +} + +TYPED_TEST_P(AnyInvTestCombinatoric, SwapEmptyLhsEmptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + + // Swap idiom + { + AnyInvType fun; + AnyInvType other; + + using std::swap; + swap(fun, other); + + EXPECT_FALSE(static_cast(fun)); + EXPECT_FALSE(static_cast(other)); + + EXPECT_TRUE( + absl::type_traits_internal::IsNothrowSwappable::value); + } + + // Member swap + { + AnyInvType fun; + AnyInvType other; + + fun.swap(other); + + EXPECT_FALSE(static_cast(fun)); + EXPECT_FALSE(static_cast(other)); + + EXPECT_TRUE(IsNothrowMemberSwappable::value); + } +} + +TYPED_TEST_P(AnyInvTestCombinatoric, SwapEmptyLhsNonemptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + // Swap idiom + { + AnyInvType fun; + AnyInvType other(absl::in_place_type, 5); + + using std::swap; + swap(fun, other); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_FALSE(static_cast(other)); + + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE( + absl::type_traits_internal::IsNothrowSwappable::value); + } + + // Member swap + { + AnyInvType fun; + AnyInvType other(absl::in_place_type, 5); + + fun.swap(other); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_FALSE(static_cast(other)); + + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(IsNothrowMemberSwappable::value); + } +} + +TYPED_TEST_P(AnyInvTestCombinatoric, SwapNonemptyLhsEmptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + // Swap idiom + { + AnyInvType fun(absl::in_place_type, 5); + AnyInvType other; + + using std::swap; + swap(fun, other); + + EXPECT_FALSE(static_cast(fun)); + EXPECT_TRUE(static_cast(other)); + + EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); + + EXPECT_TRUE( + absl::type_traits_internal::IsNothrowSwappable::value); + } + + // Member swap + { + AnyInvType fun(absl::in_place_type, 5); + AnyInvType other; + + fun.swap(other); + + EXPECT_FALSE(static_cast(fun)); + EXPECT_TRUE(static_cast(other)); + + EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); + + EXPECT_TRUE(IsNothrowMemberSwappable::value); + } +} + +TYPED_TEST_P(AnyInvTestCombinatoric, SwapNonemptyLhsNonemptyRhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + // Swap idiom + { + AnyInvType fun(absl::in_place_type, 5); + AnyInvType other(absl::in_place_type, 6); + + using std::swap; + swap(fun, other); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_TRUE(static_cast(other)); + + EXPECT_EQ(30, TypeParam::ToThisParam(fun)(7, 8, 9).value); + EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); + + EXPECT_TRUE( + absl::type_traits_internal::IsNothrowSwappable::value); + } + + // Member swap + { + AnyInvType fun(absl::in_place_type, 5); + AnyInvType other(absl::in_place_type, 6); + + fun.swap(other); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_TRUE(static_cast(other)); + + EXPECT_EQ(30, TypeParam::ToThisParam(fun)(7, 8, 9).value); + EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); + + EXPECT_TRUE(IsNothrowMemberSwappable::value); + } +} + +template +class AnyInvTestMovable : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestMovable); + +TYPED_TEST_P(AnyInvTestMovable, ConversionConstructionUserDefinedType) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType fun(AddType(5)); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); +} + +TYPED_TEST_P(AnyInvTestMovable, ConversionConstructionVoidCovariance) { + using VoidAnyInvType = typename TypeParam::VoidAnyInvType; + using AddType = typename TypeParam::AddType; + + VoidAnyInvType fun(AddType(5)); + + EXPECT_TRUE(static_cast(fun)); +} + +TYPED_TEST_P(AnyInvTestMovable, ConversionAssignUserDefinedTypeEmptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType fun; + fun = AddType(5); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); +} + +TYPED_TEST_P(AnyInvTestMovable, ConversionAssignUserDefinedTypeNonemptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType fun = &add_function; + fun = AddType(5); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); +} + +TYPED_TEST_P(AnyInvTestMovable, ConversionAssignVoidCovariance) { + using VoidAnyInvType = typename TypeParam::VoidAnyInvType; + using AddType = typename TypeParam::AddType; + + VoidAnyInvType fun; + fun = AddType(5); + + EXPECT_TRUE(static_cast(fun)); +} + +template +class AnyInvTestNoexceptFalse : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestNoexceptFalse); + +TYPED_TEST_P(AnyInvTestNoexceptFalse, ConversionConstructionConstraints) { + using AnyInvType = typename TypeParam::AnyInvType; + + EXPECT_TRUE((std::is_constructible< + AnyInvType, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); + EXPECT_FALSE(( + std::is_constructible::value)); +} + +TYPED_TEST_P(AnyInvTestNoexceptFalse, ConversionAssignConstraints) { + using AnyInvType = typename TypeParam::AnyInvType; + + EXPECT_TRUE((std::is_assignable< + AnyInvType&, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); + EXPECT_FALSE( + (std::is_assignable::value)); +} + +template +class AnyInvTestNoexceptTrue : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue); + +TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionConstructionConstraints) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Noexcept was not part of the type system before C++17."; +#else + using AnyInvType = typename TypeParam::AnyInvType; + +// TODO(b/217761454): Fix this and re-enable for MSVC. +#ifndef _MSC_VER + EXPECT_FALSE((std::is_constructible< + AnyInvType, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); +#endif + EXPECT_FALSE(( + std::is_constructible::value)); +#endif +} + +TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionAssignConstraints) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Noexcept was not part of the type system before C++17."; +#else + using AnyInvType = typename TypeParam::AnyInvType; + +// TODO(b/217761454): Fix this and re-enable for MSVC. +#ifndef _MSC_VER + EXPECT_FALSE((std::is_assignable< + AnyInvType&, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); +#endif + EXPECT_FALSE( + (std::is_assignable::value)); +#endif +} + +template +class AnyInvTestNonRvalue : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestNonRvalue); + +TYPED_TEST_P(AnyInvTestNonRvalue, ConversionConstructionReferenceWrapper) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AddType add(4); + AnyInvType fun = std::ref(add); + add.state = 5; + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); +} + +TYPED_TEST_P(AnyInvTestNonRvalue, NonMoveableResultType) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Copy/move elision was not standard before C++17"; +#else + // Define a result type that cannot be copy- or move-constructed. + struct Result { + int x; + + explicit Result(const int x_in) : x(x_in) {} + Result(Result&&) = delete; + }; + + static_assert(!std::is_move_constructible::value, ""); + static_assert(!std::is_copy_constructible::value, ""); + + // Assumption check: it should nevertheless be possible to use functors that + // return a Result struct according to the language rules. + const auto return_17 = []() noexcept { return Result(17); }; + EXPECT_EQ(17, return_17().x); + + // Just like plain functors, it should work fine to use an AnyInvocable that + // returns the non-moveable type. + using UnqualifiedFun = + absl::conditional_t; + + using Fun = + GiveQualifiersToFun; + + AnyInvocable any_inv(return_17); + EXPECT_EQ(17, any_inv().x); +#endif +} + +TYPED_TEST_P(AnyInvTestNonRvalue, ConversionAssignReferenceWrapperEmptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AddType add(4); + AnyInvType fun; + fun = std::ref(add); + add.state = 5; + EXPECT_TRUE( + (std::is_nothrow_assignable>::value)); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); +} + +TYPED_TEST_P(AnyInvTestNonRvalue, ConversionAssignReferenceWrapperNonemptyLhs) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AddType add(4); + AnyInvType fun = &mult_function; + fun = std::ref(add); + add.state = 5; + EXPECT_TRUE( + (std::is_nothrow_assignable>::value)); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); + + EXPECT_TRUE(static_cast(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); +} + +template +class AnyInvTestRvalue : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestRvalue); + +TYPED_TEST_P(AnyInvTestRvalue, ConversionConstructionReferenceWrapper) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + EXPECT_FALSE(( + std::is_convertible, AnyInvType>::value)); +} + +TYPED_TEST_P(AnyInvTestRvalue, NonMoveableResultType) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Copy/move elision was not standard before C++17"; +#else + // Define a result type that cannot be copy- or move-constructed. + struct Result { + int x; + + explicit Result(const int x_in) : x(x_in) {} + Result(Result&&) = delete; + }; + + static_assert(!std::is_move_constructible::value, ""); + static_assert(!std::is_copy_constructible::value, ""); + + // Assumption check: it should nevertheless be possible to use functors that + // return a Result struct according to the language rules. + const auto return_17 = []() noexcept { return Result(17); }; + EXPECT_EQ(17, return_17().x); + + // Just like plain functors, it should work fine to use an AnyInvocable that + // returns the non-moveable type. + using UnqualifiedFun = + absl::conditional_t; + + using Fun = + GiveQualifiersToFun; + + EXPECT_EQ(17, AnyInvocable(return_17)().x); +#endif +} + +TYPED_TEST_P(AnyInvTestRvalue, ConversionAssignReferenceWrapper) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + EXPECT_FALSE(( + std::is_assignable>::value)); +} + +// NOTE: This test suite originally attempted to enumerate all possible +// combinations of type properties but the build-time started getting too large. +// Instead, it is now assumed that certain parameters are orthogonal and so +// some combinations are elided. + +// A metafunction to form a TypeList of all cv and non-rvalue ref combinations, +// coupled with all of the other explicitly specified parameters. +template +using NonRvalueQualifiedTestParams = ::testing::Types< // + TestParams, // + TestParams, // + TestParams, // + TestParams>; + +// A metafunction to form a TypeList of const and non-const rvalue ref +// qualifiers, coupled with all of the other explicitly specified parameters. +template +using RvalueQualifiedTestParams = ::testing::Types< + TestParams, // + TestParams // + >; + +// All qualifier combinations and a noexcept function type +using TestParameterListNonRvalueQualifiersNothrowCall = + NonRvalueQualifiedTestParams; +using TestParameterListRvalueQualifiersNothrowCall = + RvalueQualifiedTestParams; + +// All qualifier combinations and a non-noexcept function type +using TestParameterListNonRvalueQualifiersCallMayThrow = + NonRvalueQualifiedTestParams; +using TestParameterListRvalueQualifiersCallMayThrow = + RvalueQualifiedTestParams; + +// Lists of various cases that should lead to remote storage +using TestParameterListRemoteMovable = ::testing::Types< + // "Normal" aligned types that are large and have trivial destructors + TestParams, // + TestParams, // + TestParams, // + TestParams, // + + // Same as above but with non-trivial destructors + TestParams, // + TestParams, // + TestParams, // + TestParams // + +// Dynamic memory allocation for over-aligned data was introduced in C++17. +// See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r4.html +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + // Types that must use remote storage because of a large alignment. + , + TestParams, // + TestParams, // + TestParams, // + TestParams // +#endif + >; +using TestParameterListRemoteNonMovable = ::testing::Types< + // "Normal" aligned types that are large and have trivial destructors + TestParams, // + TestParams, // + // Same as above but with non-trivial destructors + TestParams, // + TestParams // + >; + +// Parameters that lead to local storage +using TestParameterListLocal = ::testing::Types< + // Types that meet the requirements and have trivial destructors + TestParams, // + TestParams, // + + // Same as above but with non-trivial destructors + TestParams, // + TestParams // + >; + +// All of the tests that are run for every possible combination of types. +REGISTER_TYPED_TEST_SUITE_P( + AnyInvTestBasic, DefaultConstruction, ConstructionNullptr, + ConstructionNullFunctionPtr, ConstructionNullMemberFunctionPtr, + ConstructionNullMemberObjectPtr, ConstructionMemberFunctionPtr, + ConstructionMemberObjectPtr, ConstructionFunctionReferenceDecay, + ConstructionCompatibleAnyInvocableEmpty, + ConstructionCompatibleAnyInvocableNonempty, InPlaceConstruction, + ConversionToBool, Invocation, InPlaceConstructionInitializerList, + InPlaceNullFunPtrConstruction, InPlaceNullFunPtrConstructionValueInit, + InPlaceNullMemFunPtrConstruction, InPlaceNullMemFunPtrConstructionValueInit, + InPlaceNullMemObjPtrConstruction, InPlaceNullMemObjPtrConstructionValueInit, + InPlaceVoidCovarianceConstruction, MoveConstructionFromEmpty, + MoveConstructionFromNonEmpty, ComparisonWithNullptrEmpty, + ComparisonWithNullptrNonempty, ResultType); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestBasic, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestBasic, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestBasic, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestBasic, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestBasic, TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestBasic, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(CallNothrowRvalue, AnyInvTestBasic, + TestParameterListRvalueQualifiersNothrowCall); + +// Tests for functions that take two operands. +REGISTER_TYPED_TEST_SUITE_P( + AnyInvTestCombinatoric, MoveAssignEmptyEmptyLhsRhs, + MoveAssignEmptyLhsNonemptyRhs, MoveAssignNonemptyEmptyLhsRhs, + MoveAssignNonemptyLhsNonemptyRhs, SelfMoveAssignEmpty, + SelfMoveAssignNonempty, AssignNullptrEmptyLhs, + AssignNullFunctionPtrEmptyLhs, AssignNullMemberFunctionPtrEmptyLhs, + AssignNullMemberObjectPtrEmptyLhs, AssignMemberFunctionPtrEmptyLhs, + AssignMemberObjectPtrEmptyLhs, AssignFunctionReferenceDecayEmptyLhs, + AssignCompatibleAnyInvocableEmptyLhsEmptyRhs, + AssignCompatibleAnyInvocableEmptyLhsNonemptyRhs, AssignNullptrNonemptyLhs, + AssignNullFunctionPtrNonemptyLhs, AssignNullMemberFunctionPtrNonemptyLhs, + AssignNullMemberObjectPtrNonemptyLhs, AssignMemberFunctionPtrNonemptyLhs, + AssignMemberObjectPtrNonemptyLhs, AssignFunctionReferenceDecayNonemptyLhs, + AssignCompatibleAnyInvocableNonemptyLhsEmptyRhs, + AssignCompatibleAnyInvocableNonemptyLhsNonemptyRhs, SwapEmptyLhsEmptyRhs, + SwapEmptyLhsNonemptyRhs, SwapNonemptyLhsEmptyRhs, + SwapNonemptyLhsNonemptyRhs); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestCombinatoric, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestCombinatoric, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestCombinatoric, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestCombinatoric, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestCombinatoric, + TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestCombinatoric, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestCombinatoric, + TestParameterListRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestMovable, + ConversionConstructionUserDefinedType, + ConversionConstructionVoidCovariance, + ConversionAssignUserDefinedTypeEmptyLhs, + ConversionAssignUserDefinedTypeNonemptyLhs, + ConversionAssignVoidCovariance); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestMovable, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestMovable, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestMovable, + TestParameterListRemoteMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestMovable, + TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestMovable, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestMovable, + TestParameterListRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptFalse, + ConversionConstructionConstraints, + ConversionAssignConstraints); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestNoexceptFalse, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestNoexceptFalse, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestNoexceptFalse, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestNoexceptFalse, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestNoexceptFalse, + TestParameterListLocal); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue, + ConversionConstructionConstraints, + ConversionAssignConstraints); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestNoexceptTrue, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestNoexceptTrue, + TestParameterListRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNonRvalue, + ConversionConstructionReferenceWrapper, + NonMoveableResultType, + ConversionAssignReferenceWrapperEmptyLhs, + ConversionAssignReferenceWrapperNonemptyLhs); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestNonRvalue, + TestParameterListNonRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestNonRvalue, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestNonRvalue, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestNonRvalue, + TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestNonRvalue, + TestParameterListNonRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestRvalue, + ConversionConstructionReferenceWrapper, + NonMoveableResultType, + ConversionAssignReferenceWrapper); + +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestRvalue, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(CallNothrowRvalue, AnyInvTestRvalue, + TestParameterListRvalueQualifiersNothrowCall); + +// Minimal SFINAE testing for platforms where we can't run the tests, but we can +// build binaries for. +static_assert( + std::is_convertible>::value, ""); +static_assert(!std::is_convertible>::value, + ""); + +#undef ABSL_INTERNAL_NOEXCEPT_SPEC + +} // namespace diff --git a/absl/functional/function_type_benchmark.cc b/absl/functional/function_type_benchmark.cc index 1b27eebf..03dc31d8 100644 --- a/absl/functional/function_type_benchmark.cc +++ b/absl/functional/function_type_benchmark.cc @@ -18,6 +18,7 @@ #include "benchmark/benchmark.h" #include "absl/base/attributes.h" +#include "absl/functional/any_invocable.h" #include "absl/functional/function_ref.h" namespace absl { @@ -62,6 +63,12 @@ void BM_TrivialFunctionRef(benchmark::State& state) { } BENCHMARK(BM_TrivialFunctionRef); +void BM_TrivialAnyInvocable(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + TrivialFunctor{}); +} +BENCHMARK(BM_TrivialAnyInvocable); + void BM_LargeStdFunction(benchmark::State& state) { ConstructAndCallFunctionBenchmark>(state, LargeFunctor{}); @@ -73,6 +80,13 @@ void BM_LargeFunctionRef(benchmark::State& state) { } BENCHMARK(BM_LargeFunctionRef); + +void BM_LargeAnyInvocable(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + LargeFunctor{}); +} +BENCHMARK(BM_LargeAnyInvocable); + void BM_FunPtrStdFunction(benchmark::State& state) { ConstructAndCallFunctionBenchmark>(state, FreeFunction); } @@ -83,6 +97,11 @@ void BM_FunPtrFunctionRef(benchmark::State& state) { } BENCHMARK(BM_FunPtrFunctionRef); +void BM_FunPtrAnyInvocable(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, FreeFunction); +} +BENCHMARK(BM_FunPtrAnyInvocable); + // Doesn't include construction or copy overhead in the loop. template void CallFunctionBenchmark(benchmark::State& state, const Callable& c, @@ -114,6 +133,12 @@ void BM_TrivialArgsFunctionRef(benchmark::State& state) { } BENCHMARK(BM_TrivialArgsFunctionRef); +void BM_TrivialArgsAnyInvocable(benchmark::State& state) { + CallFunctionBenchmark>( + state, FunctorWithTrivialArgs{}, 1, 2, 3); +} +BENCHMARK(BM_TrivialArgsAnyInvocable); + struct FunctorWithNonTrivialArgs { void operator()(std::string a, std::string b, std::string c) const { benchmark::DoNotOptimize(&a); @@ -138,6 +163,14 @@ void BM_NonTrivialArgsFunctionRef(benchmark::State& state) { } BENCHMARK(BM_NonTrivialArgsFunctionRef); +void BM_NonTrivialArgsAnyInvocable(benchmark::State& state) { + std::string a, b, c; + CallFunctionBenchmark< + AnyInvocable>( + state, FunctorWithNonTrivialArgs{}, a, b, c); +} +BENCHMARK(BM_NonTrivialArgsAnyInvocable); + } // namespace ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/functional/internal/any_invocable.h b/absl/functional/internal/any_invocable.h new file mode 100644 index 00000000..f353139c --- /dev/null +++ b/absl/functional/internal/any_invocable.h @@ -0,0 +1,857 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Implementation details for `absl::AnyInvocable` + +#ifndef ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ +#define ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ + +//////////////////////////////////////////////////////////////////////////////// +// // +// This implementation of the proposed `any_invocable` uses an approach that // +// chooses between local storage and remote storage for the contained target // +// object based on the target object's size, alignment requirements, and // +// whether or not it has a nothrow move constructor. Additional optimizations // +// are performed when the object is a trivially copyable type [basic.types]. // +// // +// There are three datamembers per `AnyInvocable` instance // +// // +// 1) A union containing either // +// - A pointer to the target object referred to via a void*, or // +// - the target object, emplaced into a raw char buffer // +// // +// 2) A function pointer to a "manager" function operation that takes a // +// discriminator and logically branches to either perform a move operation // +// or destroy operation based on that discriminator. // +// // +// 3) A function pointer to an "invoker" function operation that invokes the // +// target object, directly returning the result. // +// // +// When in the logically empty state, the manager function is an empty // +// function and the invoker function is one that would be undefined-behavior // +// to call. // +// // +// An additional optimization is performed when converting from one // +// AnyInvocable to another where only the noexcept specification and/or the // +// cv/ref qualifiers of the function type differ. In these cases, the // +// conversion works by "moving the guts", similar to if they were the same // +// exact type, as opposed to having to perform an additional layer of // +// wrapping through remote storage. // +// // +//////////////////////////////////////////////////////////////////////////////// + +// IWYU pragma: private, include "absl/functional/any_invocable.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/base/internal/invoke.h" +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Helper macro used to prevent spelling `noexcept` in language versions older +// than C++17, where it is not part of the type system, in order to avoid +// compilation failures and internal compiler errors. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) noexcept(noex) +#else +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) +#endif + +// Defined in functional/any_invocable.h +template +class AnyInvocable; + +namespace internal_any_invocable { + +// Constants relating to the small-object-storage for AnyInvocable +enum StorageProperty : std::size_t { + kAlignment = alignof(std::max_align_t), // The alignment of the storage + kStorageSize = sizeof(void*) * 2 // The size of the storage +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction for checking if a type is an AnyInvocable instantiation. +// This is used during conversion operations. +template +struct IsAnyInvocable : std::false_type {}; + +template +struct IsAnyInvocable> : std::true_type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A type trait that tells us whether or not a target function type should be +// stored locally in the small object optimization storage +template +using IsStoredLocally = std::integral_constant< + bool, sizeof(T) <= kStorageSize && alignof(T) <= kAlignment && + kAlignment % alignof(T) == 0 && + std::is_nothrow_move_constructible::value>; + +// An implementation of std::remove_cvref_t of C++20. +template +using RemoveCVRef = + typename std::remove_cv::type>::type; + +//////////////////////////////////////////////////////////////////////////////// +// +// An implementation of the C++ standard INVOKE pseudo-macro, operation is +// equivalent to std::invoke except that it forces an implicit conversion to the +// specified return type. If "R" is void, the function is executed and the +// return value is simply ignored. +template ::value>> +void InvokeR(F&& f, P&&... args) { + absl::base_internal::invoke(std::forward(f), std::forward

(args)...); +} + +template ::value, int> = 0> +ReturnType InvokeR(F&& f, P&&... args) { + return absl::base_internal::invoke(std::forward(f), + std::forward

(args)...); +} + +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// +// A metafunction that takes a "T" corresponding to a parameter type of the +// user's specified function type, and yields the parameter type to use for the +// type-erased invoker. In order to prevent observable moves, this must be +// either a reference or, if the type is trivial, the original parameter type +// itself. Since the parameter type may be incomplete at the point that this +// metafunction is used, we can only do this optimization for scalar types +// rather than for any trivial type. +template +T ForwardImpl(std::true_type); + +template +T&& ForwardImpl(std::false_type); + +// NOTE: We deliberately use an intermediate struct instead of a direct alias, +// as a workaround for b/206991861 on MSVC versions < 1924. +template +struct ForwardedParameter { + using type = decltype(( + ForwardImpl)(std::integral_constant::value>())); +}; + +template +using ForwardedParameterType = typename ForwardedParameter::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// A discriminator when calling the "manager" function that describes operation +// type-erased operation should be invoked. +// +// "relocate_from_to" specifies that the manager should perform a move. +// +// "dispose" specifies that the manager should perform a destroy. +enum class FunctionToCall : bool { relocate_from_to, dispose }; + +// The portion of `AnyInvocable` state that contains either a pointer to the +// target object or the object itself in local storage +union TypeErasedState { + struct { + // A pointer to the type-erased object when remotely stored + void* target; + // The size of the object for `RemoteManagerTrivial` + std::size_t size; + } remote; + + // Local-storage for the type-erased object when small and trivial enough + alignas(kAlignment) char storage[kStorageSize]; +}; + +// A typed accessor for the object in `TypeErasedState` storage +template +T& ObjectInLocalStorage(TypeErasedState* const state) { + // We launder here because the storage may be reused with the same type. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + return *std::launder(reinterpret_cast(&state->storage)); +#elif ABSL_HAVE_BUILTIN(__builtin_launder) + return *__builtin_launder(reinterpret_cast(&state->storage)); +#else + + // When `std::launder` or equivalent are not available, we rely on undefined + // behavior, which works as intended on Abseil's officially supported + // platforms as of Q2 2022. +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#pragma GCC diagnostic push +#endif + return *reinterpret_cast(&state->storage); +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif +} + +// The type for functions issuing lifetime-related operations: move and dispose +// A pointer to such a function is contained in each `AnyInvocable` instance. +// NOTE: When specifying `FunctionToCall::`dispose, the same state must be +// passed as both "from" and "to". +using ManagerType = void(FunctionToCall /*operation*/, + TypeErasedState* /*from*/, TypeErasedState* /*to*/) + ABSL_INTERNAL_NOEXCEPT_SPEC(true); + +// The type for functions issuing the actual invocation of the object +// A pointer to such a function is contained in each AnyInvocable instance. +template +using InvokerType = ReturnType(TypeErasedState*, ForwardedParameterType

...) + ABSL_INTERNAL_NOEXCEPT_SPEC(SigIsNoexcept); + +// The manager that is used when AnyInvocable is empty +inline void EmptyManager(FunctionToCall /*operation*/, + TypeErasedState* /*from*/, + TypeErasedState* /*to*/) noexcept {} + +// The manager that is used when a target function is in local storage and is +// a trivially copyable type. +inline void LocalManagerTrivial(FunctionToCall /*operation*/, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + // This single statement without branching handles both possible operations. + // + // For FunctionToCall::dispose, "from" and "to" point to the same state, and + // so this assignment logically would do nothing. + // + // Note: Correctness here relies on http://wg21.link/p0593, which has only + // become standard in C++20, though implementations do not break it in + // practice for earlier versions of C++. + // + // The correct way to do this without that paper is to first placement-new a + // default-constructed T in "to->storage" prior to the memmove, but doing so + // requires a different function to be created for each T that is stored + // locally, which can cause unnecessary bloat and be less cache friendly. + *to = *from; + + // Note: Because the type is trivially copyable, the destructor does not need + // to be called ("trivially copyable" requires a trivial destructor). +} + +// The manager that is used when a target function is in local storage and is +// not a trivially copyable type. +template +void LocalManagerNontrivial(FunctionToCall operation, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + static_assert(IsStoredLocally::value, + "Local storage must only be used for supported types."); + static_assert(!std::is_trivially_copyable::value, + "Locally stored types must be trivially copyable."); + + T& from_object = (ObjectInLocalStorage)(from); + + switch (operation) { + case FunctionToCall::relocate_from_to: + // NOTE: Requires that the left-hand operand is already empty. + ::new (static_cast(&to->storage)) T(std::move(from_object)); + ABSL_FALLTHROUGH_INTENDED; + case FunctionToCall::dispose: + from_object.~T(); // Must not throw. // NOLINT + return; + } + ABSL_INTERNAL_UNREACHABLE; +} + +// The invoker that is used when a target function is in local storage +// Note: QualTRef here is the target function type along with cv and reference +// qualifiers that must be used when calling the function. +template +ReturnType LocalInvoker( + TypeErasedState* const state, + ForwardedParameterType

... args) noexcept(SigIsNoexcept) { + using RawT = RemoveCVRef; + static_assert( + IsStoredLocally::value, + "Target object must be in local storage in order to be invoked from it."); + + auto& f = (ObjectInLocalStorage)(state); + return (InvokeR)(static_cast(f), + static_cast>(args)...); +} + +// The manager that is used when a target function is in remote storage and it +// has a trivial destructor +inline void RemoteManagerTrivial(FunctionToCall operation, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + switch (operation) { + case FunctionToCall::relocate_from_to: + // NOTE: Requires that the left-hand operand is already empty. + to->remote = from->remote; + return; + case FunctionToCall::dispose: +#if defined(__cpp_sized_deallocation) + ::operator delete(from->remote.target, from->remote.size); +#else // __cpp_sized_deallocation + ::operator delete(from->remote.target); +#endif // __cpp_sized_deallocation + return; + } + ABSL_INTERNAL_UNREACHABLE; +} + +// The manager that is used when a target function is in remote storage and the +// destructor of the type is not trivial +template +void RemoteManagerNontrivial(FunctionToCall operation, + TypeErasedState* const from, + TypeErasedState* const to) noexcept { + static_assert(!IsStoredLocally::value, + "Remote storage must only be used for types that do not " + "qualify for local storage."); + + switch (operation) { + case FunctionToCall::relocate_from_to: + // NOTE: Requires that the left-hand operand is already empty. + to->remote.target = from->remote.target; + return; + case FunctionToCall::dispose: + ::delete static_cast(from->remote.target); // Must not throw. + return; + } + ABSL_INTERNAL_UNREACHABLE; +} + +// The invoker that is used when a target function is in remote storage +template +ReturnType RemoteInvoker( + TypeErasedState* const state, + ForwardedParameterType

... args) noexcept(SigIsNoexcept) { + using RawT = RemoveCVRef; + static_assert(!IsStoredLocally::value, + "Target object must be in remote storage in order to be " + "invoked from it."); + + auto& f = *static_cast(state->remote.target); + return (InvokeR)(static_cast(f), + static_cast>(args)...); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that checks if a type T is an instantiation of +// absl::in_place_type_t (needed for constructor constraints of AnyInvocable). +template +struct IsInPlaceType : std::false_type {}; + +template +struct IsInPlaceType> : std::true_type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A constructor name-tag used with CoreImpl (below) to request the +// conversion-constructor. QualDecayedTRef is the decayed-type of the object to +// wrap, along with the cv and reference qualifiers that must be applied when +// performing an invocation of the wrapped object. +template +struct TypedConversionConstruct {}; + +// A helper base class for all core operations of AnyInvocable. Most notably, +// this class creates the function call operator and constraint-checkers so that +// the top-level class does not have to be a series of partial specializations. +// +// Note: This definition exists (as opposed to being a declaration) so that if +// the user of the top-level template accidentally passes a template argument +// that is not a function type, they will get a static_assert in AnyInvocable's +// class body rather than an error stating that Impl is not defined. +template +class Impl {}; // Note: This is partially-specialized later. + +// A std::unique_ptr deleter that deletes memory allocated via ::operator new. +#if defined(__cpp_sized_deallocation) +class TrivialDeleter { + public: + explicit TrivialDeleter(std::size_t size) : size_(size) {} + + void operator()(void* target) const { + ::operator delete(target, size_); + } + + private: + std::size_t size_; +}; +#else // __cpp_sized_deallocation +class TrivialDeleter { + public: + explicit TrivialDeleter(std::size_t) {} + + void operator()(void* target) const { ::operator delete(target); } +}; +#endif // __cpp_sized_deallocation + +template +class CoreImpl; + +constexpr bool IsCompatibleConversion(void*, void*) { return false; } +template +constexpr bool IsCompatibleConversion(CoreImpl*, + CoreImpl*) { + return !NoExceptDest || NoExceptSrc; +} + +// A helper base class for all core operations of AnyInvocable that do not +// depend on the cv/ref qualifiers of the function type. +template +class CoreImpl { + public: + using result_type = ReturnType; + + CoreImpl() noexcept : manager_(EmptyManager), invoker_(nullptr) {} + + enum class TargetType : int { + kPointer = 0, + kCompatibleAnyInvocable = 1, + kIncompatibleAnyInvocable = 2, + kOther = 3, + }; + + // Note: QualDecayedTRef here includes the cv-ref qualifiers associated with + // the invocation of the Invocable. The unqualified type is the target object + // type to be stored. + template + explicit CoreImpl(TypedConversionConstruct, F&& f) { + using DecayedT = RemoveCVRef; + + constexpr TargetType kTargetType = + (std::is_pointer::value || + std::is_member_pointer::value) + ? TargetType::kPointer + : IsCompatibleAnyInvocable::value + ? TargetType::kCompatibleAnyInvocable + : IsAnyInvocable::value + ? TargetType::kIncompatibleAnyInvocable + : TargetType::kOther; + // NOTE: We only use integers instead of enums as template parameters in + // order to work around a bug on C++14 under MSVC 2017. + // See b/236131881. + Initialize(kTargetType), QualDecayedTRef>( + std::forward(f)); + } + + // Note: QualTRef here includes the cv-ref qualifiers associated with the + // invocation of the Invocable. The unqualified type is the target object + // type to be stored. + template + explicit CoreImpl(absl::in_place_type_t, Args&&... args) { + InitializeStorage(std::forward(args)...); + } + + CoreImpl(CoreImpl&& other) noexcept { + other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_); + manager_ = other.manager_; + invoker_ = other.invoker_; + other.manager_ = EmptyManager; + other.invoker_ = nullptr; + } + + CoreImpl& operator=(CoreImpl&& other) noexcept { + // Put the left-hand operand in an empty state. + // + // Note: A full reset that leaves us with an object that has its invariants + // intact is necessary in order to handle self-move. This is required by + // types that are used with certain operations of the standard library, such + // as the default definition of std::swap when both operands target the same + // object. + Clear(); + + // Perform the actual move/destory operation on the target function. + other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_); + manager_ = other.manager_; + invoker_ = other.invoker_; + other.manager_ = EmptyManager; + other.invoker_ = nullptr; + + return *this; + } + + ~CoreImpl() { manager_(FunctionToCall::dispose, &state_, &state_); } + + // Check whether or not the AnyInvocable is in the empty state. + bool HasValue() const { return invoker_ != nullptr; } + + // Effects: Puts the object into its empty state. + void Clear() { + manager_(FunctionToCall::dispose, &state_, &state_); + manager_ = EmptyManager; + invoker_ = nullptr; + } + + template = 0> + void Initialize(F&& f) { +// This condition handles types that decay into pointers, which includes +// function references. Since function references cannot be null, GCC warns +// against comparing their decayed form with nullptr. +// Since this is template-heavy code, we prefer to disable these warnings +// locally instead of adding yet another overload of this function. +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Waddress" +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#pragma GCC diagnostic push +#endif + if (static_cast>(f) == nullptr) { +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + manager_ = EmptyManager; + invoker_ = nullptr; + return; + } + InitializeStorage(std::forward(f)); + } + + template = 0> + void Initialize(F&& f) { + // In this case we can "steal the guts" of the other AnyInvocable. + f.manager_(FunctionToCall::relocate_from_to, &f.state_, &state_); + manager_ = f.manager_; + invoker_ = f.invoker_; + + f.manager_ = EmptyManager; + f.invoker_ = nullptr; + } + + template = 0> + void Initialize(F&& f) { + if (f.HasValue()) { + InitializeStorage(std::forward(f)); + } else { + manager_ = EmptyManager; + invoker_ = nullptr; + } + } + + template > + void Initialize(F&& f) { + InitializeStorage(std::forward(f)); + } + + // Use local (inline) storage for applicable target object types. + template >::value>> + void InitializeStorage(Args&&... args) { + using RawT = RemoveCVRef; + ::new (static_cast(&state_.storage)) + RawT(std::forward(args)...); + + invoker_ = LocalInvoker; + // We can simplify our manager if we know the type is trivially copyable. + InitializeLocalManager(); + } + + // Use remote storage for target objects that cannot be stored locally. + template >::value, + int> = 0> + void InitializeStorage(Args&&... args) { + InitializeRemoteManager>(std::forward(args)...); + // This is set after everything else in case an exception is thrown in an + // earlier step of the initialization. + invoker_ = RemoteInvoker; + } + + template ::value>> + void InitializeLocalManager() { + manager_ = LocalManagerTrivial; + } + + template ::value, int> = 0> + void InitializeLocalManager() { + manager_ = LocalManagerNontrivial; + } + + template + using HasTrivialRemoteStorage = + std::integral_constant::value && + alignof(T) <= + ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT>; + + template ::value>> + void InitializeRemoteManager(Args&&... args) { + // unique_ptr is used for exception-safety in case construction throws. + std::unique_ptr uninitialized_target( + ::operator new(sizeof(T)), TrivialDeleter(sizeof(T))); + ::new (uninitialized_target.get()) T(std::forward(args)...); + state_.remote.target = uninitialized_target.release(); + state_.remote.size = sizeof(T); + manager_ = RemoteManagerTrivial; + } + + template ::value, int> = 0> + void InitializeRemoteManager(Args&&... args) { + state_.remote.target = ::new T(std::forward(args)...); + manager_ = RemoteManagerNontrivial; + } + + ////////////////////////////////////////////////////////////////////////////// + // + // Type trait to determine if the template argument is an AnyInvocable whose + // function type is compatible enough with ours such that we can + // "move the guts" out of it when moving, rather than having to place a new + // object into remote storage. + + template + struct IsCompatibleAnyInvocable { + static constexpr bool value = false; + }; + + template + struct IsCompatibleAnyInvocable> { + static constexpr bool value = + (IsCompatibleConversion)(static_cast< + typename AnyInvocable::CoreImpl*>( + nullptr), + static_cast(nullptr)); + }; + + // + ////////////////////////////////////////////////////////////////////////////// + + TypeErasedState state_; + ManagerType* manager_; + InvokerType* invoker_; +}; + +// A constructor name-tag used with Impl to request the +// conversion-constructor +struct ConversionConstruct {}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that is normally an identity metafunction except that when +// given a std::reference_wrapper, it yields T&. This is necessary because +// currently std::reference_wrapper's operator() is not conditionally noexcept, +// so when checking if such an Invocable is nothrow-invocable, we must pull out +// the underlying type. +template +struct UnwrapStdReferenceWrapperImpl { + using type = T; +}; + +template +struct UnwrapStdReferenceWrapperImpl> { + using type = T&; +}; + +template +using UnwrapStdReferenceWrapper = + typename UnwrapStdReferenceWrapperImpl::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// An alias that always yields std::true_type (used with constraints) where +// substitution failures happen when forming the template arguments. +template +using True = + std::integral_constant*) != 0>; + +/*SFINAE constraints for the conversion-constructor.*/ +template , AnyInvocable>::value>> +using CanConvert = + True>::value>, + absl::enable_if_t::template CallIsValid::value>, + absl::enable_if_t< + Impl::template CallIsNoexceptIfSigIsNoexcept::value>, + absl::enable_if_t, F>::value>>; + +/*SFINAE constraints for the std::in_place constructors.*/ +template +using CanEmplace = True< + absl::enable_if_t::template CallIsValid::value>, + absl::enable_if_t< + Impl::template CallIsNoexceptIfSigIsNoexcept::value>, + absl::enable_if_t, Args...>::value>>; + +/*SFINAE constraints for the conversion-assign operator.*/ +template , AnyInvocable>::value>> +using CanAssign = + True::template CallIsValid::value>, + absl::enable_if_t< + Impl::template CallIsNoexceptIfSigIsNoexcept::value>, + absl::enable_if_t, F>::value>>; + +/*SFINAE constraints for the reference-wrapper conversion-assign operator.*/ +template +using CanAssignReferenceWrapper = + True::template CallIsValid>::value>, + absl::enable_if_t::template CallIsNoexceptIfSigIsNoexcept< + std::reference_wrapper>::value>>; + +//////////////////////////////////////////////////////////////////////////////// +// +// The constraint for checking whether or not a call meets the noexcept +// callability requirements. This is a preprocessor macro because specifying it +// this way as opposed to a disjunction/branch can improve the user-side error +// messages and avoids an instantiation of std::is_nothrow_invocable_r in the +// cases where the user did not specify a noexcept function type. +// +#define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT(inv_quals, noex) \ + ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_##noex(inv_quals) + +// The disjunction below is because we can't rely on std::is_nothrow_invocable_r +// to give the right result when ReturnType is non-moveable in toolchains that +// don't treat non-moveable result types correctly. For example this was the +// case in libc++ before commit c3a24882 (2022-05). +#define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true(inv_quals) \ + absl::enable_if_t> inv_quals, \ + P...>, \ + std::conjunction< \ + std::is_nothrow_invocable< \ + UnwrapStdReferenceWrapper> inv_quals, P...>, \ + std::is_same< \ + ReturnType, \ + absl::base_internal::invoke_result_t< \ + UnwrapStdReferenceWrapper> inv_quals, \ + P...>>>>::value> + +#define ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false(inv_quals) +// +//////////////////////////////////////////////////////////////////////////////// + +// A macro to generate partial specializations of Impl with the different +// combinations of supported cv/reference qualifiers and noexcept specifier. +// +// Here, `cv` are the cv-qualifiers if any, `ref` is the ref-qualifier if any, +// inv_quals is the reference type to be used when invoking the target, and +// noex is "true" if the function type is noexcept, or false if it is not. +// +// The CallIsValid condition is more complicated than simply using +// absl::base_internal::is_invocable_r because we can't rely on it to give the +// right result when ReturnType is non-moveable in toolchains that don't treat +// non-moveable result types correctly. For example this was the case in libc++ +// before commit c3a24882 (2022-05). +#define ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, noex) \ + template \ + class Impl \ + : public CoreImpl { \ + public: \ + /*The base class, which contains the datamembers and core operations*/ \ + using Core = CoreImpl; \ + \ + /*SFINAE constraint to check if F is invocable with the proper signature*/ \ + template \ + using CallIsValid = True inv_quals, P...>, \ + std::is_same inv_quals, P...>>>::value>>; \ + \ + /*SFINAE constraint to check if F is nothrow-invocable when necessary*/ \ + template \ + using CallIsNoexceptIfSigIsNoexcept = \ + True; \ + \ + /*Put the AnyInvocable into an empty state.*/ \ + Impl() = default; \ + \ + /*The implementation of a conversion-constructor from "f*/ \ + /*This forwards to Core, attaching inv_quals so that the base class*/ \ + /*knows how to properly type-erase the invocation.*/ \ + template \ + explicit Impl(ConversionConstruct, F&& f) \ + : Core(TypedConversionConstruct< \ + typename std::decay::type inv_quals>(), \ + std::forward(f)) {} \ + \ + /*Forward along the in-place construction parameters.*/ \ + template \ + explicit Impl(absl::in_place_type_t, Args&&... args) \ + : Core(absl::in_place_type inv_quals>, \ + std::forward(args)...) {} \ + \ + /*The actual invocation operation with the proper signature*/ \ + ReturnType operator()(P... args) cv ref noexcept(noex) { \ + assert(this->invoker_ != nullptr); \ + return this->invoker_(const_cast(&this->state_), \ + static_cast>(args)...); \ + } \ + } + +// Define the `noexcept(true)` specialization only for C++17 and beyond, when +// `noexcept` is part of the type system. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +// A convenience macro that defines specializations for the noexcept(true) and +// noexcept(false) forms, given the other properties. +#define ABSL_INTERNAL_ANY_INVOCABLE_IMPL(cv, ref, inv_quals) \ + ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false); \ + ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, true) +#else +#define ABSL_INTERNAL_ANY_INVOCABLE_IMPL(cv, ref, inv_quals) \ + ABSL_INTERNAL_ANY_INVOCABLE_IMPL_(cv, ref, inv_quals, false) +#endif + +// Non-ref-qualified partial specializations +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, , &); +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, , const&); + +// Lvalue-ref-qualified partial specializations +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &, &); +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &, const&); + +// Rvalue-ref-qualified partial specializations +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(, &&, &&); +ABSL_INTERNAL_ANY_INVOCABLE_IMPL(const, &&, const&&); + +// Undef the detail-only macros. +#undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL +#undef ABSL_INTERNAL_ANY_INVOCABLE_IMPL_ +#undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_false +#undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT_true +#undef ABSL_INTERNAL_ANY_INVOCABLE_NOEXCEPT_CONSTRAINT +#undef ABSL_INTERNAL_NOEXCEPT_SPEC + +} // namespace internal_any_invocable +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_ANY_INVOCABLE_H_ -- cgit v1.2.3 From d2c5297a3c3948de765100cb7e5cccca1210d23c Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Thu, 16 Jun 2022 18:45:50 -0700 Subject: Update GoogleTest version used by Abseil As part of this update, GoogleTest is now using the Abseil flags implementation, and the flags usage_test needs to be modified to pass. If building with bazel and --define=absl=1 to force GoogleTest to use Abseil, a WORKSPACE dependency on the abseil branch of the RE2 project is now required. PiperOrigin-RevId: 455512245 Change-Id: I2025df0c86006fac97a80713524c9d0aae8b358e --- WORKSPACE | 18 ++++++++++++++---- absl/flags/CMakeLists.txt | 2 +- absl/flags/internal/usage_test.cc | 18 +++++++++++++----- ci/cmake_common.sh | 2 +- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 1a1753a8..4469644d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -20,11 +20,21 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( - name = "com_google_googletest", # 2022-01-28T15:27:11Z - sha256 = "eb70a6d4520f940956a6b3e37d205d92736bb104c6a1b2b9f82bfc41bd7a2b34", - strip_prefix = "googletest-28e1da21d8d677bc98f12ccc7fc159ff19e8e817", + name = "com_google_googletest", # 2022-06-16T20:18:32Z + sha256 = "a1d3123179024258f9c399d45da3e0b09c4aaf8d2c041466ce5b4793a8929f23", + strip_prefix = "googletest-86add13493e5c881d7e4ba77fb91c1f57752b3a4", # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. - urls = ["https://github.com/google/googletest/archive/28e1da21d8d677bc98f12ccc7fc159ff19e8e817.zip"], + urls = ["https://github.com/google/googletest/archive/86add13493e5c881d7e4ba77fb91c1f57752b3a4.zip"], +) + +# RE2 (the regular expression library used by GoogleTest) +# Note this must use a commit from the `abseil` branch of the RE2 project. +# https://github.com/google/re2/tree/abseil +http_archive( + name = "com_googlesource_code_re2", # 2022-04-08 + sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9", + strip_prefix = "re2-a276a8c738735a0fe45a6ee590fe2df69bcf4502", + urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"], ) # Google benchmark. diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index 79e51a11..3e9d5adf 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt @@ -466,5 +466,5 @@ absl_cc_test( absl::flags_reflection absl::flags_usage absl::strings - GTest::gtest + GTest::gmock ) diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc index 6a65a1a3..209a7be9 100644 --- a/absl/flags/internal/usage_test.cc +++ b/absl/flags/internal/usage_test.cc @@ -20,6 +20,7 @@ #include #include +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/flags/flag.h" #include "absl/flags/internal/parse.h" @@ -105,14 +106,19 @@ class UsageReportingTest : public testing::Test { using UsageReportingDeathTest = UsageReportingTest; TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) { +#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL + // Check for kTestUsageMessage set in main() below. EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage); +#else + // Check for part of the usage message set by GoogleTest. + EXPECT_THAT(absl::ProgramUsageMessage(), + ::testing::HasSubstr( + "This program contains tests written using Google Test")); +#endif -#ifndef _WIN32 - // TODO(rogeeff): figure out why this does not work on Windows. EXPECT_DEATH_IF_SUPPORTED( absl::SetProgramUsageMessage("custom usage message"), - ".*SetProgramUsageMessage\\(\\) called twice.*"); -#endif + ::testing::HasSubstr("SetProgramUsageMessage() called twice")); } // -------------------------------------------------------------------- @@ -489,8 +495,10 @@ path. int main(int argc, char* argv[]) { (void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc flags::SetProgramInvocationName("usage_test"); +#if !defined(GTEST_HAS_ABSL) || !GTEST_HAS_ABSL + // GoogleTest calls absl::SetProgramUsageMessage() already. absl::SetProgramUsageMessage(kTestUsageMessage); +#endif ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); } diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index 8a93389b..372038a5 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="28e1da21d8d677bc98f12ccc7fc159ff19e8e817" +readonly ABSL_GOOGLETEST_COMMIT="86add13493e5c881d7e4ba77fb91c1f57752b3a4" # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then -- cgit v1.2.3 From 93ad4284ac12077b7bac07a4743df1c564e7c957 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 21 Jun 2022 08:08:09 -0700 Subject: Update GCC floor container to use Bazel 5.2.0 PiperOrigin-RevId: 456261906 Change-Id: I203199daa1687f54406be4583c2fd400bdbde2cd --- ci/linux_docker_containers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh index 0e05564f..f55e153b 100644 --- a/ci/linux_docker_containers.sh +++ b/ci/linux_docker_containers.sh @@ -18,4 +18,4 @@ readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026" readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220217" readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20220217" -readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20210617" +readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20220621" -- cgit v1.2.3 From 01cc6567cff77738e416a7ddc17de2d435a780ce Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Tue, 21 Jun 2022 12:27:47 -0700 Subject: PR #1200: absl/debugging/CMakeLists.txt: link with libexecinfo if needed Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/1200 `backtrace` and `execinfo.h` can be provided by libexecinfo on uclibc and musl resulting in the following build failure with any user of abseil-cpp (such as collectd): ``` /home/buildroot/autobuild/instance-0/output-1/host/lib/gcc/sparc-buildroot-linux-uclibc/10.3.0/../../../../sparc-buildroot-linux-uclibc/bin/ld: /home/buildroot/autobuild/instance-0/output-1/host/bin/../sparc-buildroot-linux-uclibc/sysroot/usr/lib/libabsl_stacktrace.so: undefined reference to `backtrace' ``` [...] ``` libgrpc++ . . . . . . no (libgrpc++ not found) ``` [...] ``` configure: error: "Some plugins are missing dependencies - see the summary above for details" ``` Fixes: - http://autobuild.buildroot.org/results/6a0484412f020e763ce3ad5bda48f09c78645bff Signed-off-by: Fabrice Fontaine Merge dac52cd20efb672278dc64bcd74920172a599ac0 into 93ad4284ac12077b7bac07a4743df1c564e7c957 Merging this change closes #1200 COPYBARA_INTEGRATE_REVIEW=https://github.com/abseil/abseil-cpp/pull/1200 from ffontaine:master dac52cd20efb672278dc64bcd74920172a599ac0 PiperOrigin-RevId: 456323163 Change-Id: I3d2206761524d36e2b227c571e2e513f6840ff75 --- absl/debugging/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 32952734..d8207d6a 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -14,6 +14,8 @@ # limitations under the License. # +find_library(EXECINFO_LIBRARY execinfo) + absl_cc_library( NAME stacktrace @@ -33,6 +35,8 @@ absl_cc_library( "stacktrace.cc" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + $<$:${EXECINFO_LIBRARY}> DEPS absl::debugging_internal absl::config -- cgit v1.2.3 From 8e27a61883cdfc66a4cfb8482f994de6664e1dd9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 22 Jun 2022 09:14:46 -0700 Subject: Use ABSL_INTERNAL_HAS_SSE2 instead of __SSE2__ This ensures that emmintrin.h is included with clang-cl. Otherwise, errors like this occur: .../absl/container/internal/raw_hash_set.h(536,8): error: unknown type name '__m128i' inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) { This aligns the include ifdef guards and the guards that use the provided APIs. The includes are also reordered, so they appear after the config header which provides the internal macro values. PiperOrigin-RevId: 456530271 Change-Id: I86dfd0022fd06fe7aa132138ec4d1bd14a86ba84 --- absl/container/internal/raw_hash_set.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index cd31b870..ea912f83 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -172,22 +172,6 @@ #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ -#ifdef __SSE2__ -#include -#endif - -#ifdef __SSSE3__ -#include -#endif - -#ifdef _MSC_VER -#include -#endif - -#ifdef __ARM_NEON -#include -#endif - #include #include #include @@ -215,6 +199,22 @@ #include "absl/numeric/bits.h" #include "absl/utility/utility.h" +#ifdef ABSL_INTERNAL_HAVE_SSE2 +#include +#endif + +#ifdef ABSL_INTERNAL_HAVE_SSSE3 +#include +#endif + +#ifdef _MSC_VER +#include +#endif + +#ifdef ABSL_INTERNAL_HAVE_ARM_NEON +#include +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { -- cgit v1.2.3 From e7cbb2acd34b45e469f7a46c3f9da3be1311d8c7 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 22 Jun 2022 10:39:06 -0700 Subject: Update versions of WORKSPACE dependencies PiperOrigin-RevId: 456549823 Change-Id: Iee19597c63a653b8562168367d5860cf8421b6ae --- WORKSPACE | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 4469644d..072fa93d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -31,10 +31,10 @@ http_archive( # Note this must use a commit from the `abseil` branch of the RE2 project. # https://github.com/google/re2/tree/abseil http_archive( - name = "com_googlesource_code_re2", # 2022-04-08 - sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9", - strip_prefix = "re2-a276a8c738735a0fe45a6ee590fe2df69bcf4502", - urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"], + name = "com_googlesource_code_re2", + sha256 = "0a890c2aa0bb05b2ce906a15efb520d0f5ad4c7d37b8db959c43772802991887", + strip_prefix = "re2-a427f10b9fb4622dd6d8643032600aa1b50fbd12", + urls = ["https://github.com/google/re2/archive/a427f10b9fb4622dd6d8643032600aa1b50fbd12.zip"], # 2022-06-09 ) # Google benchmark. @@ -55,7 +55,7 @@ http_archive( # Bazel platform rules. http_archive( name = "platforms", - sha256 = "b601beaf841244de5c5a50d2b2eddd34839788000fa1be4260ce6603ca0d8eb7", - strip_prefix = "platforms-98939346da932eef0b54cf808622f5bb0928f00b", - urls = ["https://github.com/bazelbuild/platforms/archive/98939346da932eef0b54cf808622f5bb0928f00b.zip"], + sha256 = "a879ea428c6d56ab0ec18224f976515948822451473a80d06c2e50af0bbe5121", + strip_prefix = "platforms-da5541f26b7de1dc8e04c075c99df5351742a4a2", + urls = ["https://github.com/bazelbuild/platforms/archive/da5541f26b7de1dc8e04c075c99df5351742a4a2.zip"], # 2022-05-27 ) -- cgit v1.2.3 From 4dc63bade3ef2c099f328f2e08f6d4bf6658f0cf Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 22 Jun 2022 13:55:21 -0700 Subject: Merge contiguous mappings from the same file. This allows symbolization to work if different parts of a binary's text are mapped differently, e.g. if they're mlock()ed or mapped onto huge pages. PiperOrigin-RevId: 456600880 Change-Id: I069264f94cf834df9201968275a00828f5eb077e --- absl/debugging/symbolize_elf.inc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index ddccd590..9bfdd915 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -1146,6 +1146,14 @@ bool Symbolizer::RegisterObjFile(const char *filename, reinterpret_cast(old->end_addr), old->filename); } return true; + } else if (old->end_addr == start_addr && + reinterpret_cast(old->start_addr) - old->offset == + reinterpret_cast(start_addr) - offset && + strcmp(old->filename, filename) == 0) { + // Two contiguous map entries that span a contiguous region of the file, + // perhaps because some part of the file was mlock()ed. Combine them. + old->end_addr = end_addr; + return true; } } ObjFile *obj = impl->addr_map_.Add(); -- cgit v1.2.3 From 273292d1cfc0a94a65082ee350509af1d113344d Mon Sep 17 00:00:00 2001 From: Derek Mauro <761129+derekmauro@users.noreply.github.com> Date: Thu, 23 Jun 2022 14:22:47 -0400 Subject: Apply LTS transformations for 20220623 LTS branch (#1202) --- CMake/AbseilHelpers.cmake | 2 +- CMakeLists.txt | 14 ++------------ WORKSPACE | 8 ++++---- absl/base/config.h | 4 ++-- absl/base/options.h | 4 ++-- ci/cmake_common.sh | 2 +- 6 files changed, 12 insertions(+), 22 deletions(-) diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 6d03381b..dbb09fe2 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -281,7 +281,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n") if(ABSL_ENABLE_INSTALL) set_target_properties(${_NAME} PROPERTIES OUTPUT_NAME "absl_${_NAME}" - SOVERSION 0 + SOVERSION "2206.0.0" ) endif() else() diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a20d04a..79869ff5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ if (POLICY CMP0091) cmake_policy(SET CMP0091 NEW) endif (POLICY CMP0091) -project(absl LANGUAGES CXX) +project(absl LANGUAGES CXX VERSION 20220623) include(CTest) # Output directory is correct by default for most build setups. However, when @@ -172,17 +172,7 @@ endif() add_subdirectory(absl) if(ABSL_ENABLE_INSTALL) - # absl:lts-remove-begin(system installation is supported for LTS releases) - # We don't support system-wide installation - list(APPEND SYSTEM_INSTALL_DIRS "/usr/local" "/usr" "/opt/" "/opt/local" "c:/Program Files/${PROJECT_NAME}") - if(NOT DEFINED CMAKE_INSTALL_PREFIX OR CMAKE_INSTALL_PREFIX IN_LIST SYSTEM_INSTALL_DIRS) - message(WARNING "\ - The default and system-level install directories are unsupported except in LTS \ - releases of Abseil. Please set CMAKE_INSTALL_PREFIX to install Abseil in your \ - source or build tree directly.\ - ") - endif() - # absl:lts-remove-end + # install as a subdirectory only install(EXPORT ${PROJECT_NAME}Targets diff --git a/WORKSPACE b/WORKSPACE index 072fa93d..c332ba4e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -20,11 +20,11 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( - name = "com_google_googletest", # 2022-06-16T20:18:32Z - sha256 = "a1d3123179024258f9c399d45da3e0b09c4aaf8d2c041466ce5b4793a8929f23", - strip_prefix = "googletest-86add13493e5c881d7e4ba77fb91c1f57752b3a4", + name = "com_google_googletest", + sha256 = "ce7366fe57eb49928311189cb0e40e0a8bf3d3682fca89af30d884c25e983786", + strip_prefix = "googletest-release-1.12.0", # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. - urls = ["https://github.com/google/googletest/archive/86add13493e5c881d7e4ba77fb91c1f57752b3a4.zip"], + urls = ["https://github.com/google/googletest/archive/refs/tags/release-1.12.0.zip"], ) # RE2 (the regular expression library used by GoogleTest) diff --git a/absl/base/config.h b/absl/base/config.h index 94f7fcb5..8533aead 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -111,8 +111,8 @@ // // LTS releases can be obtained from // https://github.com/abseil/abseil-cpp/releases. -#undef ABSL_LTS_RELEASE_VERSION -#undef ABSL_LTS_RELEASE_PATCH_LEVEL +#define ABSL_LTS_RELEASE_VERSION 20220623 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 0 // Helper macro to convert a CPP variable to a string literal. #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x diff --git a/absl/base/options.h b/absl/base/options.h index 230bf1ee..bc598470 100644 --- a/absl/base/options.h +++ b/absl/base/options.h @@ -205,8 +205,8 @@ // be changed to a new, unique identifier name. In particular "head" is not // allowed. -#define ABSL_OPTION_USE_INLINE_NAMESPACE 0 -#define ABSL_OPTION_INLINE_NAMESPACE_NAME head +#define ABSL_OPTION_USE_INLINE_NAMESPACE 1 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20220623 // ABSL_OPTION_HARDENED // diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh index 372038a5..3c08255b 100644 --- a/ci/cmake_common.sh +++ b/ci/cmake_common.sh @@ -14,7 +14,7 @@ # The commit of GoogleTest to be used in the CMake tests in this directory. # Keep this in sync with the commit in the WORKSPACE file. -readonly ABSL_GOOGLETEST_COMMIT="86add13493e5c881d7e4ba77fb91c1f57752b3a4" +readonly ABSL_GOOGLETEST_COMMIT="15460959cbbfa20e66ef0b5ab497367e47fc0a04" # release-1.12.0 # Avoid depending on GitHub by looking for a cached copy of the commit first. if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then -- cgit v1.2.3 From 104bbd297d624819b37428882c54eade8b6ab74d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 12 Jul 2022 21:56:23 -0400 Subject: Begin updating packaging for Abseil 20220623 --- debian/changelog | 6 +++ debian/control | 4 +- debian/gbp.conf | 2 +- debian/libabsl20210324.install | 15 ------- debian/libabsl20210324.lintian-overrides | 40 ----------------- debian/libabsl20210324.shlibs | 74 -------------------------------- debian/libabsl20220623.install | 15 +++++++ debian/libabsl20220623.lintian-overrides | 40 +++++++++++++++++ debian/libabsl20220623.shlibs | 74 ++++++++++++++++++++++++++++++++ 9 files changed, 138 insertions(+), 132 deletions(-) delete mode 100644 debian/libabsl20210324.install delete mode 100644 debian/libabsl20210324.lintian-overrides delete mode 100644 debian/libabsl20210324.shlibs create mode 100644 debian/libabsl20220623.install create mode 100644 debian/libabsl20220623.lintian-overrides create mode 100644 debian/libabsl20220623.shlibs diff --git a/debian/changelog b/debian/changelog index 22d44a71..64e20be9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +abseil (0~20220623.0-1) UNRELEASED; urgency=medium + + * New upstream release. (Closes: #1008730) + + -- Benjamin Barenblat Tue, 12 Jul 2022 21:50:18 -0400 + abseil (0~20210324.2-4) unstable; urgency=medium * Fix "spurious -Wl flag in some pkg-config entries" by backporting a diff --git a/debian/control b/debian/control index b31cbce9..bb83adf6 100644 --- a/debian/control +++ b/debian/control @@ -37,7 +37,7 @@ Architecture: any Multi-Arch: same Section: libdevel Depends: - libabsl20210324 (= ${binary:Version}), + libabsl20220623 (= ${binary:Version}), ${misc:Depends}, Recommends: cmake (>= 2.6) | pkg-config, g++ (>= 5.1) Description: ${source:Synopsis} (development files) @@ -46,7 +46,7 @@ Description: ${source:Synopsis} (development files) This package contains header files and other data necessary for developing with Abseil. -Package: libabsl20210324 +Package: libabsl20220623 Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} diff --git a/debian/gbp.conf b/debian/gbp.conf index 64a42303..7a154d08 100644 --- a/debian/gbp.conf +++ b/debian/gbp.conf @@ -13,4 +13,4 @@ # the License. [DEFAULT] -upstream-tag = 20210324.2 +upstream-tag = 20220623.0 diff --git a/debian/libabsl20210324.install b/debian/libabsl20210324.install deleted file mode 100644 index ab3017e5..00000000 --- a/debian/libabsl20210324.install +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -usr/lib/*/libabsl_*.so.* diff --git a/debian/libabsl20210324.lintian-overrides b/debian/libabsl20210324.lintian-overrides deleted file mode 100644 index 1ee8b551..00000000 --- a/debian/libabsl20210324.lintian-overrides +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -libabsl20210324: hardening-no-fortify-functions [usr/lib/*/libabsl_base.so*] -libabsl20210324: hardening-no-fortify-functions [usr/lib/*/libabsl_debugging_internal.so*] -libabsl20210324: hardening-no-fortify-functions [usr/lib/*/libabsl_random_internal_seed_material.so*] -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_bad_any_cast_impl.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_bad_optional_access.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_bad_variant_access.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_exponential_biased.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_flags_commandlineflag.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_flags_commandlineflag_internal.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_hash.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_hashtablez_sampler.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_log_severity.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_periodic_sampler.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen_hwaes_impl.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen_slow.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_stacktrace.so* -libabsl20210324: library-not-linked-against-libc usr/lib/*/libabsl_throw_delegate.so* -libabsl20210324: no-symbols-control-file usr/lib/*/libabsl_* -libabsl20210324: package-name-doesnt-match-sonames libabsl-* -libabsl20210324: shared-library-lacks-prerequisites usr/lib/*/libabsl_city.so* -libabsl20210324: shared-library-lacks-prerequisites usr/lib/*/libabsl_leak_check.so* -libabsl20210324: shared-library-lacks-prerequisites usr/lib/*/libabsl_leak_check_disable.so* -libabsl20210324: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_platform.so* -libabsl20210324: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_randen_hwaes.so* -libabsl20210324: shared-library-lacks-prerequisites usr/lib/*/libabsl_wyhash.so* diff --git a/debian/libabsl20210324.shlibs b/debian/libabsl20210324.shlibs deleted file mode 100644 index f1ecca62..00000000 --- a/debian/libabsl20210324.shlibs +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. -# -libabsl_bad_any_cast_impl 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_bad_optional_access 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_bad_variant_access 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_base 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_city 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_civil_time 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_cord 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_debugging_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_demangle_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_examine_stack 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_exponential_biased 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_failure_signal_handler 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_commandlineflag 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_commandlineflag_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_config 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_marshalling 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_parse 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_private_handle_accessor 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_program_name 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_reflection 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_usage 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_flags_usage_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_graphcycles_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_hash 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_hashtablez_sampler 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_int128 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_leak_check 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_leak_check_disable 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_log_severity 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_malloc_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_periodic_sampler 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_distributions 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_distribution_test_util 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_platform 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_pool_urbg 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_randen 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_randen_hwaes 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_randen_hwaes_impl 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_randen_slow 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_internal_seed_material 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_seed_gen_exception 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_random_seed_sequences 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_raw_hash_set 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_raw_logging_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_scoped_set_env 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_spinlock_wait 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_stacktrace 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_status 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_statusor 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_str_format_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_strerror 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_strings 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_strings_internal 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_symbolize 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_synchronization 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_throw_delegate 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_time 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_time_zone 20210324 libabsl20210324 (>= 0~20210324.2-1) -libabsl_wyhash 20210324 libabsl20210324 (>= 0~20210324.2-1) diff --git a/debian/libabsl20220623.install b/debian/libabsl20220623.install new file mode 100644 index 00000000..ab3017e5 --- /dev/null +++ b/debian/libabsl20220623.install @@ -0,0 +1,15 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +usr/lib/*/libabsl_*.so.* diff --git a/debian/libabsl20220623.lintian-overrides b/debian/libabsl20220623.lintian-overrides new file mode 100644 index 00000000..fa4d374b --- /dev/null +++ b/debian/libabsl20220623.lintian-overrides @@ -0,0 +1,40 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_base.so*] +libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_debugging_internal.so*] +libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_random_internal_seed_material.so*] +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_bad_any_cast_impl.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_bad_optional_access.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_bad_variant_access.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_exponential_biased.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_flags_commandlineflag.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_flags_commandlineflag_internal.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_hash.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_hashtablez_sampler.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_log_severity.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_periodic_sampler.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen_hwaes_impl.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen_slow.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_stacktrace.so* +libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_throw_delegate.so* +libabsl20220623: no-symbols-control-file usr/lib/*/libabsl_* +libabsl20220623: package-name-doesnt-match-sonames libabsl-* +libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_city.so* +libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_leak_check.so* +libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_leak_check_disable.so* +libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_platform.so* +libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_randen_hwaes.so* +libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_wyhash.so* diff --git a/debian/libabsl20220623.shlibs b/debian/libabsl20220623.shlibs new file mode 100644 index 00000000..44f011fa --- /dev/null +++ b/debian/libabsl20220623.shlibs @@ -0,0 +1,74 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +libabsl_bad_any_cast_impl 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_bad_optional_access 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_bad_variant_access 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_base 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_city 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_civil_time 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_cord 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_debugging_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_demangle_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_examine_stack 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_exponential_biased 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_failure_signal_handler 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_commandlineflag 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_commandlineflag_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_config 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_marshalling 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_parse 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_private_handle_accessor 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_program_name 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_reflection 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_usage 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_flags_usage_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_graphcycles_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_hash 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_hashtablez_sampler 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_int128 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_leak_check 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_leak_check_disable 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_log_severity 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_malloc_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_periodic_sampler 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_distributions 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_distribution_test_util 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_platform 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_pool_urbg 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_randen 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_randen_hwaes 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_randen_hwaes_impl 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_randen_slow 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_internal_seed_material 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_seed_gen_exception 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_random_seed_sequences 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_raw_hash_set 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_raw_logging_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_scoped_set_env 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_spinlock_wait 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_stacktrace 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_status 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_statusor 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_str_format_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_strerror 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_strings 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_strings_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_symbolize 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_synchronization 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_throw_delegate 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_time 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_time_zone 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_wyhash 20220623 libabsl20220623 (>= 0~20220623.0-1) -- cgit v1.2.3 From 170e05ec7974a1b7b5133f638e71881925cc7e68 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 22 Aug 2022 17:49:57 -0400 Subject: Update patches Bump SONAME and inline namespace in configuration, update location of SSE2 and SSSE3 configuration, and delete patches that have been applied upstream. --- ...ibutionTest-irrelevant-destination-buckets.diff | 63 -- debian/patches/big-endian-hash.diff | 725 --------------------- debian/patches/big-endian-hash2.diff | 164 ----- debian/patches/big-endian-random.diff | 698 -------------------- debian/patches/big-endian-random2.diff | 90 --- debian/patches/big-endian-random3.diff | 82 --- debian/patches/big-endian-random4.diff | 71 -- debian/patches/configure.diff | 54 +- debian/patches/cordrepring-typo.diff | 20 - .../patches/disable-nominalcpufrequency-test.diff | 46 -- debian/patches/empty-flags-library.diff | 2 +- debian/patches/float-rounding.diff | 46 -- debian/patches/float-tests-disable-i386.diff | 141 ---- debian/patches/hppa-symbolize.diff | 103 --- debian/patches/latomic.diff | 2 +- debian/patches/missing-rint.diff | 45 -- debian/patches/pkg-config-libs-generation.diff | 36 - debian/patches/series | 16 - debian/patches/str-format-convert-test-printf.diff | 103 --- debian/patches/thumb-function-bounds.diff | 96 --- 20 files changed, 26 insertions(+), 2577 deletions(-) delete mode 100644 debian/patches/DiscreteDistributionTest-irrelevant-destination-buckets.diff delete mode 100644 debian/patches/big-endian-hash.diff delete mode 100644 debian/patches/big-endian-hash2.diff delete mode 100644 debian/patches/big-endian-random.diff delete mode 100644 debian/patches/big-endian-random2.diff delete mode 100644 debian/patches/big-endian-random3.diff delete mode 100644 debian/patches/big-endian-random4.diff delete mode 100644 debian/patches/cordrepring-typo.diff delete mode 100644 debian/patches/disable-nominalcpufrequency-test.diff delete mode 100644 debian/patches/float-rounding.diff delete mode 100644 debian/patches/float-tests-disable-i386.diff delete mode 100644 debian/patches/hppa-symbolize.diff delete mode 100644 debian/patches/missing-rint.diff delete mode 100644 debian/patches/pkg-config-libs-generation.diff delete mode 100644 debian/patches/str-format-convert-test-printf.diff delete mode 100644 debian/patches/thumb-function-bounds.diff diff --git a/debian/patches/DiscreteDistributionTest-irrelevant-destination-buckets.diff b/debian/patches/DiscreteDistributionTest-irrelevant-destination-buckets.diff deleted file mode 100644 index f4cae4c7..00000000 --- a/debian/patches/DiscreteDistributionTest-irrelevant-destination-buckets.diff +++ /dev/null @@ -1,63 +0,0 @@ -From: Benjamin Barenblat -Subject: Don’t examine irrelevant destination buckets in DiscreteDistributionTest -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/7ba826e50dff1878e6ecc6b9af44097c040c8968 - -Abseil generates discrete distributions using Walker’s aliasing -algorithm. This creates uniformly distributed buckets, each with a -probability of sending traffic to a different bucket. Abseil represents -a bucket as a pair - - (probability of retaining traffic × - alternate bucket if traffic is passed) - -and a distribution as a vector of such pairs. For example, {(0.3, 1), -(1.0, 1)} represents a distribution with two buckets, the zeroth of -which passes 70% of its traffic to bucket 1 and the first of which holds -on to all its traffic. - -This representation is not unique: When a bucket retains traffic with -probability 1, the alternate bucket is irrelevant. Continuing the -example above, {(0.3, 1), (1.0, 0)} _also_ represents a two-bucket -distribution where the zeroth bucket passes 70% of its traffic to the -first and the first hangs on to all traffic. Exactly what representation -Abseil generates for a given input is related to how much precision is -used in intermediate floating-point operations, which is an -architectural implementation detail. Remove sensitivity to that detail -by not examining the alternate bucket when the retention probability is -1.0. - -The author works at Google. Upstream applied this patch as Piper -revision 372993410 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/random/discrete_distribution_test.cc -+++ b/absl/random/discrete_distribution_test.cc -@@ -99,6 +99,7 @@ - } - - TEST(DiscreteDistributionTest, InitDiscreteDistribution) { -+ using testing::_; - using testing::Pair; - - { -@@ -111,8 +112,8 @@ TEST(DiscreteDistributionTest, InitDiscreteDistribution) { - // Each bucket is p=1/3, so bucket 0 will send half it's traffic - // to bucket 2, while the rest will retain all of their traffic. - EXPECT_THAT(q, testing::ElementsAre(Pair(0.5, 2), // -- Pair(1.0, 1), // -- Pair(1.0, 2))); -+ Pair(1.0, _), // -+ Pair(1.0, _))); - } - - { -@@ -135,7 +136,7 @@ TEST(DiscreteDistributionTest, InitDiscreteDistribution) { - - EXPECT_THAT(q, testing::ElementsAre(Pair(b0, 3), // - Pair(b1, 3), // -- Pair(1.0, 2), // -+ Pair(1.0, _), // - Pair(b3, 2), // - Pair(b1, 3))); - } diff --git a/debian/patches/big-endian-hash.diff b/debian/patches/big-endian-hash.diff deleted file mode 100644 index 52888e74..00000000 --- a/debian/patches/big-endian-hash.diff +++ /dev/null @@ -1,725 +0,0 @@ -From: Benjamin Barenblat -Subject: Restructure wyhash_test.cc to separate golden values -Forwarded: yes -Origin: backport, https://github.com/abseil/abseil-cpp/commit/a05366d851c5cb88065272f951e03955197e7c11 - -This patch is a subset of the referenced commit, which originally also changed -the mix function on arm64. The original message is as follows: - - Alternative bit mixer for LowLevelHash on ARM - - LowLevelHash's bit-mixer is inefficient on ARM because it calculates - a 128-bit product of two 64-bit numbers. On ARM, this requires a - sequence of two instructions with a high combined latency and poor - throughput. This change provides alternative bit-mixing code for ARM - that uses only 64-bit arithmetic (multiplication, xor, and - left-shifts) and speeds things up considerably. - - The bit-mixing code for ARM was inspired by by Woothash[1] and - xxh3[1]. Once I landed on a sequence of operations that provided - good mixing, I used a test harness to search for the combination of - shift / rotate factors that provided the best mixing, as indicated - by SMHasher hash quality tests. The new mixing code passes 13 out of - 15 of the hash quality test suites in SMHasher, with the two - failures being in the noise range: e.g. 1 collision vs. zero - expected in a keyset of ~8m keys. - - [1]: https://github.com/tommyettinger/waterhash/blob/49f5cf0b63b9/woothash.h#L16-L20 - [2]: https://github.com/Cyan4973/xxHash/blob/6853ddc36e46/xxhash.h#L3240-L3265 - - PiperOrigin-RevId: 391833008 - -This restructuring is a prerequisite for big-endian-hash2.diff. - ---- a/absl/hash/internal/wyhash_test.cc -+++ b/absl/hash/internal/wyhash_test.cc -@@ -14,473 +14,469 @@ - - #include "absl/hash/internal/wyhash.h" - -+#include -+ --#include "absl/strings/escaping.h" - #include "gmock/gmock.h" - #include "gtest/gtest.h" -+#include "absl/strings/escaping.h" -+ -+#define UPDATE_GOLDEN 0 - - namespace { - --static const uint64_t kCurrentSeed = 0; - static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl, - 0x8ebc6af09c88c6e3, 0x589965cc75374cc3l, - 0x1d8e4e27c47d124f}; - --// Note: We don't account for endianness, so the values here are only correct if --// you're also running on a little endian platform. -- --TEST(WyhashTest, EmptyString) { -- const std::string s = ""; -- EXPECT_EQ( -- absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), -- 4808886099364463827); --} -- --TEST(WyhashTest, Spaces) { -- const std::string s = " "; -- EXPECT_EQ( -- absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), -- 1686201463024549249); --} -- --TEST(WyhashTest, RepeatingString) { -- const std::string s = "aaaa"; -- EXPECT_EQ( -- absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), -- 6646112255271966632); --} -- --TEST(WyhashTest, HexString) { -- const std::string small = "\x01\x02\x03"; -- const std::string med = "\x01\x02\x03\x04"; -- -- EXPECT_EQ(absl::hash_internal::Wyhash(small.c_str(), small.length(), -- kCurrentSeed, kSalt), -- 11989428023081740911ULL); -- EXPECT_EQ(absl::hash_internal::Wyhash(med.c_str(), med.length(), kCurrentSeed, -- kSalt), -- 9765997711188871556ULL); --} -- --TEST(WyhashTest, Words) { -- const std::string s = "third_party|wyhash|64"; -- EXPECT_EQ( -- absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), -- 3702018632387611330); --} -- --TEST(WyhashTest, LongString) { -- const std::string s = -- "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz" -- "0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOp" -- "QrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCdEf" -- "GhIjKlMnOpQrStUvWxYz0123456789AbCdEfGhIjKlMnOpQrStUvWxYz012345" -- "6789AbCdEfGhIjKlMnOpQrStUvWxYz0123456789"; -- -- EXPECT_EQ( -- absl::hash_internal::Wyhash(s.c_str(), s.length(), kCurrentSeed, kSalt), -- 9245411362605796064ULL); --} -- --TEST(WyhashTest, BigReference) { -+TEST(WyhashTest, VerifyGolden) { -+ constexpr size_t kNumGoldenOutputs = 134; -- struct ExpectedResult { -+ static struct { - absl::string_view base64_data; - uint64_t seed; -- uint64_t hash; -- } expected_results[] = { -+ } cases[] = { -- {"", uint64_t{0xec42b7ab404b8acb}, uint64_t{0xe5a40d39ab796423}}, -+ {"", uint64_t{0xec42b7ab404b8acb}}, -+ {"ICAg", uint64_t{0}}, -+ {"YWFhYQ==", uint64_t{0}}, -+ {"AQID", uint64_t{0}}, -+ {"AQIDBA==", uint64_t{0}}, -+ {"dGhpcmRfcGFydHl8d3loYXNofDY0", uint64_t{0}}, -- {"Zw==", uint64_t{0xeeee074043a3ee0f}, uint64_t{0xa6564b468248c683}}, -+ {"Zw==", uint64_t{0xeeee074043a3ee0f}}, -- {"xmk=", uint64_t{0x857902089c393de}, uint64_t{0xef192f401b116e1c}}, -+ {"xmk=", uint64_t{0x857902089c393de}}, -- {"c1H/", uint64_t{0x993df040024ca3af}, uint64_t{0xbe8dc0c54617639d}}, -+ {"c1H/", uint64_t{0x993df040024ca3af}}, -- {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}, uint64_t{0x93d7f665b5521c8e}}, -+ {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}}, -- {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}, uint64_t{0x646d70bb42445f28}}, -+ {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}}, -- {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}, uint64_t{0x96a7b1e3cc9bd426}}, -+ {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}}, -- {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}, -- uint64_t{0x76020289ab0790c4}}, -+ {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}}, -- {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}, -- uint64_t{0x39f842e4133b9b44}}, -+ {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}}, -- {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}, -- uint64_t{0x2b8d7047be4bcaab}}, -+ {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}}, -- {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}, -- uint64_t{0x99628abef6716a97}}, -+ {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}}, -- {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}, -- uint64_t{0x4432e02ba42b2740}}, -+ {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}}, -- {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}, -- uint64_t{0x74d810efcad7918a}}, -+ {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}}, -- {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}, -- uint64_t{0x88c84e986002507f}}, -+ {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}}, -- {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}, -- uint64_t{0x4f99acf193cf39b9}}, -+ {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}}, -- {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}, -- uint64_t{0xd90e7a3655891e37}}, -+ {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}}, -- {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}, -- uint64_t{0x3bb378b1d4df8fcf}}, -+ {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}}, -- {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}, -- uint64_t{0xf78e94045c052d47}}, -+ {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}}, -- {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}, -- uint64_t{0x26da0b2130da6b40}}, -+ {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}}, -- {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}, -- uint64_t{0x30b4d426af8c6986}}, -+ {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}}, -- {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}, -- uint64_t{0x5413b4aaf3baaeae}}, -+ {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}}, -- {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}, -- uint64_t{0x756ab265370a1597}}, -+ {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}}, -- {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}, -- uint64_t{0xdaf5f4b7d09814fb}}, -+ {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}}, -- {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}, -- uint64_t{0x8f874ae37742b75e}}, -+ {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}}, -- {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}, -- uint64_t{0x8fecd03956121ce8}}, -+ {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}}, -- {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}, -- uint64_t{0x229c292ea7a08285}}, -+ {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}}, -- {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}, -- uint64_t{0xbb4bf0692d14bae}}, -+ {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}}, -- {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}, -- uint64_t{0x207b24ca3bdac1db}}, -+ {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}}, -- {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", uint64_t{0xd902ee3e44a5705f}, -- uint64_t{0x64f6cd6745d3825b}}, -+ {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", -+ uint64_t{0xd902ee3e44a5705f}}, -- {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}, -- uint64_t{0xa2b2e1656b58df1e}}, -+ {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}}, -- {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", uint64_t{0x402d83f9f834f616}, -- uint64_t{0xd01d30d9ee7a148}}, -+ {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", -+ uint64_t{0x402d83f9f834f616}}, - {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==", -- uint64_t{0x9c604164c016b72c}, uint64_t{0x1cb4cd00ab804e3b}}, -+ uint64_t{0x9c604164c016b72c}}, - {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=", -- uint64_t{0x3f4507e01f9e73ba}, uint64_t{0x4697f2637fd90999}}, -+ uint64_t{0x3f4507e01f9e73ba}}, - {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi", -- uint64_t{0xc3fe0d5be8d2c7c7}, uint64_t{0x8383a756b5688c07}}, -+ uint64_t{0xc3fe0d5be8d2c7c7}}, - {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==", -- uint64_t{0x531858a40bfa7ea1}, uint64_t{0x695c29cb3696a975}}, -+ uint64_t{0x531858a40bfa7ea1}}, - {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=", -- uint64_t{0x86689478a7a7e8fa}, uint64_t{0xda2e5a5a5e971521}}, -+ uint64_t{0x86689478a7a7e8fa}}, - {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph", -- uint64_t{0x4ec948b8e7f27288}, uint64_t{0x7935d4befa056b2b}}, -+ uint64_t{0x4ec948b8e7f27288}}, - {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==", -- uint64_t{0xce46c7213c10032}, uint64_t{0x38dd541ca95420fe}}, -+ uint64_t{0xce46c7213c10032}}, - {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=", -- uint64_t{0xf63e96ee6f32a8b6}, uint64_t{0xcc06c7a4963f967f}}, -+ uint64_t{0xf63e96ee6f32a8b6}}, - {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy", -- uint64_t{0x1cfe85e65fc5225}, uint64_t{0xbf0f6f66e232fb20}}, -+ uint64_t{0x1cfe85e65fc5225}}, - {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==", -- uint64_t{0x45c474f1cee1d2e8}, uint64_t{0xf7efb32d373fe71a}}, -+ uint64_t{0x45c474f1cee1d2e8}}, - {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=", -- uint64_t{0x6e024e14015f329c}, uint64_t{0xe2e64634b1c12660}}, -+ uint64_t{0x6e024e14015f329c}}, - {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt", -- uint64_t{0x760c40502103ae1c}, uint64_t{0x285b8fd1638e306d}}, -+ uint64_t{0x760c40502103ae1c}}, - {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==", -- uint64_t{0x17fd05c3c560c320}, uint64_t{0x658e8a4e3b714d6c}}, -+ uint64_t{0x17fd05c3c560c320}}, - {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=", -- uint64_t{0x8b34200a6f8e90d9}, uint64_t{0xf391fb968e0eb398}}, -+ uint64_t{0x8b34200a6f8e90d9}}, - {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn", -- uint64_t{0x6be89e50818bdf69}, uint64_t{0x744a9ea0cc144bf2}}, -+ uint64_t{0x6be89e50818bdf69}}, - {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==", -- uint64_t{0xfb389773315b47d8}, uint64_t{0x12636f2be11012f1}}, -+ uint64_t{0xfb389773315b47d8}}, - {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=", -- uint64_t{0x4f2512a23f61efee}, uint64_t{0x29c57de825948f80}}, -+ uint64_t{0x4f2512a23f61efee}}, - {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23", -- uint64_t{0x59ccd92fc16c6fda}, uint64_t{0x58c6f99ab0d1c021}}, -+ uint64_t{0x59ccd92fc16c6fda}}, - {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==", -- uint64_t{0x25c5a7f5bd330919}, uint64_t{0x13e7b5a7b82fe3bb}}, -+ uint64_t{0x25c5a7f5bd330919}}, - {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=", -- uint64_t{0x51df4174d34c97d7}, uint64_t{0x10fbc87901e02b63}}, -+ uint64_t{0x51df4174d34c97d7}}, - {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I", -- uint64_t{0x80ce6d76f89cb57}, uint64_t{0xa24c9184901b748b}}, -+ uint64_t{0x80ce6d76f89cb57}}, - {"64mVTbQ47dHjHlOHGS/hjJwr/" - "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==", -- uint64_t{0x20961c911965f684}, uint64_t{0xcac4fd4c5080e581}}, -+ uint64_t{0x20961c911965f684}}, - {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/" - "+a2V5WpA=", -- uint64_t{0x4e5b926ec83868e7}, uint64_t{0xc38bdb7483ba68e1}}, -+ uint64_t{0x4e5b926ec83868e7}}, - {"PGih0zDEOWCYGxuHGDFu9Ivbff/" - "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS", -- uint64_t{0x3927b30b922eecef}, uint64_t{0xdb2a8069b2ceaffa}}, -+ uint64_t{0x3927b30b922eecef}}, - {"RnpA/" - "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q=" - "=", -- uint64_t{0xbd0291284a49b61c}, uint64_t{0xdf9fe91d0d1c7887}}, -+ uint64_t{0xbd0291284a49b61c}}, - {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+" - "BrWPRIbfprszSaPfrI=", -- uint64_t{0x73a77c575bcc956}, uint64_t{0xe83f49e96e2e6a08}}, -+ uint64_t{0x73a77c575bcc956}}, - {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/" - "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT", -- uint64_t{0x766a0e2ade6d09a6}, uint64_t{0xc69e61b62ca2b62}}, -+ uint64_t{0x766a0e2ade6d09a6}}, - {"s/" - "Jf1+" - "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db" - "w==", -- uint64_t{0x2599f4f905115869}, uint64_t{0xb4a4f3f85f8298fe}}, -+ uint64_t{0x2599f4f905115869}}, - {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+" - "7LgoUT1fJ/axybE=", -- uint64_t{0xd8256e5444d21e53}, uint64_t{0x167a1b39e1e95f41}}, -+ uint64_t{0xd8256e5444d21e53}}, - {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+" - "LW4tTuzC6CIWbRGRRD1sQV/4", -- uint64_t{0xf664a91333fb8dfd}, uint64_t{0xf8a2a5649855ee41}}, -+ uint64_t{0xf664a91333fb8dfd}}, - {"CDK0meI07yrgV2kQlZZ+" - "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==", -- uint64_t{0x9625b859be372cd1}, uint64_t{0x27992565b595c498}}, -+ uint64_t{0x9625b859be372cd1}}, - {"d23/vc5ONh/" - "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+" - "nX7eOvs=", -- uint64_t{0x7b99940782e29898}, uint64_t{0x3e08cca5b71f9346}}, -+ uint64_t{0x7b99940782e29898}}, - {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/" - "mGoT0pnMTQst7Lv2q6QN6Vm", -- uint64_t{0x4fe12fa5383b51a8}, uint64_t{0xad406b10c770a6d2}}, -+ uint64_t{0x4fe12fa5383b51a8}}, - {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/" - "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==", -- uint64_t{0xe2ccb09ac0f5b4b6}, uint64_t{0xd1713ce6e552bcf2}}, -+ uint64_t{0xe2ccb09ac0f5b4b6}}, - {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/" - "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=", -- uint64_t{0x7d0a37adbd7b753b}, uint64_t{0x753b287194c73ad3}}, -+ uint64_t{0x7d0a37adbd7b753b}}, - {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/" - "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW", -- uint64_t{0xd3ae96ef9f7185f2}, uint64_t{0x5ae41a95f600af1c}}, -+ uint64_t{0xd3ae96ef9f7185f2}}, - {"/WiHi9IQcxRImsudkA/KOTqGe8/" - "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==", -- uint64_t{0x4fb88ea63f79a0d8}, uint64_t{0x4a61163b86a8bb4c}}, -+ uint64_t{0x4fb88ea63f79a0d8}}, - {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/" - "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=", -- uint64_t{0xed564e259bb5ebe9}, uint64_t{0x42eeaa79e760c7e4}}, -+ uint64_t{0xed564e259bb5ebe9}}, - {"8FVYHx40lSQPTHheh08Oq0/" - "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi", -- uint64_t{0x3e3256b60c428000}, uint64_t{0x698df622ef465b0a}}, -+ uint64_t{0x3e3256b60c428000}}, - {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/" - "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==", -- uint64_t{0xfb05bad59ec8705}, uint64_t{0x157583111e1a6026}}, -+ uint64_t{0xfb05bad59ec8705}}, - {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/" - "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=", -- uint64_t{0xafdc251dbf97b5f8}, uint64_t{0xaa1388f078e793e0}}, -+ uint64_t{0xafdc251dbf97b5f8}}, - {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+" - "juQV4rsqYElMD/gWfDGpsWZKQ", -- uint64_t{0x10ec9c92ddb5dcbc}, uint64_t{0xf10d68d0f3309360}}, -+ uint64_t{0x10ec9c92ddb5dcbc}}, - {"oswxop+" - "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp" - "L2mRK0rcIUYA4qLt5uOw==", -- uint64_t{0x9a767d5822c7dac4}, uint64_t{0x2af056184457a3de}}, -+ uint64_t{0x9a767d5822c7dac4}}, - {"0II/" - "697p+" - "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+" - "n5bxNOD1TGrjQtzKU5r7obo=", -- uint64_t{0xee46254080d6e2db}, uint64_t{0x6d0058e1590b2489}}, -+ uint64_t{0xee46254080d6e2db}}, - {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+" - "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc", -- uint64_t{0xbbb669588d8bf398}, uint64_t{0x638f287f68817f12}}, -+ uint64_t{0xbbb669588d8bf398}}, - {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D" - "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==", -- uint64_t{0xdc2afaa529beef44}, uint64_t{0xc46b71fecefd5467}}, -+ uint64_t{0xdc2afaa529beef44}}, - {"jVDKGYIuWOP/" - "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/" - "zAvSCB+zlf6upAsBlheUKgCfKww=", -- uint64_t{0xf1f67391d45013a8}, uint64_t{0x2c8e94679d964e0a}}, -+ uint64_t{0xf1f67391d45013a8}}, - {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/" - "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d", -- uint64_t{0x16fce2b8c65a3429}, uint64_t{0x8612b797ce22503a}}, -+ uint64_t{0x16fce2b8c65a3429}}, - {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//" - "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==", -- uint64_t{0xf4b096699f49fe67}, uint64_t{0x59f929babfba7170}}, -+ uint64_t{0xf4b096699f49fe67}}, - {"DUwXFJzagljo44QeJ7/" - "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1" - "eHuyFirAlkW+zKtwg=", -- uint64_t{0xca584c4bc8198682}, uint64_t{0x9527556923fb49a0}}, -+ uint64_t{0xca584c4bc8198682}}, - {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/" - "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR", -- uint64_t{0xed269fc3818b6aad}, uint64_t{0x1039ab644f5e150b}}, -+ uint64_t{0xed269fc3818b6aad}}, - {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+" - "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==", -- uint64_t{0x33f253cbb8fe66a8}, uint64_t{0x7816c83f3aa05e6d}}, -+ uint64_t{0x33f253cbb8fe66a8}}, - {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/" - "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=", -- uint64_t{0xd0b76b2c1523d99c}, uint64_t{0xf51d2f564518c619}}, -+ uint64_t{0xd0b76b2c1523d99c}}, - {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/" - "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H", -- uint64_t{0xfd28f0811a2a237f}, uint64_t{0x67d494cff03ac004}}, -+ uint64_t{0xfd28f0811a2a237f}}, - {"ueLyMcqJXX+MhO4UApylCN9WlTQ+" - "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib" - "4/J3A5mseA3cS8w==", -- uint64_t{0x6261fb136482e84}, uint64_t{0x2802d636ced1cfbb}}, -+ uint64_t{0x6261fb136482e84}}, - {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/" - "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=", -- uint64_t{0x458efc750bca7c3a}, uint64_t{0xf64e20bad771cb12}}, -+ uint64_t{0x458efc750bca7c3a}}, - {"Q6AbOofGuTJOegPh9Clm/" - "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+" - "y4hHwLqRranl9FjvxfVKm3yvg68", -- uint64_t{0xa7e69ff84e5e7c27}, uint64_t{0xb9a6cf84a83e15e}}, -+ uint64_t{0xa7e69ff84e5e7c27}}, - {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/" - "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==", -- uint64_t{0x3c59bfd0c29efe9e}, uint64_t{0x8da6630319609301}}, -+ uint64_t{0x3c59bfd0c29efe9e}}, - {"zQUv8hFB3zh2GGl3KTvCmnfzE+" - "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//" - "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=", -- uint64_t{0x10befacc6afd298d}, uint64_t{0x40946a86e2a996f3}}, -+ uint64_t{0x10befacc6afd298d}}, - {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/" - "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd" - "P47L", -- uint64_t{0x41d5320b0a38efa7}, uint64_t{0xcab7f5997953fa76}}, -+ uint64_t{0x41d5320b0a38efa7}}, - {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP" - "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==", -- uint64_t{0x58db1c7450fe17f3}, uint64_t{0x39129ca0e04fc465}}, -+ uint64_t{0x58db1c7450fe17f3}}, - {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ" - "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=", -- uint64_t{0x6098c055a335b7a6}, uint64_t{0x5238221fd685e1b8}}, -+ uint64_t{0x6098c055a335b7a6}}, - {"gzxyMJIPlU+bJBwhFUCHSofZ/" - "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+" - "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1", -- uint64_t{0x1bbacec67845a801}, uint64_t{0x175130c407dbcaab}}, -+ uint64_t{0x1bbacec67845a801}}, - {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+" - "8yyOw8lQabism19vOQxfmocEOW/" - "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==", -- uint64_t{0xc419cfc7442190}, uint64_t{0x2f20e7536c0b0df}}, -+ uint64_t{0xc419cfc7442190}}, - {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/" - "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/" - "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=", -- uint64_t{0xc95e510d94ba270c}, uint64_t{0x2742cb488a04ad56}}, -+ uint64_t{0xc95e510d94ba270c}}, - {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/" - "u+x+" - "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX" - "8jUfh1il", -- uint64_t{0xff1ae05c98089c3f}, uint64_t{0xd6afb593879ff93b}}, -+ uint64_t{0xff1ae05c98089c3f}}, - {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs" - "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==", -- uint64_t{0x90c02b8dceced493}, uint64_t{0xf50ad64caac0ca7f}}, -+ uint64_t{0x90c02b8dceced493}}, - {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/" - "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/" - "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=", -- uint64_t{0x9f8a76697ab1aa36}, uint64_t{0x2ade95c4261364ae}}, -+ uint64_t{0x9f8a76697ab1aa36}}, - {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/" - "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND" - "55G2L1W", -- uint64_t{0x6ba1bf3d811a531d}, uint64_t{0x5c4f3299faacd07a}}, -+ uint64_t{0x6ba1bf3d811a531d}}, - {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/" - "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==", -- uint64_t{0x6a418974109c67b4}, uint64_t{0xfffe3bff0ae5e9bc}}, -+ uint64_t{0x6a418974109c67b4}}, - {"6QO5nnDrY2/" - "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+" - "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/" - "ml6fnNXEpxplWJ1vEs4=", -- uint64_t{0x8472f1c2b3d230a3}, uint64_t{0x1db785c0005166e4}}, -+ uint64_t{0x8472f1c2b3d230a3}}, - {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/" - "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww", -- uint64_t{0x5e06068f884e73a7}, uint64_t{0xea000d962ad18418}}, -+ uint64_t{0x5e06068f884e73a7}}, - {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/" - "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt" - "gwYHw7yakDUv8WvonctmnoSPKENegQg==", -- uint64_t{0x55290b1a8f170f59}, uint64_t{0xe42aef38359362d9}}, -+ uint64_t{0x55290b1a8f170f59}}, - {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+" - "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+" - "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=", -- uint64_t{0x5501cfd83dfe706a}, uint64_t{0xc8e95657348a3891}}, -+ uint64_t{0x5501cfd83dfe706a}}, - {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY" - "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj", -- uint64_t{0xe43ed13d13a66990}, uint64_t{0xc162eca864f238c6}}, -+ uint64_t{0xe43ed13d13a66990}}, - {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G" - "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/" - "ODLcPEFztFnwjvCjmHw==", -- uint64_t{0xdf43bc375cf5283f}, uint64_t{0xbe1fb373e20579ad}}, -+ uint64_t{0xdf43bc375cf5283f}}, - {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/" - "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+" - "OevnkhUn5jsPlG2r5jYlVn8=", -- uint64_t{0x8112b806d288d7b5}, uint64_t{0x628a1d4f40aa6ffd}}, -+ uint64_t{0x8112b806d288d7b5}}, - {"kUw/0z4l3a89jTwN5jpG0SHY5km/" - "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G" - "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB", -- uint64_t{0xd52a18abb001cb46}, uint64_t{0xa87bdb7456340f90}}, -+ uint64_t{0xd52a18abb001cb46}}, - {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/" - "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+" - "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==", -- uint64_t{0xe12b76a2433a1236}, uint64_t{0x5960ef3ba982c801}}, -+ uint64_t{0xe12b76a2433a1236}}, - {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/" - "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+" - "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=", -- uint64_t{0x175bf7319cf1fa00}, uint64_t{0x5026586df9a431ec}}, -+ uint64_t{0x175bf7319cf1fa00}}, - {"BrbNpb42+" - "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l" - "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi", -- uint64_t{0xd63d57b3f67525ae}, uint64_t{0xfe4b8a20fdf0840b}}, -+ uint64_t{0xd63d57b3f67525ae}}, - {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+" - "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/" - "LTmhua+rQ6Wup8ezLwfg==", -- uint64_t{0x933faea858832b73}, uint64_t{0xdcb761867da7072f}}, -+ uint64_t{0x933faea858832b73}}, - {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+" - "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+" - "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=", -- uint64_t{0x53d061e5f8e7c04f}, uint64_t{0xc10d4653667275b7}}, -+ uint64_t{0x53d061e5f8e7c04f}}, - {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/" - "9S+" - "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA" - "wCZUOEXIsLU24o2Y", -- uint64_t{0xdb4124556dd515e0}, uint64_t{0x727720deec13110b}}, -+ uint64_t{0xdb4124556dd515e0}}, - {"TKo+l+" - "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF" - "T0Gd0a2hI3+" - "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==", -- uint64_t{0x4fb31a0dd681ee71}, uint64_t{0x710b009662858dc9}}, -+ uint64_t{0x4fb31a0dd681ee71}}, - {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+" - "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+" - "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=", -- uint64_t{0x27cc72eefa138e4c}, uint64_t{0xfbf8f7a3ecac1eb7}}, -+ uint64_t{0x27cc72eefa138e4c}}, - {"/I/" - "eImMwPo1U6wekNFD1Jxjk9XQVi1D+" - "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t" - "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp", -- uint64_t{0x44bc2dfba4bd3ced}, uint64_t{0xb6fc4fcd0722e3df}}, -+ uint64_t{0x44bc2dfba4bd3ced}}, - {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC" - "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+" - "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==", -- uint64_t{0x242da1e3a439bed8}, uint64_t{0x7cb86dcc55104aac}}, -+ uint64_t{0x242da1e3a439bed8}}, - {"ZlhyQwLhXQyIUEnMH/" - "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/" - "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+" - "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=", -- uint64_t{0xdc559c746e35c139}, uint64_t{0x19e71e9b45c3a51e}}, -+ uint64_t{0xdc559c746e35c139}}, - {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg" - "Rko04h19QMG0mOw/" - "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+" - "xswhVMfL", -- uint64_t{0xd0b0350275b9989}, uint64_t{0x51de38573c2bea48}}, -+ uint64_t{0xd0b0350275b9989}}, - {"QhKlnIS6BuVCTQsnoE67E/" - "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p" - "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/" - "nxtxakyEtrNkKk471Oov9juP8oQ==", -- uint64_t{0xb04489e41d17730c}, uint64_t{0xa73ab6996d6df158}}, -+ uint64_t{0xb04489e41d17730c}}, - {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+" - "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK" - "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=", -- uint64_t{0x2217285eb4572156}, uint64_t{0x55ef2b8c930817b2}}, -+ uint64_t{0x2217285eb4572156}}, - {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+" - "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+" - "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M", -- uint64_t{0x12c2e8e68aede73b}, uint64_t{0xb2850bf5fae87157}}, -+ uint64_t{0x12c2e8e68aede73b}}, - {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/" - "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//" - "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+" - "DkYu9ND0O2swg4itGeVSzXA==", -- uint64_t{0x4d612125bdc4fd00}, uint64_t{0xecf3de1acd04651f}}, -+ uint64_t{0x4d612125bdc4fd00}}, - {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+" - "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/" - "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+" - "hQLfVSh8OGs7fsBb68nNWPLeeSOo=", -- uint64_t{0x81826b553954464e}, uint64_t{0xcc0a40552559ff32}}, -+ uint64_t{0x81826b553954464e}}, - {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+" - "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS" - "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi", -- uint64_t{0xc2e5d345dc0ddd2d}, uint64_t{0xc385c374f20315b1}}, -+ uint64_t{0xc2e5d345dc0ddd2d}}, - {"j+loZ+C87+" - "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++" - "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/" - "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==", -- uint64_t{0x3da6830a9e32631e}, uint64_t{0xb90208a4c7234183}}, -+ uint64_t{0x3da6830a9e32631e}}, - {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/" - "sOmqaq8XAQLEn68LKj6/" - "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+" - "gN+7uKpoohgAhIwpAKQXmX5xtd0=", -- uint64_t{0xc9ae5c8759b4877a}, uint64_t{0x58aa1ca7a4c075d9}}, -+ uint64_t{0xc9ae5c8759b4877a}}, -+ }; -+ -+ constexpr uint64_t kGolden[kNumGoldenOutputs] = { -+ 0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8, -+ 0xa6630143a7e6aa6f, 0x8787cb2d04b0c984, 0x33603654ff574ac2, -+ 0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d, -+ 0x93d7f665b5521c8e, 0x646d70bb42445f28, 0x96a7b1e3cc9bd426, -+ 0x76020289ab0790c4, 0x39f842e4133b9b44, 0x2b8d7047be4bcaab, -+ 0x99628abef6716a97, 0x4432e02ba42b2740, 0x74d810efcad7918a, -+ 0x88c84e986002507f, 0x4f99acf193cf39b9, 0xd90e7a3655891e37, -+ 0x3bb378b1d4df8fcf, 0xf78e94045c052d47, 0x26da0b2130da6b40, -+ 0x30b4d426af8c6986, 0x5413b4aaf3baaeae, 0x756ab265370a1597, -+ 0xdaf5f4b7d09814fb, 0x8f874ae37742b75e, 0x8fecd03956121ce8, -+ 0x229c292ea7a08285, 0x0bb4bf0692d14bae, 0x207b24ca3bdac1db, -+ 0x64f6cd6745d3825b, 0xa2b2e1656b58df1e, 0x0d01d30d9ee7a148, -+ 0x1cb4cd00ab804e3b, 0x4697f2637fd90999, 0x8383a756b5688c07, -+ 0x695c29cb3696a975, 0xda2e5a5a5e971521, 0x7935d4befa056b2b, -+ 0x38dd541ca95420fe, 0xcc06c7a4963f967f, 0xbf0f6f66e232fb20, -+ 0xf7efb32d373fe71a, 0xe2e64634b1c12660, 0x285b8fd1638e306d, -+ 0x658e8a4e3b714d6c, 0xf391fb968e0eb398, 0x744a9ea0cc144bf2, -+ 0x12636f2be11012f1, 0x29c57de825948f80, 0x58c6f99ab0d1c021, -+ 0x13e7b5a7b82fe3bb, 0x10fbc87901e02b63, 0xa24c9184901b748b, -+ 0xcac4fd4c5080e581, 0xc38bdb7483ba68e1, 0xdb2a8069b2ceaffa, -+ 0xdf9fe91d0d1c7887, 0xe83f49e96e2e6a08, 0x0c69e61b62ca2b62, -+ 0xb4a4f3f85f8298fe, 0x167a1b39e1e95f41, 0xf8a2a5649855ee41, -+ 0x27992565b595c498, 0x3e08cca5b71f9346, 0xad406b10c770a6d2, -+ 0xd1713ce6e552bcf2, 0x753b287194c73ad3, 0x5ae41a95f600af1c, -+ 0x4a61163b86a8bb4c, 0x42eeaa79e760c7e4, 0x698df622ef465b0a, -+ 0x157583111e1a6026, 0xaa1388f078e793e0, 0xf10d68d0f3309360, -+ 0x2af056184457a3de, 0x6d0058e1590b2489, 0x638f287f68817f12, -+ 0xc46b71fecefd5467, 0x2c8e94679d964e0a, 0x8612b797ce22503a, -+ 0x59f929babfba7170, 0x9527556923fb49a0, 0x1039ab644f5e150b, -+ 0x7816c83f3aa05e6d, 0xf51d2f564518c619, 0x67d494cff03ac004, -+ 0x2802d636ced1cfbb, 0xf64e20bad771cb12, 0x0b9a6cf84a83e15e, -+ 0x8da6630319609301, 0x40946a86e2a996f3, 0xcab7f5997953fa76, -+ 0x39129ca0e04fc465, 0x5238221fd685e1b8, 0x175130c407dbcaab, -+ 0x02f20e7536c0b0df, 0x2742cb488a04ad56, 0xd6afb593879ff93b, -+ 0xf50ad64caac0ca7f, 0x2ade95c4261364ae, 0x5c4f3299faacd07a, -+ 0xfffe3bff0ae5e9bc, 0x1db785c0005166e4, 0xea000d962ad18418, -+ 0xe42aef38359362d9, 0xc8e95657348a3891, 0xc162eca864f238c6, -+ 0xbe1fb373e20579ad, 0x628a1d4f40aa6ffd, 0xa87bdb7456340f90, -+ 0x5960ef3ba982c801, 0x5026586df9a431ec, 0xfe4b8a20fdf0840b, -+ 0xdcb761867da7072f, 0xc10d4653667275b7, 0x727720deec13110b, -+ 0x710b009662858dc9, 0xfbf8f7a3ecac1eb7, 0xb6fc4fcd0722e3df, -+ 0x7cb86dcc55104aac, 0x19e71e9b45c3a51e, 0x51de38573c2bea48, -+ 0xa73ab6996d6df158, 0x55ef2b8c930817b2, 0xb2850bf5fae87157, -+ 0xecf3de1acd04651f, 0xcc0a40552559ff32, 0xc385c374f20315b1, -+ 0xb90208a4c7234183, 0x58aa1ca7a4c075d9, - }; - -- for (const auto& expected_result : expected_results) { -+#if UPDATE_GOLDEN -+ (void)kGolden; // Silence warning. -+ for (size_t i = 0; i < kNumGoldenOutputs; ++i) { -+ std::string str; -+ ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str)); -+ uint64_t h = absl::hash_internal::Wyhash(str.data(), str.size(), -+ cases[i].seed, kSalt); -+ printf("0x%016" PRIx64 ", ", h); -+ if (i % 3 == 2) { -+ printf("\n"); -+ } -+ } -+ printf("\n\n\n"); -+ EXPECT_FALSE(true); -+#else -+ for (size_t i = 0; i < kNumGoldenOutputs; ++i) { -+ SCOPED_TRACE(::testing::Message() -+ << "i = " << i << "; input = " << cases[i].base64_data); - std::string str; -- ASSERT_TRUE(absl::Base64Unescape(expected_result.base64_data, &str)); -- EXPECT_EQ(absl::hash_internal::Wyhash(str.data(), str.size(), -- expected_result.seed, kSalt), -- expected_result.hash); -+ ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str)); -+ EXPECT_EQ(absl::hash_internal::Wyhash(str.data(), str.size(), cases[i].seed, -+ kSalt), -+ kGolden[i]); - } -+#endif - } - - } // namespace diff --git a/debian/patches/big-endian-hash2.diff b/debian/patches/big-endian-hash2.diff deleted file mode 100644 index 69ffa8a5..00000000 --- a/debian/patches/big-endian-hash2.diff +++ /dev/null @@ -1,164 +0,0 @@ -From: Milad Fa <46688537+miladfarca@users.noreply.github.com> -Subject: Fix hashing on big endian platforms (#1028) -Forwarded: https://github.com/abseil/abseil-cpp/pull/1028 -Origin: backport, https://github.com/abseil/abseil-cpp/commit/ae0f4c266095c9003786cd571bc1fb72544104a1 -Bug-Debian: https://bugs.debian.org/977638 - -Avoid using libstdc++'s implementation of std::hash and -std::hash on big endian platforms in the implementation -of absl::Hash. - -This is a workaround for a buggy implementation that results in many -collisions. - -https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 -https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98731 - ---- a/absl/hash/internal/hash.h -+++ b/absl/hash/internal/hash.h -@@ -21,6 +21,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -489,8 +490,9 @@ - - // AbslHashValue for hashing std::vector - // --// Do not use this for vector. It does not have a .data(), and a fallback --// for std::hash<> is most likely faster. -+// Do not use this for vector on platforms that have a working -+// implementation of std::hash. It does not have a .data(), and a fallback for -+// std::hash<> is most likely faster. - template - typename std::enable_if::value && !std::is_same::value, - H>::type -@@ -500,6 +502,27 @@ - vector.size()); - } - -+#if defined(ABSL_IS_BIG_ENDIAN) && \ -+ (defined(__GLIBCXX__) || defined(__GLIBCPP__)) -+// AbslHashValue for hashing std::vector -+// -+// std::hash in libstdc++ does not work correctly with vector on Big -+// Endian platforms therefore we need to implement a custom AbslHashValue for -+// it. More details on the bug: -+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 -+template -+typename std::enable_if::value && std::is_same::value, -+ H>::type -+AbslHashValue(H hash_state, const std::vector& vector) { -+ typename H::AbslInternalPiecewiseCombiner combiner; -+ for (const auto& i : vector) { -+ unsigned char c = static_cast(i); -+ hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c)); -+ } -+ return H::combine(combiner.finalize(std::move(hash_state)), vector.size()); -+} -+#endif -+ - // ----------------------------------------------------------------------------- - // AbslHashValue for Ordered Associative Containers - // ----------------------------------------------------------------------------- -@@ -592,9 +615,28 @@ - // AbslHashValue for Other Types - // ----------------------------------------------------------------------------- - --// AbslHashValue for hashing std::bitset is not defined, for the same reason as --// for vector (see std::vector above): It does not expose the raw bytes, --// and a fallback to std::hash<> is most likely faster. -+// AbslHashValue for hashing std::bitset is not defined on Little Endian -+// platforms, for the same reason as for vector (see std::vector above): -+// It does not expose the raw bytes, and a fallback to std::hash<> is most -+// likely faster. -+ -+#if defined(ABSL_IS_BIG_ENDIAN) && \ -+ (defined(__GLIBCXX__) || defined(__GLIBCPP__)) -+// AbslHashValue for hashing std::bitset -+// -+// std::hash in libstdc++ does not work correctly with std::bitset on Big Endian -+// platforms therefore we need to implement a custom AbslHashValue for it. More -+// details on the bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 -+template -+H AbslHashValue(H hash_state, const std::bitset& set) { -+ typename H::AbslInternalPiecewiseCombiner combiner; -+ for (int i = 0; i < N; i++) { -+ unsigned char c = static_cast(set[i]); -+ hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c)); -+ } -+ return H::combine(combiner.finalize(std::move(hash_state)), N); -+} -+#endif - - // ----------------------------------------------------------------------------- - ---- a/absl/hash/internal/wyhash_test.cc -+++ b/absl/hash/internal/wyhash_test.cc -@@ -404,6 +404,55 @@ - uint64_t{0xc9ae5c8759b4877a}}, - }; - -+#if defined(ABSL_IS_BIG_ENDIAN) -+ constexpr uint64_t kGolden[kNumGoldenOutputs] = { -+ 0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8, -+ 0xa6630143a7e6aa6f, 0x17645cb7318b86b, 0x218b175f30ba61f8, -+ 0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d, -+ 0xe7b01610fc22dbb8, 0x99d9f694404af913, 0xf4eecd37464b45c5, -+ 0x7d2c653d63596d9b, 0x3f15c8544ec5393a, 0x6b9dc0c1704f796c, -+ 0xf1ded7a7eae5ed5a, 0x2db2fd7c6dd4641b, 0x151ca2d3d4cd33ab, -+ 0xa5af5994ac2ccd64, 0x2b2a4ca3191d2fce, 0xf89e68c9364e7c05, -+ 0x71724c70b799c21, 0x70536fabfd157369, 0xdee92794c3c3082b, -+ 0xac033a6743d3b3eb, 0xed2956b506cd5151, 0xbd669644755264b6, -+ 0x6ab1ff5d5f549a63, 0xf6bd551a2e3e04e, 0x7b5a8cef6875ea73, -+ 0x22bccf4d4db0a91c, 0x4f2bc07754c7c7eb, 0xfb6b8342a86725db, -+ 0x13a1a0d4c5854da, 0x5f6e44655f7dedac, 0x54a9198dff2bdf85, -+ 0xdb17e6915d4e4042, 0xa69926cf5c3b89f, 0xf77f031bfd74c096, -+ 0x1d6f916fdd50ec3c, 0x334ac76013ade393, 0x99370f899111de15, -+ 0x352457a03ada6de, 0x341974d4f42d854d, 0xda89ab02872aeb5, -+ 0x6ec2b74e143b10d9, 0x6f284c0b5cd60522, 0xf9670de353438f88, -+ 0xde920913adf0a2b4, 0xb7a07d7c0c17a8ec, 0x879a69f558ba3a98, -+ 0x360cf6d802df20f9, 0x53530f8046673738, 0xbd8f5f2bcf35e483, -+ 0x3f171f047144b983, 0x644d04e820823465, 0x50e44773a20b2702, -+ 0xe584ed4c05c745dd, 0x9a825c85b95ab6c0, 0xbce2931deb74e775, -+ 0x10468e9e705c7cfe, 0x12e01de3104141e2, 0x5c11ae2ee3713abd, -+ 0x6ac5ffb0860319e6, 0xc1e6da1849d30fc9, 0xa0e4d247a458b447, -+ 0x4530d4615c32b89b, 0x116aa09107a76505, 0xf941339d00d9bb73, -+ 0x573a0fc1615afb33, 0xa975c81dc868b258, 0x3ab2c5250ab54bda, -+ 0x37f99f208a3e3b11, 0x4b49b0ff706689d, 0x30bafa0b8f0a87fe, -+ 0xea6787a65cc20cdd, 0x55861729f1fc3ab8, 0xea38e009c5be9b72, -+ 0xcb8522cba33c3c66, 0x352e77653fe306f3, 0xe0bb760793bac064, -+ 0xf66ec59322662956, 0x637aa320455d56f8, 0x46ee546be5824a89, -+ 0x9e6842421e83d8a4, 0xf98ac2bc96b9fb8c, 0xf2c1002fd9a70b99, -+ 0x4c2b62b1e39e9405, 0x3248555fa3ade9c4, 0xd4d04c37f6417c21, -+ 0xf40cd506b1bf5653, 0x6c45d6005c760d2f, 0x61d88a7e61ff0d7e, -+ 0x131591e8a53cc967, 0xdae85cb9bc29bab6, 0xe98835334905e626, -+ 0x7cce50a2b66b8754, 0x5b0b3d0c5ac498ae, 0xd35a218c974d1756, -+ 0xfce436ddc1d003c, 0xd183901de90bb741, 0x9378f8f34974a66, -+ 0x21f11ae0a0402368, 0xf2fbd7c94ef89cb6, 0xc329c69d0f0d080b, -+ 0xf2841cba16216a61, 0x47aba97b44916df1, 0x724d4e00a8019fcf, -+ 0x2df9005c2a728d63, 0xc788892a1a5d7515, 0x9e993a65f9df0480, -+ 0x76876721ff49f969, 0xbe7a796cfba15bf5, 0xa4c8bd54586f5488, -+ 0xb390a325275501ab, 0x893f11317427ccf1, 0x92f2bb57da5695b9, -+ 0x30985b90da88269f, 0x2c690e268e086de8, 0x1c02df6097997196, -+ 0x1f9778f8bbdf6455, 0x7d57378c7bf8416d, 0xba8582a5f8d84d38, -+ 0xe8ca43b85050be4e, 0x5048cf6bed8a5d9f, 0xfbc5ba80917d0ea4, -+ 0x8011026525bf1691, 0x26b8dc6aed9fb50d, 0x191f5bfee77c1fe3, -+ 0xdd497891465a2cc1, 0x6f1fe8c57a33072e, 0x2c9f4ec078c460c0, -+ 0x9a725bde8f6a1437, 0x6ce545fa3ef61e4d, -+ }; -+#else - constexpr uint64_t kGolden[kNumGoldenOutputs] = { - 0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8, - 0xa6630143a7e6aa6f, 0x8787cb2d04b0c984, 0x33603654ff574ac2, -@@ -451,6 +500,7 @@ - 0xecf3de1acd04651f, 0xcc0a40552559ff32, 0xc385c374f20315b1, - 0xb90208a4c7234183, 0x58aa1ca7a4c075d9, - }; -+#endif - - #if UPDATE_GOLDEN - (void)kGolden; // Silence warning. diff --git a/debian/patches/big-endian-random.diff b/debian/patches/big-endian-random.diff deleted file mode 100644 index a859aca5..00000000 --- a/debian/patches/big-endian-random.diff +++ /dev/null @@ -1,698 +0,0 @@ -From: Benjamin Barenblat -Subject: Use absl::uint128 for AES random number generator -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/b06e719ee985ecd63e0dffbc68499549216f817f - -Replace randen’s internal 128-bit integer struct, u64x2, with -absl::uint128. This eliminates some code and improves support for -big-endian platforms. - -The author works at Google. Upstream applied this patch as Piper -revision 383475671 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/random/internal/BUILD.bazel -+++ b/absl/random/internal/BUILD.bazel -@@ -296,6 +296,7 @@ cc_library(name = "randen_slow", - ":platform", - "//absl/base:config", - "//absl/base:core_headers", -+ "//absl/numeric:int128", - ], - ) - -@@ -336,6 +337,7 @@ cc_library(name="randen_hwaes_impl", - ":platform", - "//absl/base:config", - "//absl/base:core_headers", -+ "//absl/numeric:int128", - ], - ) - ---- a/absl/random/internal/randen_hwaes.cc -+++ b/absl/random/internal/randen_hwaes.cc -@@ -23,6 +23,7 @@ - #include - - #include "absl/base/attributes.h" -+#include "absl/numeric/int128.h" - #include "absl/random/internal/platform.h" - #include "absl/random/internal/randen_traits.h" - -@@ -120,11 +121,6 @@ - - using absl::random_internal::RandenTraits; - --// Randen operates on 128-bit vectors. --struct alignas(16) u64x2 { -- uint64_t data[2]; --}; -- - } // namespace - - // TARGET_CRYPTO defines a crypto attribute for each architecture. -@@ -186,7 +182,7 @@ - } - - // Enables native loads in the round loop by pre-swapping. --inline ABSL_TARGET_CRYPTO void SwapEndian(u64x2* state) { -+inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) { - for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) { - Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block); - } -@@ -327,7 +323,7 @@ - - // Block shuffles applies a shuffle to the entire state between AES rounds. - // Improved odd-even shuffle from "New criterion for diffusion property". --inline ABSL_TARGET_CRYPTO void BlockShuffle(u64x2* state) { -+inline ABSL_TARGET_CRYPTO void BlockShuffle(absl::uint128* state) { - static_assert(RandenTraits::kFeistelBlocks == 16, - "Expecting 16 FeistelBlocks."); - -@@ -374,8 +370,9 @@ - // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in - // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel - // XORs are 'free' (included in the second AES instruction). --inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( -- u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { -+inline ABSL_TARGET_CRYPTO const absl::uint128* FeistelRound( -+ absl::uint128* state, -+ const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { - static_assert(RandenTraits::kFeistelBlocks == 16, - "Expecting 16 FeistelBlocks."); - -@@ -436,7 +433,8 @@ - // 2^64 queries if the round function is a PRF. This is similar to the b=8 case - // of Simpira v2, but more efficient than its generic construction for b=16. - inline ABSL_TARGET_CRYPTO void Permute( -- u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { -+ absl::uint128* state, -+ const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { - // (Successfully unrolled; the first iteration jumps into the second half) - #ifdef __clang__ - #pragma clang loop unroll_count(2) -@@ -473,10 +471,11 @@ void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void, - static_assert(RandenTraits::kStateBytes / sizeof(Vector128) == 16, - "Unexpected Randen kStateBlocks"); - -- auto* state = -- reinterpret_cast(state_void); -+ auto* state = reinterpret_cast( -+ state_void); - const auto* seed = -- reinterpret_cast(seed_void); -+ reinterpret_cast( -+ seed_void); - - Vector128 b1 = Vector128Load(state + 1); - b1 ^= Vector128Load(seed + 0); -@@ -545,8 +544,8 @@ void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys_void, - static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128), - "Capacity mismatch"); - -- auto* state = reinterpret_cast(state_void); -- const auto* keys = reinterpret_cast(keys_void); -+ auto* state = reinterpret_cast(state_void); -+ const auto* keys = reinterpret_cast(keys_void); - - const Vector128 prev_inner = Vector128Load(state); - ---- a/absl/random/internal/randen_slow.cc -+++ b/absl/random/internal/randen_slow.cc -@@ -19,6 +19,7 @@ - #include - - #include "absl/base/attributes.h" -+#include "absl/numeric/int128.h" - #include "absl/random/internal/platform.h" - #include "absl/random/internal/randen_traits.h" - -@@ -38,192 +39,193 @@ - namespace { - - // AES portions based on rijndael-alg-fst.c, --// https://fastcrypto.org/front/misc/rijndael-alg-fst.c -+// https://fastcrypto.org/front/misc/rijndael-alg-fst.c, and modified for -+// little-endianness. - // - // Implementation of - // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf - constexpr uint32_t te0[256] = { -- 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, -- 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, -- 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, -- 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, -- 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, -- 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, -- 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, -- 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, -- 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, -- 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, -- 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, -- 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, -- 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, -- 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, -- 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, -- 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, -- 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, -- 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, -- 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, -- 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, -- 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, -- 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, -- 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, -- 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, -- 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, -- 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, -- 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, -- 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, -- 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, -- 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, -- 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, -- 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, -- 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, -- 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, -- 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, -- 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, -- 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, -- 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, -- 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, -- 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, -- 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, -- 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, -- 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, -+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, -+ 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, -+ 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, -+ 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, -+ 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, -+ 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, -+ 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, -+ 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, -+ 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, -+ 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, -+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, -+ 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, -+ 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, -+ 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, -+ 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, -+ 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, -+ 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, -+ 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, -+ 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, -+ 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, -+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, -+ 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, -+ 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, -+ 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, -+ 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, -+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, -+ 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, -+ 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, -+ 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, -+ 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, -+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, -+ 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, -+ 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, -+ 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, -+ 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, -+ 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, -+ 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, -+ 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, -+ 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, -+ 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, -+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, -+ 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, -+ 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, - }; - - constexpr uint32_t te1[256] = { -- 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, -- 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, -- 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, -- 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, -- 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, -- 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, -- 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, -- 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, -- 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, -- 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, -- 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, -- 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, -- 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, -- 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, -- 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, -- 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, -- 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, -- 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, -- 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, -- 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, -- 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, -- 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, -- 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, -- 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, -- 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, -- 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, -- 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, -- 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, -- 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, -- 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, -- 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, -- 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, -- 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, -- 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, -- 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, -- 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, -- 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, -- 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, -- 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, -- 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, -- 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, -- 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, -- 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, -+ 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, -+ 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, -+ 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, -+ 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, -+ 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, -+ 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, -+ 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, -+ 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, -+ 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, -+ 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, -+ 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, -+ 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, -+ 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, -+ 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, -+ 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, -+ 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, -+ 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, -+ 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, -+ 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, -+ 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, -+ 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, -+ 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, -+ 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, -+ 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, -+ 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, -+ 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, -+ 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, -+ 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, -+ 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, -+ 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, -+ 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, -+ 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, -+ 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, -+ 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, -+ 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, -+ 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, -+ 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, -+ 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, -+ 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, -+ 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, -+ 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, -+ 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, -+ 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a, - }; - - constexpr uint32_t te2[256] = { -- 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, -- 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, -- 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, -- 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, -- 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, -- 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, -- 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, -- 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, -- 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, -- 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, -- 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, -- 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, -- 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, -- 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, -- 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, -- 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, -- 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, -- 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, -- 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, -- 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, -- 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, -- 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, -- 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, -- 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, -- 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, -- 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, -- 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, -- 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, -- 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, -- 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, -- 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, -- 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, -- 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, -- 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, -- 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, -- 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, -- 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, -- 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, -- 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, -- 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, -- 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, -- 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, -- 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, -+ 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, -+ 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, -+ 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, -+ 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, -+ 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, -+ 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, -+ 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, -+ 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, -+ 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, -+ 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, -+ 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, -+ 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, -+ 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, -+ 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, -+ 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, -+ 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, -+ 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, -+ 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, -+ 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, -+ 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, -+ 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, -+ 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, -+ 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, -+ 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, -+ 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, -+ 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, -+ 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, -+ 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, -+ 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, -+ 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, -+ 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, -+ 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, -+ 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, -+ 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, -+ 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, -+ 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, -+ 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, -+ 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, -+ 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, -+ 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, -+ 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, -+ 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, -+ 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16, - }; - - constexpr uint32_t te3[256] = { -- 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, -- 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, -- 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, -- 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, -- 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, -- 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, -- 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, -- 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, -- 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, -- 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, -- 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, -- 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, -- 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, -- 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, -- 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, -- 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, -- 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, -- 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, -- 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, -- 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, -- 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, -- 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, -- 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, -- 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, -- 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, -- 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, -- 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, -- 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, -- 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, -- 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, -- 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, -- 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, -- 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, -- 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, -- 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, -- 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, -- 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, -- 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, -- 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, -- 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, -- 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, -- 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, -- 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, -+ 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, -+ 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, -+ 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, -+ 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, -+ 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, -+ 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, -+ 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, -+ 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, -+ 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, -+ 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, -+ 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, -+ 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, -+ 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, -+ 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, -+ 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, -+ 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, -+ 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, -+ 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, -+ 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, -+ 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, -+ 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, -+ 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, -+ 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, -+ 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, -+ 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, -+ 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, -+ 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, -+ 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, -+ 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, -+ 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, -+ 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, -+ 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, -+ 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, -+ 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, -+ 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, -+ 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, -+ 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, -+ 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, -+ 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, -+ 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, -+ 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, -+ 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, -+ 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616, - }; - - // Software implementation of the Vector128 class, using uint32_t -@@ -235,45 +237,13 @@ - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 - Vector128Load(const void* from) { - Vector128 result; -- const uint8_t* src = reinterpret_cast(from); -- result.s[0] = static_cast(src[0]) << 24 | -- static_cast(src[1]) << 16 | -- static_cast(src[2]) << 8 | -- static_cast(src[3]); -- result.s[1] = static_cast(src[4]) << 24 | -- static_cast(src[5]) << 16 | -- static_cast(src[6]) << 8 | -- static_cast(src[7]); -- result.s[2] = static_cast(src[8]) << 24 | -- static_cast(src[9]) << 16 | -- static_cast(src[10]) << 8 | -- static_cast(src[11]); -- result.s[3] = static_cast(src[12]) << 24 | -- static_cast(src[13]) << 16 | -- static_cast(src[14]) << 8 | -- static_cast(src[15]); -+ std::memcpy(result.s, from, sizeof(Vector128)); - return result; - } - - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store( - const Vector128& v, void* to) { -- uint8_t* dst = reinterpret_cast(to); -- dst[0] = static_cast(v.s[0] >> 24); -- dst[1] = static_cast(v.s[0] >> 16); -- dst[2] = static_cast(v.s[0] >> 8); -- dst[3] = static_cast(v.s[0]); -- dst[4] = static_cast(v.s[1] >> 24); -- dst[5] = static_cast(v.s[1] >> 16); -- dst[6] = static_cast(v.s[1] >> 8); -- dst[7] = static_cast(v.s[1]); -- dst[8] = static_cast(v.s[2] >> 24); -- dst[9] = static_cast(v.s[2] >> 16); -- dst[10] = static_cast(v.s[2] >> 8); -- dst[11] = static_cast(v.s[2]); -- dst[12] = static_cast(v.s[3] >> 24); -- dst[13] = static_cast(v.s[3] >> 16); -- dst[14] = static_cast(v.s[3] >> 8); -- dst[15] = static_cast(v.s[3]); -+ std::memcpy(to, v.s, sizeof(Vector128)); - } - - // One round of AES. "round_key" is a public constant for breaking the -@@ -282,38 +252,33 @@ - AesRound(const Vector128& state, const Vector128& round_key) { - Vector128 result; - result.s[0] = round_key.s[0] ^ // -- te0[uint8_t(state.s[0] >> 24)] ^ // -- te1[uint8_t(state.s[1] >> 16)] ^ // -- te2[uint8_t(state.s[2] >> 8)] ^ // -- te3[uint8_t(state.s[3])]; -+ te0[uint8_t(state.s[0])] ^ // -+ te1[uint8_t(state.s[1] >> 8)] ^ // -+ te2[uint8_t(state.s[2] >> 16)] ^ // -+ te3[uint8_t(state.s[3] >> 24)]; - result.s[1] = round_key.s[1] ^ // -- te0[uint8_t(state.s[1] >> 24)] ^ // -- te1[uint8_t(state.s[2] >> 16)] ^ // -- te2[uint8_t(state.s[3] >> 8)] ^ // -- te3[uint8_t(state.s[0])]; -+ te0[uint8_t(state.s[1])] ^ // -+ te1[uint8_t(state.s[2] >> 8)] ^ // -+ te2[uint8_t(state.s[3] >> 16)] ^ // -+ te3[uint8_t(state.s[0] >> 24)]; - result.s[2] = round_key.s[2] ^ // -- te0[uint8_t(state.s[2] >> 24)] ^ // -- te1[uint8_t(state.s[3] >> 16)] ^ // -- te2[uint8_t(state.s[0] >> 8)] ^ // -- te3[uint8_t(state.s[1])]; -+ te0[uint8_t(state.s[2])] ^ // -+ te1[uint8_t(state.s[3] >> 8)] ^ // -+ te2[uint8_t(state.s[0] >> 16)] ^ // -+ te3[uint8_t(state.s[1] >> 24)]; - result.s[3] = round_key.s[3] ^ // -- te0[uint8_t(state.s[3] >> 24)] ^ // -- te1[uint8_t(state.s[0] >> 16)] ^ // -- te2[uint8_t(state.s[1] >> 8)] ^ // -- te3[uint8_t(state.s[2])]; -+ te0[uint8_t(state.s[3])] ^ // -+ te1[uint8_t(state.s[0] >> 8)] ^ // -+ te2[uint8_t(state.s[1] >> 16)] ^ // -+ te3[uint8_t(state.s[2] >> 24)]; - return result; - } - - using ::absl::random_internal::RandenTraits; - --// Randen operates on 128-bit vectors. --struct alignas(16) u64x2 { -- uint64_t data[2]; --}; -- - // The improved Feistel block shuffle function for 16 blocks. - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( -- u64x2* state) { -+ absl::uint128* state) { - static_assert(RandenTraits::kFeistelBlocks == 16, - "Feistel block shuffle only works for 16 blocks."); - -@@ -323,31 +288,31 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( - // The fully unrolled loop without the memcpy improves the speed by about - // 30% over the equivalent: - #if 0 -- u64x2 source[RandenTraits::kFeistelBlocks]; -+ absl::uint128 source[RandenTraits::kFeistelBlocks]; - std::memcpy(source, state, sizeof(source)); - for (size_t i = 0; i < RandenTraits::kFeistelBlocks; i++) { -- const u64x2 v0 = source[shuffle[i]]; -+ const absl::uint128 v0 = source[shuffle[i]]; - state[i] = v0; - } - return; - #endif - -- const u64x2 v0 = state[shuffle[0]]; -- const u64x2 v1 = state[shuffle[1]]; -- const u64x2 v2 = state[shuffle[2]]; -- const u64x2 v3 = state[shuffle[3]]; -- const u64x2 v4 = state[shuffle[4]]; -- const u64x2 v5 = state[shuffle[5]]; -- const u64x2 v6 = state[shuffle[6]]; -- const u64x2 v7 = state[shuffle[7]]; -- const u64x2 w0 = state[shuffle[8]]; -- const u64x2 w1 = state[shuffle[9]]; -- const u64x2 w2 = state[shuffle[10]]; -- const u64x2 w3 = state[shuffle[11]]; -- const u64x2 w4 = state[shuffle[12]]; -- const u64x2 w5 = state[shuffle[13]]; -- const u64x2 w6 = state[shuffle[14]]; -- const u64x2 w7 = state[shuffle[15]]; -+ const absl::uint128 v0 = state[shuffle[0]]; -+ const absl::uint128 v1 = state[shuffle[1]]; -+ const absl::uint128 v2 = state[shuffle[2]]; -+ const absl::uint128 v3 = state[shuffle[3]]; -+ const absl::uint128 v4 = state[shuffle[4]]; -+ const absl::uint128 v5 = state[shuffle[5]]; -+ const absl::uint128 v6 = state[shuffle[6]]; -+ const absl::uint128 v7 = state[shuffle[7]]; -+ const absl::uint128 w0 = state[shuffle[8]]; -+ const absl::uint128 w1 = state[shuffle[9]]; -+ const absl::uint128 w2 = state[shuffle[10]]; -+ const absl::uint128 w3 = state[shuffle[11]]; -+ const absl::uint128 w4 = state[shuffle[12]]; -+ const absl::uint128 w5 = state[shuffle[13]]; -+ const absl::uint128 w6 = state[shuffle[14]]; -+ const absl::uint128 w7 = state[shuffle[15]]; - state[0] = v0; - state[1] = v1; - state[2] = v2; -@@ -371,9 +336,9 @@ - // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in - // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel - // XORs are 'free' (included in the second AES instruction). --inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound( -- u64x2* ABSL_RANDOM_INTERNAL_RESTRICT state, -- const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { -+inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const absl::uint128* -+FeistelRound(absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT state, -+ const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { - for (size_t branch = 0; branch < RandenTraits::kFeistelBlocks; branch += 4) { - const Vector128 s0 = Vector128Load(state + branch); - const Vector128 s1 = Vector128Load(state + branch + 1); -@@ -398,7 +363,8 @@ - // 2^64 queries if the round function is a PRF. This is similar to the b=8 case - // of Simpira v2, but more efficient than its generic construction for b=16. - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute( -- u64x2* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { -+ absl::uint128* state, -+ const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { - for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) { - keys = FeistelRound(state, keys); - BlockShuffle(state); -@@ -437,19 +403,18 @@ - } - - void RandenSlow::Generate(const void* keys_void, void* state_void) { -- static_assert(RandenTraits::kCapacityBytes == sizeof(u64x2), -+ static_assert(RandenTraits::kCapacityBytes == sizeof(absl::uint128), - "Capacity mismatch"); - -- auto* state = reinterpret_cast(state_void); -- const auto* keys = reinterpret_cast(keys_void); -+ auto* state = reinterpret_cast(state_void); -+ const auto* keys = reinterpret_cast(keys_void); - -- const u64x2 prev_inner = state[0]; -+ const absl::uint128 prev_inner = state[0]; - - Permute(state, keys); - - // Ensure backtracking resistance. -- state[0].data[0] ^= prev_inner.data[0]; -- state[0].data[1] ^= prev_inner.data[1]; -+ *state ^= prev_inner; - } - - } // namespace random_internal diff --git a/debian/patches/big-endian-random2.diff b/debian/patches/big-endian-random2.diff deleted file mode 100644 index 12893c9a..00000000 --- a/debian/patches/big-endian-random2.diff +++ /dev/null @@ -1,90 +0,0 @@ -From: Benjamin Barenblat -Subject: Make randen_slow endian-correct -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/33541e751039a8c4bd3a395dd1a3a0928885814a - -Pay attention to the platform endianness when pulling bytes out of each -AES block, and use platform-endian round keys. - -The author works at Google. Upstream applied this patch as Piper -revision 383878281 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/random/internal/BUILD.bazel -+++ b/absl/random/internal/BUILD.bazel -@@ -296,6 +296,7 @@ - ":platform", - "//absl/base:config", - "//absl/base:core_headers", -+ "//absl/base:endian", - "//absl/numeric:int128", - ], - ) ---- a/absl/random/internal/randen_slow.cc -+++ b/absl/random/internal/randen_slow.cc -@@ -19,6 +19,7 @@ - #include - - #include "absl/base/attributes.h" -+#include "absl/base/internal/endian.h" - #include "absl/numeric/int128.h" - #include "absl/random/internal/platform.h" - #include "absl/random/internal/randen_traits.h" -@@ -40,7 +41,7 @@ - - // AES portions based on rijndael-alg-fst.c, - // https://fastcrypto.org/front/misc/rijndael-alg-fst.c, and modified for --// little-endianness. -+// platform-endianness. - // - // Implementation of - // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf -@@ -251,6 +252,7 @@ - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 - AesRound(const Vector128& state, const Vector128& round_key) { - Vector128 result; -+#ifdef ABSL_IS_LITTLE_ENDIAN - result.s[0] = round_key.s[0] ^ // - te0[uint8_t(state.s[0])] ^ // - te1[uint8_t(state.s[1] >> 8)] ^ // -@@ -271,6 +273,28 @@ - te1[uint8_t(state.s[0] >> 8)] ^ // - te2[uint8_t(state.s[1] >> 16)] ^ // - te3[uint8_t(state.s[2] >> 24)]; -+#else -+ result.s[0] = round_key.s[0] ^ // -+ te0[uint8_t(state.s[0])] ^ // -+ te1[uint8_t(state.s[3] >> 8)] ^ // -+ te2[uint8_t(state.s[2] >> 16)] ^ // -+ te3[uint8_t(state.s[1] >> 24)]; -+ result.s[1] = round_key.s[1] ^ // -+ te0[uint8_t(state.s[1])] ^ // -+ te1[uint8_t(state.s[0] >> 8)] ^ // -+ te2[uint8_t(state.s[3] >> 16)] ^ // -+ te3[uint8_t(state.s[2] >> 24)]; -+ result.s[2] = round_key.s[2] ^ // -+ te0[uint8_t(state.s[2])] ^ // -+ te1[uint8_t(state.s[1] >> 8)] ^ // -+ te2[uint8_t(state.s[0] >> 16)] ^ // -+ te3[uint8_t(state.s[3] >> 24)]; -+ result.s[3] = round_key.s[3] ^ // -+ te0[uint8_t(state.s[3])] ^ // -+ te1[uint8_t(state.s[2] >> 8)] ^ // -+ te2[uint8_t(state.s[1] >> 16)] ^ // -+ te3[uint8_t(state.s[0] >> 24)]; -+#endif - return result; - } - -@@ -380,7 +404,11 @@ - const void* RandenSlow::GetKeys() { - // Round keys for one AES per Feistel round and branch. - // The canonical implementation uses first digits of Pi. -+#ifdef ABSL_IS_LITTLE_ENDIAN - return kRandenRoundKeys; -+#else -+ return kRandenRoundKeysBE; -+#endif - } - - void RandenSlow::Absorb(const void* seed_void, void* state_void) { diff --git a/debian/patches/big-endian-random3.diff b/debian/patches/big-endian-random3.diff deleted file mode 100644 index a7847a00..00000000 --- a/debian/patches/big-endian-random3.diff +++ /dev/null @@ -1,82 +0,0 @@ -From: Laramie Leavitt -Subject: Also use uint8_t golden values in randen_test.cc -Forwarded: yes -Origin: upstream, https://github.com/abseil/abseil-cpp/commit/cc413f8b674d61e3aa948386432e526e051afca0 - -Also use uint8_t golden values in randen_test.cc - -This makes randen_test, randen_slow_test, and randen_hwaes_test essentially -identical, as is the intent. - -The author works at Google. Upstream applied this patch as Piper -revision 405484423 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/random/internal/randen_test.cc -+++ b/absl/random/internal/randen_test.cc -@@ -23,9 +23,6 @@ - - using absl::random_internal::Randen; - --// Local state parameters. --constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t); -- - TEST(RandenTest, CopyAndMove) { - static_assert(std::is_copy_constructible::value, - "Randen must be copy constructible"); -@@ -41,30 +38,38 @@ - } - - TEST(RandenTest, Default) { -- constexpr uint64_t kGolden[] = { -- 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, -- 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, -- 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, -- 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, -- 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, -- 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, -- 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, -- 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, -- 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, -- 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, -- 0x026ff374c101da7e, 0x811ef0821c3de851, -+ constexpr uint8_t kGolden[] = { -+ 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d, -+ 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3, -+ 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f, -+ 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0, -+ 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff, -+ 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2, -+ 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5, -+ 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c, -+ 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56, -+ 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4, -+ 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91, -+ 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5, -+ 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d, -+ 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58, -+ 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e, -+ 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49, -+ 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0, -+ 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6, -+ 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61, -+ 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35, -+ 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c, -+ 0x82, 0xf0, 0x1e, 0x81, - }; - -- alignas(16) uint64_t state[kStateSizeT]; -+ alignas(16) uint8_t state[Randen::kStateBytes]; - std::memset(state, 0, sizeof(state)); - - Randen r; - r.Generate(state); - -- auto id = std::begin(state); -- for (const auto& elem : kGolden) { -- EXPECT_EQ(elem, *id++); -- } -+ EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state))); - } - - } // namespace diff --git a/debian/patches/big-endian-random4.diff b/debian/patches/big-endian-random4.diff deleted file mode 100644 index 15110401..00000000 --- a/debian/patches/big-endian-random4.diff +++ /dev/null @@ -1,71 +0,0 @@ -From: Milad Fa <46688537+miladfarca@users.noreply.github.com> -Subject: Fix Randen and PCG on Big Endian platforms (#1031) -Forwarded: https://github.com/abseil/abseil-cpp/pull/1031 -Origin: upstream, https://github.com/abseil/abseil-cpp/commit/022527c50e0e2bc937f9fa3c516e3e36cbba0845 - ---- a/absl/random/internal/explicit_seed_seq.h -+++ b/absl/random/internal/explicit_seed_seq.h -@@ -74,7 +74,7 @@ - template - void generate(OutIterator begin, OutIterator end) { - for (size_t index = 0; begin != end; begin++) { -- *begin = state_.empty() ? 0 : little_endian::FromHost32(state_[index++]); -+ *begin = state_.empty() ? 0 : state_[index++]; - if (index >= state_.size()) { - index = 0; - } ---- a/absl/random/internal/randen_engine.h -+++ b/absl/random/internal/randen_engine.h -@@ -121,6 +121,13 @@ void reseed(SeedSequence& seq) { - const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size; - std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0); - seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy); -+#ifdef ABSL_IS_BIG_ENDIAN -+ // Randen expects the seed buffer to be in Little Endian; reverse it on -+ // Big Endian platforms. -+ for (sequence_result_type& e : buffer) { -+ e = absl::little_endian::FromHost(e); -+ } -+#endif - // The Randen paper suggests preferentially initializing even-numbered - // 128-bit vectors of the randen state (there are 16 such vectors). - // The seed data is merged into the state offset by 128-bits, which ---- a/absl/random/internal/randen_slow.cc -+++ b/absl/random/internal/randen_slow.cc -@@ -395,6 +395,23 @@ - } - } - -+// Enables native loads in the round loop by pre-swapping. -+inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void SwapEndian( -+ absl::uint128* state) { -+#ifdef ABSL_IS_BIG_ENDIAN -+ for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) { -+ uint64_t new_lo = absl::little_endian::ToHost64( -+ static_cast(state[block] >> 64)); -+ uint64_t new_hi = absl::little_endian::ToHost64( -+ static_cast((state[block] << 64) >> 64)); -+ state[block] = (static_cast(new_hi) << 64) | new_lo; -+ } -+#else -+ // Avoid warning about unused variable. -+ (void)state; -+#endif -+} -+ - } // namespace - - namespace absl { -@@ -439,8 +456,12 @@ void RandenSlow::Generate(const void* keys_void, void* state_void) { - - const absl::uint128 prev_inner = state[0]; - -+ SwapEndian(state); -+ - Permute(state, keys); - -+ SwapEndian(state); -+ - // Ensure backtracking resistance. - *state ^= prev_inner; - } diff --git a/debian/patches/configure.diff b/debian/patches/configure.diff index 8138899f..15a20382 100644 --- a/debian/patches/configure.diff +++ b/debian/patches/configure.diff @@ -22,13 +22,13 @@ Configure Abseil for Debian. --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake -@@ -263,7 +263,8 @@ +@@ -281,7 +281,8 @@ if(ABSL_ENABLE_INSTALL) set_target_properties(${_NAME} PROPERTIES OUTPUT_NAME "absl_${_NAME}" -- SOVERSION "2103.0.1" -+ SOVERSION 20210324 -+ VERSION "20210324.0.0" +- SOVERSION "2206.0.0" ++ SOVERSION 20220623 ++ VERSION "20220623.0.0" ) endif() else() @@ -74,8 +74,8 @@ Configure Abseil for Debian. // allowed. #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 --#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20210324 -+#define ABSL_OPTION_INLINE_NAMESPACE_NAME debian2 +-#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20220623 ++#define ABSL_OPTION_INLINE_NAMESPACE_NAME debian3 // ABSL_OPTION_HARDENED // @@ -87,29 +87,23 @@ Configure Abseil for Debian. +#define ABSL_OPTION_HARDENED 1 #endif // ABSL_BASE_OPTIONS_H_ ---- a/absl/container/internal/have_sse.h -+++ b/absl/container/internal/have_sse.h -@@ -17,22 +17,14 @@ - #define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ - - #ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 --#if defined(__SSE2__) || \ -- (defined(_MSC_VER) && \ -- (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) -+#if defined(__x86_64__) || (defined(_MSC_VER) && defined(_M_X64)) - #define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 1 - #else - #define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 0 +--- a/absl/base/config.h ++++ b/absl/base/config.h +@@ -877,7 +877,7 @@ + // which architectures support the various x86 instruction sets. + #ifdef ABSL_INTERNAL_HAVE_SSE2 + #error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set +-#elif defined(__SSE2__) ++#elif defined(__x86_64__) + #define ABSL_INTERNAL_HAVE_SSE2 1 + #elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) + // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 +@@ -898,8 +898,6 @@ + // by the CPU. + #ifdef ABSL_INTERNAL_HAVE_SSSE3 + #error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set +-#elif defined(__SSSE3__) +-#define ABSL_INTERNAL_HAVE_SSSE3 1 #endif - #endif - --#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 --#ifdef __SSSE3__ --#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 1 --#else - #define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 0 --#endif --#endif - #if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 && \ - !ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 + // ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM diff --git a/debian/patches/cordrepring-typo.diff b/debian/patches/cordrepring-typo.diff deleted file mode 100644 index 6f21896b..00000000 --- a/debian/patches/cordrepring-typo.diff +++ /dev/null @@ -1,20 +0,0 @@ -From: Benjamin Barenblat -Subject: Fix typo in CordRepRing error message -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/b97a1ecda869ca8754d467a56c50275cebfeb328 - -The author works at Google. Upstream applied this patch as Piper -revision 367481280 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/strings/internal/cord_rep_ring.cc -+++ b/absl/strings/internal/cord_rep_ring.cc -@@ -301,7 +301,7 @@ - if (offset >= child->length || entry_length > child->length - offset) { - output << "entry[" << head << "] has offset " << offset - << " and entry length " << entry_length -- << " which are outside of the childs length of " << child->length; -+ << " which are outside of the child's length of " << child->length; - return false; - } - diff --git a/debian/patches/disable-nominalcpufrequency-test.diff b/debian/patches/disable-nominalcpufrequency-test.diff deleted file mode 100644 index dc15dc9e..00000000 --- a/debian/patches/disable-nominalcpufrequency-test.diff +++ /dev/null @@ -1,46 +0,0 @@ -From: Derek Mauro -Subject: Remove the test for absl::base_internal::NominalCPUFrequency() from OSS code -Origin: upstream, https://github.com/abseil/abseil-cpp/commit/732b5580f089101ce4b8cdff55bb6461c59a6720 - -Remove the test for absl::base_internal::NominalCPUFrequency() from OSS code - -This is an internal-only function that should never by called by OSS code. -By its nature fails on unsupported platforms. -Google code has tests for this function on supported internal platforms. - -Fixes #1053 - -PiperOrigin-RevId: 408692861 - ---- a/absl/base/internal/sysinfo_test.cc -+++ b/absl/base/internal/sysinfo_test.cc -@@ -37,29 +37,6 @@ - << "NumCPUs() should not have the default value of 0"; - } - --// Ensure that NominalCPUFrequency returns a reasonable value, or 1.00 on --// platforms where the CPU frequency is not available through sysfs. --// --// POWER is particularly problematic here; some Linux kernels expose the CPU --// frequency, while others do not. Since we can't predict a priori what a given --// machine is going to do, just disable this test on POWER on Linux. --#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__))) --TEST(SysinfoTest, NominalCPUFrequency) { -- // Linux only exposes the CPU frequency on certain architectures, and -- // Emscripten doesn't expose it at all. --#if defined(__linux__) && \ -- (defined(__aarch64__) || defined(__hppa__) || defined(__mips__) || \ -- defined(__riscv) || defined(__s390x__)) || \ -- defined(__EMSCRIPTEN__) -- EXPECT_EQ(NominalCPUFrequency(), 1.0) -- << "CPU frequency detection was fixed! Please update unittest."; --#else -- EXPECT_GE(NominalCPUFrequency(), 1000.0) -- << "NominalCPUFrequency() did not return a reasonable value"; --#endif --} --#endif -- - TEST(SysinfoTest, GetTID) { - EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test. - #ifdef __native_client__ diff --git a/debian/patches/empty-flags-library.diff b/debian/patches/empty-flags-library.diff index fe1d3962..8c26a6d7 100644 --- a/debian/patches/empty-flags-library.diff +++ b/debian/patches/empty-flags-library.diff @@ -8,7 +8,7 @@ library header-only. --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt -@@ -197,8 +197,6 @@ +@@ -199,8 +199,6 @@ absl_cc_library( NAME flags diff --git a/debian/patches/float-rounding.diff b/debian/patches/float-rounding.diff deleted file mode 100644 index 005e08be..00000000 --- a/debian/patches/float-rounding.diff +++ /dev/null @@ -1,46 +0,0 @@ -From: Benjamin Barenblat -Subject: Round floats using round(x), not static_cast(x + 0.5) -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/d96e287417766deddbff2d01b96321288c59491e - -Adding 0.5 to an IEEE float may cause one bit of precision loss, which -is enough to change the result in certain cases. For example, - - static_cast(std::round(0.49999999999999994)) == 0 - static_cast(0.49999999999999994 + 0.5) == 1 - -The author works at Google. Upstream applied this patch as Piper -revision 369926519 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/time/duration_test.cc -+++ b/absl/time/duration_test.cc -@@ -1320,7 +1320,7 @@ TEST(Duration, SmallConversions) { - - EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0)); - // TODO(bww): Is the next one OK? -- EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0.124999999e-9)); -+ EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(0.125e-9, 0))); - EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.125e-9)); - EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.250e-9)); - EXPECT_EQ(absl::Nanoseconds(1) / 2, absl::Seconds(0.375e-9)); -@@ -1330,7 +1330,7 @@ TEST(Duration, SmallConversions) { - EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(0.875e-9)); - EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(1.000e-9)); - -- EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(-0.124999999e-9)); -+ EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(-0.125e-9, 0))); - EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.125e-9)); - EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.250e-9)); - EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.375e-9)); ---- a/absl/time/time.h -+++ b/absl/time/time.h -@@ -1352,7 +1352,7 @@ - inline Duration MakePosDoubleDuration(double n) { - const int64_t int_secs = static_cast(n); - const uint32_t ticks = static_cast( -- (n - static_cast(int_secs)) * kTicksPerSecond + 0.5); -+ std::round((n - static_cast(int_secs)) * kTicksPerSecond)); - return ticks < kTicksPerSecond - ? MakeDuration(int_secs, ticks) - : MakeDuration(int_secs + 1, ticks - kTicksPerSecond); diff --git a/debian/patches/float-tests-disable-i386.diff b/debian/patches/float-tests-disable-i386.diff deleted file mode 100644 index 0aef2805..00000000 --- a/debian/patches/float-tests-disable-i386.diff +++ /dev/null @@ -1,141 +0,0 @@ -From: Benjamin Barenblat -Subject: Skip floating-point edge-case tests when using an x87 -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/311bbd2e50ea35e921a08186840d3b6ca279e880 - -32-bit Intel CPUs use 80-bit floats for intermediate values, which can -change the results of floating point computations from what we normally -expect. Identify tests that are sensitive to the x87, and skip them when -we’re on 32-bit Intel. - -The author works at Google. Upstream applied this patch as Piper -revision 378722613 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/random/beta_distribution_test.cc -+++ b/absl/random/beta_distribution_test.cc -@@ -15,6 +15,7 @@ - #include "absl/random/beta_distribution.h" - - #include -+#include - #include - #include - #include -@@ -558,6 +559,14 @@ - // dependencies of the distribution change, such as RandU64ToDouble, then this - // is also likely to change. - TEST(BetaDistributionTest, AlgorithmBounds) { -+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 -+ // We're using an x87-compatible FPU, and intermediate operations are -+ // performed with 80-bit floats. This produces slightly different results from -+ // what we expect below. -+ GTEST_SKIP() -+ << "Skipping the test because we detected x87 floating-point semantics"; -+#endif -+ - { - absl::random_internal::sequence_urbg urbg( - {0x7fbe76c8b4395800ull, 0x8000000000000000ull}); ---- a/absl/random/distributions_test.cc -+++ b/absl/random/distributions_test.cc -@@ -14,6 +14,7 @@ - - #include "absl/random/distributions.h" - -+#include - #include - #include - #include -@@ -224,6 +225,15 @@ - TEST_F(RandomDistributionsTest, UniformNonsenseRanges) { - // The ranges used in this test are undefined behavior. - // The results are arbitrary and subject to future changes. -+ -+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 -+ // We're using an x87-compatible FPU, and intermediate operations can be -+ // performed with 80-bit floats. This produces slightly different results from -+ // what we expect below. -+ GTEST_SKIP() -+ << "Skipping the test because we detected x87 floating-point semantics"; -+#endif -+ - absl::InsecureBitGen gen; - - // ---- a/absl/random/exponential_distribution_test.cc -+++ b/absl/random/exponential_distribution_test.cc -@@ -15,6 +15,7 @@ - #include "absl/random/exponential_distribution.h" - - #include -+#include - #include - #include - #include -@@ -384,6 +385,15 @@ - TEST(ExponentialDistributionTest, AlgorithmBounds) { - // Relies on absl::uniform_real_distribution, so some of these comments - // reference that. -+ -+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 -+ // We're using an x87-compatible FPU, and intermediate operations can be -+ // performed with 80-bit floats. This produces slightly different results from -+ // what we expect below. -+ GTEST_SKIP() -+ << "Skipping the test because we detected x87 floating-point semantics"; -+#endif -+ - absl::exponential_distribution dist; - - { ---- a/absl/random/uniform_real_distribution_test.cc -+++ b/absl/random/uniform_real_distribution_test.cc -@@ -14,6 +14,7 @@ - - #include "absl/random/uniform_real_distribution.h" - -+#include - #include - #include - #include -@@ -70,6 +71,14 @@ - TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); - - TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { -+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 -+ // We're using an x87-compatible FPU, and intermediate operations are -+ // performed with 80-bit floats. This produces slightly different results from -+ // what we expect below. -+ GTEST_SKIP() -+ << "Skipping the test because we detected x87 floating-point semantics"; -+#endif -+ - using param_type = - typename absl::uniform_real_distribution::param_type; - ---- a/absl/time/duration_test.cc -+++ b/absl/time/duration_test.cc -@@ -17,6 +17,7 @@ - #endif - - #include // NOLINT(build/c++11) -+#include - #include - #include - #include -@@ -1390,6 +1391,14 @@ - // Seconds(point) returns a duration near point * Seconds(1.0). (They may - // not be exactly equal due to fused multiply/add contraction.) - TEST(Duration, ToDoubleSecondsCheckEdgeCases) { -+#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 -+ // We're using an x87-compatible FPU, and intermediate operations can be -+ // performed with 80-bit floats. This means the edge cases are different than -+ // what we expect here, so just skip this test. -+ GTEST_SKIP() -+ << "Skipping the test because we detected x87 floating-point semantics"; -+#endif -+ - constexpr uint32_t kTicksPerSecond = absl::time_internal::kTicksPerSecond; - constexpr auto duration_tick = absl::time_internal::MakeDuration(0, 1u); - int misses = 0; diff --git a/debian/patches/hppa-symbolize.diff b/debian/patches/hppa-symbolize.diff deleted file mode 100644 index 8c039df9..00000000 --- a/debian/patches/hppa-symbolize.diff +++ /dev/null @@ -1,103 +0,0 @@ -From: Benjamin Barenblat -Subject: Support symbolization on PA-RISC -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/7f850b3167fb38e6b4a9ce1824e6fabd733b5d62 - -Null out supervisor bits in PA-RISC addresses before symbolizing, and -handle function descriptor tables correctly. - -Change symbolize_test.cc to use 32-bit aligned addresses, allowing that -test to pass on PA-RISC. - -The author works at Google. Upstream applied this patch as Piper -revision 428590564 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/debugging/symbolize_elf.inc -+++ b/absl/debugging/symbolize_elf.inc -@@ -319,6 +319,7 @@ - const ptrdiff_t relocation, - char *out, int out_size, - char *tmp_buf, int tmp_buf_size); -+ const char *GetUncachedSymbol(const void *pc); - - enum { - SYMBOL_BUF_SIZE = 3072, -@@ -1329,13 +1330,7 @@ - // they are called here as well. - // To keep stack consumption low, we would like this function to not - // get inlined. --const char *Symbolizer::GetSymbol(const void *const pc) { -- const char *entry = FindSymbolInCache(pc); -- if (entry != nullptr) { -- return entry; -- } -- symbol_buf_[0] = '\0'; -- -+const char *Symbolizer::GetUncachedSymbol(const void *pc) { - ObjFile *const obj = FindObjFile(pc, 1); - ptrdiff_t relocation = 0; - int fd = -1; -@@ -1423,6 +1418,42 @@ - return InsertSymbolInCache(pc, symbol_buf_); - } - -+const char *Symbolizer::GetSymbol(const void *pc) { -+ const char *entry = FindSymbolInCache(pc); -+ if (entry != nullptr) { -+ return entry; -+ } -+ symbol_buf_[0] = '\0'; -+ -+#ifdef __hppa__ -+ { -+ // In some contexts (e.g., return addresses), PA-RISC uses the lowest two -+ // bits of the address to indicate the privilege level. Clear those bits -+ // before trying to symbolize. -+ const auto pc_bits = reinterpret_cast(pc); -+ const auto address = pc_bits & ~0x3; -+ entry = GetUncachedSymbol(reinterpret_cast(address)); -+ if (entry != nullptr) { -+ return entry; -+ } -+ -+ // In some contexts, PA-RISC also uses bit 1 of the address to indicate that -+ // this is a cross-DSO function pointer. Such function pointers actually -+ // point to a procedure label, a struct whose first 32-bit (pointer) element -+ // actually points to the function text. With no symbol found for this -+ // address so far, try interpreting it as a cross-DSO function pointer and -+ // see how that goes. -+ if (pc_bits & 0x2) { -+ return GetUncachedSymbol(*reinterpret_cast(address)); -+ } -+ -+ return nullptr; -+ } -+#else -+ return GetUncachedSymbol(pc); -+#endif -+} -+ - bool RemoveAllSymbolDecorators(void) { - if (!g_decorators_mu.TryLock()) { - // Someone else is using decorators. Get out. ---- a/absl/debugging/symbolize_test.cc -+++ b/absl/debugging/symbolize_test.cc -@@ -378,12 +378,14 @@ - DummySymbolDecorator, &c_message), - 0); - -- char *address = reinterpret_cast(1); -- EXPECT_STREQ("abc", TrySymbolize(address++)); -+ // Use addresses 4 and 8 here to ensure that we always use valid addresses -+ // even on systems that require instructions to be 32-bit aligned. -+ char *address = reinterpret_cast(4); -+ EXPECT_STREQ("abc", TrySymbolize(address)); - - EXPECT_TRUE(absl::debugging_internal::RemoveSymbolDecorator(ticket_b)); - -- EXPECT_STREQ("ac", TrySymbolize(address++)); -+ EXPECT_STREQ("ac", TrySymbolize(address + 4)); - - // Cleanup: remove all remaining decorators so other stack traces don't - // get mystery "ac" decoration. diff --git a/debian/patches/latomic.diff b/debian/patches/latomic.diff index 861f20bf..71cf7959 100644 --- a/debian/patches/latomic.diff +++ b/debian/patches/latomic.diff @@ -9,7 +9,7 @@ if anything needs libatomic and add the DT_NEEDED entry where necessary. --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake -@@ -62,4 +62,8 @@ +@@ -94,4 +94,8 @@ set(ABSL_TEST_COPTS "") endif() diff --git a/debian/patches/missing-rint.diff b/debian/patches/missing-rint.diff deleted file mode 100644 index d9865364..00000000 --- a/debian/patches/missing-rint.diff +++ /dev/null @@ -1,45 +0,0 @@ -From: Benjamin Barenblat -Subject: Round a double multiplication before casting it to integer -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/60be12ed9822078970f05f3c560324184302df6b - -The code - - static_cast(x * y) - -(for double x and y) performs a double multiplication into a temporary -that, by standard, may have excess precision. The subsequent cast to int -discards the excess precision. However, the cast may examine the excess -precision during conversion, producing surprising results like - - static_cast(1.7 * 10) == 16 - -on certain systems. Correct this case by explicitly rounding 1.7 * 10 -before casting it. - -The author works at Google. Upstream applied this patch as Piper -revision 378922064 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/random/mocking_bit_gen_test.cc -+++ b/absl/random/mocking_bit_gen_test.cc -@@ -15,6 +15,7 @@ - // - #include "absl/random/mocking_bit_gen.h" - -+#include - #include - #include - -@@ -328,8 +329,9 @@ TEST(BasicMocking, WillByDefaultWithArgs) { - - absl::MockingBitGen gen; - ON_CALL(absl::MockPoisson(), Call(gen, _)) -- .WillByDefault( -- [](double lambda) { return static_cast(lambda * 10); }); -+ .WillByDefault([](double lambda) { -+ return static_cast(std::rint(lambda * 10)); -+ }); - EXPECT_EQ(absl::Poisson(gen, 1.7), 17); - EXPECT_EQ(absl::Poisson(gen, 0.03), 0); - } diff --git a/debian/patches/pkg-config-libs-generation.diff b/debian/patches/pkg-config-libs-generation.diff deleted file mode 100644 index 5f217765..00000000 --- a/debian/patches/pkg-config-libs-generation.diff +++ /dev/null @@ -1,36 +0,0 @@ -From: Benjamin Barenblat -Subject: Check printf format strings in str_format_convert_test -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/1963f10ae5cb32a7ea6d96b928f69d3c7fba0139 -Bug-Debian: https://bugs.debian.org/1011294 - -Don’t use generator expression to build .pc Libs lines - -When building pkg-config files, compute linker flags with a string -substitution rather than the JOIN generator expression. This ensures -that commas in linker flags don’t get treated as argument separators in -JOIN. - -The author works at Google. Upstream applied this patch as Piper -revision 450675966 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/CMake/AbseilHelpers.cmake -+++ b/CMake/AbseilHelpers.cmake -@@ -167,6 +167,7 @@ - set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") - endif() - endforeach() -+ string(REPLACE ";" " " PC_LINKOPTS "${ABSL_CC_LIB_LINKOPTS}") - FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\ - prefix=${CMAKE_INSTALL_PREFIX}\n\ - exec_prefix=\${prefix}\n\ -@@ -178,7 +179,7 @@ - URL: https://abseil.io/\n\ - Version: ${PC_VERSION}\n\ - Requires:${PC_DEPS}\n\ --Libs: -L\${libdir} $ $<$>:-labsl_${_NAME}>\n\ -+Libs: -L\${libdir} ${PC_LINKOPTS} $<$>:-labsl_${_NAME}>\n\ - Cflags: -I\${includedir}${PC_CFLAGS}\n") - INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/debian/patches/series b/debian/patches/series index f335570e..7134596e 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,19 +1,3 @@ configure.diff latomic.diff empty-flags-library.diff -cordrepring-typo.diff -thumb-function-bounds.diff -float-rounding.diff -DiscreteDistributionTest-irrelevant-destination-buckets.diff -float-tests-disable-i386.diff -missing-rint.diff -big-endian-hash.diff -big-endian-hash2.diff -big-endian-random.diff -big-endian-random2.diff -big-endian-random3.diff -big-endian-random4.diff -disable-nominalcpufrequency-test.diff -hppa-symbolize.diff -str-format-convert-test-printf.diff -pkg-config-libs-generation.diff diff --git a/debian/patches/str-format-convert-test-printf.diff b/debian/patches/str-format-convert-test-printf.diff deleted file mode 100644 index 1808f2e1..00000000 --- a/debian/patches/str-format-convert-test-printf.diff +++ /dev/null @@ -1,103 +0,0 @@ -From: Benjamin Barenblat -Subject: Check printf format strings in str_format_convert_test -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/9fed77a6fea29b8c8468bd41c6259c7f67163a65 - -Add ABSL_PRINTF_ATTRIBUTE to appropriate functions in -strings/internal/str_format/convert_test. Correct -TypedFormatConvertTest.Char, which was accidentally passing values of -types larger than int to StrPrint. - -The author works at Google. Upstream applied this patch as Piper -revision 439388148 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/strings/BUILD.bazel -+++ b/absl/strings/BUILD.bazel -@@ -787,6 +787,7 @@ - deps = [ - ":str_format_internal", - ":strings", -+ "//absl/base:core_headers", - "//absl/base:raw_logging_internal", - "//absl/types:optional", - "@com_google_googletest//:gtest_main", ---- a/absl/strings/CMakeLists.txt -+++ b/absl/strings/CMakeLists.txt -@@ -492,6 +492,7 @@ - DEPS - absl::strings - absl::str_format_internal -+ absl::core_headers - absl::raw_logging_internal - absl::int128 - gmock_main ---- a/absl/strings/internal/str_format/convert_test.cc -+++ b/absl/strings/internal/str_format/convert_test.cc -@@ -24,6 +24,7 @@ - - #include "gmock/gmock.h" - #include "gtest/gtest.h" -+#include "absl/base/attributes.h" - #include "absl/base/internal/raw_logging.h" - #include "absl/strings/internal/str_format/bind.h" - #include "absl/strings/match.h" -@@ -124,6 +125,7 @@ - delete[] buf; - } - -+void StrAppend(std::string *, const char *, ...) ABSL_PRINTF_ATTRIBUTE(2, 3); - void StrAppend(std::string *out, const char *format, ...) { - va_list ap; - va_start(ap, format); -@@ -131,6 +133,7 @@ - va_end(ap); - } - -+std::string StrPrint(const char *, ...) ABSL_PRINTF_ATTRIBUTE(1, 2); - std::string StrPrint(const char *format, ...) { - va_list ap; - va_start(ap, format); -@@ -452,21 +455,32 @@ - } - - TYPED_TEST_P(TypedFormatConvertTest, Char) { -+ // Pass a bunch of values of type TypeParam to both FormatPack and libc's -+ // vsnprintf("%c", ...) (wrapped in StrPrint) to make sure we get the same -+ // value. - typedef TypeParam T; - using remove_volatile_t = typename std::remove_volatile::type; -- static const T kMin = std::numeric_limits::min(); -- static const T kMax = std::numeric_limits::max(); -- T kVals[] = { -- remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10), -- remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10), -- remove_volatile_t(0), -- kMin + remove_volatile_t(1), kMin, -- kMax - remove_volatile_t(1), kMax -+ std::vector vals = { -+ remove_volatile_t(1), remove_volatile_t(2), remove_volatile_t(10), // -+ remove_volatile_t(-1), remove_volatile_t(-2), remove_volatile_t(-10), // -+ remove_volatile_t(0), - }; -- for (const T &c : kVals) { -+ -+ // We'd like to test values near std::numeric_limits::min() and -+ // std::numeric_limits::max(), too, but vsnprintf("%c", ...) can't handle -+ // anything larger than an int. Add in the most extreme values we can without -+ // exceeding that range. -+ static const T kMin = -+ static_cast(std::numeric_limits::min()); -+ static const T kMax = -+ static_cast(std::numeric_limits::max()); -+ vals.insert(vals.end(), {kMin + 1, kMin, kMax - 1, kMax}); -+ -+ for (const T c : vals) { - const FormatArgImpl args[] = {FormatArgImpl(c)}; - UntypedFormatSpecImpl format("%c"); -- EXPECT_EQ(StrPrint("%c", c), FormatPack(format, absl::MakeSpan(args))); -+ EXPECT_EQ(StrPrint("%c", static_cast(c)), -+ FormatPack(format, absl::MakeSpan(args))); - } - } - diff --git a/debian/patches/thumb-function-bounds.diff b/debian/patches/thumb-function-bounds.diff deleted file mode 100644 index 1fd8c729..00000000 --- a/debian/patches/thumb-function-bounds.diff +++ /dev/null @@ -1,96 +0,0 @@ -From: Benjamin Barenblat -Subject: Correct Thumb function bound computation in the symbolizer -Forwarded: yes -Applied-Upstream: https://github.com/abseil/abseil-cpp/commit/1ae9b71c474628d60eb251a3f62967fe64151bb2 - -On 32-bit ARM, all functions are aligned to multiples of two bytes, and -the lowest-order bit in a function’s address is ignored by the CPU when -computing branch targets. That bit is still present in instructions and -ELF symbol tables, though; it’s repurposed to indicate whether the -function contains ARM or Thumb code. If the symbolizer doesn’t ignore -that bit, it will believe Thumb functions have boundaries that are off -by one byte, so instruct the symbolizer to null out the lowest-order bit -after retrieving it from the symbol table. - -The author works at Google. Upstream applied this patch as Piper -revision 369254082 and exported it to GitHub; the Applied-Upstream URL -above points to the exported commit. - ---- a/absl/debugging/symbolize_elf.inc -+++ b/absl/debugging/symbolize_elf.inc -@@ -701,6 +701,16 @@ - const char *start_address = - ComputeOffset(original_start_address, relocation); - -+#ifdef __arm__ -+ // ARM functions are always aligned to multiples of two bytes; the -+ // lowest-order bit in start_address is ignored by the CPU and indicates -+ // whether the function contains ARM (0) or Thumb (1) code. We don't care -+ // about what encoding is being used; we just want the real start address -+ // of the function. -+ start_address = reinterpret_cast( -+ reinterpret_cast(start_address) & ~1); -+#endif -+ - if (deref_function_descriptor_pointer && - InSection(original_start_address, opd)) { - // The opd section is mapped into memory. Just dereference ---- a/absl/debugging/symbolize_test.cc -+++ b/absl/debugging/symbolize_test.cc -@@ -477,6 +477,46 @@ - #endif - } - -+#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) -+// Test that we correctly identify bounds of Thumb functions on ARM. -+// -+// Thumb functions have the lowest-order bit set in their addresses in the ELF -+// symbol table. This requires some extra logic to properly compute function -+// bounds. To test this logic, nudge a Thumb function right up against an ARM -+// function and try to symbolize the ARM function. -+// -+// A naive implementation will simply use the Thumb function's entry point as -+// written in the symbol table and will therefore treat the Thumb function as -+// extending one byte further in the instruction stream than it actually does. -+// When asked to symbolize the start of the ARM function, it will identify an -+// overlap between the Thumb and ARM functions, and it will return the name of -+// the Thumb function. -+// -+// A correct implementation, on the other hand, will null out the lowest-order -+// bit in the Thumb function's entry point. It will correctly compute the end of -+// the Thumb function, it will find no overlap between the Thumb and ARM -+// functions, and it will return the name of the ARM function. -+ -+__attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) { -+ return x * x * x; -+} -+ -+__attribute__((target("arm"))) int ArmThumbOverlapArm(int x) { -+ return x * x * x; -+} -+ -+void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() { -+#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) -+ const char *symbol = TrySymbolize((void *)&ArmThumbOverlapArm); -+ ABSL_RAW_CHECK(symbol != nullptr, "TestArmThumbOverlap failed"); -+ ABSL_RAW_CHECK(strcmp("ArmThumbOverlapArm()", symbol) == 0, -+ "TestArmThumbOverlap failed"); -+ std::cout << "TestArmThumbOverlap passed" << std::endl; -+#endif -+} -+ -+#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) -+ - #elif defined(_WIN32) - #if !defined(ABSL_CONSUME_DLL) - -@@ -551,6 +591,9 @@ - TestWithPCInsideInlineFunction(); - TestWithPCInsideNonInlineFunction(); - TestWithReturnAddress(); -+#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) -+ TestArmThumbOverlap(); -+#endif - #endif - - return RUN_ALL_TESTS(); -- cgit v1.2.3 From 73fdf3fb48dd390f4c517c11eac0fc68d3f07388 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 12 Jul 2022 22:23:45 -0400 Subject: Explicitly specify support status for SSE and NEON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turn SSE off on i386 since Debian still supports the Pentium II. Similarly, turn NEON off on armel and armhf since it’s not universally supported. Split CPU feature support configuration into its own patch for clarity. --- debian/patches/configure.diff | 27 -------------------- debian/patches/cpu-features.diff | 54 ++++++++++++++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 debian/patches/cpu-features.diff diff --git a/debian/patches/configure.diff b/debian/patches/configure.diff index 15a20382..ff0c64e9 100644 --- a/debian/patches/configure.diff +++ b/debian/patches/configure.diff @@ -13,13 +13,6 @@ Configure Abseil for Debian. - Enable upstream's hardened build mode. - - Disable Intel SSE2 on i386, since Debian supports some i386 processors - without that extension. Keep it enabled on amd64, since all amd64 processors - have it. - - - Disable Intel SSSE3 entirely, since no i386 processor supports it and Debian - supports amd64 processors without it. - --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -281,7 +281,8 @@ @@ -87,23 +80,3 @@ Configure Abseil for Debian. +#define ABSL_OPTION_HARDENED 1 #endif // ABSL_BASE_OPTIONS_H_ ---- a/absl/base/config.h -+++ b/absl/base/config.h -@@ -877,7 +877,7 @@ - // which architectures support the various x86 instruction sets. - #ifdef ABSL_INTERNAL_HAVE_SSE2 - #error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set --#elif defined(__SSE2__) -+#elif defined(__x86_64__) - #define ABSL_INTERNAL_HAVE_SSE2 1 - #elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) - // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 -@@ -898,8 +898,6 @@ - // by the CPU. - #ifdef ABSL_INTERNAL_HAVE_SSSE3 - #error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set --#elif defined(__SSSE3__) --#define ABSL_INTERNAL_HAVE_SSSE3 1 - #endif - - // ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM diff --git a/debian/patches/cpu-features.diff b/debian/patches/cpu-features.diff new file mode 100644 index 00000000..7610e6b1 --- /dev/null +++ b/debian/patches/cpu-features.diff @@ -0,0 +1,54 @@ +From: Benjamin Barenblat +Subject: Canonicalize supported CPU feature set +Forwarded: not-needed + +Explicitly set supported CPU features. + + - Disable Intel SSE and SSE2 on i386, since Debian supports some i386 + processors without those extensions. Keep them enabled on amd64, since all + amd64 processors have them. + + - Disable Intel SSSE3 entirely, since no i386 processor supports it and Debian + supports amd64 processors without it. + + - Disable NEON on armel and armhf, since no armel processor supports NEON and + Debian supports some armhf processors without it. Keep it enabled on arm64, + since all arm64 processors have it. + +--- a/absl/base/config.h ++++ b/absl/base/config.h +@@ -862,7 +862,7 @@ + // which architectures support the various x86 instruction sets. + #ifdef ABSL_INTERNAL_HAVE_SSE + #error ABSL_INTERNAL_HAVE_SSE cannot be directly set +-#elif defined(__SSE__) ++#elif defined(__x86_64__) + #define ABSL_INTERNAL_HAVE_SSE 1 + #elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) + // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1 +@@ -877,7 +877,7 @@ + // which architectures support the various x86 instruction sets. + #ifdef ABSL_INTERNAL_HAVE_SSE2 + #error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set +-#elif defined(__SSE2__) ++#elif defined(__x86_64__) + #define ABSL_INTERNAL_HAVE_SSE2 1 + #elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) + // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 +@@ -898,15 +898,13 @@ + // by the CPU. + #ifdef ABSL_INTERNAL_HAVE_SSSE3 + #error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set +-#elif defined(__SSSE3__) +-#define ABSL_INTERNAL_HAVE_SSSE3 1 + #endif + + // ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM + // SIMD). + #ifdef ABSL_INTERNAL_HAVE_ARM_NEON + #error ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set +-#elif defined(__ARM_NEON) ++#elif defined(__aarch64__) + #define ABSL_INTERNAL_HAVE_ARM_NEON 1 + #endif + diff --git a/debian/patches/series b/debian/patches/series index 7134596e..b64772ac 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,3 +1,4 @@ configure.diff +cpu-features.diff latomic.diff empty-flags-library.diff -- cgit v1.2.3 From 2d3219a143b5dae0fe85a7c2ceae72fab8c66864 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 12 Jul 2022 22:28:24 -0400 Subject: Build in C++17 mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debian 12’s GCC will default to C++17, so start building Abseil in C++17 mode. To maximize compatibility, continue to treat absl::any, absl::optional, absl::string_view, and absl::variant as distinct types rather than aliases for C++17 types in the std namespace. --- debian/rules | 6 +++--- debian/tests/cmake | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/debian/rules b/debian/rules index 536ca72a..0584fd92 100755 --- a/debian/rules +++ b/debian/rules @@ -34,11 +34,11 @@ override_dh_auto_clean: $(RM) -r $(CURDIR)/shared override_dh_auto_configure: - dh_auto_configure -Bstatic -- -DCMAKE_CXX_STANDARD=14 -DBUILD_SHARED_LIBS=OFF + dh_auto_configure -Bstatic -- -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=OFF ifeq ($(ABSL_RUN_TESTS),ON) - dh_auto_configure -Bshared -- -DCMAKE_CXX_STANDARD=14 -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=OFF + dh_auto_configure -Bshared -- -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=OFF else - dh_auto_configure -Bshared -- -DCMAKE_CXX_STANDARD=14 -DBUILD_SHARED_LIBS=ON + dh_auto_configure -Bshared -- -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON endif override_dh_auto_build: diff --git a/debian/tests/cmake b/debian/tests/cmake index 1eb04de9..43eb3408 100755 --- a/debian/tests/cmake +++ b/debian/tests/cmake @@ -32,7 +32,7 @@ EOF cat >CMakeLists.txt < Date: Tue, 12 Jul 2022 22:30:03 -0400 Subject: Bump googletest dependency Abseil 20220623 requires a fairly new googletest for tests to compile correctly. Record that dependency in debian/control. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index bb83adf6..1a7ec77f 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,7 @@ Maintainer: Benjamin Barenblat Build-Depends: cmake (>= 3.5), debhelper-compat (= 12), - googletest (>= 1.10.0.20200926) [!mipsel !ppc64] + googletest (>= 1.12) [!mipsel !ppc64] Rules-Requires-Root: no Standards-Version: 4.6.0 Section: libs -- cgit v1.2.3 From 9b799d6f69653b49151412e1bacd44aa7923649d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 22 Aug 2022 17:30:49 -0400 Subject: Set ABSL_BUILD_TESTING Since upstream commit fb7dd24b18e82893e5922be5d1c8ae0f3fe3c9fa, Abseil requires both BUILD_TESTING=ON and ABSL_BUILD_TESTING=ON to build unit tests. --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 0584fd92..86492154 100755 --- a/debian/rules +++ b/debian/rules @@ -36,7 +36,7 @@ override_dh_auto_clean: override_dh_auto_configure: dh_auto_configure -Bstatic -- -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=OFF ifeq ($(ABSL_RUN_TESTS),ON) - dh_auto_configure -Bshared -- -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=OFF + dh_auto_configure -Bshared -- -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DABSL_BUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=OFF else dh_auto_configure -Bshared -- -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON endif -- cgit v1.2.3 From 22472eec9f3745d411cfa7f59480520f2c1b2f79 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 22 Aug 2022 17:56:17 -0400 Subject: Update Standards-Version --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 1a7ec77f..aeb749e4 100644 --- a/debian/control +++ b/debian/control @@ -20,7 +20,7 @@ Build-Depends: debhelper-compat (= 12), googletest (>= 1.12) [!mipsel !ppc64] Rules-Requires-Root: no -Standards-Version: 4.6.0 +Standards-Version: 4.6.1 Section: libs Homepage: https://abseil.io/ Vcs-Browser: https://salsa.debian.org/debian/abseil -- cgit v1.2.3 From 249486eb57cbc93a8fe56a453bfcee6601914423 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 22 Aug 2022 22:09:04 -0400 Subject: Update shlibs file / Lintian overrides library list --- debian/libabsl20220623.lintian-overrides | 3 +-- debian/libabsl20220623.shlibs | 8 ++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/debian/libabsl20220623.lintian-overrides b/debian/libabsl20220623.lintian-overrides index fa4d374b..d4c26706 100644 --- a/debian/libabsl20220623.lintian-overrides +++ b/debian/libabsl20220623.lintian-overrides @@ -34,7 +34,6 @@ libabsl20220623: no-symbols-control-file usr/lib/*/libabsl_* libabsl20220623: package-name-doesnt-match-sonames libabsl-* libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_city.so* libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_leak_check.so* -libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_leak_check_disable.so* +libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_low_level_hash.so* libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_platform.so* libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_randen_hwaes.so* -libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_wyhash.so* diff --git a/debian/libabsl20220623.shlibs b/debian/libabsl20220623.shlibs index 44f011fa..b5e77c0c 100644 --- a/debian/libabsl20220623.shlibs +++ b/debian/libabsl20220623.shlibs @@ -19,6 +19,11 @@ libabsl_base 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_city 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_civil_time 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_cord 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_cord_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_cordz_functions 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_cordz_handle 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_cordz_info 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_cordz_sample_token 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_debugging_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_demangle_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_examine_stack 20220623 libabsl20220623 (>= 0~20220623.0-1) @@ -40,8 +45,8 @@ libabsl_hash 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_hashtablez_sampler 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_int128 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_leak_check 20220623 libabsl20220623 (>= 0~20220623.0-1) -libabsl_leak_check_disable 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_log_severity 20220623 libabsl20220623 (>= 0~20220623.0-1) +libabsl_low_level_hash 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_malloc_internal 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_periodic_sampler 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_random_distributions 20220623 libabsl20220623 (>= 0~20220623.0-1) @@ -71,4 +76,3 @@ libabsl_synchronization 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_throw_delegate 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_time 20220623 libabsl20220623 (>= 0~20220623.0-1) libabsl_time_zone 20220623 libabsl20220623 (>= 0~20220623.0-1) -libabsl_wyhash 20220623 libabsl20220623 (>= 0~20220623.0-1) -- cgit v1.2.3 From af492d49300e83d93b7107bc9bf7a15763e9e98a Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 22 Aug 2022 17:59:23 -0400 Subject: Update Lintian overrides’ format for latest Lintian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debian/libabsl20220623.lintian-overrides | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/debian/libabsl20220623.lintian-overrides b/debian/libabsl20220623.lintian-overrides index d4c26706..ce96faa8 100644 --- a/debian/libabsl20220623.lintian-overrides +++ b/debian/libabsl20220623.lintian-overrides @@ -15,25 +15,25 @@ libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_base.so*] libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_debugging_internal.so*] libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_random_internal_seed_material.so*] -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_bad_any_cast_impl.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_bad_optional_access.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_bad_variant_access.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_exponential_biased.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_flags_commandlineflag.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_flags_commandlineflag_internal.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_hash.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_hashtablez_sampler.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_log_severity.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_periodic_sampler.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen_hwaes_impl.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_random_internal_randen_slow.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_stacktrace.so* -libabsl20220623: library-not-linked-against-libc usr/lib/*/libabsl_throw_delegate.so* +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_bad_any_cast_impl.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_bad_optional_access.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_bad_variant_access.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_exponential_biased.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_flags_commandlineflag.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_flags_commandlineflag_internal.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_hash.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_hashtablez_sampler.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_log_severity.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_periodic_sampler.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_random_internal_randen.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_random_internal_randen_hwaes_impl.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_random_internal_randen_slow.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_stacktrace.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_throw_delegate.so*] libabsl20220623: no-symbols-control-file usr/lib/*/libabsl_* libabsl20220623: package-name-doesnt-match-sonames libabsl-* -libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_city.so* -libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_leak_check.so* -libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_low_level_hash.so* -libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_platform.so* -libabsl20220623: shared-library-lacks-prerequisites usr/lib/*/libabsl_random_internal_randen_hwaes.so* +libabsl20220623: shared-library-lacks-prerequisites [usr/lib/*/libabsl_city.so*] +libabsl20220623: shared-library-lacks-prerequisites [usr/lib/*/libabsl_leak_check.so*] +libabsl20220623: shared-library-lacks-prerequisites [usr/lib/*/libabsl_low_level_hash.so*] +libabsl20220623: shared-library-lacks-prerequisites [usr/lib/*/libabsl_random_internal_platform.so*] +libabsl20220623: shared-library-lacks-prerequisites [usr/lib/*/libabsl_random_internal_randen_hwaes.so*] -- cgit v1.2.3 From a4ddd83bdb4e4afe0f761c49859843f45b4feaab Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 22 Aug 2022 22:09:13 -0400 Subject: Update Lintian overrides --- debian/libabsl20220623.lintian-overrides | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/debian/libabsl20220623.lintian-overrides b/debian/libabsl20220623.lintian-overrides index ce96faa8..e00d857e 100644 --- a/debian/libabsl20220623.lintian-overrides +++ b/debian/libabsl20220623.lintian-overrides @@ -15,20 +15,21 @@ libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_base.so*] libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_debugging_internal.so*] libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_random_internal_seed_material.so*] +libabsl20220623: hardening-no-fortify-functions [usr/lib/*/libabsl_time_zone.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_bad_any_cast_impl.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_bad_optional_access.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_bad_variant_access.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_cordz_functions.so*] +libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_cordz_sample_token.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_exponential_biased.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_flags_commandlineflag.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_flags_commandlineflag_internal.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_hash.so*] -libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_hashtablez_sampler.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_log_severity.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_periodic_sampler.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_random_internal_randen.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_random_internal_randen_hwaes_impl.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_random_internal_randen_slow.so*] -libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_stacktrace.so*] libabsl20220623: library-not-linked-against-libc [usr/lib/*/libabsl_throw_delegate.so*] libabsl20220623: no-symbols-control-file usr/lib/*/libabsl_* libabsl20220623: package-name-doesnt-match-sonames libabsl-* -- cgit v1.2.3 From 1da493fa0f1003aa14bfd59a64cb6fef8fe40f08 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 22 Aug 2022 22:26:31 -0400 Subject: Release for unstable --- debian/changelog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 64e20be9..e5a1c38c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -abseil (0~20220623.0-1) UNRELEASED; urgency=medium +abseil (0~20220623.0-1) unstable; urgency=medium - * New upstream release. (Closes: #1008730) + * New upstream release. (Closes: #1008730, #1012194) - -- Benjamin Barenblat Tue, 12 Jul 2022 21:50:18 -0400 + -- Benjamin Barenblat Mon, 22 Aug 2022 22:17:36 -0400 abseil (0~20210324.2-4) unstable; urgency=medium -- cgit v1.2.3 From de1b4de8da1cb0127da16824c40d205a0d1c29aa Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 30 Aug 2022 22:40:57 -0400 Subject: Backport an upstream patch to fix pkg-config file generation See https://github.com/abseil/abseil-cpp/pull/1216 (upstream commit 09e96049995584c3489e4bd1467313e3e85af99c) for full details. --- debian/changelog | 6 ++++++ debian/patches/leaky-pkgconfig-cflags.diff | 16 ++++++++++++++++ debian/patches/series | 1 + 3 files changed, 23 insertions(+) create mode 100644 debian/patches/leaky-pkgconfig-cflags.diff diff --git a/debian/changelog b/debian/changelog index e5a1c38c..8056265d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +abseil (0~20220623.0-2) UNRELEASED; urgency=medium + + * Backport an upstream patch to correct pkg-config file generation. + + -- Benjamin Barenblat Tue, 30 Aug 2022 22:40:21 -0400 + abseil (0~20220623.0-1) unstable; urgency=medium * New upstream release. (Closes: #1008730, #1012194) diff --git a/debian/patches/leaky-pkgconfig-cflags.diff b/debian/patches/leaky-pkgconfig-cflags.diff new file mode 100644 index 00000000..cf1479d4 --- /dev/null +++ b/debian/patches/leaky-pkgconfig-cflags.diff @@ -0,0 +1,16 @@ +From: Bruno Pitrus +Subject: Do not leak -maes -msse4.1 into pkgconfig +Forwarded: https://github.com/abseil/abseil-cpp/pull/1216 +Origin: upstream, https://github.com/abseil/abseil-cpp/commit/09e96049995584c3489e4bd1467313e3e85af99c + +--- a/CMake/AbseilHelpers.cmake ++++ b/CMake/AbseilHelpers.cmake +@@ -166,6 +166,8 @@ + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + elseif(${cflag} MATCHES "^(-W|/w[1234eo])") + # Don't impose our warnings on others. ++ elseif(${cflag} MATCHES "^-m") ++ # Don't impose CPU instruction requirements on others, as the code performs feature detection on runtime. + else() + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + endif() diff --git a/debian/patches/series b/debian/patches/series index b64772ac..c30e9953 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -2,3 +2,4 @@ configure.diff cpu-features.diff latomic.diff empty-flags-library.diff +leaky-pkgconfig-cflags.diff -- cgit v1.2.3 From 564922c415f4576a4eb3ba2edd34c1cbf0c68de5 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 30 Aug 2022 23:00:34 -0400 Subject: Temporarily disable a failing cordz_info_statistics_test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temporarily skip CordzInfoStatisticsTest.ThreadSafety, since it fails on ppc64el. I’ll reenable it once the failure has been root-caused and fixed. Bug: https://bugs.debian.org/1018804 --- debian/changelog | 4 ++-- debian/patches/cordz-info-statistics-test.diff | 11 +++++++++++ debian/patches/series | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 debian/patches/cordz-info-statistics-test.diff diff --git a/debian/changelog b/debian/changelog index 8056265d..711e66f3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -abseil (0~20220623.0-2) UNRELEASED; urgency=medium +abseil (0~20220623.0-2) unstable; urgency=medium * Backport an upstream patch to correct pkg-config file generation. - -- Benjamin Barenblat Tue, 30 Aug 2022 22:40:21 -0400 + -- Benjamin Barenblat Tue, 30 Aug 2022 22:54:45 -0400 abseil (0~20220623.0-1) unstable; urgency=medium diff --git a/debian/patches/cordz-info-statistics-test.diff b/debian/patches/cordz-info-statistics-test.diff new file mode 100644 index 00000000..6e0bfcce --- /dev/null +++ b/debian/patches/cordz-info-statistics-test.diff @@ -0,0 +1,11 @@ +--- a/absl/strings/internal/cordz_info_statistics_test.cc ++++ b/absl/strings/internal/cordz_info_statistics_test.cc +@@ -466,6 +466,8 @@ + } + + TEST(CordzInfoStatisticsTest, ThreadSafety) { ++ GTEST_SKIP() << "Skipping test; see https://bugs.debian.org/1018804"; ++ + Notification stop; + static constexpr int kNumThreads = 8; + int64_t sampled_node_count = 0; diff --git a/debian/patches/series b/debian/patches/series index c30e9953..277aba50 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -3,3 +3,4 @@ cpu-features.diff latomic.diff empty-flags-library.diff leaky-pkgconfig-cflags.diff +cordz-info-statistics-test.diff -- cgit v1.2.3 From 8c0b94e793a66495e0b1f34a5eb26bd7dc672db0 Mon Sep 17 00:00:00 2001 From: Andy Getz Date: Wed, 31 Aug 2022 13:15:21 -0400 Subject: Switch time_state to explicit default initialization instead of value initialization. (#1261) It looks to me like the language rules treat these the same for this type, but evidently GCC feels differently. This only matters under TSAN where SpinLock has a non-trivial destructor, and under C++20 where ABSL_CONST_INIT is implemented (as constinit) by gcc. Fixes #1253 PiperOrigin-RevId: 469806751 Change-Id: Ic01b0142101f361bc19c95f9f9474e635669c58d --- absl/base/config.h | 2 +- absl/time/clock.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/absl/base/config.h b/absl/base/config.h index 8533aead..705ecea0 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -112,7 +112,7 @@ // LTS releases can be obtained from // https://github.com/abseil/abseil-cpp/releases. #define ABSL_LTS_RELEASE_VERSION 20220623 -#define ABSL_LTS_RELEASE_PATCH_LEVEL 0 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 1 // Helper macro to convert a CPP variable to a string literal. #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x diff --git a/absl/time/clock.cc b/absl/time/clock.cc index 7b204c4e..a091efdc 100644 --- a/absl/time/clock.cc +++ b/absl/time/clock.cc @@ -196,7 +196,7 @@ struct ABSL_CACHELINE_ALIGNED TimeState { absl::base_internal::SpinLock lock{absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY}; }; -ABSL_CONST_INIT static TimeState time_state{}; +ABSL_CONST_INIT static TimeState time_state; // Return the time in ns as told by the kernel interface. Place in *cycleclock // the value of the cycleclock at about the time of the syscall. -- cgit v1.2.3 From 6c810c2fadb33f0a1e68bfc63cd2217b3972f40f Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 18 Oct 2022 08:13:43 -0400 Subject: Begin updating packaging for Abseil 20220623.1 --- debian/changelog | 6 ++++++ debian/gbp.conf | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 711e66f3..d092a841 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +abseil (20220623.1-1) UNRELEASED; urgency=medium + + * New upstream release. + + -- Benjamin Barenblat Tue, 18 Oct 2022 08:13:27 -0400 + abseil (0~20220623.0-2) unstable; urgency=medium * Backport an upstream patch to correct pkg-config file generation. diff --git a/debian/gbp.conf b/debian/gbp.conf index 7a154d08..aea1f7a0 100644 --- a/debian/gbp.conf +++ b/debian/gbp.conf @@ -13,4 +13,4 @@ # the License. [DEFAULT] -upstream-tag = 20220623.0 +upstream-tag = 20220623.1 -- cgit v1.2.3 From 30f359b8df30cb72f2505f1e843e6054d01c8bdd Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 18 Oct 2022 08:37:23 -0400 Subject: Support nocheck profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per release-team’s recent request that maintainers support the nocheck profile , annotate googletest with and disable tests in debian/rules if that profile is set. --- debian/control | 2 +- debian/rules | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/debian/control b/debian/control index aeb749e4..b0038378 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,7 @@ Maintainer: Benjamin Barenblat Build-Depends: cmake (>= 3.5), debhelper-compat (= 12), - googletest (>= 1.12) [!mipsel !ppc64] + googletest (>= 1.12) [!mipsel !ppc64] Rules-Requires-Root: no Standards-Version: 4.6.1 Section: libs diff --git a/debian/rules b/debian/rules index 86492154..0dbb698e 100755 --- a/debian/rules +++ b/debian/rules @@ -20,8 +20,12 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+bindnow reproducible=+fixfilepath # Unit tests require more than 2 GB of RAM, so disable them on mipsel. # # Unit tests are not yet passing on ppc64, so disable them there as well. +# +# Disable unit tests unconditionally if nocheck is set. ifneq ($(filter $(DEB_HOST_ARCH),mipsel ppc64),) ABSL_RUN_TESTS=OFF +else ifneq ($(filter nocheck,$(DEB_BUILD_OPTIONS)),) +ABSL_RUN_TESTS=OFF else ABSL_RUN_TESTS=ON endif -- cgit v1.2.3 From f5afcb784c9b1c501c1144b7aab84555881ca871 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 18 Oct 2022 10:03:02 -0400 Subject: Release for unstable --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index d092a841..560f785c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -abseil (20220623.1-1) UNRELEASED; urgency=medium +abseil (20220623.1-1) unstable; urgency=medium * New upstream release. - -- Benjamin Barenblat Tue, 18 Oct 2022 08:13:27 -0400 + -- Benjamin Barenblat Tue, 18 Oct 2022 10:02:49 -0400 abseil (0~20220623.0-2) unstable; urgency=medium -- cgit v1.2.3