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:
parent
070f6e47b3
commit
7b46e1d31a
22 changed files with 566 additions and 113 deletions
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -72,23 +72,19 @@ void ThreadTwo(absl::Mutex* mutex, absl::CondVar* condvar,
|
|||
// Launch thread 1 and thread 2, and block on their completion.
|
||||
// If any of 'mutex', 'condvar', or 'notification' is nullptr, use a locally
|
||||
// constructed instance instead.
|
||||
void RunTests(absl::Mutex* mutex, absl::CondVar* condvar,
|
||||
absl::Notification* notification) {
|
||||
void RunTests(absl::Mutex* mutex, absl::CondVar* condvar) {
|
||||
absl::Mutex default_mutex;
|
||||
absl::CondVar default_condvar;
|
||||
absl::Notification default_notification;
|
||||
absl::Notification notification;
|
||||
if (!mutex) {
|
||||
mutex = &default_mutex;
|
||||
}
|
||||
if (!condvar) {
|
||||
condvar = &default_condvar;
|
||||
}
|
||||
if (!notification) {
|
||||
notification = &default_notification;
|
||||
}
|
||||
bool state = false;
|
||||
std::thread thread_one(ThreadOne, mutex, condvar, notification, &state);
|
||||
std::thread thread_two(ThreadTwo, mutex, condvar, notification, &state);
|
||||
std::thread thread_one(ThreadOne, mutex, condvar, ¬ification, &state);
|
||||
std::thread thread_two(ThreadTwo, mutex, condvar, ¬ification, &state);
|
||||
thread_one.join();
|
||||
thread_two.join();
|
||||
}
|
||||
|
@ -96,8 +92,7 @@ void RunTests(absl::Mutex* mutex, absl::CondVar* condvar,
|
|||
void TestLocals() {
|
||||
absl::Mutex mutex;
|
||||
absl::CondVar condvar;
|
||||
absl::Notification notification;
|
||||
RunTests(&mutex, &condvar, ¬ification);
|
||||
RunTests(&mutex, &condvar);
|
||||
}
|
||||
|
||||
// Global variables during start and termination
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "absl/time/time.h"
|
||||
|
||||
namespace absl {
|
||||
|
||||
void Notification::Notify() {
|
||||
MutexLock l(&this->mutex_);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
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";
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
t = cs - civil_second();
|
||||
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};
|
||||
}
|
||||
cl.kind = time_zone::civil_lookup::UNIQUE;
|
||||
cl.pre = cl.trans = cl.post = FromUnixSeconds(t);
|
||||
return cl;
|
||||
|
||||
// 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 {
|
||||
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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue