summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2018-11-13 13:22:00 -0800
committerGravatar Jon Cohen <cohenjon@google.com>2018-11-13 17:56:57 -0500
commit7b46e1d31a6b08b1c6da2a13e7b151a20446fa07 (patch)
tree0e4f4ad83ffb009e5e54004b2f3c003ecd216135
parent070f6e47b33a2909d039e620c873204f78809492 (diff)
Export of internal Abseil changes.
-- 07575526242a8e1275ac4223a3d2822795f46569 by CJ Johnson <johnsoncj@google.com>: Comment cleanup on InlinedVector PiperOrigin-RevId: 221322176 -- 49a5e643f85e34d53c41f5e6cc33357c55c9115d by Matt Kulukundis <kfm@google.com>: Internal cleanup PiperOrigin-RevId: 221309185 -- bb35be87ec9c74244b7d902e7e7d2d33ab139d76 by Abseil Team <absl-team@google.com>: Fix typo in comment. PiperOrigin-RevId: 221145354 -- afd4d7c106919708004e06aeea068a57c28aec44 by Derek Mauro <dmauro@google.com>: Update the debugging log message in CallOnceImpl() PiperOrigin-RevId: 221103254 -- 0b9dace8b88113777bf26a6d38f9bc0bcaf053a1 by Abseil Team <absl-team@google.com>: Workaround an MSVC 2015 bug in compile-time initialization. PiperOrigin-RevId: 220871483 -- ea0a3854511ed26beab827e5a5113766b334db86 by Marek Gilbert <mcg@google.com>: Fix ABSL_HAVE_THREAD_LOCAL when compiling for iOS 8 with Xcode 10. Xcode 10 has moved the check for thread_local to a link time, so clang reports __has_feature(cxx_thread_local) but then linking fails with messages like this: ld: targeted OS version does not support use of thread local variables PiperOrigin-RevId: 220815885 -- 485b6876c158c3dcf37eb32d7e512242d5d4ecc6 by Greg Falcon <gfalcon@google.com>: Make the absl::c_set_xxxx() algorithms refuse to compile when passed an unordered collection from std:: or absl::. These algorithms operate on sorted sequences; passing an unordered container to them is nearly certainly a bug. This change is technically an API break, but it only breaks incorrect code. We could try to be more clever and detect unordered collections from other libraries, but false positives will break legal code, and this would constitute an API break Abseil cannot afford. PiperOrigin-RevId: 220794190 -- c47cff7f9cc70a4c1604eee0131af552f40e46d6 by Jon Cohen <cohenjon@google.com>: MSVC 2017's STL throws a Structured Exception (not a C++ exception, essentially equivalent to SIGSEGV) when variant::emplace calls a throwing constructor when using the debug multithreaded MSVC runtime DLL. This manifests in dbg mode in Bazel builds. Disable tests which trigger this bug. It's impossible to specifically pull out MSVC 2017 -dbg modes because there's no way for Bazel to know when version of MSVC is being used -- you tell Bazel the directory where the MSVC tools live, not which version of MSVC tools to use. Thus the best we can do is switch on _DEBUG, which is set whenever the debug runtime is selected with the /MDd build flag, as in Bazel -dbg modes. See https://msdn.microsoft.com/en-us/library/b0084kay.aspx ctrl-f "_DEBUG" PiperOrigin-RevId: 220706161 -- 43993d4af309d92f4ebff38391dcc245f154ecc7 by Shaindel Schwartz <shaindel@google.com>: Internal change PiperOrigin-RevId: 220688429 -- 2448802972dcc261af153af464f2b022ef54a2a9 by Abseil Team <absl-team@google.com>: Speed up operator* for uint128 in WIN64. PiperOrigin-RevId: 220678790 -- 7b376403dd05ba10152fb52e40b29d8af79b58bb by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 220654834 -- ae08af58111c3f838b8d4de25f501c3559c86002 by Abseil Team <absl-team@google.com>: CMake: Add absl_cc_test function PiperOrigin-RevId: 220603940 GitOrigin-RevId: 07575526242a8e1275ac4223a3d2822795f46569 Change-Id: Iba7f53eb394c8a9de564582a976793f9bb0596d9
-rw-r--r--CMake/AbseilHelpers.cmake83
-rw-r--r--absl/algorithm/container.h78
-rw-r--r--absl/base/call_once.h8
-rw-r--r--absl/base/config.h24
-rw-r--r--absl/container/BUILD.bazel4
-rw-r--r--absl/container/flat_hash_map.h12
-rw-r--r--absl/container/flat_hash_set.h12
-rw-r--r--absl/container/inlined_vector.h41
-rw-r--r--absl/container/internal/hash_generator_testing.cc8
-rw-r--r--absl/container/internal/hash_generator_testing.h8
-rw-r--r--absl/container/node_hash_map.h14
-rw-r--r--absl/container/node_hash_set.h11
-rw-r--r--absl/numeric/int128.h11
-rw-r--r--absl/synchronization/lifetime_test.cc15
-rw-r--r--absl/synchronization/notification.cc1
-rw-r--r--absl/time/duration_test.cc11
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.cc200
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc100
-rw-r--r--absl/time/time.h18
-rw-r--r--absl/types/BUILD.bazel1
-rw-r--r--absl/types/variant_exception_safety_test.cc6
-rw-r--r--absl/types/variant_test.cc9
22 files changed, 564 insertions, 111 deletions
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
index d4870365..3b9148ea 100644
--- a/CMake/AbseilHelpers.cmake
+++ b/CMake/AbseilHelpers.cmake
@@ -62,7 +62,6 @@ function(absl_library)
endif()
endfunction()
-#
# CMake function to imitate Bazel's cc_library rule.
#
# Parameters:
@@ -77,14 +76,13 @@ endfunction()
# TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake.
#
# Note:
-#
# By default, absl_cc_library will always create a library named absl_internal_${NAME},
# which means other targets can only depend this library as absl_internal_${NAME}, not ${NAME}.
# This is to reduce namespace pollution.
#
# absl_cc_library(
# NAME
-# awesome_lib
+# awesome
# HDRS
# "a.h"
# SRCS
@@ -96,7 +94,7 @@ endfunction()
# SRCS
# "b.cc"
# DEPS
-# absl_internal_awesome_lib # not "awesome_lib"!
+# absl_internal_awesome # not "awesome"!
# )
#
# If PUBLIC is set, absl_cc_library will instead create a target named
@@ -112,7 +110,6 @@ endfunction()
# User can then use the library as absl::main_lib (although absl_main_lib is defined too).
#
# TODO: Implement "ALWAYSLINK"
-
function(absl_cc_library)
cmake_parse_arguments(ABSL_CC_LIB
"DISABLE_INSTALL;PUBLIC;TESTONLY"
@@ -153,7 +150,8 @@ function(absl_cc_library)
else()
# Generating header-only library
add_library(${_NAME} INTERFACE)
- target_include_directories(${_NAME} INTERFACE ${ABSL_COMMON_INCLUDE_DIRS})
+ target_include_directories(${_NAME}
+ INTERFACE ${ABSL_COMMON_INCLUDE_DIRS})
target_link_libraries(${_NAME}
INTERFACE ${ABSL_CC_LIB_DEPS} ${ABSL_CC_LIB_LINKOPTS}
)
@@ -166,6 +164,78 @@ function(absl_cc_library)
endif()
endfunction()
+# absl_cc_test()
+#
+# CMake function to imitate Bazel's cc_test rule.
+#
+# Parameters:
+# NAME: name of target (see Usage below)
+# SRCS: List of source files for the binary
+# DEPS: List of other libraries to be linked in to the binary targets
+# COPTS: List of private compile options
+# DEFINES: List of public defines
+# LINKOPTS: List of link options
+#
+# Note:
+# By default, absl_cc_test will always create a binary named absl_${NAME}.
+# This will also add it to ctest list as absl_${NAME}.
+#
+# Usage:
+# absl_cc_library(
+# NAME
+# awesome
+# HDRS
+# "a.h"
+# SRCS
+# "a.cc"
+# PUBLIC
+# )
+#
+# absl_cc_test(
+# NAME
+# awesome_test
+# SRCS
+# "awesome_test.cc"
+# DEPS
+# absl::awesome
+# gmock
+# gtest_main
+# )
+function(absl_cc_test)
+ if(NOT ABSL_RUN_TESTS)
+ return()
+ endif()
+
+ cmake_parse_arguments(ABSL_CC_TEST
+ ""
+ "NAME"
+ "SRCS;COPTS;DEFINES;LINKOPTS;DEPS"
+ ${ARGN}
+ )
+
+ set(_NAME "absl_${ABSL_CC_TEST_NAME}")
+ add_executable(${_NAME} "")
+ target_sources(${_NAME} PRIVATE ${ABSL_CC_TEST_SRCS})
+ target_include_directories(${_NAME}
+ PUBLIC ${ABSL_COMMON_INCLUDE_DIRS}
+ PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}
+ )
+ target_compile_definitions(${_NAME}
+ PUBLIC ${ABSL_CC_TEST_DEFINES}
+ )
+ target_compile_options(${_NAME}
+ PRIVATE ${ABSL_CC_TEST_COPTS}
+ )
+ target_link_libraries(${_NAME}
+ PUBLIC ${ABSL_CC_TEST_DEPS}
+ PRIVATE ${ABSL_CC_TEST_LINKOPTS}
+ )
+ # Add all Abseil targets to a a folder in the IDE for organization.
+ set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER})
+
+ add_test(NAME ${_NAME} COMMAND ${_NAME})
+endfunction()
+
#
# header only virtual target creation
#
@@ -211,7 +281,6 @@ function(absl_header_library)
endfunction()
-
#
# create an abseil unit_test and add it to the executed test list
#
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, &notification, &state);
+ std::thread thread_two(ThreadTwo, mutex, condvar, &notification, &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, &notification);
+ 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);