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
This commit is contained in:
Abseil Team 2018-11-13 13:22:00 -08:00 committed by Jon Cohen
parent 070f6e47b3
commit 7b46e1d31a
22 changed files with 566 additions and 113 deletions

View file

@ -62,7 +62,6 @@ function(absl_library)
endif() endif()
endfunction() endfunction()
#
# CMake function to imitate Bazel's cc_library rule. # CMake function to imitate Bazel's cc_library rule.
# #
# Parameters: # Parameters:
@ -77,14 +76,13 @@ endfunction()
# TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake. # TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake.
# #
# Note: # Note:
#
# By default, absl_cc_library will always create a library named absl_internal_${NAME}, # 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}. # which means other targets can only depend this library as absl_internal_${NAME}, not ${NAME}.
# This is to reduce namespace pollution. # This is to reduce namespace pollution.
# #
# absl_cc_library( # absl_cc_library(
# NAME # NAME
# awesome_lib # awesome
# HDRS # HDRS
# "a.h" # "a.h"
# SRCS # SRCS
@ -96,7 +94,7 @@ endfunction()
# SRCS # SRCS
# "b.cc" # "b.cc"
# DEPS # 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 # 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). # User can then use the library as absl::main_lib (although absl_main_lib is defined too).
# #
# TODO: Implement "ALWAYSLINK" # TODO: Implement "ALWAYSLINK"
function(absl_cc_library) function(absl_cc_library)
cmake_parse_arguments(ABSL_CC_LIB cmake_parse_arguments(ABSL_CC_LIB
"DISABLE_INSTALL;PUBLIC;TESTONLY" "DISABLE_INSTALL;PUBLIC;TESTONLY"
@ -153,7 +150,8 @@ function(absl_cc_library)
else() else()
# Generating header-only library # Generating header-only library
add_library(${_NAME} INTERFACE) 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} target_link_libraries(${_NAME}
INTERFACE ${ABSL_CC_LIB_DEPS} ${ABSL_CC_LIB_LINKOPTS} INTERFACE ${ABSL_CC_LIB_DEPS} ${ABSL_CC_LIB_LINKOPTS}
) )
@ -166,6 +164,78 @@ function(absl_cc_library)
endif() endif()
endfunction() 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 # header only virtual target creation
# #
@ -211,7 +281,6 @@ function(absl_header_library)
endfunction() endfunction()
# #
# create an abseil unit_test and add it to the executed test list # create an abseil unit_test and add it to the executed test list
# #

View file

@ -46,6 +46,8 @@
#include <iterator> #include <iterator>
#include <numeric> #include <numeric>
#include <type_traits> #include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -54,7 +56,6 @@
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
namespace absl { namespace absl {
namespace container_algorithm_internal { namespace container_algorithm_internal {
// NOTE: it is important to defer to ADL lookup for building with C++ modules, // 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> template <typename C>
ContainerIter<C> c_end(C& c) { return end(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 } // namespace container_algorithm_internal
// PUBLIC API // 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 // Container-based version of the <algorithm> `std::set_union()` function
// to return an iterator containing the union of two containers; duplicate // to return an iterator containing the union of two containers; duplicate
// values are not copied into the output. // 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) { OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) {
return std::set_union(container_algorithm_internal::c_begin(c1), return std::set_union(container_algorithm_internal::c_begin(c1),
container_algorithm_internal::c_end(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 // Overload of c_set_union() for performing a merge using a `comp` other than
// `operator<`. // `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, OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output,
Compare&& comp) { Compare&& comp) {
return std::set_union(container_algorithm_internal::c_begin(c1), 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 // Container-based version of the <algorithm> `std::set_intersection()` function
// to return an iterator containing the intersection of two containers. // 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 c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output) { OutputIterator output) {
return std::set_intersection(container_algorithm_internal::c_begin(c1), 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 // Overload of c_set_intersection() for performing a merge using a `comp` other
// than `operator<`. // 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 c_set_intersection(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) { OutputIterator output, Compare&& comp) {
return std::set_intersection(container_algorithm_internal::c_begin(c1), 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 // Container-based version of the <algorithm> `std::set_difference()` function
// to return an iterator containing elements present in the first container but // to return an iterator containing elements present in the first container but
// not in the second. // 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 c_set_difference(const C1& c1, const C2& c2,
OutputIterator output) { OutputIterator output) {
return std::set_difference(container_algorithm_internal::c_begin(c1), 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 // Overload of c_set_difference() for performing a merge using a `comp` other
// than `operator<`. // 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 c_set_difference(const C1& c1, const C2& c2,
OutputIterator output, Compare&& comp) { OutputIterator output, Compare&& comp) {
return std::set_difference(container_algorithm_internal::c_begin(c1), 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()` // Container-based version of the <algorithm> `std::set_symmetric_difference()`
// function to return an iterator containing elements present in either one // function to return an iterator containing elements present in either one
// container or the other, but not both. // 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 c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output) { OutputIterator output) {
return std::set_symmetric_difference( 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 // Overload of c_set_symmetric_difference() for performing a merge using a
// `comp` other than `operator<`. // `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 c_set_symmetric_difference(const C1& c1, const C2& c2,
OutputIterator output, OutputIterator output,
Compare&& comp) { Compare&& comp) {

View file

@ -150,12 +150,8 @@ void CallOnceImpl(std::atomic<uint32_t>* control,
old_control != kOnceRunning && old_control != kOnceRunning &&
old_control != kOnceWaiter && old_control != kOnceWaiter &&
old_control != kOnceDone) { old_control != kOnceDone) {
ABSL_RAW_LOG( ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx",
FATAL, static_cast<unsigned long>(old_control)); // NOLINT
"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
} }
} }
#endif // NDEBUG #endif // NDEBUG

View file

@ -139,12 +139,18 @@
#ifdef ABSL_HAVE_THREAD_LOCAL #ifdef ABSL_HAVE_THREAD_LOCAL
#error ABSL_HAVE_THREAD_LOCAL cannot be directly set #error ABSL_HAVE_THREAD_LOCAL cannot be directly set
#elif defined(__APPLE__) #elif defined(__APPLE__)
// Notes: Xcode's clang did not support `thread_local` until version // Notes:
// 8, and even then not for all iOS < 9.0. Also, Xcode 9.3 started disallowing // * Xcode's clang did not support `thread_local` until version 8, and
// `thread_local` for 32-bit iOS simulator targeting iOS 9.x. // even then not for all iOS < 9.0.
// `__has_feature` is only supported by Clang so it has be inside // * 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. // `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 #define ABSL_HAVE_THREAD_LOCAL 1
#endif #endif
#else // !defined(__APPLE__) #else // !defined(__APPLE__)
@ -423,4 +429,12 @@
#define ABSL_HAVE_STD_STRING_VIEW 1 #define ABSL_HAVE_STD_STRING_VIEW 1
#endif #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_ #endif // ABSL_BASE_CONFIG_H_

View file

@ -212,6 +212,7 @@ cc_library(
":container_memory", ":container_memory",
":hash_function_defaults", ":hash_function_defaults",
":raw_hash_map", ":raw_hash_map",
"//absl/algorithm:container",
"//absl/memory", "//absl/memory",
], ],
) )
@ -240,6 +241,7 @@ cc_library(
":container_memory", ":container_memory",
":hash_function_defaults", ":hash_function_defaults",
":raw_hash_set", ":raw_hash_set",
"//absl/algorithm:container",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/memory", "//absl/memory",
], ],
@ -271,6 +273,7 @@ cc_library(
":hash_function_defaults", ":hash_function_defaults",
":node_hash_policy", ":node_hash_policy",
":raw_hash_map", ":raw_hash_map",
"//absl/algorithm:container",
"//absl/memory", "//absl/memory",
], ],
) )
@ -299,6 +302,7 @@ cc_library(
":hash_function_defaults", ":hash_function_defaults",
":node_hash_policy", ":node_hash_policy",
":raw_hash_set", ":raw_hash_set",
"//absl/algorithm:container",
"//absl/memory", "//absl/memory",
], ],
) )

View file

@ -35,6 +35,7 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include "absl/algorithm/container.h"
#include "absl/container/internal/container_memory.h" #include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/raw_hash_map.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_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 } // namespace absl
#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_ #endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_

View file

@ -32,6 +32,7 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include "absl/algorithm/container.h"
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/container/internal/container_memory.h" #include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #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; } static size_t space_used(const T*) { return 0; }
}; };
} // namespace container_internal } // 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 } // namespace absl
#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_ #endif // ABSL_CONTAINER_FLAT_HASH_SET_H_

View file

@ -20,17 +20,17 @@
// vector" which behaves in an equivalent fashion to a `std::vector`, except // vector" which behaves in an equivalent fashion to a `std::vector`, except
// that storage for small sequences of the vector are provided inline without // that storage for small sequences of the vector are provided inline without
// requiring any heap allocation. // requiring any heap allocation.
//
// An `absl::InlinedVector<T,N>` specifies the size N at which to inline as one // An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of
// of its template parameters. Vectors of length <= N are provided inline. // its template parameters. Instances where `size() <= N` hold contained
// Typically N is very small (e.g., 4) so that sequences that are expected to be // elements in inline space. Typically `N` is very small so that sequences that
// short do not require allocations. // are expected to be short do not require allocations.
//
// An `absl::InlinedVector` does not usually require a specific allocator; if // An `absl::InlinedVector` does not usually require a specific allocator. If
// the inlined vector grows beyond its initial constraints, it will need to // 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 // allocate (as any normal `std::vector` would). This is usually performed with
// default allocator in that case; optionally, a custom allocator may be // the default allocator (defined as `std::allocator<T>`). Optionally, a custom
// specified using an `absl::InlinedVector<T,N,A>` construction. // allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`.
#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_ #ifndef ABSL_CONTAINER_INLINED_VECTOR_H_
#define 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 // 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 // `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 // 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 // capacity, it will trigger an initial allocation on the heap, and will behave
// `std:vector`. The API of the `absl::InlinedVector` within this file is // 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`. // designed to cover the same API footprint as covered by `std::vector`.
template <typename T, size_t N, typename A = std::allocator<T>> template <typename T, size_t N, typename A = std::allocator<T>>
class InlinedVector { class InlinedVector {
@ -101,7 +101,6 @@ class InlinedVector {
using reverse_iterator = std::reverse_iterator<iterator>; using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// InlinedVector Constructors and Destructor // InlinedVector Constructors and Destructor
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -135,11 +134,12 @@ class InlinedVector {
AppendRange(init_list.begin(), init_list.end()); 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 // NOTE: The `enable_if` prevents ambiguous interpretation between a call to
// this constructor with two integral arguments and a call to the preceding // this constructor with two integral arguments and a call to the above
// `InlinedVector(n, v)` constructor. // `InlinedVector(size_type, const_reference)` constructor.
template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr> template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
InlinedVector(InputIterator first, InputIterator last, InlinedVector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type()) const allocator_type& alloc = allocator_type())
@ -153,11 +153,11 @@ class InlinedVector {
// Creates a copy of `other` but with a specified allocator. // Creates a copy of `other` but with a specified allocator.
InlinedVector(const InlinedVector& other, const allocator_type& alloc); 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 // NOTE: This move constructor does not allocate and only moves the underlying
// objects, so its `noexcept` specification depends on whether moving the // 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 // a) move constructors should only throw due to allocation failure and
// b) if `value_type`'s move constructor allocates, it uses the same // b) if `value_type`'s move constructor allocates, it uses the same
// allocation function as the `InlinedVector`'s allocator, so the move // allocation function as the `InlinedVector`'s allocator, so the move
@ -167,9 +167,9 @@ class InlinedVector {
absl::allocator_is_nothrow<allocator_type>::value || absl::allocator_is_nothrow<allocator_type>::value ||
std::is_nothrow_move_constructible<value_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 // objects, so its `noexcept` specification depends on whether the allocation
// can throw and whether moving the underlying objects can throw. Based on the // can throw and whether moving the underlying objects can throw. Based on the
// same assumptions as above, the `noexcept` specification is dominated by // same assumptions as above, the `noexcept` specification is dominated by
@ -180,7 +180,6 @@ class InlinedVector {
~InlinedVector() { clear(); } ~InlinedVector() { clear(); }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// InlinedVector Member Accessors // InlinedVector Member Accessors
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View file

@ -39,9 +39,9 @@ class RandomDeviceSeedSeq {
} // namespace } // namespace
std::mt19937_64* GetThreadLocalRng() { std::mt19937_64* GetSharedRng() {
RandomDeviceSeedSeq seed_seq; RandomDeviceSeedSeq seed_seq;
thread_local auto* rng = new std::mt19937_64(seed_seq); static auto* rng = new std::mt19937_64(seed_seq);
return rng; return rng;
} }
@ -51,7 +51,7 @@ std::string Generator<std::string>::operator()() const {
std::string res; std::string res;
res.resize(32); res.resize(32);
std::generate(res.begin(), res.end(), std::generate(res.begin(), res.end(),
[&]() { return chars(*GetThreadLocalRng()); }); [&]() { return chars(*GetSharedRng()); });
return res; return res;
} }
@ -63,7 +63,7 @@ absl::string_view Generator<absl::string_view>::operator()() const {
auto& res = arena->back(); auto& res = arena->back();
res.resize(32); res.resize(32);
std::generate(res.begin(), res.end(), std::generate(res.begin(), res.end(),
[&]() { return chars(*GetThreadLocalRng()); }); [&]() { return chars(*GetSharedRng()); });
return res; return res;
} }

View file

@ -43,7 +43,7 @@ struct IsMap<Map, absl::void_t<typename Map::mapped_type>> : std::true_type {};
} // namespace generator_internal } // namespace generator_internal
std::mt19937_64* GetThreadLocalRng(); std::mt19937_64* GetSharedRng();
enum Enum { enum Enum {
kEnumEmpty, kEnumEmpty,
@ -66,7 +66,7 @@ template <class T>
struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> { struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> {
T operator()() const { T operator()() const {
std::uniform_int_distribution<T> dist; 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> std::uniform_int_distribution<typename std::underlying_type<Enum>::type>
dist; dist;
while (true) { while (true) {
auto variate = dist(*GetThreadLocalRng()); auto variate = dist(*GetSharedRng());
if (variate != kEnumEmpty && variate != kEnumDeleted) if (variate != kEnumEmpty && variate != kEnumDeleted)
return static_cast<Enum>(variate); return static_cast<Enum>(variate);
} }
@ -90,7 +90,7 @@ struct Generator<EnumClass> {
typename std::underlying_type<EnumClass>::type> typename std::underlying_type<EnumClass>::type>
dist; dist;
while (true) { while (true) {
EnumClass variate = static_cast<EnumClass>(dist(*GetThreadLocalRng())); EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng()));
if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted) if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted)
return static_cast<EnumClass>(variate); return static_cast<EnumClass>(variate);
} }

View file

@ -40,6 +40,7 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include "absl/algorithm/container.h"
#include "absl/container/internal/container_memory.h" #include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_hash_policy.h" #include "absl/container/internal/node_hash_policy.h"
@ -91,7 +92,7 @@ class NodeHashMapPolicy;
// std::string search_key = "b"; // std::string search_key = "b";
// auto result = ducks.find(search_key); // auto result = ducks.find(search_key);
// if (result != ducks.end()) { // if (result != ducks.end()) {
// std::cout << "Result: " << search_key->second << std::endl; // std::cout << "Result: " << result->second << std::endl;
// } // }
template <class Key, class Value, template <class Key, class Value,
class Hash = absl::container_internal::hash_default_hash<Key>, 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; } static const Value& value(const value_type* elem) { return elem->second; }
}; };
} // namespace container_internal } // 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 } // namespace absl
#endif // ABSL_CONTAINER_NODE_HASH_MAP_H_ #endif // ABSL_CONTAINER_NODE_HASH_MAP_H_

View file

@ -37,6 +37,7 @@
#include <type_traits> #include <type_traits>
#include "absl/algorithm/container.h"
#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export
#include "absl/container/internal/node_hash_policy.h" #include "absl/container/internal/node_hash_policy.h"
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export #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); } static size_t element_space_used(const T*) { return sizeof(T); }
}; };
} // namespace container_internal } // 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 } // namespace absl
#endif // ABSL_CONTAINER_NODE_HASH_SET_H_ #endif // ABSL_CONTAINER_NODE_HASH_SET_H_

View file

@ -37,6 +37,11 @@
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/base/port.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 { namespace absl {
@ -661,6 +666,12 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) {
// can be used for uint128 storage. // can be used for uint128 storage.
return static_cast<unsigned __int128>(lhs) * return static_cast<unsigned __int128>(lhs) *
static_cast<unsigned __int128>(rhs); 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 #else // ABSL_HAVE_INTRINSIC128
uint64_t a32 = Uint128Low64(lhs) >> 32; uint64_t a32 = Uint128Low64(lhs) >> 32;
uint64_t a00 = Uint128Low64(lhs) & 0xffffffff; uint64_t a00 = Uint128Low64(lhs) & 0xffffffff;

View file

@ -72,23 +72,19 @@ void ThreadTwo(absl::Mutex* mutex, absl::CondVar* condvar,
// Launch thread 1 and thread 2, and block on their completion. // Launch thread 1 and thread 2, and block on their completion.
// If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally // If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally
// constructed instance instead. // constructed instance instead.
void RunTests(absl::Mutex* mutex, absl::CondVar* condvar, void RunTests(absl::Mutex* mutex, absl::CondVar* condvar) {
absl::Notification* notification) {
absl::Mutex default_mutex; absl::Mutex default_mutex;
absl::CondVar default_condvar; absl::CondVar default_condvar;
absl::Notification default_notification; absl::Notification notification;
if (!mutex) { if (!mutex) {
mutex = &default_mutex; mutex = &default_mutex;
} }
if (!condvar) { if (!condvar) {
condvar = &default_condvar; condvar = &default_condvar;
} }
if (!notification) {
notification = &default_notification;
}
bool state = false; bool state = false;
std::thread thread_one(ThreadOne, mutex, condvar, notification, &state); std::thread thread_one(ThreadOne, mutex, condvar, &notification, &state);
std::thread thread_two(ThreadTwo, mutex, condvar, notification, &state); std::thread thread_two(ThreadTwo, mutex, condvar, &notification, &state);
thread_one.join(); thread_one.join();
thread_two.join(); thread_two.join();
} }
@ -96,8 +92,7 @@ void RunTests(absl::Mutex* mutex, absl::CondVar* condvar,
void TestLocals() { void TestLocals() {
absl::Mutex mutex; absl::Mutex mutex;
absl::CondVar condvar; absl::CondVar condvar;
absl::Notification notification; RunTests(&mutex, &condvar);
RunTests(&mutex, &condvar, &notification);
} }
// Global variables during start and termination // Global variables during start and termination

View file

@ -22,6 +22,7 @@
#include "absl/time/time.h" #include "absl/time/time.h"
namespace absl { namespace absl {
void Notification::Notify() { void Notification::Notify() {
MutexLock l(&this->mutex_); MutexLock l(&this->mutex_);

View file

@ -56,6 +56,17 @@ MATCHER_P(TimevalMatcher, tv, "") {
return false; 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) { TEST(Duration, ValueSemantics) {
// If this compiles, the test passes. // If this compiles, the test passes.
constexpr absl::Duration a; // Default construction constexpr absl::Duration a; // Default construction

View file

@ -20,6 +20,7 @@
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <limits>
#include <tuple> #include <tuple>
#include <utility> #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 // !defined(__tm_gmtoff) && !defined(__tm_zone)
#endif #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 } // namespace
TimeZoneLibC::TimeZoneLibC(const std::string& name) TimeZoneLibC::TimeZoneLibC(const std::string& name)
@ -93,50 +164,107 @@ TimeZoneLibC::TimeZoneLibC(const std::string& name)
time_zone::absolute_lookup TimeZoneLibC::BreakTime( time_zone::absolute_lookup TimeZoneLibC::BreakTime(
const time_point<seconds>& tp) const { const time_point<seconds>& tp) const {
time_zone::absolute_lookup al; time_zone::absolute_lookup al;
std::time_t t = ToUnixSeconds(tp); al.offset = 0;
std::tm tm; al.is_dst = false;
if (local_) { al.abbr = "-00";
#if defined(_WIN32) || defined(_WIN64)
localtime_s(&tm, &t); const std::int_fast64_t s = ToUnixSeconds(tp);
#else
localtime_r(&t, &tm); // If std::time_t cannot hold the input we saturate the output.
#endif if (s < std::numeric_limits<std::time_t>::min()) {
std::tie(al.offset, al.abbr) = get_offset_abbr(tm); al.cs = civil_second::min();
} else { return al;
#if defined(_WIN32) || defined(_WIN64)
gmtime_s(&tm, &t);
#else
gmtime_r(&t, &tm);
#endif
al.offset = 0;
al.abbr = "UTC";
} }
al.cs = civil_second(tm.tm_year + year_t{1900}, tm.tm_mon + 1, tm.tm_mday, if (s > std::numeric_limits<std::time_t>::max()) {
tm.tm_hour, tm.tm_min, tm.tm_sec); al.cs = civil_second::max();
al.is_dst = tm.tm_isdst > 0; return al;
}
const std::time_t t = static_cast<std::time_t>(s);
std::tm tm;
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;
}
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; return al;
} }
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
time_zone::civil_lookup cl; if (!local_) {
std::time_t t; // If time_point<seconds> cannot hold the result we saturate.
if (local_) { static const civil_second min_tp_cs =
// Does not handle SKIPPED/AMBIGUOUS or huge years. civil_second() + ToUnixSeconds(time_point<seconds>::min());
std::tm tm; static const civil_second max_tp_cs =
tm.tm_year = static_cast<int>(cs.year() - 1900); civil_second() + ToUnixSeconds(time_point<seconds>::max());
tm.tm_mon = cs.month() - 1; const time_point<seconds> tp =
tm.tm_mday = cs.day(); (cs < min_tp_cs)
tm.tm_hour = cs.hour(); ? time_point<seconds>::min()
tm.tm_min = cs.minute(); : (cs > max_tp_cs) ? time_point<seconds>::max()
tm.tm_sec = cs.second(); : FromUnixSeconds(cs - civil_second());
tm.tm_isdst = -1; return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
t = std::mktime(&tm);
} else {
t = cs - civil_second();
} }
cl.kind = time_zone::civil_lookup::UNIQUE;
cl.pre = cl.trans = cl.post = FromUnixSeconds(t); // If tm_year cannot hold the requested year we saturate the result.
return cl; 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 {
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};
}
// 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, bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp,

View file

@ -16,7 +16,9 @@
#include <chrono> #include <chrono>
#include <cstddef> #include <cstddef>
#include <cstdlib>
#include <future> #include <future>
#include <limits>
#include <string> #include <string>
#include <thread> #include <thread>
#include <vector> #include <vector>
@ -925,7 +927,7 @@ TEST(MakeTime, Normalization) {
EXPECT_EQ(tp, convert(civil_second(2009, 2, 13, 18, 30, 90), tz)); // second 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) { TEST(MakeTime, SysSecondsLimits) {
const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez"; const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
const time_zone utc = utc_time_zone(); const time_zone utc = utc_time_zone();
@ -991,19 +993,107 @@ TEST(MakeTime, SysSecondsLimits) {
tp = convert(civil_second::min(), west); tp = convert(civil_second::min(), west);
EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); 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) { if (sizeof(std::time_t) >= 8) {
// Checks that "tm_year + 1900", as used by the "libc" implementation, // Checks that "tm_year + 1900", as used by the "libc" implementation,
// can produce year values beyond the range on an int without overflow. // can produce year values beyond the range on an int without overflow.
#if defined(_WIN32) || defined(_WIN64) #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 #else
const time_zone libc_utc = LoadZone("libc:UTC"); const time_zone utc = LoadZone("libc:UTC");
tp = convert(civil_year(year_t{2147483648}), libc_utc); const year_t max_tm_year = year_t{std::numeric_limits<int>::max()} + 1900;
EXPECT_EQ("2147483648-01-01T00:00:00+00:00", format(RFC3339, tp, libc_utc)); 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 #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) { TEST(NextTransition, UTC) {
const auto tz = utc_time_zone(); const auto tz = utc_time_zone();
time_zone::civil_transition trans; time_zone::civil_transition trans;

View file

@ -153,6 +153,16 @@ class Duration {
// Value semantics. // Value semantics.
constexpr Duration() : rep_hi_(0), rep_lo_(0) {} // zero-length duration 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. // Compound assignment operators.
Duration& operator+=(Duration d); Duration& operator+=(Duration d);
Duration& operator-=(Duration d); Duration& operator-=(Duration d);
@ -584,7 +594,11 @@ class Time {
// absl::Time t = absl::Now(); // absl::Time t = absl::Now();
// absl::Time t = absl::TimeFromTimeval(tv); // absl::Time t = absl::TimeFromTimeval(tv);
// absl::Time t = absl::InfinitePast(); // 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. // Assignment operators.
Time& operator+=(Duration d) { Time& operator+=(Duration d) {
@ -826,6 +840,8 @@ class TimeZone {
public: public:
explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {} explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {}
TimeZone() = default; // UTC, but prefer UTCTimeZone() to be explicit. TimeZone() = default; // UTC, but prefer UTCTimeZone() to be explicit.
// Copyable.
TimeZone(const TimeZone&) = default; TimeZone(const TimeZone&) = default;
TimeZone& operator=(const TimeZone&) = default; TimeZone& operator=(const TimeZone&) = default;

View file

@ -287,6 +287,7 @@ cc_test(
linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
deps = [ deps = [
":variant", ":variant",
"//absl/base:config",
"//absl/base:exception_safety_testing", "//absl/base:exception_safety_testing",
"//absl/memory", "//absl/memory",
"@com_google_googletest//:gtest_main", "@com_google_googletest//:gtest_main",

View file

@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "absl/types/variant.h" #include "absl/types/variant.h"
#include <iostream> #include <iostream>
@ -20,8 +21,11 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/config.h"
#include "absl/base/internal/exception_safety_testing.h" #include "absl/base/internal/exception_safety_testing.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
// See comment in absl/base/config.h
#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
namespace absl { namespace absl {
namespace { namespace {
@ -506,3 +510,5 @@ TEST(VariantExceptionSafetyTest, Swap) {
} // namespace } // namespace
} // namespace absl } // namespace absl
#endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)

View file

@ -559,9 +559,14 @@ TEST(VariantTest, TestDtor) {
} }
#ifdef ABSL_HAVE_EXCEPTIONS #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 destruction when in the valueless_by_exception state.
TEST(VariantTest, TestDtorValuelessByException) { TEST(VariantTest, TestDtorValuelessByException)
#endif
{
int counter = 0; int counter = 0;
IncrementInDtor counter_adjuster(&counter); IncrementInDtor counter_adjuster(&counter);