diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 5d67a50..7d2ff30 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel
@@ -479,6 +479,7 @@ copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":base", ":config", ":core_headers", ],
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 3d930b8..981b8cc 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt
@@ -418,6 +418,7 @@ COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::config absl::core_headers PUBLIC
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index 9677530..dad0e9a 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h
@@ -26,6 +26,7 @@ #endif #include <cstdint> +#include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" @@ -173,6 +174,36 @@ #endif /* ENDIAN */ +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x))); +} + // Functions to do unaligned loads and stores in little-endian order. inline uint16_t Load16(const void *p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); @@ -233,6 +264,36 @@ #endif /* ENDIAN */ +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x))); +} + // Functions to do unaligned loads and stores in big-endian order. inline uint16_t Load16(const void *p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
diff --git a/absl/cleanup/cleanup.h b/absl/cleanup/cleanup.h index 5a4bc54..8ebf1e9 100644 --- a/absl/cleanup/cleanup.h +++ b/absl/cleanup/cleanup.h
@@ -41,7 +41,7 @@ // // Data data; // while (ReadData(source_file, &data)) { -// if (data.IsBad()) { +// if (!data.IsGood()) { // absl::Status result = absl::FailedPreconditionError("Read bad data"); // return result; // Both cleanups execute // } @@ -87,7 +87,7 @@ public: Cleanup(Callback callback) // NOLINT - : storage_(std::move(callback), /*engaged=*/true) {} + : storage_(std::move(callback), /* is_callback_engaged = */ true) {} Cleanup(Cleanup&& other) = default;
diff --git a/absl/cleanup/internal/cleanup.h b/absl/cleanup/internal/cleanup.h index b68e3dd..b4c4073 100644 --- a/absl/cleanup/internal/cleanup.h +++ b/absl/cleanup/internal/cleanup.h
@@ -45,12 +45,14 @@ public: Storage() = delete; - Storage(Callback callback, bool engaged) - : callback_(std::move(callback)), engaged_(engaged) {} + Storage(Callback callback, bool is_callback_engaged) + : callback_(std::move(callback)), + is_callback_engaged_(is_callback_engaged) {} Storage(Storage&& other) : callback_(std::move(other.callback_)), - engaged_(absl::exchange(other.engaged_, false)) {} + is_callback_engaged_( + absl::exchange(other.is_callback_engaged_, false)) {} Storage(const Storage& other) = delete; @@ -58,9 +60,9 @@ Storage& operator=(const Storage& other) = delete; - bool IsCallbackEngaged() const { return engaged_; } + bool IsCallbackEngaged() const { return is_callback_engaged_; } - void DisengageCallback() { engaged_ = false; } + void DisengageCallback() { is_callback_engaged_ = false; } void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS { std::move(callback_)(); @@ -68,7 +70,7 @@ private: Callback callback_; - bool engaged_; + bool is_callback_engaged_; }; } // namespace cleanup_internal
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index e4484fb..7024e54 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc
@@ -72,6 +72,7 @@ total_probe_length.store(0, std::memory_order_relaxed); 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); create_time = absl::Now(); // The inliner makes hardcoded skip_count difficult (especially when combined @@ -235,6 +236,7 @@ info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed); info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed); + info->hashes_bitwise_xor.fetch_xor(hash, std::memory_order_relaxed); info->max_probe_length.store( std::max(info->max_probe_length.load(std::memory_order_relaxed), probe_length),
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 394348d..65d3fb5 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h
@@ -78,6 +78,7 @@ std::atomic<size_t> total_probe_length; std::atomic<size_t> hashes_bitwise_or; std::atomic<size_t> hashes_bitwise_and; + std::atomic<size_t> hashes_bitwise_xor; // `HashtablezSampler` maintains intrusive linked lists for all samples. See // comments on `HashtablezSampler::all_` for details on these. `init_mu`
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index 8d10a1e..5f4c83b 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -89,6 +89,7 @@ EXPECT_EQ(info.total_probe_length.load(), 0); 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_GE(info.create_time, test_start); info.capacity.store(1, std::memory_order_relaxed); @@ -98,6 +99,7 @@ info.total_probe_length.store(1, std::memory_order_relaxed); 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.create_time = test_start - absl::Hours(20); info.PrepareForSampling(); @@ -109,6 +111,7 @@ EXPECT_EQ(info.total_probe_length.load(), 0); 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_GE(info.create_time, test_start); } @@ -133,14 +136,17 @@ EXPECT_EQ(info.max_probe_length.load(), 6); EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00); EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x0000FF00); RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength); EXPECT_EQ(info.max_probe_length.load(), 6); EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000); EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x000F0F00); RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength); EXPECT_EQ(info.max_probe_length.load(), 12); EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000); EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x00F00F00); } TEST(HashtablezInfoTest, RecordErase) {
diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h index 76ee95e..3f90ad7 100644 --- a/absl/container/internal/unordered_map_constructor_test.h +++ b/absl/container/internal/unordered_map_constructor_test.h
@@ -16,6 +16,7 @@ #define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ #include <algorithm> +#include <unordered_map> #include <vector> #include "gmock/gmock.h"
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 90c6c8a..4b2c220 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel
@@ -38,6 +38,7 @@ deps = [ ":city", ":wyhash", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", "//absl/container:fixed_array",
diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index 6d19877..b43bfa5 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt
@@ -26,6 +26,7 @@ ${ABSL_DEFAULT_COPTS} DEPS absl::city + absl::config absl::core_headers absl::endian absl::fixed_array
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index eb3471d..7fb0af0 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h
@@ -38,7 +38,8 @@ #include <utility> #include <vector> -#include "absl/base/internal/endian.h" +#include "absl/base/config.h" +#include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" #include "absl/container/fixed_array.h" #include "absl/hash/internal/wyhash.h" @@ -804,26 +805,54 @@ size_t len); // Reads 9 to 16 bytes from p. - // The first 8 bytes are in .first, the rest (zero padded) bytes are in - // .second. + // The least significant 8 bytes are in .first, the rest (zero padded) bytes + // are in .second. static std::pair<uint64_t, uint64_t> Read9To16(const unsigned char* p, size_t len) { - uint64_t high = little_endian::Load64(p + len - 8); - return {little_endian::Load64(p), high >> (128 - len * 8)}; + uint64_t low_mem = absl::base_internal::UnalignedLoad64(p); + uint64_t high_mem = absl::base_internal::UnalignedLoad64(p + len - 8); +#ifdef ABSL_IS_LITTLE_ENDIAN + uint64_t most_significant = high_mem; + uint64_t least_significant = low_mem; +#else + uint64_t most_significant = low_mem; + uint64_t least_significant = high_mem; +#endif + return {least_significant, most_significant >> (128 - len * 8)}; } // Reads 4 to 8 bytes from p. Zero pads to fill uint64_t. static uint64_t Read4To8(const unsigned char* p, size_t len) { - return (static_cast<uint64_t>(little_endian::Load32(p + len - 4)) - << (len - 4) * 8) | - little_endian::Load32(p); + uint32_t low_mem = absl::base_internal::UnalignedLoad32(p); + uint32_t high_mem = absl::base_internal::UnalignedLoad32(p + len - 4); +#ifdef ABSL_IS_LITTLE_ENDIAN + uint32_t most_significant = high_mem; + uint32_t least_significant = low_mem; +#else + uint32_t most_significant = low_mem; + uint32_t least_significant = high_mem; +#endif + return (static_cast<uint64_t>(most_significant) << (len - 4) * 8) | + least_significant; } // Reads 1 to 3 bytes from p. Zero pads to fill uint32_t. static uint32_t Read1To3(const unsigned char* p, size_t len) { - return static_cast<uint32_t>((p[0]) | // - (p[len / 2] << (len / 2 * 8)) | // - (p[len - 1] << ((len - 1) * 8))); + unsigned char mem0 = p[0]; + unsigned char mem1 = p[len / 2]; + unsigned char mem2 = p[len - 1]; +#ifdef ABSL_IS_LITTLE_ENDIAN + unsigned char significant2 = mem2; + unsigned char significant1 = mem1; + unsigned char significant0 = mem0; +#else + unsigned char significant2 = mem0; + unsigned char significant1 = mem1; + unsigned char significant0 = mem2; +#endif + return static_cast<uint32_t>(significant0 | // + (significant1 << (len / 2 * 8)) | // + (significant2 << ((len - 1) * 8))); } ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) {
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 7d7bec8..13093d6 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt
@@ -611,6 +611,7 @@ ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::endian TESTONLY ) @@ -758,6 +759,7 @@ LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::endian absl::random_internal_iostream_state_saver absl::random_internal_randen absl::raw_logging_internal @@ -1119,6 +1121,7 @@ LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::endian absl::random_internal_randen_slow gtest_main )
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 2c1a5f4..4e778ae 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel
@@ -124,7 +124,10 @@ ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - deps = ["//absl/base:config"], + deps = [ + "//absl/base:config", + "//absl/base:endian", + ], ) cc_library( @@ -242,6 +245,7 @@ deps = [ ":iostream_state_saver", ":randen", + "//absl/base:endian", "//absl/meta:type_traits", ], ) @@ -606,6 +610,7 @@ deps = [ ":platform", ":randen_slow", + "//absl/base:endian", "@com_google_googletest//:gtest_main", ], )
diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h index 6a743ea..e3aa31a 100644 --- a/absl/random/internal/explicit_seed_seq.h +++ b/absl/random/internal/explicit_seed_seq.h
@@ -23,6 +23,7 @@ #include <vector> #include "absl/base/config.h" +#include "absl/base/internal/endian.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -73,7 +74,7 @@ template <typename OutIterator> void generate(OutIterator begin, OutIterator end) { for (size_t index = 0; begin != end; begin++) { - *begin = state_.empty() ? 0 : state_[index++]; + *begin = state_.empty() ? 0 : little_endian::FromHost32(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 6b33731..92bb890 100644 --- a/absl/random/internal/randen_engine.h +++ b/absl/random/internal/randen_engine.h
@@ -23,6 +23,7 @@ #include <limits> #include <type_traits> +#include "absl/base/internal/endian.h" #include "absl/meta/type_traits.h" #include "absl/random/internal/iostream_state_saver.h" #include "absl/random/internal/randen.h" @@ -76,7 +77,7 @@ impl_.Generate(state_); } - return state_[next_++]; + return little_endian::ToHost(state_[next_++]); } template <class SeedSequence> @@ -181,7 +182,8 @@ // 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<numeric_type>(elem) << os.fill(); + os << static_cast<numeric_type>(little_endian::FromHost(elem)) + << os.fill(); } os << engine.next_; return os; @@ -200,7 +202,7 @@ // necessary to read a wider type and then cast it to uint8_t. numeric_type value; is >> value; - elem = static_cast<result_type>(value); + elem = little_endian::ToHost(static_cast<result_type>(value)); } is >> next; if (is.fail()) {
diff --git a/absl/random/internal/randen_slow_test.cc b/absl/random/internal/randen_slow_test.cc index 4a53583..4861ffa 100644 --- a/absl/random/internal/randen_slow_test.cc +++ b/absl/random/internal/randen_slow_test.cc
@@ -17,6 +17,7 @@ #include <cstring> #include "gtest/gtest.h" +#include "absl/base/internal/endian.h" #include "absl/random/internal/randen_traits.h" namespace { @@ -56,7 +57,7 @@ uint64_t* id = d.state; for (const auto& elem : kGolden) { - EXPECT_EQ(elem, *id++); + EXPECT_EQ(absl::little_endian::FromHost64(elem), *id++); } }
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 39191ef..9353375 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc
@@ -495,7 +495,9 @@ data_ = src.data_; if (is_tree()) { + data_.set_profiled(false); CordRep::Ref(tree()); + clear_cordz_info(); } } @@ -509,12 +511,6 @@ // -------------------------------------------------------------------- // Constructors and destructors -Cord::Cord(const Cord& src) : contents_(src.contents_) { - if (CordRep* tree = contents_.tree()) { - CordRep::Ref(tree); - } -} - Cord::Cord(absl::string_view src) { const size_t n = src.size(); if (n <= InlineRep::kMaxInline) {
diff --git a/absl/strings/cord.h b/absl/strings/cord.h index aefb5e5..fa9cb91 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h
@@ -25,7 +25,7 @@ // // Because a Cord consists of these chunks, data can be added to or removed from // a Cord during its lifetime. Chunks may also be shared between Cords. Unlike a -// `std::string`, a Cord can therefore accomodate data that changes over its +// `std::string`, a Cord can therefore accommodate data that changes over its // lifetime, though it's not quite "mutable"; it can change only in the // attachment, detachment, or rearrangement of chunks of its constituent data. // @@ -755,6 +755,23 @@ bool is_tree() const { return data_.is_tree(); } + // Returns true if the Cord is being profiled by cordz. + bool is_profiled() const { return data_.is_tree() && data_.is_profiled(); } + + // Returns the profiled CordzInfo, or nullptr if not sampled. + absl::cord_internal::CordzInfo* cordz_info() const { + return data_.cordz_info(); + } + + // Sets the profiled CordzInfo. `cordz_info` must not be null. + void set_cordz_info(cord_internal::CordzInfo* cordz_info) { + assert(cordz_info != nullptr); + data_.set_cordz_info(cordz_info); + } + + // Resets the current cordz_info to null / empty. + void clear_cordz_info() { data_.clear_cordz_info(); } + private: friend class Cord; @@ -921,8 +938,12 @@ constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data) : data_(data) {} -inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) { - data_ = src.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()); + } } inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) { @@ -956,7 +977,6 @@ if (rhs == this) { return; } - std::swap(data_, rhs->data_); } @@ -1037,6 +1057,8 @@ return *this; } +inline Cord::Cord(const Cord& src) : contents_(src.contents_) {} + inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {} inline void Cord::swap(Cord& other) noexcept {
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index bf7a682..f998242 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc
@@ -183,6 +183,10 @@ } static bool IsTree(const Cord& c) { return c.contents_.is_tree(); } + + static cord_internal::CordzInfo* GetCordzInfo(const Cord& c) { + return c.contents_.cordz_info(); + } }; ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h index cda00a4..a1ba67f 100644 --- a/absl/strings/internal/cord_internal.h +++ b/absl/strings/internal/cord_internal.h
@@ -387,6 +387,12 @@ as_tree_.cordz_info = absl::big_endian::FromHost64(info); } + // Resets the current cordz_info to null / empty. + void clear_cordz_info() { + assert(is_tree()); + as_tree_.cordz_info = kNullCordzInfo; + } + // Returns a read only pointer to the character data inside this instance. // Requires the current instance to hold inline data. const char* as_chars() const {
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index 375db0a..926283c 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc
@@ -554,7 +554,8 @@ } template <typename Floating> -void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats) { +void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats, + const std::set<Floating> &skip_verify) { const NativePrintfTraits &native_traits = VerifyNativeImplementation(); // Reserve the space to ensure we don't allocate memory in the output itself. std::string str_format_result; @@ -602,7 +603,16 @@ AppendPack(&str_format_result, format, absl::MakeSpan(args)); } - if (string_printf_result != str_format_result) { +#ifdef _MSC_VER + // MSVC has a different rounding policy than us so we can't test our + // implementation against the native one there. + continue; +#elif defined(__APPLE__) + // Apple formats NaN differently (+nan) vs. (nan) + if (std::isnan(d)) continue; +#endif + if (string_printf_result != str_format_result && + skip_verify.find(d) == skip_verify.end()) { // We use ASSERT_EQ here because failures are usually correlated and a // bug would print way too many failed expectations causing the test // to time out. @@ -616,12 +626,6 @@ } TEST_F(FormatConvertTest, Float) { -#ifdef _MSC_VER - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - return; -#endif // _MSC_VER - std::vector<float> floats = {0.0f, -0.0f, .9999999f, @@ -635,7 +639,8 @@ std::numeric_limits<float>::epsilon(), std::numeric_limits<float>::epsilon() + 1.0f, std::numeric_limits<float>::infinity(), - -std::numeric_limits<float>::infinity()}; + -std::numeric_limits<float>::infinity(), + std::nanf("")}; // Some regression tests. floats.push_back(0.999999989f); @@ -664,21 +669,14 @@ std::sort(floats.begin(), floats.end()); floats.erase(std::unique(floats.begin(), floats.end()), floats.end()); -#ifndef __APPLE__ - // Apple formats NaN differently (+nan) vs. (nan) - floats.push_back(std::nan("")); -#endif - - TestWithMultipleFormatsHelper(floats); + TestWithMultipleFormatsHelper(floats, {}); } TEST_F(FormatConvertTest, Double) { -#ifdef _MSC_VER - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - return; -#endif // _MSC_VER - + // For values that we know won't match the standard library implementation we + // skip verification, but still run the algorithm to catch asserts/sanitizer + // bugs. + std::set<double> skip_verify; std::vector<double> doubles = {0.0, -0.0, .99999999999999, @@ -692,7 +690,8 @@ std::numeric_limits<double>::epsilon(), std::numeric_limits<double>::epsilon() + 1, std::numeric_limits<double>::infinity(), - -std::numeric_limits<double>::infinity()}; + -std::numeric_limits<double>::infinity(), + std::nan("")}; // Some regression tests. doubles.push_back(0.99999999999999989); @@ -722,33 +721,29 @@ "5084551339423045832369032229481658085593321233482747978262041447231" "68738177180919299881250404026184124858368.000000"; - if (!gcc_bug_22142) { - for (int exp = -300; exp <= 300; ++exp) { - const double all_ones_mantissa = 0x1fffffffffffff; - doubles.push_back(std::ldexp(all_ones_mantissa, exp)); + for (int exp = -300; exp <= 300; ++exp) { + const double all_ones_mantissa = 0x1fffffffffffff; + doubles.push_back(std::ldexp(all_ones_mantissa, exp)); + if (gcc_bug_22142) { + skip_verify.insert(doubles.back()); } } if (gcc_bug_22142) { - for (auto &d : doubles) { - using L = std::numeric_limits<double>; - double d2 = std::abs(d); - if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) { - d = 0; - } - } + using L = std::numeric_limits<double>; + skip_verify.insert(L::max()); + skip_verify.insert(L::min()); // NOLINT + skip_verify.insert(L::denorm_min()); + skip_verify.insert(-L::max()); + skip_verify.insert(-L::min()); // NOLINT + skip_verify.insert(-L::denorm_min()); } // Remove duplicates to speed up the logic below. std::sort(doubles.begin(), doubles.end()); doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end()); -#ifndef __APPLE__ - // Apple formats NaN differently (+nan) vs. (nan) - doubles.push_back(std::nan("")); -#endif - - TestWithMultipleFormatsHelper(doubles); + TestWithMultipleFormatsHelper(doubles, skip_verify); } TEST_F(FormatConvertTest, DoubleRound) { @@ -1069,11 +1064,6 @@ } TEST_F(FormatConvertTest, LongDouble) { -#ifdef _MSC_VER - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - return; -#endif // _MSC_VER const NativePrintfTraits &native_traits = VerifyNativeImplementation(); const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000", "%.60", "%+", "% ", "%-10"}; @@ -1134,10 +1124,18 @@ for (auto d : doubles) { FormatArgImpl arg(d); UntypedFormatSpecImpl format(fmt_str); + std::string result = FormatPack(format, {&arg, 1}); + +#ifdef _MSC_VER + // MSVC has a different rounding policy than us so we can't test our + // implementation against the native one there. + continue; +#endif // _MSC_VER + // We use ASSERT_EQ here because failures are usually correlated and a // bug would print way too many failed expectations causing the test to // time out. - ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1})) + ASSERT_EQ(StrPrint(fmt_str.c_str(), d), result) << fmt_str << " " << StrPrint("%.18Lg", d) << " " << StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d); }
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc index 2aa41aa..2b1fd8c 100644 --- a/absl/strings/internal/str_format/float_conversion.cc +++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -112,12 +112,22 @@ return next_carry % divisor; } +constexpr bool IsDoubleDouble() { + // This is the `double-double` representation of `long double`. + // We do not handle it natively. Fallback to snprintf. + return std::numeric_limits<long double>::digits == + 2 * std::numeric_limits<double>::digits; +} + +using MaxFloatType = + typename std::conditional<IsDoubleDouble(), double, long double>::type; + // Generates the decimal representation for an integer of the form `v * 2^exp`, // where `v` and `exp` are both positive integers. // It generates the digits from the left (ie the most significant digit first) // to allow for direct printing into the sink. // -// Requires `0 <= exp` and `exp <= numeric_limits<long double>::max_exponent`. +// Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`. class BinaryToDecimal { static constexpr int ChunksNeeded(int exp) { // We will left shift a uint128 by `exp` bits, so we need `128+exp` total @@ -132,10 +142,10 @@ static void RunConversion(uint128 v, int exp, absl::FunctionRef<void(BinaryToDecimal)> f) { assert(exp > 0); - assert(exp <= std::numeric_limits<long double>::max_exponent); + assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent); static_assert( static_cast<int>(StackArray::kMaxCapacity) >= - ChunksNeeded(std::numeric_limits<long double>::max_exponent), + ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent), ""); StackArray::RunWithCapacity( @@ -232,14 +242,14 @@ // Converts a value of the form `x * 2^-exp` into a sequence of decimal digits. // Requires `-exp < 0` and -// `-exp >= limits<long double>::min_exponent - limits<long double>::digits`. +// `-exp >= limits<MaxFloatType>::min_exponent - limits<MaxFloatType>::digits`. class FractionalDigitGenerator { public: // Run the conversion for `v * 2^exp` and call `f(generator)`. // This function will allocate enough stack space to perform the conversion. static void RunConversion( uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) { - using Limits = std::numeric_limits<long double>; + using Limits = std::numeric_limits<MaxFloatType>; assert(-exp < 0); assert(-exp >= Limits::min_exponent - 128); static_assert(StackArray::kMaxCapacity >= @@ -871,10 +881,10 @@ // This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the // size with long double which is the largest of the floats. constexpr size_t kBufSizeForHexFloatRepr = - 2 // 0x - + std::numeric_limits<long double>::digits / 4 // number of hex digits - + 1 // round up - + 1; // "." (dot) + 2 // 0x + + std::numeric_limits<MaxFloatType>::digits / 4 // number of hex digits + + 1 // round up + + 1; // "." (dot) char digits_buffer[kBufSizeForHexFloatRepr]; char *digits_iter = digits_buffer; const char *const digits = @@ -1393,10 +1403,7 @@ bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { - if (std::numeric_limits<long double>::digits == - 2 * std::numeric_limits<double>::digits) { - // This is the `double-double` representation of `long double`. - // We do not handle it natively. Fallback to snprintf. + if (IsDoubleDouble()) { return FallbackToSnprintf(v, conv, sink); }
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index 4dd51fe..8c6d573 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h
@@ -147,7 +147,7 @@ // // Example usage: // namespace foo { - // ABSL_CONST_INIT Mutex mu(absl::kConstInit); + // ABSL_CONST_INIT absl::Mutex mu(absl::kConstInit); // } explicit constexpr Mutex(absl::ConstInitType);