diff options
Diffstat (limited to 'absl')
-rw-r--r-- | absl/algorithm/container.h | 78 | ||||
-rw-r--r-- | absl/base/call_once.h | 8 | ||||
-rw-r--r-- | absl/base/config.h | 24 | ||||
-rw-r--r-- | absl/container/BUILD.bazel | 4 | ||||
-rw-r--r-- | absl/container/flat_hash_map.h | 12 | ||||
-rw-r--r-- | absl/container/flat_hash_set.h | 12 | ||||
-rw-r--r-- | absl/container/inlined_vector.h | 41 | ||||
-rw-r--r-- | absl/container/internal/hash_generator_testing.cc | 8 | ||||
-rw-r--r-- | absl/container/internal/hash_generator_testing.h | 8 | ||||
-rw-r--r-- | absl/container/node_hash_map.h | 14 | ||||
-rw-r--r-- | absl/container/node_hash_set.h | 11 | ||||
-rw-r--r-- | absl/numeric/int128.h | 11 | ||||
-rw-r--r-- | absl/synchronization/lifetime_test.cc | 15 | ||||
-rw-r--r-- | absl/synchronization/notification.cc | 1 | ||||
-rw-r--r-- | absl/time/duration_test.cc | 11 | ||||
-rw-r--r-- | absl/time/internal/cctz/src/time_zone_libc.cc | 200 | ||||
-rw-r--r-- | absl/time/internal/cctz/src/time_zone_lookup_test.cc | 100 | ||||
-rw-r--r-- | absl/time/time.h | 18 | ||||
-rw-r--r-- | absl/types/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/types/variant_exception_safety_test.cc | 6 | ||||
-rw-r--r-- | absl/types/variant_test.cc | 9 |
21 files changed, 488 insertions, 104 deletions
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 53ab1568..6d5f6630 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -46,6 +46,8 @@ #include <iterator> #include <numeric> #include <type_traits> +#include <unordered_map> +#include <unordered_set> #include <utility> #include <vector> @@ -54,7 +56,6 @@ #include "absl/meta/type_traits.h" namespace absl { - namespace container_algorithm_internal { // NOTE: it is important to defer to ADL lookup for building with C++ modules, @@ -101,6 +102,17 @@ ContainerIter<C> c_begin(C& c) { return begin(c); } template <typename C> ContainerIter<C> c_end(C& c) { return end(c); } +template <typename T> +struct IsUnorderedContainer : std::false_type {}; + +template <class Key, class T, class Hash, class KeyEqual, class Allocator> +struct IsUnorderedContainer< + std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {}; + +template <class Key, class Hash, class KeyEqual, class Allocator> +struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>> + : std::true_type {}; + } // namespace container_algorithm_internal // PUBLIC API @@ -1154,7 +1166,13 @@ bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { // Container-based version of the <algorithm> `std::set_union()` function // to return an iterator containing the union of two containers; duplicate // values are not copied into the output. -template <typename C1, typename C2, typename OutputIterator> +template <typename C1, typename C2, typename OutputIterator, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), @@ -1164,7 +1182,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { // Overload of c_set_union() for performing a merge using a `comp` other than // `operator<`. -template <typename C1, typename C2, typename OutputIterator, typename Compare> +template <typename C1, typename C2, typename OutputIterator, typename Compare, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { return std::set_union(container_algorithm_internal::c_begin(c1), @@ -1178,7 +1202,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, // // Container-based version of the <algorithm> `std::set_intersection()` function // to return an iterator containing the intersection of two containers. -template <typename C1, typename C2, typename OutputIterator> +template <typename C1, typename C2, typename OutputIterator, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output) { return std::set_intersection(container_algorithm_internal::c_begin(c1), @@ -1189,7 +1219,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Overload of c_set_intersection() for performing a merge using a `comp` other // than `operator<`. -template <typename C1, typename C2, typename OutputIterator, typename Compare> +template <typename C1, typename C2, typename OutputIterator, typename Compare, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { return std::set_intersection(container_algorithm_internal::c_begin(c1), @@ -1204,7 +1240,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Container-based version of the <algorithm> `std::set_difference()` function // to return an iterator containing elements present in the first container but // not in the second. -template <typename C1, typename C2, typename OutputIterator> +template <typename C1, typename C2, typename OutputIterator, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_difference(const C1& c1, const C2& c2, OutputIterator output) { return std::set_difference(container_algorithm_internal::c_begin(c1), @@ -1215,7 +1257,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Overload of c_set_difference() for performing a merge using a `comp` other // than `operator<`. -template <typename C1, typename C2, typename OutputIterator, typename Compare> +template <typename C1, typename C2, typename OutputIterator, typename Compare, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_difference(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { return std::set_difference(container_algorithm_internal::c_begin(c1), @@ -1230,7 +1278,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Container-based version of the <algorithm> `std::set_symmetric_difference()` // function to return an iterator containing elements present in either one // container or the other, but not both. -template <typename C1, typename C2, typename OutputIterator> +template <typename C1, typename C2, typename OutputIterator, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output) { return std::set_symmetric_difference( @@ -1242,7 +1296,13 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, // Overload of c_set_symmetric_difference() for performing a merge using a // `comp` other than `operator<`. -template <typename C1, typename C2, typename OutputIterator, typename Compare> +template <typename C1, typename C2, typename OutputIterator, typename Compare, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C1>::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer<C2>::value, + void>::type> OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { diff --git a/absl/base/call_once.h b/absl/base/call_once.h index 532ee2e3..f6c8ebb2 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -150,12 +150,8 @@ void CallOnceImpl(std::atomic<uint32_t>* control, old_control != kOnceRunning && old_control != kOnceWaiter && old_control != kOnceDone) { - ABSL_RAW_LOG( - FATAL, - "Unexpected value for control word: %lx. Either the control word " - "has non-static storage duration (where GoogleOnceDynamic might " - "be appropriate), or there's been a memory corruption.", - static_cast<unsigned long>(old_control)); // NOLINT + ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx", + static_cast<unsigned long>(old_control)); // NOLINT } } #endif // NDEBUG diff --git a/absl/base/config.h b/absl/base/config.h index d4eb7d0c..e3edb2bf 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -139,12 +139,18 @@ #ifdef ABSL_HAVE_THREAD_LOCAL #error ABSL_HAVE_THREAD_LOCAL cannot be directly set #elif defined(__APPLE__) -// Notes: Xcode's clang did not support `thread_local` until version -// 8, and even then not for all iOS < 9.0. Also, Xcode 9.3 started disallowing -// `thread_local` for 32-bit iOS simulator targeting iOS 9.x. -// `__has_feature` is only supported by Clang so it has be inside +// Notes: +// * Xcode's clang did not support `thread_local` until version 8, and +// even then not for all iOS < 9.0. +// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator +// targeting iOS 9.x. +// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time +// making __has_feature unreliable there. +// +// Otherwise, `__has_feature` is only supported by Clang so it has be inside // `defined(__APPLE__)` check. -#if __has_feature(cxx_thread_local) +#if __has_feature(cxx_thread_local) && \ + !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) #define ABSL_HAVE_THREAD_LOCAL 1 #endif #else // !defined(__APPLE__) @@ -423,4 +429,12 @@ #define ABSL_HAVE_STD_STRING_VIEW 1 #endif +// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION +// SEH exception from emplace for variant<SomeStruct> when constructing the +// struct can throw. This defeats some of variant_test and +// variant_exception_safety_test. +#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG) +#define ABSL_INTERNAL_MSVC_2017_DBG_MODE +#endif + #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index d75f8916..afc869f4 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -212,6 +212,7 @@ cc_library( ":container_memory", ":hash_function_defaults", ":raw_hash_map", + "//absl/algorithm:container", "//absl/memory", ], ) @@ -240,6 +241,7 @@ cc_library( ":container_memory", ":hash_function_defaults", ":raw_hash_set", + "//absl/algorithm:container", "//absl/base:core_headers", "//absl/memory", ], @@ -271,6 +273,7 @@ cc_library( ":hash_function_defaults", ":node_hash_policy", ":raw_hash_map", + "//absl/algorithm:container", "//absl/memory", ], ) @@ -299,6 +302,7 @@ cc_library( ":hash_function_defaults", ":node_hash_policy", ":raw_hash_set", + "//absl/algorithm:container", "//absl/memory", ], ) diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index de632be2..7bc21380 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -35,6 +35,7 @@ #include <type_traits> #include <utility> +#include "absl/algorithm/container.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export @@ -564,5 +565,16 @@ struct FlatHashMapPolicy { }; } // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template <class Key, class T, class Hash, class KeyEqual, class Allocator> +struct IsUnorderedContainer< + absl::flat_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {}; + +} // namespace container_algorithm_internal + } // namespace absl + #endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_ diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index a2584d66..f7c1acaa 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -32,6 +32,7 @@ #include <type_traits> #include <utility> +#include "absl/algorithm/container.h" #include "absl/base/macros.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export @@ -475,5 +476,16 @@ struct FlatHashSetPolicy { static size_t space_used(const T*) { return 0; } }; } // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template <class Key, class Hash, class KeyEqual, class Allocator> +struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>> + : std::true_type {}; + +} // namespace container_algorithm_internal + } // namespace absl + #endif // ABSL_CONTAINER_FLAT_HASH_SET_H_ diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index ea8cb02b..642dae6c 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -20,17 +20,17 @@ // vector" which behaves in an equivalent fashion to a `std::vector`, except // that storage for small sequences of the vector are provided inline without // requiring any heap allocation. - -// An `absl::InlinedVector<T,N>` specifies the size N at which to inline as one -// of its template parameters. Vectors of length <= N are provided inline. -// Typically N is very small (e.g., 4) so that sequences that are expected to be -// short do not require allocations. - -// An `absl::InlinedVector` does not usually require a specific allocator; if +// +// An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of +// its template parameters. Instances where `size() <= N` hold contained +// elements in inline space. Typically `N` is very small so that sequences that +// are expected to be short do not require allocations. +// +// An `absl::InlinedVector` does not usually require a specific allocator. If // the inlined vector grows beyond its initial constraints, it will need to -// allocate (as any normal `std::vector` would) and it will generally use the -// default allocator in that case; optionally, a custom allocator may be -// specified using an `absl::InlinedVector<T,N,A>` construction. +// allocate (as any normal `std::vector` would). This is usually performed with +// the default allocator (defined as `std::allocator<T>`). Optionally, a custom +// allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`. #ifndef ABSL_CONTAINER_INLINED_VECTOR_H_ #define ABSL_CONTAINER_INLINED_VECTOR_H_ @@ -61,8 +61,8 @@ namespace absl { // An `absl::InlinedVector` is designed to be a drop-in replacement for // `std::vector` for use cases where the vector's size is sufficiently small // that it can be inlined. If the inlined vector does grow beyond its estimated -// size, it will trigger an initial allocation on the heap, and will behave as a -// `std:vector`. The API of the `absl::InlinedVector` within this file is +// capacity, it will trigger an initial allocation on the heap, and will behave +// as a `std:vector`. The API of the `absl::InlinedVector` within this file is // designed to cover the same API footprint as covered by `std::vector`. template <typename T, size_t N, typename A = std::allocator<T>> class InlinedVector { @@ -101,7 +101,6 @@ class InlinedVector { using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; - // --------------------------------------------------------------------------- // InlinedVector Constructors and Destructor // --------------------------------------------------------------------------- @@ -135,11 +134,12 @@ class InlinedVector { AppendRange(init_list.begin(), init_list.end()); } - // Creates and initialize with the elements [`first`, `last`). + // Creates an inlined vector with elements constructed from the provided + // Iterator range [`first`, `last`). // // NOTE: The `enable_if` prevents ambiguous interpretation between a call to - // this constructor with two integral arguments and a call to the preceding - // `InlinedVector(n, v)` constructor. + // this constructor with two integral arguments and a call to the above + // `InlinedVector(size_type, const_reference)` constructor. template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr> InlinedVector(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()) @@ -153,11 +153,11 @@ class InlinedVector { // Creates a copy of `other` but with a specified allocator. InlinedVector(const InlinedVector& other, const allocator_type& alloc); - // Creates an inlined vector with the contents of `other`. + // Creates an inlined vector by moving in the contents of `other`. // // NOTE: This move constructor does not allocate and only moves the underlying // objects, so its `noexcept` specification depends on whether moving the - // underlying objects can throw or not. We assume + // underlying objects can throw or not. We assume: // a) move constructors should only throw due to allocation failure and // b) if `value_type`'s move constructor allocates, it uses the same // allocation function as the `InlinedVector`'s allocator, so the move @@ -167,9 +167,9 @@ class InlinedVector { absl::allocator_is_nothrow<allocator_type>::value || std::is_nothrow_move_constructible<value_type>::value); - // Creates an inlined vector with the contents of `other`. + // Creates an inlined vector by moving in the contents of `other`. // - // NOTE: This move constructor allocates and also moves the underlying + // NOTE: This move constructor allocates and subsequently moves the underlying // objects, so its `noexcept` specification depends on whether the allocation // can throw and whether moving the underlying objects can throw. Based on the // same assumptions as above, the `noexcept` specification is dominated by @@ -180,7 +180,6 @@ class InlinedVector { ~InlinedVector() { clear(); } - // --------------------------------------------------------------------------- // InlinedVector Member Accessors // --------------------------------------------------------------------------- diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc index 0d6a9df1..e0fefbff 100644 --- a/absl/container/internal/hash_generator_testing.cc +++ b/absl/container/internal/hash_generator_testing.cc @@ -39,9 +39,9 @@ class RandomDeviceSeedSeq { } // namespace -std::mt19937_64* GetThreadLocalRng() { +std::mt19937_64* GetSharedRng() { RandomDeviceSeedSeq seed_seq; - thread_local auto* rng = new std::mt19937_64(seed_seq); + static auto* rng = new std::mt19937_64(seed_seq); return rng; } @@ -51,7 +51,7 @@ std::string Generator<std::string>::operator()() const { std::string res; res.resize(32); std::generate(res.begin(), res.end(), - [&]() { return chars(*GetThreadLocalRng()); }); + [&]() { return chars(*GetSharedRng()); }); return res; } @@ -63,7 +63,7 @@ absl::string_view Generator<absl::string_view>::operator()() const { auto& res = arena->back(); res.resize(32); std::generate(res.begin(), res.end(), - [&]() { return chars(*GetThreadLocalRng()); }); + [&]() { return chars(*GetSharedRng()); }); return res; } diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h index 50d77102..6521efe8 100644 --- a/absl/container/internal/hash_generator_testing.h +++ b/absl/container/internal/hash_generator_testing.h @@ -43,7 +43,7 @@ struct IsMap<Map, absl::void_t<typename Map::mapped_type>> : std::true_type {}; } // namespace generator_internal -std::mt19937_64* GetThreadLocalRng(); +std::mt19937_64* GetSharedRng(); enum Enum { kEnumEmpty, @@ -66,7 +66,7 @@ template <class T> struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> { T operator()() const { std::uniform_int_distribution<T> dist; - return dist(*GetThreadLocalRng()); + return dist(*GetSharedRng()); } }; @@ -76,7 +76,7 @@ struct Generator<Enum> { std::uniform_int_distribution<typename std::underlying_type<Enum>::type> dist; while (true) { - auto variate = dist(*GetThreadLocalRng()); + auto variate = dist(*GetSharedRng()); if (variate != kEnumEmpty && variate != kEnumDeleted) return static_cast<Enum>(variate); } @@ -90,7 +90,7 @@ struct Generator<EnumClass> { typename std::underlying_type<EnumClass>::type> dist; while (true) { - EnumClass variate = static_cast<EnumClass>(dist(*GetThreadLocalRng())); + EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng())); if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted) return static_cast<EnumClass>(variate); } diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index 00710e52..18d3f28f 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -40,6 +40,7 @@ #include <type_traits> #include <utility> +#include "absl/algorithm/container.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/node_hash_policy.h" @@ -91,7 +92,7 @@ class NodeHashMapPolicy; // std::string search_key = "b"; // auto result = ducks.find(search_key); // if (result != ducks.end()) { -// std::cout << "Result: " << search_key->second << std::endl; +// std::cout << "Result: " << result->second << std::endl; // } template <class Key, class Value, class Hash = absl::container_internal::hash_default_hash<Key>, @@ -566,5 +567,16 @@ class NodeHashMapPolicy static const Value& value(const value_type* elem) { return elem->second; } }; } // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template <class Key, class T, class Hash, class KeyEqual, class Allocator> +struct IsUnorderedContainer< + absl::node_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {}; + +} // namespace container_algorithm_internal + } // namespace absl + #endif // ABSL_CONTAINER_NODE_HASH_MAP_H_ diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index 813fdeff..e0897c99 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -37,6 +37,7 @@ #include <type_traits> +#include "absl/algorithm/container.h" #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/node_hash_policy.h" #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export @@ -475,5 +476,15 @@ struct NodeHashSetPolicy static size_t element_space_used(const T*) { return sizeof(T); } }; } // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template <class Key, class Hash, class KeyEqual, class Allocator> +struct IsUnorderedContainer<absl::node_hash_set<Key, Hash, KeyEqual, Allocator>> + : std::true_type {}; + +} // namespace container_algorithm_internal } // namespace absl + #endif // ABSL_CONTAINER_NODE_HASH_SET_H_ diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index 5d14a4a8..9c2e00f6 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -37,6 +37,11 @@ #include "absl/base/macros.h" #include "absl/base/port.h" +#if defined(_MSC_VER) && defined(_WIN64) +#include <intrin.h> +#pragma intrinsic(_umul128) +#endif // defined(_MSC_VER) && defined(_WIN64) + namespace absl { @@ -661,6 +666,12 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { // can be used for uint128 storage. return static_cast<unsigned __int128>(lhs) * static_cast<unsigned __int128>(rhs); +#elif defined(_MSC_VER) && defined(_WIN64) + uint64_t carry; + uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry); + return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) + + Uint128High64(lhs) * Uint128Low64(rhs) + carry, + low); #else // ABSL_HAVE_INTRINSIC128 uint64_t a32 = Uint128Low64(lhs) >> 32; uint64_t a00 = Uint128Low64(lhs) & 0xffffffff; diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc index 90c9009b..b7360c29 100644 --- a/absl/synchronization/lifetime_test.cc +++ b/absl/synchronization/lifetime_test.cc @@ -72,23 +72,19 @@ void ThreadTwo(absl::Mutex* mutex, absl::CondVar* condvar, // Launch thread 1 and thread 2, and block on their completion. // If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally // constructed instance instead. -void RunTests(absl::Mutex* mutex, absl::CondVar* condvar, - absl::Notification* notification) { +void RunTests(absl::Mutex* mutex, absl::CondVar* condvar) { absl::Mutex default_mutex; absl::CondVar default_condvar; - absl::Notification default_notification; + absl::Notification notification; if (!mutex) { mutex = &default_mutex; } if (!condvar) { condvar = &default_condvar; } - if (!notification) { - notification = &default_notification; - } bool state = false; - std::thread thread_one(ThreadOne, mutex, condvar, notification, &state); - std::thread thread_two(ThreadTwo, mutex, condvar, notification, &state); + std::thread thread_one(ThreadOne, mutex, condvar, ¬ification, &state); + std::thread thread_two(ThreadTwo, mutex, condvar, ¬ification, &state); thread_one.join(); thread_two.join(); } @@ -96,8 +92,7 @@ void RunTests(absl::Mutex* mutex, absl::CondVar* condvar, void TestLocals() { absl::Mutex mutex; absl::CondVar condvar; - absl::Notification notification; - RunTests(&mutex, &condvar, ¬ification); + RunTests(&mutex, &condvar); } // Global variables during start and termination diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc index ed8cc906..cdcbc134 100644 --- a/absl/synchronization/notification.cc +++ b/absl/synchronization/notification.cc @@ -22,6 +22,7 @@ #include "absl/time/time.h" namespace absl { + void Notification::Notify() { MutexLock l(&this->mutex_); diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index 7ae25dc6..775da91e 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -56,6 +56,17 @@ MATCHER_P(TimevalMatcher, tv, "") { return false; } +TEST(Duration, ConstExpr) { + constexpr absl::Duration d0 = absl::ZeroDuration(); + static_assert(d0 == absl::ZeroDuration(), "ZeroDuration()"); + constexpr absl::Duration d1 = absl::Seconds(1); + static_assert(d1 == absl::Seconds(1), "Seconds(1)"); + static_assert(d1 != absl::ZeroDuration(), "Seconds(1)"); + constexpr absl::Duration d2 = absl::InfiniteDuration(); + static_assert(d2 == absl::InfiniteDuration(), "InfiniteDuration()"); + static_assert(d2 != absl::ZeroDuration(), "InfiniteDuration()"); +} + TEST(Duration, ValueSemantics) { // If this compiles, the test passes. constexpr absl::Duration a; // Default construction diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc index e35fa18b..6db519e1 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/absl/time/internal/cctz/src/time_zone_libc.cc @@ -20,6 +20,7 @@ #include <chrono> #include <ctime> +#include <limits> #include <tuple> #include <utility> @@ -85,6 +86,76 @@ OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr, #endif // !defined(__tm_gmtoff) && !defined(__tm_zone) #endif +inline std::tm* gm_time(const std::time_t *timep, std::tm *result) { +#if defined(_WIN32) || defined(_WIN64) + return gmtime_s(result, timep) ? nullptr : result; +#else + return gmtime_r(timep, result); +#endif +} + +inline std::tm* local_time(const std::time_t *timep, std::tm *result) { +#if defined(_WIN32) || defined(_WIN64) + return localtime_s(result, timep) ? nullptr : result; +#else + return localtime_r(timep, result); +#endif +} + +// Converts a civil second and "dst" flag into a time_t and UTC offset. +// Returns false if time_t cannot represent the requested civil second. +// Caller must have already checked that cs.year() will fit into a tm_year. +bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) { + std::tm tm; + tm.tm_year = static_cast<int>(cs.year() - year_t{1900}); + tm.tm_mon = cs.month() - 1; + tm.tm_mday = cs.day(); + tm.tm_hour = cs.hour(); + tm.tm_min = cs.minute(); + tm.tm_sec = cs.second(); + tm.tm_isdst = is_dst; + *t = std::mktime(&tm); + if (*t == std::time_t{-1}) { + std::tm tm2; + const std::tm* tmp = local_time(t, &tm2); + if (tmp == nullptr || tmp->tm_year != tm.tm_year || + tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday || + tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min || + tmp->tm_sec != tm.tm_sec) { + // A true error (not just one second before the epoch). + return false; + } + } + *off = get_offset_abbr(tm).first; + return true; +} + +// Find the least time_t in [lo:hi] where local time matches offset, given: +// (1) lo doesn't match, (2) hi does, and (3) there is only one transition. +std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { + std::tm tm; + while (lo + 1 != hi) { + const std::time_t mid = lo + (hi - lo) / 2; + if (std::tm* tmp = local_time(&mid, &tm)) { + if (get_offset_abbr(*tmp).first == offset) { + hi = mid; + } else { + lo = mid; + } + } else { + // If std::tm cannot hold some result we resort to a linear search, + // ignoring all failed conversions. Slow, but never really happens. + while (++lo != hi) { + if (std::tm* tmp = local_time(&lo, &tm)) { + if (get_offset_abbr(*tmp).first == offset) break; + } + } + return lo; + } + } + return hi; +} + } // namespace TimeZoneLibC::TimeZoneLibC(const std::string& name) @@ -93,50 +164,107 @@ TimeZoneLibC::TimeZoneLibC(const std::string& name) time_zone::absolute_lookup TimeZoneLibC::BreakTime( const time_point<seconds>& tp) const { time_zone::absolute_lookup al; - std::time_t t = ToUnixSeconds(tp); + al.offset = 0; + al.is_dst = false; + al.abbr = "-00"; + + const std::int_fast64_t s = ToUnixSeconds(tp); + + // If std::time_t cannot hold the input we saturate the output. + if (s < std::numeric_limits<std::time_t>::min()) { + al.cs = civil_second::min(); + return al; + } + if (s > std::numeric_limits<std::time_t>::max()) { + al.cs = civil_second::max(); + return al; + } + + const std::time_t t = static_cast<std::time_t>(s); std::tm tm; - if (local_) { -#if defined(_WIN32) || defined(_WIN64) - localtime_s(&tm, &t); -#else - localtime_r(&t, &tm); -#endif - std::tie(al.offset, al.abbr) = get_offset_abbr(tm); - } else { -#if defined(_WIN32) || defined(_WIN64) - gmtime_s(&tm, &t); -#else - gmtime_r(&t, &tm); -#endif - al.offset = 0; - al.abbr = "UTC"; + std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm); + + // If std::tm cannot hold the result we saturate the output. + if (tmp == nullptr) { + al.cs = (s < 0) ? civil_second::min() : civil_second::max(); + return al; } - al.cs = civil_second(tm.tm_year + year_t{1900}, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - al.is_dst = tm.tm_isdst > 0; + + const year_t year = tmp->tm_year + year_t{1900}; + al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + std::tie(al.offset, al.abbr) = get_offset_abbr(*tmp); + if (!local_) al.abbr = "UTC"; // as expected by cctz + al.is_dst = tmp->tm_isdst > 0; return al; } time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { - time_zone::civil_lookup cl; - std::time_t t; - if (local_) { - // Does not handle SKIPPED/AMBIGUOUS or huge years. - std::tm tm; - tm.tm_year = static_cast<int>(cs.year() - 1900); - tm.tm_mon = cs.month() - 1; - tm.tm_mday = cs.day(); - tm.tm_hour = cs.hour(); - tm.tm_min = cs.minute(); - tm.tm_sec = cs.second(); - tm.tm_isdst = -1; - t = std::mktime(&tm); + if (!local_) { + // If time_point<seconds> cannot hold the result we saturate. + static const civil_second min_tp_cs = + civil_second() + ToUnixSeconds(time_point<seconds>::min()); + static const civil_second max_tp_cs = + civil_second() + ToUnixSeconds(time_point<seconds>::max()); + const time_point<seconds> tp = + (cs < min_tp_cs) + ? time_point<seconds>::min() + : (cs > max_tp_cs) ? time_point<seconds>::max() + : FromUnixSeconds(cs - civil_second()); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } + + // If tm_year cannot hold the requested year we saturate the result. + if (cs.year() < 0) { + if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) { + const time_point<seconds> tp = time_point<seconds>::min(); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } } else { - t = cs - civil_second(); + if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) { + const time_point<seconds> tp = time_point<seconds>::max(); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } + } + + // We probe with "is_dst" values of 0 and 1 to try to distinguish unique + // civil seconds from skipped or repeated ones. This is not always possible + // however, as the "dst" flag does not change over some offset transitions. + // We are also subject to the vagaries of mktime() implementations. + std::time_t t0, t1; + int offset0, offset1; + if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) { + if (t0 == t1) { + // The civil time was singular (pre == trans == post). + const time_point<seconds> tp = FromUnixSeconds(t0); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; + } + + if (t0 > t1) { + std::swap(t0, t1); + std::swap(offset0, offset1); + } + const std::time_t tt = find_trans(t0, t1, offset1); + const time_point<seconds> trans = FromUnixSeconds(tt); + + if (offset0 < offset1) { + // The civil time did not exist (pre >= trans > post). + const time_point<seconds> pre = FromUnixSeconds(t1); + const time_point<seconds> post = FromUnixSeconds(t0); + return {time_zone::civil_lookup::SKIPPED, pre, trans, post}; + } + + // The civil time was ambiguous (pre < trans <= post). + const time_point<seconds> pre = FromUnixSeconds(t0); + const time_point<seconds> post = FromUnixSeconds(t1); + return {time_zone::civil_lookup::REPEATED, pre, trans, post}; } - cl.kind = time_zone::civil_lookup::UNIQUE; - cl.pre = cl.trans = cl.post = FromUnixSeconds(t); - return cl; + + // make_time() failed somehow so we saturate the result. + const time_point<seconds> tp = (cs < civil_second()) + ? time_point<seconds>::min() + : time_point<seconds>::max(); + return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp, diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index f28e7f85..e84b9469 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -16,7 +16,9 @@ #include <chrono> #include <cstddef> +#include <cstdlib> #include <future> +#include <limits> #include <string> #include <thread> #include <vector> @@ -925,7 +927,7 @@ TEST(MakeTime, Normalization) { EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz)); // second } -// NOTE: Run this with --copt=-ftrapv to detect overflow problems. +// NOTE: Run this with -ftrapv to detect overflow problems. TEST(MakeTime, SysSecondsLimits) { const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez"; const time_zone utc = utc_time_zone(); @@ -991,19 +993,107 @@ TEST(MakeTime, SysSecondsLimits) { tp = convert(civil_second::min(), west); EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); + // Some similar checks for the "libc" time-zone implementation. if (sizeof(std::time_t) >= 8) { // Checks that "tm_year + 1900", as used by the "libc" implementation, // can produce year values beyond the range on an int without overflow. #if defined(_WIN32) || defined(_WIN64) - // localtime_s() and gmtime_s() don't believe in years past 3000. + // localtime_s() and gmtime_s() don't believe in years outside [1970:3000]. #else - const time_zone libc_utc = LoadZone("libc:UTC"); - tp = convert(civil_year(year_t{2147483648}), libc_utc); - EXPECT_EQ("2147483648-01-01T00:00:00+00:00", format(RFC3339, tp, libc_utc)); + const time_zone utc = LoadZone("libc:UTC"); + const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900; + tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), utc); + EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, utc)); + const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900; + tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, utc)); #endif } } +TEST(MakeTime, LocalTimeLibC) { + // Checks that cctz and libc agree on transition points in [1970:2037]. + // + // We limit this test case to environments where: + // 1) we know how to change the time zone used by localtime()/mktime(), + // 2) cctz and localtime()/mktime() will use similar-enough tzdata, and + // 3) we have some idea about how mktime() behaves during transitions. +#if defined(__linux__) + const char* const ep = getenv("TZ"); + std::string tz_name = (ep != nullptr) ? ep : ""; + for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) { + ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means + const auto zi = local_time_zone(); + const auto lc = LoadZone("libc:localtime"); + time_zone::civil_transition trans; + for (auto tp = zi.lookup(civil_second()).trans; + zi.next_transition(tp, &trans); + tp = zi.lookup(trans.to).trans) { + const auto fcl = zi.lookup(trans.from); + const auto tcl = zi.lookup(trans.to); + civil_second cs; // compare cs in zi and lc + if (fcl.kind == time_zone::civil_lookup::UNIQUE) { + if (tcl.kind == time_zone::civil_lookup::UNIQUE) { + // Both unique; must be an is_dst or abbr change. + ASSERT_EQ(trans.from, trans.to); + const auto trans = fcl.trans; + const auto tal = zi.lookup(trans); + const auto tprev = trans - absl::time_internal::cctz::seconds(1); + const auto pal = zi.lookup(tprev); + if (pal.is_dst == tal.is_dst) { + ASSERT_STRNE(pal.abbr, tal.abbr); + } + continue; + } + ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind); + cs = trans.to; + } else { + ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind); + ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind); + cs = trans.from; + } + if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t) + const auto cl_zi = zi.lookup(cs); + if (zi.lookup(cl_zi.pre).is_dst == zi.lookup(cl_zi.post).is_dst) { + // The "libc" implementation cannot correctly classify transitions + // that don't change the "tm_isdst" flag. In Europe/Volgograd, for + // example, there is a SKIPPED transition from +03 to +04 with dst=F + // on both sides ... + // 1540681199 = 2018-10-28 01:59:59 +03:00:00 [dst=F off=10800] + // 1540681200 = 2018-10-28 03:00:00 +04:00:00 [dst=F off=14400] + // but std::mktime(2018-10-28 02:00:00, tm_isdst=0) fails, unlike, + // say, the similar Europe/Chisinau transition from +02 to +03 ... + // 1521935999 = 2018-03-25 01:59:59 +02:00:00 [dst=F off=7200] + // 1521936000 = 2018-03-25 03:00:00 +03:00:00 [dst=T off=10800] + // where std::mktime(2018-03-25 02:00:00, tm_isdst=0) succeeds and + // returns 1521936000. + continue; + } + if (cs == civil_second(2037, 10, 4, 2, 0, 0)) { + const std::string tzname = *np; + if (tzname == "Africa/Casablanca" || tzname == "Africa/El_Aaiun") { + // The "libc" implementation gets this transition wrong (at least + // until 2018g when it was removed), returning an offset of 3600 + // instead of 0. TODO: Revert this when 2018g is ubiquitous. + continue; + } + } + const auto cl_lc = lc.lookup(cs); + SCOPED_TRACE(testing::Message() << "For " << cs << " in " << *np); + EXPECT_EQ(cl_zi.kind, cl_lc.kind); + EXPECT_EQ(cl_zi.pre, cl_lc.pre); + EXPECT_EQ(cl_zi.trans, cl_lc.trans); + EXPECT_EQ(cl_zi.post, cl_lc.post); + } + } + if (ep == nullptr) { + ASSERT_EQ(0, unsetenv("TZ")); + } else { + ASSERT_EQ(0, setenv("TZ", tz_name.c_str(), 1)); + } +#endif +} + TEST(NextTransition, UTC) { const auto tz = utc_time_zone(); time_zone::civil_transition trans; diff --git a/absl/time/time.h b/absl/time/time.h index e9e989cb..de12e560 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -153,6 +153,16 @@ class Duration { // Value semantics. constexpr Duration() : rep_hi_(0), rep_lo_(0) {} // zero-length duration + // Copyable. +#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1910 + // Explicitly defining the constexpr copy constructor avoids an MSVC bug. + constexpr Duration(const Duration& d) + : rep_hi_(d.rep_hi_), rep_lo_(d.rep_lo_) {} +#else + constexpr Duration(const Duration& d) = default; +#endif + Duration& operator=(const Duration& d) = default; + // Compound assignment operators. Duration& operator+=(Duration d); Duration& operator-=(Duration d); @@ -584,7 +594,11 @@ class Time { // absl::Time t = absl::Now(); // absl::Time t = absl::TimeFromTimeval(tv); // absl::Time t = absl::InfinitePast(); - constexpr Time() {} + constexpr Time() = default; + + // Copyable. + constexpr Time(const Time& t) = default; + Time& operator=(const Time& t) = default; // Assignment operators. Time& operator+=(Duration d) { @@ -826,6 +840,8 @@ class TimeZone { public: explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {} TimeZone() = default; // UTC, but prefer UTCTimeZone() to be explicit. + + // Copyable. TimeZone(const TimeZone&) = default; TimeZone& operator=(const TimeZone&) = default; diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 43899ca7..d56fea6e 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -287,6 +287,7 @@ cc_test( linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, deps = [ ":variant", + "//absl/base:config", "//absl/base:exception_safety_testing", "//absl/memory", "@com_google_googletest//:gtest_main", diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc index 58436f07..82425dbd 100644 --- a/absl/types/variant_exception_safety_test.cc +++ b/absl/types/variant_exception_safety_test.cc @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + #include "absl/types/variant.h" #include <iostream> @@ -20,8 +21,11 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/base/internal/exception_safety_testing.h" #include "absl/memory/memory.h" +// See comment in absl/base/config.h +#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) namespace absl { namespace { @@ -506,3 +510,5 @@ TEST(VariantExceptionSafetyTest, Swap) { } // namespace } // namespace absl + +#endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc index 626d5e61..f47f3a72 100644 --- a/absl/types/variant_test.cc +++ b/absl/types/variant_test.cc @@ -559,9 +559,14 @@ TEST(VariantTest, TestDtor) { } #ifdef ABSL_HAVE_EXCEPTIONS - +// See comment in absl/base/config.h +#if defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) +TEST(VariantTest, DISABLED_TestDtorValuelessByException) +#else // Test destruction when in the valueless_by_exception state. -TEST(VariantTest, TestDtorValuelessByException) { +TEST(VariantTest, TestDtorValuelessByException) +#endif +{ int counter = 0; IncrementInDtor counter_adjuster(&counter); |