diff options
author | Abseil Team <absl-team@google.com> | 2019-10-16T01·18-0700 |
---|---|---|
committer | Andy Soffer <asoffer@google.com> | 2019-10-16T14·42-0400 |
commit | ab3552a18964e7063c8324f45b3896a6a20b08a8 (patch) | |
tree | 74c2403dae6781ebdc1932fc0bddcc4193aa131e /absl | |
parent | e9f9000c7c80993cb589d011616b7a8016e42f4a (diff) |
Export of internal Abseil changes
-- f13697e3d33803f9667d124072da4f6dd8bfbf85 by Andy Soffer <asoffer@google.com>: Addressing https://github.com/abseil/abseil-cpp/issues/314, fixing CMakeLists.txt to reference ABSL_TEST_COPTS rather than ABSL_DEFAULT_COPTS. ABSL_TEST_COPTS should be preferred for all tests so that they are configured consistently (moreover, CMake should agree with Bazel). PiperOrigin-RevId: 274932312 -- c31c24a1fa6bb98136adf51ef37c0818ac366690 by Derek Mauro <dmauro@google.com>: Silence MSAN in the stack consumption test utility PiperOrigin-RevId: 274912950 -- 2412913c05a246cd527cd4c31452f126e9129f3a by CJ Johnson <johnsoncj@google.com>: Internal change PiperOrigin-RevId: 274847103 -- 75e984a93b5760873501b96ac3229ccfd955daf8 by Abseil Team <absl-team@google.com>: Reformat BUILD file to current standards. PiperOrigin-RevId: 274815392 -- a2780e085f1df1e4ca2c814a58c893d1b78a1d9c by Samuel Benzaquen <sbenza@google.com>: Fix invalid result regarding leading zeros in the exponent. PiperOrigin-RevId: 274808017 -- dd402e1cb5c4ebacb576372ae24bf289d729d323 by Samuel Benzaquen <sbenza@google.com>: Make string_view's relational operators constexpr when possible. PiperOrigin-RevId: 274807873 -- b4ef32565653a5da1cb8bb8d0351586d23519658 by Abseil Team <absl-team@google.com>: Internal rework. PiperOrigin-RevId: 274787159 -- 70d81971c5914e6785b8e8a9d4f6eb2655dd62c0 by Gennadiy Rozental <rogeeff@google.com>: Internal rework. PiperOrigin-RevId: 274715557 -- 14f5b0440e353b899cafaaa15b53e77f98f401af by Gennadiy Rozental <rogeeff@google.com>: Make deprecated statements about ParseFLag/UnparseFlag consistent in a file. PiperOrigin-RevId: 274668123 -- 2e85adbdbb92612e4d750bc34fbca3333128b42d by Abseil Team <absl-team@google.com>: Allow absl::c_equal to be used with arrays. This is achieved by allowing container size computation for arrays. PiperOrigin-RevId: 274426830 -- 219719f107226d328773e6cec99fb473f5d3119c by Gennadiy Rozental <rogeeff@google.com>: Release correct extension interfaces to support usage of absl::Time and absl::Duration as ABSL_FLAG PiperOrigin-RevId: 274273788 -- 47a77f93fda23b69b4a6bdbd506fe643c69a5579 by Gennadiy Rozental <rogeeff@google.com>: Rework of flags persistence/FlagSaver internals. PiperOrigin-RevId: 274225213 -- 7807be3fe757c19e3b0c487298387683d4c9f5b3 by Abseil Team <absl-team@google.com>: Switch reference to sdkddkver.h to lowercase, matching conventions used in the Windows SDK and other uses. This helps to avoid confusion on case-sensitive filesystems. PiperOrigin-RevId: 274061877 -- 561304090087a19f1d10f0475f564fe132ebf06e by Andy Getzendanner <durandal@google.com>: Fix ABSL_WAITER_MODE detection for mingw Import of https://github.com/abseil/abseil-cpp/pull/342 PiperOrigin-RevId: 274030071 -- 9b3caac2cf202b9d440dfa1b4ffd538ac4bf715b by Derek Mauro <dmauro@google.com>: Support using Abseil with the musl libc implementation. Only test changes were required: * Workaround for a bug in sigaltstack() on musl * printf-style pointer formatting (%p) is implementation defined, so verify StrFromat produces something compatible * Fix detection of feenableexcept() PiperOrigin-RevId: 274011666 -- 73e8a938fc139e1cc8670d4513a445bacc855539 by Abseil Team <absl-team@google.com>: nvcc workaround: explicitly specify the definition of node_handle::Base PiperOrigin-RevId: 274011392 -- ab9cc6d042aca7d48e16c504ab10eab39433f4b2 by Andy Soffer <asoffer@google.com>: Internal change PiperOrigin-RevId: 273996318 -- e567c4979ca99c7e71821ec1523b8f5edd2c76ac by Abseil Team <absl-team@google.com>: Introduce a type alias to work around an nvcc bug. On the previous code, nvcc gets confused thinking that T has to be a parameter pack, as IsDecomposable accepts one. PiperOrigin-RevId: 273980472 -- 105b6e6339b77a32f4432de05f44cd3f9c436751 by Eric Fiselier <ericwf@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 273955589 -- 8feb87ff1d7e721fe094855e67c19539d5e582b7 by Abseil Team <absl-team@google.com>: Avoid dual-exporting scheduling_mode.h PiperOrigin-RevId: 273825112 -- fbc37854776d295dae98fb9d06a541f296daab95 by Andy Getzendanner <durandal@google.com>: Fix ABSL_HAVE_ALARM check on mingw Import of https://github.com/abseil/abseil-cpp/pull/341 PiperOrigin-RevId: 273817839 -- 6aedcd63a735b9133e143b043744ba0a25407f6f by Andy Soffer <asoffer@google.com>: Remove bit_gen_view.h now that all callers have been migrated to bit_gen_ref.h Tested: TGP - https://test.corp.google.com/ui#id=OCL:273762409:BASE:273743370:1570639020744:3001bcb5 PiperOrigin-RevId: 273810331 -- 6573de24a66ba715c579f7f32b5c48a1d743c7f8 by Abseil Team <absl-team@google.com>: Internal change. PiperOrigin-RevId: 273589963 -- 91c8c28b6dca26d98b39e8e06a8ed17c701ff793 by Abseil Team <absl-team@google.com>: Update macro name for `ABSL_GUARDED_BY()` in the example section. PiperOrigin-RevId: 273286983 -- 0ff7d1a93d70f8ecd693f8dbb98b7a4a016ca2a4 by Abseil Team <absl-team@google.com>: Fix potential integer overflow in the absl time library. In absl::FromTM, the tm.tm_year is added by 1900 regarding that tm.tm_year represents the years since 1900. This change checks integer overflow before doing the arithmetic operation. PiperOrigin-RevId: 273092952 -- b41c2a1310086807be09a833099ae6d4009f037c by Gennadiy Rozental <rogeeff@google.com>: Correctly Unlock the global mutex in case of concurrent flag initialization. Fixes #386 PiperOrigin-RevId: 272979749 -- c53103e71b2a6063af3c6d4ff68aa2d8f9ae9e06 by Abseil Team <absl-team@google.com>: Try to become idle only when there is no wakeup. Immediately after waking up (when futex wait returns), the current thread tries to become idle doing bunch of memory loads and a branch. Problem is that there is a good chance that we woke up due to a wakeup, especially for actively used threads. For such wakeups, calling MaybeBecomeIdle() would be a waste of cycles. Instead, call MaybeBecomeIdle() only when we are sure there is no wakeup. For idle threads the net effect should be the same. For active, threads this will be more efficient. Moreover, since MaybeBecomeIdle() is called before waiting on the futex, the current thread will try to become idle before sleeping. This should result in more accurate idleness and more efficient release of thread resources. PiperOrigin-RevId: 272940381 GitOrigin-RevId: f13697e3d33803f9667d124072da4f6dd8bfbf85 Change-Id: I36de05aec12595183725652dd362dfa58fb095d0
Diffstat (limited to 'absl')
41 files changed, 485 insertions, 291 deletions
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index 853330d447eb..5a03acf87ca8 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -14,12 +14,15 @@ # limitations under the License. # +load( + ":compiler_config_setting.bzl", + "create_llvm_config", +) + package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 -load(":compiler_config_setting.bzl", "create_llvm_config") - create_llvm_config( name = "llvm_compiler", visibility = [":__subpackages__"], diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index c84de461ac28..adcea8a79995 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -112,6 +112,18 @@ template <class Key, class Hash, class KeyEqual, class Allocator> struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>> : std::true_type {}; +// container_algorithm_internal::c_size. It is meant for internal use only. + +template <class C> +auto c_size(C& c) -> decltype(c.size()) { + return c.size(); +} + +template <class T, std::size_t N> +constexpr std::size_t c_size(T (&)[N]) { + return N; +} + } // namespace container_algorithm_internal // PUBLIC API @@ -365,7 +377,8 @@ c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) { template <typename C1, typename C2> bool c_equal(const C1& c1, const C2& c2) { - return ((c1.size() == c2.size()) && + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && std::equal(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2))); @@ -375,7 +388,8 @@ bool c_equal(const C1& c1, const C2& c2) { // the function's test condition. template <typename C1, typename C2, typename BinaryPredicate> bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { - return ((c1.size() == c2.size()) && + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && std::equal(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 86bf9d3ea327..0a4abe946272 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc @@ -163,23 +163,29 @@ TEST_F(NonMutatingTest, MismatchWithPredicate) { TEST_F(NonMutatingTest, Equal) { EXPECT_TRUE(absl::c_equal(vector_, sequence_)); EXPECT_TRUE(absl::c_equal(sequence_, vector_)); + EXPECT_TRUE(absl::c_equal(sequence_, array_)); + EXPECT_TRUE(absl::c_equal(array_, vector_)); // Test that behavior appropriately differs from that of equal(). std::vector<int> vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus)); + EXPECT_FALSE(absl::c_equal(array_, vector_plus)); } TEST_F(NonMutatingTest, EqualWithPredicate) { EXPECT_TRUE(absl::c_equal(vector_, sequence_, Equals)); EXPECT_TRUE(absl::c_equal(sequence_, vector_, Equals)); + EXPECT_TRUE(absl::c_equal(array_, sequence_, Equals)); + EXPECT_TRUE(absl::c_equal(vector_, array_, Equals)); // Test that behavior appropriately differs from that of equal(). std::vector<int> vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_, Equals)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus, Equals)); + EXPECT_FALSE(absl::c_equal(vector_plus, array_, Equals)); } TEST_F(NonMutatingTest, IsPermutation) { diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 1e1f0d2de318..d74bac631a61 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -71,16 +71,16 @@ cc_library( "internal/spinlock_wait.cc", "internal/spinlock_win32.inc", ], - hdrs = [ - "internal/scheduling_mode.h", - "internal/spinlock_wait.h", - ], + hdrs = ["internal/spinlock_wait.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", ], - deps = [":core_headers"], + deps = [ + ":base_internal", + ":core_headers", + ], ) cc_library( @@ -141,11 +141,11 @@ cc_library( ], deps = [ ":base", + ":base_internal", ":config", ":core_headers", ":dynamic_annotations", ":raw_logging_internal", - ":spinlock_wait", ], ) @@ -369,8 +369,8 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", + ":base_internal", ":core_headers", - ":spinlock_wait", "//absl/synchronization", "@com_google_googletest//:gtest", ], @@ -385,8 +385,8 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", + ":base_internal", ":core_headers", - ":spinlock_wait", "//absl/synchronization", "@com_google_googletest//:gtest_main", ], diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 51705a5a102c..79ee5b93ed71 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -56,7 +56,6 @@ absl_cc_library( NAME spinlock_wait HDRS - "internal/scheduling_mode.h" "internal/spinlock_wait.h" SRCS "internal/spinlock_akaros.inc" @@ -67,6 +66,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base_internal absl::core_headers ) @@ -125,11 +125,11 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::base_internal absl::config absl::core_headers absl::dynamic_annotations absl::raw_logging_internal - absl::spinlock_wait Threads::Threads ) @@ -141,6 +141,7 @@ absl_cc_library( "internal/identity.h" "internal/inline_variable.h" "internal/invoke.h" + "internal/scheduling_mode.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -348,8 +349,8 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::base + absl::base_internal absl::core_headers - absl::spinlock_wait absl::synchronization gtest TESTONLY @@ -365,8 +366,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base + absl::base_internal absl::core_headers - absl::spinlock_wait absl::synchronization gtest_main ) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index f298297e9942..7b7656a859b8 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -599,7 +599,6 @@ // // Note that this attribute is redundant if the variable is declared constexpr. #if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) -// NOLINTNEXTLINE(whitespace/braces) #define ABSL_CONST_INIT [[clang::require_constant_initialization]] #else #define ABSL_CONST_INIT diff --git a/absl/container/inlined_vector_exception_safety_test.cc b/absl/container/inlined_vector_exception_safety_test.cc index 25994f1685a2..937e43a5bf21 100644 --- a/absl/container/inlined_vector_exception_safety_test.cc +++ b/absl/container/inlined_vector_exception_safety_test.cc @@ -16,7 +16,7 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_EXCEPTIONS +#if defined(ABSL_HAVE_EXCEPTIONS) #include <array> #include <initializer_list> @@ -493,4 +493,4 @@ TYPED_TEST(TwoSizeTest, Swap) { } // namespace -#endif // ABSL_HAVE_EXCEPTIONS +#endif // defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 591d3ea11fa2..4bd5d469746b 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h @@ -117,7 +117,7 @@ class node_handle_base { template <typename Policy, typename PolicyTraits, typename Alloc, typename = void> class node_handle : public node_handle_base<PolicyTraits, Alloc> { - using Base = typename node_handle::node_handle_base; + using Base = node_handle_base<PolicyTraits, Alloc>; public: using value_type = typename PolicyTraits::value_type; @@ -137,7 +137,7 @@ template <typename Policy, typename PolicyTraits, typename Alloc> class node_handle<Policy, PolicyTraits, Alloc, absl::void_t<typename Policy::mapped_type>> : public node_handle_base<PolicyTraits, Alloc> { - using Base = typename node_handle::node_handle_base; + using Base = node_handle_base<PolicyTraits, Alloc>; public: using key_type = typename Policy::key_type; diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 2e6f4dd3e66e..42b3c4681133 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -938,8 +938,11 @@ class raw_hash_set { // // flat_hash_map<std::string, int> m; // m.insert(std::make_pair("abc", 42)); + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. template <class T, RequiresInsertable<T> = 0, - typename std::enable_if<IsDecomposable<T>::value, int>::type = 0, + class T2 = T, + typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0, T* = nullptr> std::pair<iterator, bool> insert(T&& value) { return emplace(std::forward<T>(value)); @@ -975,8 +978,10 @@ class raw_hash_set { return emplace(std::move(value)); } - template <class T, RequiresInsertable<T> = 0, - typename std::enable_if<IsDecomposable<T>::value, int>::type = 0, + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. + template <class T, RequiresInsertable<T> = 0, class T2 = T, + typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0, T* = nullptr> iterator insert(const_iterator, T&& value) { return insert(std::forward<T>(value)).first; diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl index 2829e4ec6e6f..9dd6bd0ae4fe 100644 --- a/absl/copts/configure_copts.bzl +++ b/absl/copts/configure_copts.bzl @@ -6,6 +6,8 @@ change Abseil copts, edit absl/copts/copts.py load( "//absl:copts/GENERATED_copts.bzl", + "ABSL_CLANG_CL_FLAGS", + "ABSL_CLANG_CL_TEST_FLAGS", "ABSL_GCC_FLAGS", "ABSL_GCC_TEST_FLAGS", "ABSL_LLVM_FLAGS", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index c409e33e39c6..81492c0c0f30 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -238,7 +238,7 @@ absl_cc_test( SRCS "leak_check_test.cc" COPTS - ${ABSL_DEFAULT_COPTS} + ${ABSL_TEST_COPTS} "$<$<BOOL:${ABSL_HAVE_LSAN}>:-DABSL_EXPECT_LEAK_SANITIZER>" LINKOPTS "${ABSL_LSAN_LINKOPTS}" diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc index 4b05f4955ee0..d4703264a666 100644 --- a/absl/debugging/internal/stack_consumption.cc +++ b/absl/debugging/internal/stack_consumption.cc @@ -115,10 +115,11 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { // Set up the alt-signal-stack (and save the older one). stack_t sigstk; memset(&sigstk, 0, sizeof(sigstk)); - stack_t old_sigstk; sigstk.ss_sp = altstack; sigstk.ss_size = kAlternateStackSize; sigstk.ss_flags = 0; + stack_t old_sigstk; + memset(&old_sigstk, 0, sizeof(old_sigstk)); ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0, "sigaltstack() failed"); @@ -152,6 +153,15 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { int signal_handler_stack_consumption = GetStackConsumption(altstack); // Now restore the old alt-signal-stack and signal handlers. + if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 && + (old_sigstk.ss_flags & SS_DISABLE)) { + // https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070 + // The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some + // versions of musl have a bug that rejects ss_size==0. Work around this by + // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel + // when SS_DISABLE is set. + old_sigstk.ss_size = MINSIGSTKSZ; + } ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0, "sigaltstack() failed"); ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0, diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 2bf562f898c7..2e0dc3891f56 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -40,6 +40,8 @@ cc_library( deps = [ ":handle", ":registry", + "//absl/memory", + "//absl/strings", "//absl/synchronization", ], ) @@ -184,6 +186,7 @@ cc_library( ":marshalling", "//absl/base", "//absl/base:core_headers", + "//absl/memory", "//absl/strings", ], ) diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc index a02d069e9b00..69038bbf48a0 100644 --- a/absl/flags/flag.cc +++ b/absl/flags/flag.cc @@ -49,9 +49,7 @@ namespace flags_internal { ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit); -void LockGlobalConstructionGuard() { construction_guard.Lock(); } - -void UnlockGlobalConstructionGuard() { construction_guard.Unlock(); } +absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; } } // namespace flags_internal diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 86ad59dd9f16..4927757b8fbe 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -80,8 +80,7 @@ using Flag = flags_internal::Flag<T>; // if two threads attempt to construct the flag concurrently only one wins. namespace flags_internal { -void LockGlobalConstructionGuard(); -void UnlockGlobalConstructionGuard(); +absl::Mutex* GetGlobalConstructionGuard(); } // namespace flags_internal template <typename T> @@ -100,7 +99,7 @@ class Flag { flags_internal::Flag<T>* GetImpl() const { if (!inited_.load(std::memory_order_acquire)) { - flags_internal::LockGlobalConstructionGuard(); + absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); if (inited_.load(std::memory_order_acquire)) { return impl_; @@ -109,8 +108,6 @@ class Flag { impl_ = new flags_internal::Flag<T>(name_, help_gen_, filename_, marshalling_op_, initial_value_gen_); inited_.store(true, std::memory_order_release); - - flags_internal::UnlockGlobalConstructionGuard(); } return impl_; @@ -130,7 +127,6 @@ class Flag { std::string Filename() const { return GetImpl()->Filename(); } std::string DefaultValue() const { return GetImpl()->DefaultValue(); } std::string CurrentValue() const { return GetImpl()->CurrentValue(); } - bool HasValidatorFn() const { return GetImpl()->HasValidatorFn(); } bool InvokeValidator(const void* value) const { return GetImpl()->InvokeValidator(value); } diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index 53e2b84ecc13..99f73611d8f1 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc @@ -149,6 +149,12 @@ std::string CommandLineFlag::CurrentValue() const { return Unparse(marshalling_op_, cur_); } +int64_t CommandLineFlag::MutationCounter() const { + absl::MutexLock l(InitFlagIfNecessary()); + + return counter_; +} + // Attempts to parse supplied `value` string using parsing routine in the `flag` // argument. If parsing is successful, it will try to validate that the parsed // value is valid for the specified 'flag'. Finally this function stores the diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 284286b6be18..528d3106bfa8 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -17,6 +17,7 @@ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #include <atomic> +#include <memory> #include "absl/base/macros.h" #include "absl/flags/marshalling.h" @@ -186,6 +187,16 @@ class HelpText { const char* help_message_; }; +// Handle to FlagState objects. Specific flag state objects will restore state +// of a flag produced this flag state from method CommandLineFlag::SaveState(). +class FlagStateInterface { + public: + virtual ~FlagStateInterface() {} + + // Restores the flag originated this object to the saved state. + virtual void Restore() const = 0; +}; + // Holds all information for a flag. class CommandLineFlag { public: @@ -239,7 +250,6 @@ class CommandLineFlag { virtual void StoreAtomic() {} // Interfaces to operate on validators. - virtual bool HasValidatorFn() const { return false; } virtual bool InvokeValidator(const void* /*value*/) const { return true; } // Invoke the flag validators for old flags. // TODO(rogeeff): implement proper validators for Abseil Flags @@ -264,6 +274,10 @@ class CommandLineFlag { return res; } + // Interface to save flag to some persistent state. Returns current flag state + // or nullptr if flag does not support saving and restoring a state. + virtual std::unique_ptr<FlagStateInterface> SaveState() = 0; + // Interfaces to overate on callbacks. virtual void InvokeCallback() {} @@ -285,6 +299,9 @@ class CommandLineFlag { protected: ~CommandLineFlag() = default; + // Thread safe access to mutation counter. + int64_t MutationCounter() const; + const char* const name_; const HelpText help_; const char* const filename_; @@ -323,11 +340,6 @@ class CommandLineFlag { friend bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value, std::string* err); friend absl::Mutex* InitFlag(CommandLineFlag* flag); - - // This is a short term, until we completely rework persistent state - // storage API. - virtual void* GetValidator() const { return nullptr; } - virtual bool SetValidator(void*) { return false; } }; // Update any copy of the flag value that is stored in an atomic word. diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 16330380f548..2b21c4407363 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -20,12 +20,44 @@ #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" namespace absl { namespace flags_internal { constexpr int64_t AtomicInit() { return 0xababababababababll; } +template <typename T> +class Flag; + +template <typename T> +class FlagState : public flags_internal::FlagStateInterface { + public: + FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line, + int64_t counter) + : flag_(flag), + cur_value_(std::move(cur)), + modified_(modified), + on_command_line_(on_command_line), + counter_(counter) {} + + ~FlagState() override = default; + + private: + friend class Flag<T>; + + // Restores the flag to the saved state. + void Restore() const override; + + // Flag and saved flag data. + Flag<T>* flag_; + T cur_value_; + bool modified_; + bool on_command_line_; + int64_t counter_; +}; + // Signature for the mutation callback used by watched Flags // The callback is noexcept. // TODO(rogeeff): add noexcept after C++17 support is added. @@ -98,15 +130,12 @@ class Flag final : public flags_internal::CommandLineFlag { InvokeCallback(); } - void InvokeCallback() override - ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { - flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu, - callback_); - } private: + friend class FlagState<T>; + void Destroy() const override { - // Values are heap allocated Abseil Flags. + // Values are heap allocated for Abseil Flags. if (cur_) Delete(op_, cur_); if (def_) Delete(op_, def_); @@ -121,6 +150,41 @@ class Flag final : public flags_internal::CommandLineFlag { } } + // Interfaces to save and restore flags to/from persistent state. + // Returns current flag state or nullptr if flag does not support + // saving and restoring a state. + std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override { + T curr_value = Get(); + + absl::MutexLock l(InitFlagIfNecessary()); + + return absl::make_unique<flags_internal::FlagState<T>>( + this, std::move(curr_value), modified_, on_command_line_, counter_); + } + + // Restores the flag state to the supplied state object. If there is + // nothing to restore returns false. Otherwise returns true. + bool RestoreState(const flags_internal::FlagState<T>& flag_state) { + if (MutationCounter() == flag_state.counter_) return false; + + Set(flag_state.cur_value_); + + // Race condition here? This should disappear once we move the rest of the + // flag's data into Flag's internals. + + absl::MutexLock l(InitFlagIfNecessary()); + modified_ = flag_state.modified_; + on_command_line_ = flag_state.on_command_line_; + return true; + } + + // Interfaces to overate on callbacks. + void InvokeCallback() override + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu, + callback_); + } + // Flag's data // For some types, a copy of the current value is kept in an atomically // accessible field. @@ -128,6 +192,15 @@ class Flag final : public flags_internal::CommandLineFlag { FlagCallback callback_; // Mutation callback }; +template <typename T> +inline void FlagState<T>::Restore() const { + if (flag_->RestoreState(*this)) { + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("Restore saved value of ", flag_->Name(), + " to: ", flag_->CurrentValue())); + } +} + // This class facilitates Flag object registration and tail expression-based // flag definition, for example: // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index 4bea313bb1eb..6b2564d13a78 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -188,114 +188,34 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) { class FlagSaverImpl { public: - // Constructs an empty FlagSaverImpl object. - FlagSaverImpl() {} - ~FlagSaverImpl() { - // reclaim memory from each of our CommandLineFlags - for (const SavedFlag& src : backup_registry_) { - Delete(src.op, src.current); - Delete(src.op, src.default_value); - } - } + FlagSaverImpl() = default; + FlagSaverImpl(const FlagSaverImpl&) = delete; + void operator=(const FlagSaverImpl&) = delete; // Saves the flag states from the flag registry into this object. // It's an error to call this more than once. - // Must be called when the registry mutex is not held. void SaveFromRegistry() { assert(backup_registry_.empty()); // call only once! - SavedFlag saved; flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { - if (flag->IsRetired()) return; - - saved.name = flag->Name(); - saved.op = flag->op_; - saved.marshalling_op = flag->marshalling_op_; - { - absl::MutexLock l(flag->InitFlagIfNecessary()); - saved.validator = flag->GetValidator(); - saved.modified = flag->modified_; - saved.on_command_line = flag->on_command_line_; - saved.current = Clone(saved.op, flag->cur_); - saved.default_value = Clone(saved.op, flag->def_); - saved.counter = flag->counter_; + if (auto flag_state = flag->SaveState()) { + backup_registry_.emplace_back(std::move(flag_state)); } - backup_registry_.push_back(saved); }); } - // Restores the saved flag states into the flag registry. We - // assume no flags were added or deleted from the registry since - // the SaveFromRegistry; if they were, that's trouble! Must be - // called when the registry mutex is not held. + // Restores the saved flag states into the flag registry. void RestoreToRegistry() { - FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry(); - FlagRegistryLock frl(global_registry); - for (const SavedFlag& src : backup_registry_) { - CommandLineFlag* flag = global_registry->FindFlagLocked(src.name); - // If null, flag got deleted from registry. - if (!flag) continue; - - bool restored = false; - { - // This function encapsulate the lock. - flag->SetValidator(src.validator); - - absl::MutexLock l(flag->InitFlagIfNecessary()); - flag->modified_ = src.modified; - flag->on_command_line_ = src.on_command_line; - if (flag->counter_ != src.counter || - ChangedDirectly(flag, src.default_value, flag->def_)) { - restored = true; - Copy(src.op, src.default_value, flag->def_); - } - if (flag->counter_ != src.counter || - ChangedDirectly(flag, src.current, flag->cur_)) { - restored = true; - Copy(src.op, src.current, flag->cur_); - UpdateCopy(flag); - flag->InvokeCallback(); - } - } - - if (restored) { - flag->counter_++; - - // Revalidate the flag because the validator might store state based - // on the flag's value, which just changed due to the restore. - // Failing validation is ignored because it's assumed that the flag - // was valid previously and there's little that can be done about it - // here, anyway. - flag->ValidateInputValue(flag->CurrentValue()); - - ABSL_INTERNAL_LOG( - INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ", - Unparse(src.marshalling_op, src.current))); - } + for (const auto& flag_state : backup_registry_) { + flag_state->Restore(); } } private: - struct SavedFlag { - absl::string_view name; - FlagOpFn op; - FlagMarshallingOpFn marshalling_op; - int64_t counter; - void* validator; - bool modified; - bool on_command_line; - const void* current; // nullptr after restore - const void* default_value; // nullptr after restore - }; - - std::vector<SavedFlag> backup_registry_; - - FlagSaverImpl(const FlagSaverImpl&); // no copying! - void operator=(const FlagSaverImpl&); + std::vector<std::unique_ptr<flags_internal::FlagStateInterface>> + backup_registry_; }; -FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) { - impl_->SaveFromRegistry(); -} +FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); } void FlagSaver::Ignore() { delete impl_; @@ -376,6 +296,10 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag { delete this; } + + std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override { + return nullptr; + } }; } // namespace diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index c904618da2a0..92111827c150 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -26,7 +26,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "random", diff --git a/absl/random/distribution_format_traits.h b/absl/random/distribution_format_traits.h index 3298c2cdb64b..f9f070589f00 100644 --- a/absl/random/distribution_format_traits.h +++ b/absl/random/distribution_format_traits.h @@ -249,12 +249,12 @@ struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> { } }; -template <typename TagType, typename NumType> +template <typename NumType> struct UniformDistributionWrapper; -template <typename TagType, typename NumType> -struct DistributionFormatTraits<UniformDistributionWrapper<TagType, NumType>> { - using distribution_t = UniformDistributionWrapper<TagType, NumType>; +template <typename NumType> +struct DistributionFormatTraits<UniformDistributionWrapper<NumType>> { + using distribution_t = UniformDistributionWrapper<NumType>; using result_t = NumType; static constexpr const char* Name() { return "Uniform"; } @@ -263,19 +263,7 @@ struct DistributionFormatTraits<UniformDistributionWrapper<TagType, NumType>> { return absl::StrCat(Name(), "<", ScalarTypeName<NumType>(), ">"); } static std::string FormatArgs(const distribution_t& d) { - absl::string_view tag; - if (std::is_same<TagType, IntervalClosedClosedTag>::value) { - tag = "IntervalClosedClosed"; - } else if (std::is_same<TagType, IntervalClosedOpenTag>::value) { - tag = "IntervalClosedOpen"; - } else if (std::is_same<TagType, IntervalOpenClosedTag>::value) { - tag = "IntervalOpenClosed"; - } else if (std::is_same<TagType, IntervalOpenOpenTag>::value) { - tag = "IntervalOpenOpen"; - } else { - tag = "[[unknown tag type]]"; - } - return absl::StrCat(tag, ", ", (d.min)(), ", ", (d.max)()); + return absl::StrCat((d.min)(), ", ", (d.max)()); } static std::string FormatResults(absl::Span<const result_t> results) { return absl::StrJoin(results, ", "); diff --git a/absl/random/distributions.h b/absl/random/distributions.h index 3a4e93abfa2f..6ced60616158 100644 --- a/absl/random/distributions.h +++ b/absl/random/distributions.h @@ -124,7 +124,15 @@ Uniform(TagType tag, URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { using gen_t = absl::decay_t<URBG>; - return random_internal::UniformImpl<R, TagType, gen_t>(tag, urbg, lo, hi); + using distribution_t = random_internal::UniformDistributionWrapper<R>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, tag, lo, hi); } // absl::Uniform<T>(bitgen, lo, hi) @@ -135,11 +143,17 @@ template <typename R = void, typename URBG> typename absl::enable_if_t<!std::is_same<R, void>::value, R> // Uniform(URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { - constexpr auto tag = absl::IntervalClosedOpen; - using tag_t = decltype(tag); using gen_t = absl::decay_t<URBG>; + using distribution_t = random_internal::UniformDistributionWrapper<R>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + constexpr auto tag = absl::IntervalClosedOpen; + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; - return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi); + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, lo, hi); } // absl::Uniform(tag, bitgen, lo, hi) @@ -156,9 +170,16 @@ Uniform(TagType tag, A lo, B hi) { using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; + using distribution_t = random_internal::UniformDistributionWrapper<return_t>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; - return random_internal::UniformImpl<return_t, TagType, gen_t>(tag, urbg, lo, - hi); + auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); + auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, tag, static_cast<return_t>(lo), + static_cast<return_t>(hi)); } // absl::Uniform(bitgen, lo, hi) @@ -171,13 +192,19 @@ typename absl::enable_if_t<std::is_same<R, void>::value, random_internal::uniform_inferred_return_t<A, B>> Uniform(URBG&& urbg, // NOLINT(runtime/references) A lo, B hi) { - constexpr auto tag = absl::IntervalClosedOpen; - using tag_t = decltype(tag); using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; + using distribution_t = random_internal::UniformDistributionWrapper<return_t>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; - return random_internal::UniformImpl<return_t, tag_t, gen_t>(tag, urbg, lo, - hi); + constexpr auto tag = absl::IntervalClosedOpen; + auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); + auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, static_cast<return_t>(lo), + static_cast<return_t>(hi)); } // absl::Uniform<unsigned T>(bitgen) @@ -187,13 +214,12 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references) template <typename R, typename URBG> typename absl::enable_if_t<!std::is_signed<R>::value, R> // Uniform(URBG&& urbg) { // NOLINT(runtime/references) - constexpr auto tag = absl::IntervalClosedClosed; - constexpr auto lo = std::numeric_limits<R>::lowest(); - constexpr auto hi = (std::numeric_limits<R>::max)(); - using tag_t = decltype(tag); using gen_t = absl::decay_t<URBG>; + using distribution_t = random_internal::UniformDistributionWrapper<R>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; - return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi); + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg); } // ----------------------------------------------------------------------------- diff --git a/absl/random/internal/distributions.h b/absl/random/internal/distributions.h index 96f8bae3918f..c8cec02b7fbc 100644 --- a/absl/random/internal/distributions.h +++ b/absl/random/internal/distributions.h @@ -24,36 +24,6 @@ namespace absl { namespace random_internal { -template <typename D> -struct DistributionFormatTraits; - -// UniformImpl implements the core logic of the Uniform<T> call, which is to -// select the correct distribution type, compute the bounds based on the -// interval tag, and then generate a value. -template <typename NumType, typename TagType, typename URBG> -NumType UniformImpl(TagType tag, - URBG& urbg, // NOLINT(runtime/references) - NumType lo, NumType hi) { - static_assert( - std::is_arithmetic<NumType>::value, - "absl::Uniform<T>() must use an integer or real parameter type."); - - using distribution_t = - UniformDistributionWrapper<absl::decay_t<TagType>, NumType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; - auto a = uniform_lower_bound(tag, lo, hi); - auto b = uniform_upper_bound(tag, lo, hi); - - // TODO(lar): it doesn't make a lot of sense to ask for a random number in an - // empty range. Right now we just return a boundary--even though that - // boundary is not an acceptable value! Is there something better we can do - // here? - if (a > b) return a; - - using gen_t = absl::decay_t<URBG>; - return DistributionCaller<gen_t>::template Call<distribution_t, format_t>( - &urbg, tag, lo, hi); -} // In the absence of an explicitly provided return-type, the template // "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h index 2929407e1497..f68b1823ef00 100644 --- a/absl/random/internal/uniform_helper.h +++ b/absl/random/internal/uniform_helper.h @@ -154,12 +154,22 @@ using UniformDistribution = absl::uniform_int_distribution<NumType>, absl::uniform_real_distribution<NumType>>::type; -template <typename TagType, typename NumType> +template <typename NumType> struct UniformDistributionWrapper : public UniformDistribution<NumType> { + template <typename TagType> explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi) : UniformDistribution<NumType>( uniform_lower_bound<NumType>(TagType{}, lo, hi), uniform_upper_bound<NumType>(TagType{}, lo, hi)) {} + + explicit UniformDistributionWrapper(NumType lo, NumType hi) + : UniformDistribution<NumType>( + uniform_lower_bound<NumType>(IntervalClosedOpenTag(), lo, hi), + uniform_upper_bound<NumType>(IntervalClosedOpenTag(), lo, hi)) {} + + explicit UniformDistributionWrapper() + : UniformDistribution<NumType>(std::numeric_limits<NumType>::lowest(), + (std::numeric_limits<NumType>::max)()) {} }; } // namespace random_internal diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 729bdd761f0f..4863ead2992e 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -630,6 +630,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":str_format_internal", + "//absl/base:raw_logging_internal", "//absl/numeric:int128", "@com_google_googletest//:gtest_main", ], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index ccff44419341..3f9079575011 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -461,6 +461,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format_internal + absl::raw_logging_internal absl::int128 gmock_main ) diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc index b58fad269d8b..9090e9c89c50 100644 --- a/absl/strings/charconv_test.cc +++ b/absl/strings/charconv_test.cc @@ -511,6 +511,13 @@ TEST(FromChars, Overflow) { EXPECT_EQ(f, std::numeric_limits<float>::max()); } +TEST(FromChars, RegressionTestsFromFuzzer) { + absl::string_view src = "0x21900000p00000000099"; + float f; + auto result = absl::from_chars(src.data(), src.data() + src.size(), f); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); +} + TEST(FromChars, ReturnValuePtr) { // Check that `ptr` points one past the number scanned, even if that number // is not representable. diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc index f3c7232403a1..fa9a8965fe6b 100644 --- a/absl/strings/internal/charconv_parse.cc +++ b/absl/strings/internal/charconv_parse.cc @@ -253,6 +253,12 @@ std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits, assert(max_digits * 4 <= std::numeric_limits<T>::digits); } const char* const original_begin = begin; + + // Skip leading zeros, but only if *out is zero. + // They don't cause an overflow so we don't have to count them for + // `max_digits`. + while (!*out && end != begin && *begin == '0') ++begin; + T accumulator = *out; const char* significant_digits_end = (end - begin > max_digits) ? begin + max_digits : end; diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index 814ccf4c65e6..ab8d53914119 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -5,7 +5,9 @@ #include <cmath> #include <string> +#include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" #include "absl/strings/internal/str_format/bind.h" namespace absl { @@ -157,12 +159,20 @@ TEST_F(FormatConvertTest, StringPrecision) { EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)})); } +// Pointer formatting is implementation defined. This checks that the argument +// can be matched to `ptr`. +MATCHER_P(MatchesPointerString, ptr, "") { + if (ptr == nullptr && arg == "(nil)") { + return true; + } + void* parsed = nullptr; + if (sscanf(arg.c_str(), "%p", &parsed) != 1) { + ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str()); + } + return ptr == parsed; +} + TEST_F(FormatConvertTest, Pointer) { -#ifdef _MSC_VER - // MSVC's printf implementation prints pointers differently. We can't easily - // compare our implementation to theirs. - return; -#endif static int x = 0; const int *xp = &x; char c = 'h'; @@ -173,48 +183,62 @@ TEST_F(FormatConvertTest, Pointer) { using VoidF = void (*)(); VoidF fp = [] {}, fnil = nullptr; volatile char vc; - volatile char* vcp = &vc; - volatile char* vcnil = nullptr; - const FormatArgImpl args[] = { + volatile char *vcp = &vc; + volatile char *vcnil = nullptr; + const FormatArgImpl args_array[] = { FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil), FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp), FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil), }; - struct Expectation { - std::string out; - const char *fmt; - }; - const Expectation kExpect[] = { - {StrPrint("%p", &x), "%p"}, - {StrPrint("%20p", &x), "%20p"}, - {StrPrint("%.1p", &x), "%.1p"}, - {StrPrint("%.20p", &x), "%.20p"}, - {StrPrint("%30.20p", &x), "%30.20p"}, - - {StrPrint("%-p", &x), "%-p"}, - {StrPrint("%-20p", &x), "%-20p"}, - {StrPrint("%-.1p", &x), "%-.1p"}, - {StrPrint("%.20p", &x), "%.20p"}, - {StrPrint("%-30.20p", &x), "%-30.20p"}, - - {StrPrint("%p", cp), "%2$p"}, // const char* - {"(nil)", "%3$p"}, // null const char * - {"(nil)", "%4$p"}, // null const int * - {StrPrint("%p", mcp), "%5$p"}, // nonconst char* - - {StrPrint("%p", fp), "%6$p"}, // function pointer - {StrPrint("%p", vcp), "%8$p"}, // function pointer - -#ifndef __APPLE__ - // Apple's printf differs here (0x0 vs. nil) - {StrPrint("%p", fnil), "%7$p"}, // null function pointer - {StrPrint("%p", vcnil), "%9$p"}, // null function pointer -#endif - }; - for (const Expectation &e : kExpect) { - UntypedFormatSpecImpl format(e.fmt); - EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))) << e.fmt; - } + auto args = absl::MakeConstSpan(args_array); + + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args), + MatchesPointerString(&x)); + + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args), + MatchesPointerString(&x)); + + // const char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args), + MatchesPointerString(cp)); + // null const int* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args), + MatchesPointerString(nullptr)); + // null const char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args), + MatchesPointerString(nullptr)); + // nonconst char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args), + MatchesPointerString(mcp)); + + // function pointers + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args), + MatchesPointerString(reinterpret_cast<const void*>(fp))); + EXPECT_THAT( + FormatPack(UntypedFormatSpecImpl("%8$p"), args), + MatchesPointerString(reinterpret_cast<volatile const void *>(vcp))); + + // null function pointers + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args), + MatchesPointerString(nullptr)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args), + MatchesPointerString(nullptr)); } struct Cardinal { diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index 77d7e7835b8b..4edf8891be3a 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -713,11 +713,11 @@ TEST(stringtest, safe_strtou64_base_length_delimited) { } } -// feenableexcept() and fedisableexcept() are missing on macOS, MSVC, -// and WebAssembly. -#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__) -#define ABSL_MISSING_FEENABLEEXCEPT 1 -#define ABSL_MISSING_FEDISABLEEXCEPT 1 +// feenableexcept() and fedisableexcept() are extensions supported by some libc +// implementations. +#if defined(__GLIBC__) || defined(__BIONIC__) +#define ABSL_HAVE_FEENABLEEXCEPT 1 +#define ABSL_HAVE_FEDISABLEEXCEPT 1 #endif class SimpleDtoaTest : public testing::Test { @@ -725,7 +725,7 @@ class SimpleDtoaTest : public testing::Test { void SetUp() override { // Store the current floating point env & clear away any pending exceptions. feholdexcept(&fp_env_); -#ifndef ABSL_MISSING_FEENABLEEXCEPT +#ifdef ABSL_HAVE_FEENABLEEXCEPT // Turn on floating point exceptions. feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif @@ -735,7 +735,7 @@ class SimpleDtoaTest : public testing::Test { // Restore the floating point environment to the original state. // In theory fedisableexcept is unnecessary; fesetenv will also do it. // In practice, our toolchains have subtle bugs. -#ifndef ABSL_MISSING_FEDISABLEEXCEPT +#ifdef ABSL_HAVE_FEDISABLEEXCEPT fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif fesetenv(&fp_env_); diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 82344eb246b3..3438ccc12a7d 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -40,6 +40,13 @@ using std::string_view; #else // ABSL_HAVE_STD_STRING_VIEW +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp +#else // ABSL_HAVE_BUILTIN(__builtin_memcmp) +#define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp +#endif // ABSL_HAVE_BUILTIN(__builtin_memcmp) + #include <cassert> #include <cstddef> #include <cstring> @@ -381,16 +388,13 @@ class string_view { // 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. - int compare(string_view x) const noexcept { - auto min_length = (std::min)(length_, x.length_); - if (min_length > 0) { - int r = memcmp(ptr_, x.ptr_, min_length); - if (r < 0) return -1; - if (r > 0) return 1; - } - if (length_ < x.length_) return -1; - if (length_ > x.length_) return 1; - return 0; + constexpr int compare(string_view x) const noexcept { + return CompareImpl( + length_, x.length_, + length_ == 0 || x.length_ == 0 + ? 0 + : ABSL_INTERNAL_STRING_VIEW_MEMCMP( + ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_)); } // Overload of `string_view::compare()` for comparing a substring of the @@ -528,6 +532,14 @@ class string_view { #endif } + static constexpr int CompareImpl(size_type length_a, size_type length_b, + int compare_result) { + return compare_result == 0 ? static_cast<int>(length_a > length_b) - + static_cast<int>(length_a < length_b) + : static_cast<int>(compare_result > 0) - + static_cast<int>(compare_result < 0); + } + const char* ptr_; size_type length_; }; @@ -535,33 +547,29 @@ class string_view { // This large function is defined inline so that in a fairly common case where // one of the arguments is a literal, the compiler can elide a lot of the // following comparisons. -inline bool operator==(string_view x, string_view y) noexcept { - auto len = x.size(); - if (len != y.size()) { - return false; - } - - return x.data() == y.data() || len <= 0 || - memcmp(x.data(), y.data(), len) == 0; +constexpr bool operator==(string_view x, string_view y) noexcept { + return x.size() == y.size() && + (x.empty() || + ABSL_INTERNAL_STRING_VIEW_MEMCMP(x.data(), y.data(), x.size()) == 0); } -inline bool operator!=(string_view x, string_view y) noexcept { +constexpr bool operator!=(string_view x, string_view y) noexcept { return !(x == y); } -inline bool operator<(string_view x, string_view y) noexcept { - auto min_size = (std::min)(x.size(), y.size()); - const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size); - return (r < 0) || (r == 0 && x.size() < y.size()); +constexpr bool operator<(string_view x, string_view y) noexcept { + return x.compare(y) < 0; } -inline bool operator>(string_view x, string_view y) noexcept { return y < x; } +constexpr bool operator>(string_view x, string_view y) noexcept { + return y < x; +} -inline bool operator<=(string_view x, string_view y) noexcept { +constexpr bool operator<=(string_view x, string_view y) noexcept { return !(y < x); } -inline bool operator>=(string_view x, string_view y) noexcept { +constexpr bool operator>=(string_view x, string_view y) noexcept { return !(x < y); } @@ -570,6 +578,8 @@ std::ostream& operator<<(std::ostream& o, string_view piece); } // namespace absl +#undef ABSL_INTERNAL_STRING_VIEW_MEMCMP + #endif // ABSL_HAVE_STD_STRING_VIEW namespace absl { diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index eb8b170bbbde..86f2fbcd003a 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -974,6 +974,29 @@ TEST(StringViewTest, ConstexprCompiles) { EXPECT_EQ(cstr_strlen.length(), 3); constexpr absl::string_view cstr_strlen2 = "bar"; EXPECT_EQ(cstr_strlen2, "bar"); + +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON 1 +#endif +#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON + constexpr absl::string_view foo = "foo"; + constexpr absl::string_view bar = "bar"; + constexpr bool foo_eq_bar = foo == bar; + constexpr bool foo_ne_bar = foo != bar; + constexpr bool foo_lt_bar = foo < bar; + constexpr bool foo_le_bar = foo <= bar; + constexpr bool foo_gt_bar = foo > bar; + constexpr bool foo_ge_bar = foo >= bar; + constexpr int foo_compare_bar = foo.compare(bar); + EXPECT_FALSE(foo_eq_bar); + EXPECT_TRUE(foo_ne_bar); + EXPECT_FALSE(foo_lt_bar); + EXPECT_FALSE(foo_le_bar); + EXPECT_TRUE(foo_gt_bar); + EXPECT_TRUE(foo_ge_bar); + EXPECT_GT(foo_compare_bar, 0); +#endif #endif #if !defined(__clang__) || 3 < __clang_major__ || \ diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 961457809f12..d83d8087f8d5 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -129,6 +129,9 @@ void Waiter::Init() { 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. + // 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); if (x != 0) { @@ -140,6 +143,8 @@ 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) { if (err == -EINTR || err == -EWOULDBLOCK) { @@ -150,8 +155,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err); } } - - MaybeBecomeIdle(); + first_pass = false; } } @@ -219,6 +223,9 @@ bool Waiter::Wait(KernelTimeout t) { PthreadMutexHolder h(&mu_); waiter_count_.fetch_add(1, std::memory_order_relaxed); // Loop until we find a wakeup to consume or timeout. + // 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) { int x = wakeup_count_.load(std::memory_order_relaxed); if (x != 0) { @@ -232,6 +239,7 @@ bool Waiter::Wait(KernelTimeout t) { return true; } + if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!t.has_timeout()) { const int err = pthread_cond_wait(&cv_, &mu_); @@ -248,7 +256,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err); } } - MaybeBecomeIdle(); + first_pass = false; } } @@ -288,6 +296,9 @@ bool Waiter::Wait(KernelTimeout t) { } // Loop until we timeout or consume a wakeup. + // 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) { int x = wakeups_.load(std::memory_order_relaxed); if (x != 0) { @@ -300,6 +311,7 @@ bool Waiter::Wait(KernelTimeout t) { return true; } + if (!first_pass) MaybeBecomeIdle(); // Nothing to consume, wait (looping on EINTR). while (true) { if (!t.has_timeout()) { @@ -313,7 +325,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "sem_timedwait failed: %d", errno); } } - MaybeBecomeIdle(); + first_pass = false; } } @@ -401,6 +413,9 @@ bool Waiter::Wait(KernelTimeout t) { waiter_count_.fetch_add(1, std::memory_order_relaxed); // Loop until we find a wakeup to consume or timeout. + // 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) { int x = wakeup_count_.load(std::memory_order_relaxed); if (x != 0) { @@ -414,6 +429,7 @@ bool Waiter::Wait(KernelTimeout t) { return true; } + if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) { // GetLastError() returns a Win32 DWORD, but we assign to @@ -427,8 +443,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err); } } - - MaybeBecomeIdle(); + first_pass = false; } } diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 0757b91d79da..099935453bee 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -19,7 +19,7 @@ #include "absl/base/config.h" #ifdef _WIN32 -#include <SdkDdkVer.h> +#include <sdkddkver.h> #else #include <pthread.h> #endif diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index d33318d347df..ccd94618d4a5 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -628,7 +628,7 @@ class ABSL_SCOPED_LOCKABLE WriterMutexLock { // Example: // // // assume count_ is not internal reference count -// int count_ GUARDED_BY(mu_); +// int count_ ABSL_GUARDED_BY(mu_); // // mu_.LockWhen(Condition(+[](int* count) { return *count == 0; }, // &count_)); diff --git a/absl/time/duration.cc b/absl/time/duration.cc index a3ac61a91894..9a8768113f31 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -906,6 +906,11 @@ bool ParseDuration(const std::string& dur_string, Duration* d) { return true; } +bool AbslParseFlag(absl::string_view text, Duration* dst, std::string*) { + return ParseDuration(std::string(text), dst); +} + +std::string AbslUnparseFlag(Duration d) { return FormatDuration(d); } bool ParseFlag(const std::string& text, Duration* dst, std::string* ) { return ParseDuration(text, dst); } diff --git a/absl/time/format.cc b/absl/time/format.cc index d6ca860038dc..ebe872cf7402 100644 --- a/absl/time/format.cc +++ b/absl/time/format.cc @@ -129,6 +129,14 @@ bool ParseTime(const std::string& format, const std::string& input, } // Functions required to support absl::Time flags. +bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) { + return absl::ParseTime(RFC3339_full, std::string(text), absl::UTCTimeZone(), + t, error); +} + +std::string AbslUnparseFlag(absl::Time t) { + return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone()); +} bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) { return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error); } diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc index a26151d3e917..a241e951d1fb 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/absl/time/internal/cctz/src/time_zone_impl.cc @@ -14,6 +14,7 @@ #include "time_zone_impl.h" +#include <deque> #include <mutex> #include <string> #include <unordered_map> @@ -91,8 +92,14 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { void time_zone::Impl::ClearTimeZoneMapTestOnly() { std::lock_guard<std::mutex> lock(TimeZoneMutex()); if (time_zone_map != nullptr) { - // Existing time_zone::Impl* entries are in the wild, so we simply - // leak them. Future requests will result in reloading the data. + // Existing time_zone::Impl* entries are in the wild, so we can't delete + // them. Instead, we move them to a private container, where they are + // logically unreachable but not "leaked". Future requests will result + // in reloading the data. + static auto* cleared = new std::deque<const time_zone::Impl*>; + for (const auto& element : *time_zone_map) { + cleared->push_back(element.second); + } time_zone_map->clear(); } } diff --git a/absl/time/time.cc b/absl/time/time.cc index 6a387bcee61f..60382be7b801 100644 --- a/absl/time/time.cc +++ b/absl/time/time.cc @@ -430,9 +430,17 @@ absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour, } absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) { - const CivilSecond cs(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - const auto ti = tz.At(cs); + civil_year_t tm_year = tm.tm_year; + // Avoids years that are too extreme for CivilSecond to normalize. + if (tm_year > 300000000000ll) return InfiniteFuture(); + if (tm_year < -300000000000ll) return InfinitePast(); + int tm_mon = tm.tm_mon; + if (tm_mon == std::numeric_limits<int>::max()) { + tm_mon -= 12; + tm_year += 1; + } + const auto ti = tz.At(CivilSecond(tm_year + 1900, tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec)); return tm.tm_isdst == 0 ? ti.post : ti.pre; } diff --git a/absl/time/time.h b/absl/time/time.h index 9c8f31774ac6..0b7312ee983d 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -83,6 +83,7 @@ struct timeval; #include <type_traits> #include <utility> +#include "absl/base/macros.h" #include "absl/strings/string_view.h" #include "absl/time/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" @@ -545,7 +546,11 @@ bool ParseDuration(const std::string& 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(). +bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error); +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.") std::string UnparseFlag(Duration d); // Time @@ -815,7 +820,11 @@ std::chrono::system_clock::time_point ToChronoTime(Time); // Additionally, if you'd like to specify a time as a count of // 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); +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.") std::string UnparseFlag(Time t); // TimeZone diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index 37af39d99830..9c4709e6b469 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc @@ -795,6 +795,30 @@ TEST(Time, FromTM) { tm.tm_isdst = 1; t = FromTM(tm, nyc); EXPECT_EQ("2014-03-09T03:30:42-04:00", absl::FormatTime(t, nyc)); // DST + + // Adjusts tm to refer to a time with a year larger than 2147483647. + tm.tm_year = 2147483647 - 1900 + 1; + tm.tm_mon = 6 - 1; + tm.tm_mday = 28; + tm.tm_hour = 1; + tm.tm_min = 2; + tm.tm_sec = 3; + tm.tm_isdst = -1; + t = FromTM(tm, absl::UTCTimeZone()); + EXPECT_EQ("2147483648-06-28T01:02:03+00:00", + absl::FormatTime(t, absl::UTCTimeZone())); + + // Adjusts tm to refer to a time with a very large month. + tm.tm_year = 2019 - 1900; + tm.tm_mon = 2147483647; + tm.tm_mday = 28; + tm.tm_hour = 1; + tm.tm_min = 2; + tm.tm_sec = 3; + tm.tm_isdst = -1; + t = FromTM(tm, absl::UTCTimeZone()); + EXPECT_EQ("178958989-08-28T01:02:03+00:00", + absl::FormatTime(t, absl::UTCTimeZone())); } TEST(Time, TMRoundTrip) { |