diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 20:06:25 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 20:06:25 +0000 |
commit | c8b87cbbb60ce659a77868518b51138eecf7a4e3 (patch) | |
tree | 2ce94d7f0804ccb77d1fa9b2a1bca00eecdff1e2 | |
parent | f60eaea2240ba9e1c508e8e0c91d39ee9fc47be5 (diff) | |
parent | 7563023510bf04108a954596ea9393a4c11ac279 (diff) | |
download | webrtc-android13-mainline-go-adservices-release.tar.gz |
Snap for 8505378 from 7563023510bf04108a954596ea9393a4c11ac279 to mainline-go-adservices-releaseaml_go_ads_330915100aml_go_ads_330915000aml_go_ads_330913000android13-mainline-go-adservices-release
Change-Id: I10c5dc32563ab8c33e29752b6fd1e2585d6d3e75
1159 files changed, 53165 insertions, 14669 deletions
diff --git a/Android.bp b/Android.bp index 204ee672ef..31b6a63252 100644 --- a/Android.bp +++ b/Android.bp @@ -2919,19 +2919,6 @@ cc_library_static { "modules/audio_device/audio_device_impl.cc", "modules/audio_device/dummy/file_audio_device_factory.cc", ], - target: { - linux_glibc: { - srcs: [ - "modules/audio_device/linux/alsasymboltable_linux.cc", - "modules/audio_device/linux/audio_device_alsa_linux.cc", - "modules/audio_device/linux/audio_mixer_manager_alsa_linux.cc", - "modules/audio_device/linux/latebindingsymboltable_linux.cc", - "modules/audio_device/linux/audio_device_pulse_linux.cc", - "modules/audio_device/linux/audio_mixer_manager_pulse_linux.cc", - "modules/audio_device/linux/pulseaudiosymboltable_linux.cc", - ], - }, - }, host_supported: true, cflags: ["-DWEBRTC_DUMMY_FILE_DEVICES"], static_libs: [ diff --git a/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/00-bug_report.md b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/00-bug_report.md new file mode 100644 index 0000000000..1edf3de0ba --- /dev/null +++ b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/00-bug_report.md @@ -0,0 +1,41 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'bug' +assignees: '' +--- + +**Describe the bug** + +Include a clear and concise description of what the problem is, including what +you expected to happen, and what actually happened. + +**Steps to reproduce the bug** + +It's important that we are able to reproduce the problem that you are +experiencing. Please provide all code and relevant steps to reproduce the +problem, including your `BUILD`/`CMakeLists.txt` file and build commands. Links +to a GitHub branch or [godbolt.org](https://godbolt.org/) that demonstrate the +problem are also helpful. + +**What version of Abseil are you using?** + +**What operating system and version are you using** + +If you are using a Linux distribution please include the name and version of the +distribution as well. + +**What compiler and version are you using?** + +Please include the output of `gcc -v` or `clang -v`, or the equivalent for your +compiler. + +**What build system are you using?** + +Please include the output of `bazel --version` or `cmake --version`, or the +equivalent for your build system. + +**Additional context** + +Add any other context about the problem here. diff --git a/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/90-question.md b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/90-question.md new file mode 100644 index 0000000000..84cf349189 --- /dev/null +++ b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/90-question.md @@ -0,0 +1,7 @@ +--- +name: Question +about: Have a question? Ask us anything! :-) +title: '' +labels: 'question' +assignees: '' +--- diff --git a/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..0086358db1 --- /dev/null +++ b/third_party/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/third_party/abseil-cpp/Android.bp b/third_party/abseil-cpp/Android.bp index 6e8578dea8..f1a577f680 100644 --- a/third_party/abseil-cpp/Android.bp +++ b/third_party/abseil-cpp/Android.bp @@ -54,14 +54,12 @@ cc_library_host_static { name: "libwebrtc_absl_base", defaults: ["libwebrtc_absl_defaults"], srcs: [ - "absl/base/dynamic_annotations.cc", "absl/base/internal/cycleclock.cc", - "absl/base/internal/exponential_biased.cc", "absl/base/internal/low_level_alloc.cc", - "absl/base/internal/periodic_sampler.cc", "absl/base/internal/raw_logging.cc", "absl/base/internal/spinlock.cc", "absl/base/internal/spinlock_wait.cc", + "absl/base/internal/strerror.cc", "absl/base/internal/sysinfo.cc", "absl/base/internal/thread_identity.cc", "absl/base/internal/throw_delegate.cc", @@ -102,17 +100,19 @@ cc_library_host_static { name: "libwebrtc_absl_flags", defaults: ["libwebrtc_absl_defaults"], srcs: [ + "absl/flags/commandlineflag.cc", "absl/flags/usage_config.cc", "absl/flags/marshalling.cc", "absl/flags/usage.cc", "absl/flags/flag.cc", "absl/flags/parse.cc", + "absl/flags/internal/commandlineflag.cc", "absl/flags/internal/flag.cc", + "absl/flags/internal/private_handle_accessor.cc", "absl/flags/internal/program_name.cc", - "absl/flags/internal/registry.cc", "absl/flags/internal/usage.cc", - "absl/flags/internal/type_erased.cc", "absl/flags/flag_test_defs.cc", + "absl/flags/reflection.cc", ], } @@ -122,6 +122,7 @@ cc_library_host_static { srcs: [ "absl/hash/internal/city.cc", "absl/hash/internal/hash.cc", + "absl/hash/internal/low_level_hash.cc", ], } @@ -132,11 +133,20 @@ cc_library_host_static { } cc_library_host_static { + name: "libwebrtc_absl_profiling", + srcs: [ + "absl/profiling/internal/exponential_biased.cc", + "absl/profiling/internal/periodic_sampler.cc", + ], +} + +cc_library_host_static { name: "libwebrtc_absl_status", defaults: ["libwebrtc_absl_defaults"], srcs: [ "absl/status/status.cc", "absl/status/status_payload_printer.cc", + "absl/status/statusor.cc", ], } @@ -150,6 +160,15 @@ cc_library_host_static { "absl/strings/escaping.cc", "absl/strings/internal/charconv_bigint.cc", "absl/strings/internal/charconv_parse.cc", + "absl/strings/internal/cord_internal.cc", + "absl/strings/internal/cord_rep_btree.cc", + "absl/strings/internal/cord_rep_btree_navigator.cc", + "absl/strings/internal/cord_rep_btree_reader.cc", + "absl/strings/internal/cord_rep_consume.cc", + "absl/strings/internal/cord_rep_ring.cc", + "absl/strings/internal/cordz_functions.cc", + "absl/strings/internal/cordz_handle.cc", + "absl/strings/internal/cordz_info.cc", "absl/strings/internal/escaping.cc", "absl/strings/internal/memutil.cc", "absl/strings/internal/ostringstream.cc", diff --git a/third_party/abseil-cpp/BUILD.bazel b/third_party/abseil-cpp/BUILD.bazel new file mode 100644 index 0000000000..79fb0ecd73 --- /dev/null +++ b/third_party/abseil-cpp/BUILD.bazel @@ -0,0 +1,25 @@ +# +# Copyright 2020 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. +# + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +# Expose license for external usage through bazel. +exports_files([ + "AUTHORS", + "LICENSE", +]) diff --git a/third_party/abseil-cpp/CMake/AbseilDll.cmake b/third_party/abseil-cpp/CMake/AbseilDll.cmake index 90c9f1f642..fa323ff84b 100644 --- a/third_party/abseil-cpp/CMake/AbseilDll.cmake +++ b/third_party/abseil-cpp/CMake/AbseilDll.cmake @@ -1,4 +1,5 @@ include(CMakeParseArguments) +include(GNUInstallDirs) set(ABSL_INTERNAL_DLL_FILES "algorithm/algorithm.h" @@ -8,17 +9,15 @@ set(ABSL_INTERNAL_DLL_FILES "base/casts.h" "base/config.h" "base/const_init.h" - "base/dynamic_annotations.cc" "base/dynamic_annotations.h" "base/internal/atomic_hook.h" - "base/internal/bits.h" "base/internal/cycleclock.cc" "base/internal/cycleclock.h" "base/internal/direct_mmap.h" + "base/internal/dynamic_annotations.h" "base/internal/endian.h" "base/internal/errno_saver.h" - "base/internal/exponential_biased.cc" - "base/internal/exponential_biased.h" + "base/internal/fast_type_id.h" "base/internal/hide_ptr.h" "base/internal/identity.h" "base/internal/invoke.h" @@ -27,14 +26,14 @@ set(ABSL_INTERNAL_DLL_FILES "base/internal/low_level_alloc.h" "base/internal/low_level_scheduling.h" "base/internal/per_thread_tls.h" - "base/internal/periodic_sampler.cc" - "base/internal/periodic_sampler.h" "base/internal/pretty_function.h" "base/internal/raw_logging.cc" "base/internal/raw_logging.h" "base/internal/scheduling_mode.h" "base/internal/scoped_set_env.cc" "base/internal/scoped_set_env.h" + "base/internal/strerror.h" + "base/internal/strerror.cc" "base/internal/spinlock.cc" "base/internal/spinlock.h" "base/internal/spinlock_wait.cc" @@ -58,6 +57,8 @@ set(ABSL_INTERNAL_DLL_FILES "base/policy_checks.h" "base/port.h" "base/thread_annotations.h" + "cleanup/cleanup.h" + "cleanup/internal/cleanup.h" "container/btree_map.h" "container/btree_set.h" "container/fixed_array.h" @@ -119,27 +120,35 @@ set(ABSL_INTERNAL_DLL_FILES "hash/internal/hash.h" "hash/internal/hash.cc" "hash/internal/spy_hash_state.h" + "hash/internal/low_level_hash.h" + "hash/internal/low_level_hash.cc" "memory/memory.h" "meta/type_traits.h" + "numeric/bits.h" "numeric/int128.cc" "numeric/int128.h" + "numeric/internal/bits.h" + "numeric/internal/representation.h" + "profiling/internal/exponential_biased.cc" + "profiling/internal/exponential_biased.h" + "profiling/internal/periodic_sampler.cc" + "profiling/internal/periodic_sampler.h" + "profiling/internal/sample_recorder.h" "random/bernoulli_distribution.h" "random/beta_distribution.h" "random/bit_gen_ref.h" "random/discrete_distribution.cc" "random/discrete_distribution.h" - "random/distribution_format_traits.h" "random/distributions.h" "random/exponential_distribution.h" "random/gaussian_distribution.cc" "random/gaussian_distribution.h" - "random/internal/distributions.h" "random/internal/distribution_caller.h" - "random/internal/fast_uniform_bits.h" "random/internal/fastmath.h" - "random/internal/gaussian_distribution_gentables.cc" + "random/internal/fast_uniform_bits.h" "random/internal/generate_real.h" "random/internal/iostream_state_saver.h" + "random/internal/mock_helpers.h" "random/internal/nonsecure_base.h" "random/internal/pcg_engine.h" "random/internal/platform.h" @@ -152,6 +161,7 @@ set(ABSL_INTERNAL_DLL_FILES "random/internal/randen_engine.h" "random/internal/randen_hwaes.cc" "random/internal/randen_hwaes.h" + "random/internal/randen_round_keys.cc" "random/internal/randen_slow.cc" "random/internal/randen_slow.h" "random/internal/randen_traits.h" @@ -172,8 +182,12 @@ set(ABSL_INTERNAL_DLL_FILES "random/uniform_int_distribution.h" "random/uniform_real_distribution.h" "random/zipf_distribution.h" + "status/internal/status_internal.h" + "status/internal/statusor_internal.h" "status/status.h" "status/status.cc" + "status/statusor.h" + "status/statusor.cc" "status/status_payload_printer.h" "status/status_payload_printer.cc" "strings/ascii.cc" @@ -184,12 +198,37 @@ set(ABSL_INTERNAL_DLL_FILES "strings/cord.h" "strings/escaping.cc" "strings/escaping.h" - "strings/internal/cord_internal.h" "strings/internal/charconv_bigint.cc" "strings/internal/charconv_bigint.h" "strings/internal/charconv_parse.cc" "strings/internal/charconv_parse.h" + "strings/internal/cord_internal.cc" + "strings/internal/cord_internal.h" + "strings/internal/cord_rep_consume.h" + "strings/internal/cord_rep_consume.cc" + "strings/internal/cord_rep_btree.cc" + "strings/internal/cord_rep_btree.h" + "strings/internal/cord_rep_btree_navigator.cc" + "strings/internal/cord_rep_btree_navigator.h" + "strings/internal/cord_rep_btree_reader.cc" + "strings/internal/cord_rep_btree_reader.h" + "strings/internal/cord_rep_flat.h" + "strings/internal/cord_rep_ring.cc" + "strings/internal/cord_rep_ring.h" + "strings/internal/cord_rep_ring_reader.h" + "strings/internal/cordz_functions.cc" + "strings/internal/cordz_functions.h" + "strings/internal/cordz_handle.cc" + "strings/internal/cordz_handle.h" + "strings/internal/cordz_info.cc" + "strings/internal/cordz_info.h" + "strings/internal/cordz_sample_token.cc" + "strings/internal/cordz_sample_token.h" + "strings/internal/cordz_statistics.h" + "strings/internal/cordz_update_scope.h" + "strings/internal/cordz_update_tracker.h" "strings/internal/stl_type_traits.h" + "strings/internal/string_constant.h" "strings/match.cc" "strings/match.h" "strings/numbers.cc" @@ -244,6 +283,7 @@ set(ABSL_INTERNAL_DLL_FILES "synchronization/notification.h" "synchronization/internal/create_thread_identity.cc" "synchronization/internal/create_thread_identity.h" + "synchronization/internal/futex.h" "synchronization/internal/graphcycles.cc" "synchronization/internal/graphcycles.h" "synchronization/internal/kernel_timeout.h" @@ -292,6 +332,8 @@ set(ABSL_INTERNAL_DLL_FILES "types/internal/conformance_aliases.h" "types/internal/conformance_archetype.h" "types/internal/conformance_profile.h" + "types/internal/parentheses.h" + "types/internal/transform_args.h" "types/internal/variant.h" "types/optional.h" "types/internal/optional.h" @@ -416,6 +458,7 @@ set(ABSL_INTERNAL_DLL_TARGETS "raw_hash_set" "layout" "tracked" + "sample_recorder" ) function(absl_internal_dll_contains) @@ -479,7 +522,7 @@ function(absl_make_dll) abseil_dll PUBLIC "$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>" - $<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) target_compile_options( @@ -497,8 +540,8 @@ function(absl_make_dll) ${ABSL_CC_LIB_DEFINES} ) install(TARGETS abseil_dll EXPORT ${PROJECT_NAME}Targets - RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR} - LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) endfunction() diff --git a/third_party/abseil-cpp/CMake/AbseilHelpers.cmake b/third_party/abseil-cpp/CMake/AbseilHelpers.cmake index 86ff9eba2a..f2ce567511 100644 --- a/third_party/abseil-cpp/CMake/AbseilHelpers.cmake +++ b/third_party/abseil-cpp/CMake/AbseilHelpers.cmake @@ -17,13 +17,14 @@ include(CMakeParseArguments) include(AbseilConfigureCopts) include(AbseilDll) -include(AbseilInstallDirs) # The IDE folder for Abseil that will be used if Abseil is included in a CMake # project that sets # set_property(GLOBAL PROPERTY USE_FOLDERS ON) # For example, Visual Studio supports folders. -set(ABSL_IDE_FOLDER Abseil) +if(NOT DEFINED ABSL_IDE_FOLDER) + set(ABSL_IDE_FOLDER Abseil) +endif() # absl_cc_library() # @@ -39,7 +40,7 @@ set(ABSL_IDE_FOLDER Abseil) # LINKOPTS: List of link options # PUBLIC: Add this so that this library will be exported under absl:: # Also in IDE, target will appear in Abseil folder while non PUBLIC will be in Abseil/internal. -# 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 BUILD_TESTING=ON. # # Note: # By default, absl_cc_library will always create a library named absl_${NAME}, @@ -81,7 +82,7 @@ function(absl_cc_library) ${ARGN} ) - if(ABSL_CC_LIB_TESTONLY AND NOT ABSL_RUN_TESTS) + if(ABSL_CC_LIB_TESTONLY AND NOT BUILD_TESTING) return() endif() @@ -102,7 +103,7 @@ function(absl_cc_library) endif() endforeach() - if("${ABSL_CC_SRCS}" STREQUAL "") + if(ABSL_CC_SRCS STREQUAL "") set(ABSL_CC_LIB_IS_INTERFACE 1) else() set(ABSL_CC_LIB_IS_INTERFACE 0) @@ -120,7 +121,11 @@ function(absl_cc_library) # 4. "static" -- This target does not depend on the DLL and should be built # statically. if (${ABSL_BUILD_DLL}) - absl_internal_dll_contains(TARGET ${_NAME} OUTPUT _in_dll) + if(ABSL_ENABLE_INSTALL) + absl_internal_dll_contains(TARGET ${_NAME} OUTPUT _in_dll) + else() + absl_internal_dll_contains(TARGET ${ABSL_CC_LIB_NAME} OUTPUT _in_dll) + endif() if (${_in_dll}) # This target should be replaced by the DLL set(_build_type "dll") @@ -135,8 +140,54 @@ function(absl_cc_library) set(_build_type "static") endif() + # Generate a pkg-config file for every library: + if((_build_type STREQUAL "static" OR _build_type STREQUAL "shared") + AND ABSL_ENABLE_INSTALL) + if(NOT ABSL_CC_LIB_TESTONLY) + if(absl_VERSION) + set(PC_VERSION "${absl_VERSION}") + else() + set(PC_VERSION "head") + endif() + foreach(dep ${ABSL_CC_LIB_DEPS}) + if(${dep} MATCHES "^absl::(.*)") + # Join deps with commas. + if(PC_DEPS) + set(PC_DEPS "${PC_DEPS},") + endif() + set(PC_DEPS "${PC_DEPS} absl_${CMAKE_MATCH_1} = ${PC_VERSION}") + endif() + endforeach() + foreach(cflag ${ABSL_CC_LIB_COPTS}) + if(${cflag} MATCHES "^(-Wno|/wd)") + # These flags are needed to suppress warnings that might fire in our headers. + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + elseif(${cflag} MATCHES "^(-W|/w[1234eo])") + # Don't impose our warnings on others. + else() + set(PC_CFLAGS "${PC_CFLAGS} ${cflag}") + endif() + endforeach() + FILE(GENERATE OUTPUT "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" CONTENT "\ +prefix=${CMAKE_INSTALL_PREFIX}\n\ +exec_prefix=\${prefix}\n\ +libdir=${CMAKE_INSTALL_FULL_LIBDIR}\n\ +includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}\n\ +\n\ +Name: absl_${_NAME}\n\ +Description: Abseil ${_NAME} library\n\ +URL: https://abseil.io/\n\ +Version: ${PC_VERSION}\n\ +Requires:${PC_DEPS}\n\ +Libs: -L\${libdir} $<JOIN:${ABSL_CC_LIB_LINKOPTS}, > $<$<NOT:$<BOOL:${ABSL_CC_LIB_IS_INTERFACE}>>:-labsl_${_NAME}>\n\ +Cflags: -I\${includedir}${PC_CFLAGS}\n") + INSTALL(FILES "${CMAKE_BINARY_DIR}/lib/pkgconfig/absl_${_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + endif() + endif() + if(NOT ABSL_CC_LIB_IS_INTERFACE) - if(${_build_type} STREQUAL "dll_dep") + if(_build_type STREQUAL "dll_dep") # This target depends on the DLL. When adding dependencies to this target, # any depended-on-target which is contained inside the DLL is replaced # with a dependency on the DLL. @@ -165,7 +216,7 @@ function(absl_cc_library) "${_gtest_link_define}" ) - elseif(${_build_type} STREQUAL "static" OR ${_build_type} STREQUAL "shared") + elseif(_build_type STREQUAL "static" OR _build_type STREQUAL "shared") add_library(${_NAME} "") target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS}) target_link_libraries(${_NAME} @@ -188,7 +239,7 @@ function(absl_cc_library) target_include_directories(${_NAME} PUBLIC "$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>" - $<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) target_compile_options(${_NAME} PRIVATE ${ABSL_CC_LIB_COPTS}) @@ -203,9 +254,23 @@ function(absl_cc_library) set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal) endif() - # INTERFACE libraries can't have the CXX_STANDARD property set - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++11 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} PUBLIC cxx_std_11) + else() + # Note: This is legacy (before CMake 3.8) behavior. Setting the + # target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is + # initialized by CMAKE_CXX_STANDARD) should have no real effect, since + # that is the default value anyway. + # + # CXX_STANDARD_REQUIRED does guard against the top-level CMake project + # not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents + # "decaying" to an older standard if the requested one isn't available). + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + endif() # When being installed, we lose the absl_ prefix. We want to put it back # to have properly named lib files. This is a no-op when we are not being @@ -213,6 +278,7 @@ function(absl_cc_library) if(ABSL_ENABLE_INSTALL) set_target_properties(${_NAME} PROPERTIES OUTPUT_NAME "absl_${_NAME}" + SOVERSION "2111.0.0" ) endif() else() @@ -221,10 +287,10 @@ function(absl_cc_library) target_include_directories(${_NAME} INTERFACE "$<BUILD_INTERFACE:${ABSL_COMMON_INCLUDE_DIRS}>" - $<INSTALL_INTERFACE:${ABSL_INSTALL_INCLUDEDIR}> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) - if (${_build_type} STREQUAL "dll") + if (_build_type STREQUAL "dll") set(ABSL_CC_LIB_DEPS abseil_dll) endif() @@ -235,15 +301,25 @@ function(absl_cc_library) ${ABSL_DEFAULT_LINKOPTS} ) target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES}) + + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++11 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} INTERFACE cxx_std_11) + + # (INTERFACE libraries can't have the CXX_STANDARD property set, so there + # is no legacy behavior else case). + endif() endif() # TODO currently we don't install googletest alongside abseil sources, so # installed abseil can't be tested. if(NOT ABSL_CC_LIB_TESTONLY AND ABSL_ENABLE_INSTALL) install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets - RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR} - LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) endif() @@ -284,11 +360,11 @@ endfunction() # "awesome_test.cc" # DEPS # absl::awesome -# gmock -# gtest_main +# GTest::gmock +# GTest::gtest_main # ) function(absl_cc_test) - if(NOT ABSL_RUN_TESTS) + if(NOT BUILD_TESTING) return() endif() @@ -338,8 +414,23 @@ function(absl_cc_test) # Add all Abseil targets to a folder in the IDE for organization. set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + if(ABSL_PROPAGATE_CXX_STD) + # Abseil libraries require C++11 as the current minimum standard. + # Top-level application CMake projects should ensure a consistent C++ + # standard for all compiled sources by setting CMAKE_CXX_STANDARD. + target_compile_features(${_NAME} PUBLIC cxx_std_11) + else() + # Note: This is legacy (before CMake 3.8) behavior. Setting the + # target-level CXX_STANDARD property to ABSL_CXX_STANDARD (which is + # initialized by CMAKE_CXX_STANDARD) should have no real effect, since + # that is the default value anyway. + # + # CXX_STANDARD_REQUIRED does guard against the top-level CMake project + # not having enabled CMAKE_CXX_STANDARD_REQUIRED (which prevents + # "decaying" to an older standard if the requested one isn't available). + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + endif() add_test(NAME ${_NAME} COMMAND ${_NAME}) endfunction() diff --git a/third_party/abseil-cpp/CMake/AbseilInstallDirs.cmake b/third_party/abseil-cpp/CMake/AbseilInstallDirs.cmake deleted file mode 100644 index b67272f830..0000000000 --- a/third_party/abseil-cpp/CMake/AbseilInstallDirs.cmake +++ /dev/null @@ -1,20 +0,0 @@ -include(GNUInstallDirs) - -# absl_VERSION is only set if we are an LTS release being installed, in which -# case it may be into a system directory and so we need to make subdirectories -# for each installed version of Abseil. This mechanism is implemented in -# Abseil's internal Copybara (https://github.com/google/copybara) workflows and -# isn't visible in the CMake buildsystem itself. - -if(absl_VERSION) - set(ABSL_SUBDIR "${PROJECT_NAME}_${PROJECT_VERSION}") - set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}/${ABSL_SUBDIR}") - set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${ABSL_SUBDIR}") - set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/{ABSL_SUBDIR}") - set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${ABSL_SUBDIR}") -else() - set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}") - set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") - set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") - set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}") -endif()
\ No newline at end of file diff --git a/third_party/abseil-cpp/CMake/Googletest/CMakeLists.txt.in b/third_party/abseil-cpp/CMake/Googletest/CMakeLists.txt.in index d60a33e9ac..5769e3a97b 100644 --- a/third_party/abseil-cpp/CMake/Googletest/CMakeLists.txt.in +++ b/third_party/abseil-cpp/CMake/Googletest/CMakeLists.txt.in @@ -1,15 +1,14 @@ cmake_minimum_required(VERSION 2.8.2) -project(googletest-download NONE) +project(googletest-external NONE) include(ExternalProject) ExternalProject_Add(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG master - SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" + URL "${absl_gtest_download_url}" # May be empty + SOURCE_DIR "${absl_gtest_src_dir}" + BINARY_DIR "${absl_gtest_build_dir}" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" -)
\ No newline at end of file +) diff --git a/third_party/abseil-cpp/CMake/Googletest/DownloadGTest.cmake b/third_party/abseil-cpp/CMake/Googletest/DownloadGTest.cmake index 8a00b4550c..9d071c9170 100644 --- a/third_party/abseil-cpp/CMake/Googletest/DownloadGTest.cmake +++ b/third_party/abseil-cpp/CMake/Googletest/DownloadGTest.cmake @@ -1,10 +1,11 @@ -# Downloads and unpacks googletest at configure time. Based on the instructions -# at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project +# Integrates googletest at configure time. Based on the instructions at +# https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project -# Download the latest googletest from Github master +# Set up the external googletest project, downloading the latest from Github +# master if requested. configure_file( ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in - ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt + ${CMAKE_BINARY_DIR}/googletest-external/CMakeLists.txt ) set(ABSL_SAVE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) @@ -14,17 +15,17 @@ if (BUILD_SHARED_LIBS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_CREATE_SHARED_LIBRARY=1") endif() -# Configure and build the downloaded googletest source +# Configure and build the googletest source. execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external ) if(result) message(FATAL_ERROR "CMake step for googletest failed: ${result}") endif() execute_process(COMMAND ${CMAKE_COMMAND} --build . RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external) if(result) message(FATAL_ERROR "Build step for googletest failed: ${result}") endif() @@ -37,6 +38,4 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Add googletest directly to our build. This defines the gtest and gtest_main # targets. -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) +add_subdirectory(${absl_gtest_src_dir} ${absl_gtest_build_dir} EXCLUDE_FROM_ALL) diff --git a/third_party/abseil-cpp/CMake/README.md b/third_party/abseil-cpp/CMake/README.md index 04d5df3ab0..f8b27e63f6 100644 --- a/third_party/abseil-cpp/CMake/README.md +++ b/third_party/abseil-cpp/CMake/README.md @@ -34,15 +34,16 @@ to include Abseil directly in your CMake project. 4. Add the **absl::** target you wish to use to the [`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) section of your executable or of your library.<br> -Here is a short CMakeLists.txt example of a project file using Abseil. +Here is a short CMakeLists.txt example of an application project using Abseil. ```cmake -cmake_minimum_required(VERSION 3.5) -project(my_project) +cmake_minimum_required(VERSION 3.8.2) +project(my_app_project) # Pick the C++ standard to compile with. # Abseil currently supports C++11, C++14, and C++17. set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) add_subdirectory(abseil-cpp) @@ -50,9 +51,47 @@ add_executable(my_exe source.cpp) target_link_libraries(my_exe absl::base absl::synchronization absl::strings) ``` +Note that if you are developing a library designed for use by other clients, you +should instead leave `CMAKE_CXX_STANDARD` unset (or only set if being built as +the current top-level CMake project) and configure the minimum required C++ +standard at the target level. If you require a later minimum C++ standard than +Abseil does, it's a good idea to also enforce that `CMAKE_CXX_STANDARD` (which +will control Abseil library targets) is set to at least that minimum. For +example: + +```cmake +cmake_minimum_required(VERSION 3.8.2) +project(my_lib_project) + +# Leave C++ standard up to the root application, so set it only if this is the +# current top-level CMake project. +if(CMAKE_SOURCE_DIR STREQUAL my_lib_project_SOURCE_DIR) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +add_subdirectory(abseil-cpp) + +add_library(my_lib source.cpp) +target_link_libraries(my_lib absl::base absl::synchronization absl::strings) + +# Enforce that my_lib requires C++17. Important to document for clients that they +# must set CMAKE_CXX_STANDARD to 17 or higher for proper Abseil ABI compatibility +# (since otherwise, Abseil library targets could be compiled with a lower C++ +# standard than my_lib). +target_compile_features(my_lib PUBLIC cxx_std_17) +if(CMAKE_CXX_STANDARD LESS 17) + message(FATAL_ERROR + "my_lib_project requires CMAKE_CXX_STANDARD >= 17 (got: ${CMAKE_CXX_STANDARD})") +endif() +``` + +Then the top-level application project that uses your library is responsible for +setting a consistent `CMAKE_CXX_STANDARD` that is sufficiently high. + ### Running Abseil Tests with CMake -Use the `-DABSL_RUN_TESTS=ON` flag to run Abseil tests. Note that if the `-DBUILD_TESTING=OFF` flag is passed then Abseil tests will not be run. +Use the `-DBUILD_TESTING=ON` flag to run Abseil tests. You will need to provide Abseil with a Googletest dependency. There are two options for how to do this: @@ -70,7 +109,7 @@ For example, to run just the Abseil tests, you could use this script: cd path/to/abseil-cpp mkdir build cd build -cmake -DABSL_USE_GOOGLETEST_HEAD=ON -DABSL_RUN_TESTS=ON .. +cmake -DBUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=ON .. make -j ctest ``` @@ -93,9 +132,54 @@ absl::flags absl::memory absl::meta absl::numeric -absl::random +absl::random_random absl::strings absl::synchronization absl::time absl::utility ``` + +## Traditional CMake Set-Up + +For larger projects, it may make sense to use the traditional CMake set-up where you build and install projects separately. + +First, you'd need to build and install Google Test: +``` +cmake -S /source/googletest -B /build/googletest -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/installation/dir -DBUILD_GMOCK=ON +cmake --build /build/googletest --target install +``` + +Then you need to configure and build Abseil. Make sure you enable `ABSL_USE_EXTERNAL_GOOGLETEST` and `ABSL_FIND_GOOGLETEST`. You also need to enable `ABSL_ENABLE_INSTALL` so that you can install Abseil itself. +``` +cmake -S /source/abseil-cpp -B /build/abseil-cpp -DCMAKE_PREFIX_PATH=/installation/dir -DCMAKE_INSTALL_PREFIX=/installation/dir -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON +cmake --build /temporary/build/abseil-cpp +``` + +(`CMAKE_PREFIX_PATH` is where you already have Google Test installed; `CMAKE_INSTALL_PREFIX` is where you want to have Abseil installed; they can be different.) + +Run the tests: +``` +ctest --test-dir /temporary/build/abseil-cpp +``` + +And finally install: +``` +cmake --build /temporary/build/abseil-cpp --target install +``` + +# CMake Option Synposis + +## Enable Standard CMake Installation + +`-DABSL_ENABLE_INSTALL=ON` + +## Google Test Options + +`-DBUILD_TESTING=ON` must be set to enable testing + +- Have Abseil download and build Google Test for you: `-DABSL_USE_EXTERNAL_GOOGLETEST=OFF` (default) + - Download and build latest Google Test: `-DABSL_USE_GOOGLETEST_HEAD=ON` + - Download specific Google Test version (ZIP archive): `-DABSL_GOOGLETEST_DOWNLOAD_URL=https://.../version.zip` + - Use Google Test from specific local directory: `-DABSL_LOCAL_GOOGLETEST_DIR=/path/to/googletest` +- Use Google Test included elsewhere in your project: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON` +- Use standard CMake `find_package(CTest)` to find installed Google Test: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON` diff --git a/third_party/abseil-cpp/CMake/abslConfig.cmake.in b/third_party/abseil-cpp/CMake/abslConfig.cmake.in index 60847fa772..62d246d01e 100644 --- a/third_party/abseil-cpp/CMake/abslConfig.cmake.in +++ b/third_party/abseil-cpp/CMake/abslConfig.cmake.in @@ -1,7 +1,8 @@ # absl CMake configuration file. -include(FindThreads) +include(CMakeFindDependencyMacro) +find_dependency(Threads) @PACKAGE_INIT@ -include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
\ No newline at end of file +include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/third_party/abseil-cpp/CMake/install_test_project/CMakeLists.txt b/third_party/abseil-cpp/CMake/install_test_project/CMakeLists.txt index 06b797e9ed..b865b2ec50 100644 --- a/third_party/abseil-cpp/CMake/install_test_project/CMakeLists.txt +++ b/third_party/abseil-cpp/CMake/install_test_project/CMakeLists.txt @@ -18,10 +18,8 @@ cmake_minimum_required(VERSION 3.5) project(absl_cmake_testing CXX) -set(CMAKE_CXX_STANDARD 11) - add_executable(simple simple.cc) find_package(absl REQUIRED) -target_link_libraries(simple absl::strings) +target_link_libraries(simple absl::strings absl::config) diff --git a/third_party/abseil-cpp/CMake/install_test_project/simple.cc b/third_party/abseil-cpp/CMake/install_test_project/simple.cc index e9e352912b..7daa7f0901 100644 --- a/third_party/abseil-cpp/CMake/install_test_project/simple.cc +++ b/third_party/abseil-cpp/CMake/install_test_project/simple.cc @@ -14,8 +14,17 @@ // limitations under the License. #include <iostream> +#include "absl/base/config.h" #include "absl/strings/substitute.h" +#if !defined(ABSL_LTS_RELEASE_VERSION) || ABSL_LTS_RELEASE_VERSION != 99998877 +#error ABSL_LTS_RELEASE_VERSION is not set correctly. +#endif + +#if !defined(ABSL_LTS_RELEASE_PATCH_LEVEL) || ABSL_LTS_RELEASE_PATCH_LEVEL != 0 +#error ABSL_LTS_RELEASE_PATCH_LEVEL is not set correctly. +#endif + int main(int argc, char** argv) { for (int i = 0; i < argc; ++i) { std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]); diff --git a/third_party/abseil-cpp/CMake/install_test_project/test.sh b/third_party/abseil-cpp/CMake/install_test_project/test.sh index 99989b031d..5a78c92cd1 100644..100755 --- a/third_party/abseil-cpp/CMake/install_test_project/test.sh +++ b/third_party/abseil-cpp/CMake/install_test_project/test.sh @@ -13,70 +13,60 @@ # 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. - -# "Unit" and integration tests for Absl CMake installation - -# TODO(absl-team): This script isn't fully hermetic because -# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed -# version of GoogleTest. This means that an upstream change to GoogleTest could -# break this test. Fix this by allowing this script to pin to a known-good -# version of GoogleTest. +# +# Unit and integration tests for Abseil LTS CMake installation # Fail on any error. Treat unset variables an error. Print commands as executed. set -euox pipefail -install_absl() { - pushd "${absl_build_dir}" - if [[ "${#}" -eq 1 ]]; then - cmake -DCMAKE_INSTALL_PREFIX="${1}" "${absl_dir}" - else - cmake "${absl_dir}" - fi - cmake --build . --target install -- -j - popd -} - -uninstall_absl() { - xargs rm < "${absl_build_dir}"/install_manifest.txt - rm -rf "${absl_build_dir}" - mkdir -p "${absl_build_dir}" -} - -lts_install="" - -while getopts ":l" lts; do - case "${lts}" in - l ) - lts_install="true" - ;; - esac -done - absl_dir=/abseil-cpp -absl_build_dir=/buildfs/absl-build +absl_build_dir=/buildfs +googletest_builddir=/googletest_builddir project_dir="${absl_dir}"/CMake/install_test_project project_build_dir=/buildfs/project-build -mkdir -p "${absl_build_dir}" -mkdir -p "${project_build_dir}" - -if [[ "${lts_install}" ]]; then - install_dir="/usr/local" -else - install_dir="${project_build_dir}"/install +build_shared_libs="OFF" +if [ "${LINK_TYPE:-}" = "DYNAMIC" ]; then + build_shared_libs="ON" fi -mkdir -p "${install_dir}" -# Test build, install, and link against installed abseil -pushd "${project_build_dir}" -if [[ "${lts_install}" ]]; then - install_absl - cmake "${project_dir}" -else - install_absl "${install_dir}" - cmake "${project_dir}" -DCMAKE_PREFIX_PATH="${install_dir}" -fi +# Build and install GoogleTest +mkdir "${googletest_builddir}" +pushd "${googletest_builddir}" +curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${ABSL_GOOGLETEST_COMMIT}".zip +unzip "${ABSL_GOOGLETEST_COMMIT}".zip +pushd "googletest-${ABSL_GOOGLETEST_COMMIT}" +mkdir build +pushd build +cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS="${build_shared_libs}" .. +make -j $(nproc) +make install +ldconfig +popd +popd +popd +# Run the LTS transformations +./create_lts.py 99998877 + +# Build and install Abseil +pushd "${absl_build_dir}" +cmake "${absl_dir}" \ + -DABSL_USE_EXTERNAL_GOOGLETEST=ON \ + -DABSL_FIND_GOOGLETEST=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON \ + -DBUILD_SHARED_LIBS="${build_shared_libs}" +make -j $(nproc) +ctest -j $(nproc) +make install +ldconfig +popd + +# Test the project against the installed Abseil +mkdir -p "${project_build_dir}" +pushd "${project_build_dir}" +cmake "${project_dir}" cmake --build . --target simple output="$(${project_build_dir}/simple "printme" 2>&1)" @@ -88,57 +78,35 @@ fi popd -# Test that we haven't accidentally made absl::abslblah -pushd "${install_dir}" - -# Starting in CMake 3.12 the default install dir is lib$bit_width -if [[ -d lib64 ]]; then - libdir="lib64" -elif [[ -d lib ]]; then - libdir="lib" -else - echo "ls *, */*, */*/*:" - ls * - ls */* - ls */*/* - echo "unknown lib dir" -fi - -if [[ "${lts_install}" ]]; then - # LTS versions append the date of the release to the subdir. - # 9999/99/99 is the dummy date used in the local_lts workflow. - absl_subdir="absl_99999999" -else - absl_subdir="absl" -fi - -if ! grep absl::strings "${libdir}/cmake/${absl_subdir}/abslTargets.cmake"; then - cat "${libdir}"/cmake/absl/abslTargets.cmake +if ! grep absl::strings "/usr/local/lib/cmake/absl/abslTargets.cmake"; then + cat "/usr/local/lib/cmake/absl/abslTargets.cmake" echo "CMake targets named incorrectly" exit 1 fi -uninstall_absl -popd +pushd "${HOME}" +cat > hello-abseil.cc << EOF +#include <cstdlib> -if [[ ! "${lts_install}" ]]; then - # Test that we warn if installed without a prefix or a system prefix - output="$(install_absl 2>&1)" - if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then - echo "Install without prefix didn't warn as expected. Output:" - echo "${output}" - exit 1 - fi - uninstall_absl - - output="$(install_absl /usr 2>&1)" - if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then - echo "Install with /usr didn't warn as expected. Output:" - echo "${output}" - exit 1 - fi - uninstall_absl +#include "absl/strings/str_format.h" + +int main(int argc, char **argv) { + absl::PrintF("Hello Abseil!\n"); + return EXIT_SUCCESS; +} +EOF + +if [ "${LINK_TYPE:-}" != "DYNAMIC" ]; then + pc_args=($(pkg-config --cflags --libs --static absl_str_format)) + g++ -static -o hello-abseil hello-abseil.cc "${pc_args[@]}" +else + pc_args=($(pkg-config --cflags --libs absl_str_format)) + g++ -o hello-abseil hello-abseil.cc "${pc_args[@]}" fi +hello="$(./hello-abseil)" +[[ "${hello}" == "Hello Abseil!" ]] + +popd echo "Install test complete!" exit 0 diff --git a/third_party/abseil-cpp/CMakeLists.txt b/third_party/abseil-cpp/CMakeLists.txt index 48cb6eb5cf..750a47557d 100644 --- a/third_party/abseil-cpp/CMakeLists.txt +++ b/third_party/abseil-cpp/CMakeLists.txt @@ -22,15 +22,36 @@ cmake_minimum_required(VERSION 3.5) # Compiler id for Apple Clang is now AppleClang. -cmake_policy(SET CMP0025 NEW) +if (POLICY CMP0025) + cmake_policy(SET CMP0025 NEW) +endif (POLICY CMP0025) # if command can use IN_LIST -cmake_policy(SET CMP0057 NEW) +if (POLICY CMP0057) + cmake_policy(SET CMP0057 NEW) +endif (POLICY CMP0057) -# Project version variables are the empty std::string if version is unspecified -cmake_policy(SET CMP0048 NEW) +# Project version variables are the empty string if version is unspecified +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif (POLICY CMP0048) -project(absl CXX) +# option() honor variables +if (POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif (POLICY CMP0077) + +# Allow the user to specify the MSVC runtime +if (POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) +endif (POLICY CMP0091) + +# Set BUILD_TESTING to OFF by default. +# This must come before the project() and include(CTest) lines. +OPTION(BUILD_TESTING "Build tests" OFF) + +project(absl LANGUAGES CXX VERSION 20211102) +include(CTest) # Output directory is correct by default for most build setups. However, when # building Abseil as a DLL, it is important to have the DLL in the same @@ -40,10 +61,17 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # when absl is included as subproject (i.e. using add_subdirectory(abseil-cpp)) # in the source tree of a project that uses it, install rules are disabled. -if(NOT "^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$") - set(ABSL_ENABLE_INSTALL FALSE) +if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + option(ABSL_ENABLE_INSTALL "Enable install rule" OFF) else() - set(ABSL_ENABLE_INSTALL TRUE) + option(ABSL_ENABLE_INSTALL "Enable install rule" ON) +endif() + +option(ABSL_PROPAGATE_CXX_STD + "Use CMake C++ standard meta features (e.g. cxx_std_11) that propagate to targets that link to Abseil" + OFF) # TODO: Default to ON for CMake 3.8 and greater. +if((${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.8) AND (NOT ABSL_PROPAGATE_CXX_STD)) + message(WARNING "A future Abseil release will default ABSL_PROPAGATE_CXX_STD to ON for CMake 3.8 and up. We recommend enabling this option to ensure your project still builds correctly.") endif() list(APPEND CMAKE_MODULE_PATH @@ -51,8 +79,8 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/absl/copts ) -include(AbseilInstallDirs) include(CMakePackageConfigHelpers) +include(GNUInstallDirs) include(AbseilDll) include(AbseilHelpers) @@ -81,56 +109,87 @@ endif() ## pthread find_package(Threads REQUIRED) +include(CMakeDependentOption) + +option(ABSL_USE_EXTERNAL_GOOGLETEST + "If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subproject." OFF) + +cmake_dependent_option(ABSL_FIND_GOOGLETEST + "If ON, Abseil will use find_package(GTest) rather than assuming that GoogleTest is already provided by the including project." + ON + "ABSL_USE_EXTERNAL_GOOGLETEST" + OFF) + + option(ABSL_USE_GOOGLETEST_HEAD - "If ON, abseil will download HEAD from googletest at config time." OFF) + "If ON, abseil will download HEAD from GoogleTest at config time." OFF) -option(ABSL_RUN_TESTS "If ON, Abseil tests will be run." OFF) +set(ABSL_GOOGLETEST_DOWNLOAD_URL "" CACHE STRING "If set, download GoogleTest from this URL") -if(${ABSL_RUN_TESTS}) - # enable CTest. This will set BUILD_TESTING to ON unless otherwise specified - # on the command line - include(CTest) - enable_testing() -endif() +set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH + "If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout." + ) -## check targets if(BUILD_TESTING) - - if(${ABSL_USE_GOOGLETEST_HEAD}) - include(CMake/Googletest/DownloadGTest.cmake) - set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src) + ## check targets + if (ABSL_USE_EXTERNAL_GOOGLETEST) + if (ABSL_FIND_GOOGLETEST) + find_package(GTest REQUIRED) + else() + if (NOT TARGET gtest AND NOT TARGET GTest::gtest) + message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.") + endif() + endif() + else() set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build) + if(ABSL_USE_GOOGLETEST_HEAD AND ABSL_GOOGLETEST_DOWNLOAD_URL) + message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL") + endif() + if(ABSL_USE_GOOGLETEST_HEAD) + set(absl_gtest_download_url "https://github.com/google/googletest/archive/master.zip") + elseif(ABSL_GOOGLETEST_DOWNLOAD_URL) + set(absl_gtest_download_url ${ABSL_GOOGLETEST_DOWNLOAD_URL}) + endif() + if(absl_gtest_download_url) + set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src) + else() + set(absl_gtest_src_dir ${ABSL_LOCAL_GOOGLETEST_DIR}) + endif() + include(CMake/Googletest/DownloadGTest.cmake) endif() - check_target(gtest) - check_target(gtest_main) - check_target(gmock) + if (NOT ABSL_FIND_GOOGLETEST) + # When Google Test is included directly rather than through find_package, the aliases are missing. + add_library(GTest::gtest ALIAS gtest) + add_library(GTest::gtest_main ALIAS gtest_main) + add_library(GTest::gmock ALIAS gmock) + add_library(GTest::gmock_main ALIAS gmock_main) + endif() - list(APPEND ABSL_TEST_COMMON_LIBRARIES - gtest_main - gtest - gmock - ${CMAKE_THREAD_LIBS_INIT} - ) + check_target(GTest::gtest) + check_target(GTest::gtest_main) + check_target(GTest::gmock) + check_target(GTest::gmock_main) endif() add_subdirectory(absl) if(ABSL_ENABLE_INSTALL) + # install as a subdirectory only install(EXPORT ${PROJECT_NAME}Targets NAMESPACE absl:: - DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) configure_package_config_file( CMake/abslConfig.cmake.in "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) # Abseil only has a version in LTS releases. This mechanism is accomplished @@ -143,14 +202,16 @@ if(ABSL_ENABLE_INSTALL) ) install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION ${ABSL_INSTALL_CONFIGDIR} + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) endif() # absl_VERSION install(DIRECTORY absl - DESTINATION ${ABSL_INSTALL_INCLUDEDIR} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.inc" PATTERN "*.h" - ) + PATTERN "copts" EXCLUDE + PATTERN "testdata" EXCLUDE + ) endif() # ABSL_ENABLE_INSTALL diff --git a/third_party/abseil-cpp/FAQ.md b/third_party/abseil-cpp/FAQ.md index 78028fc09f..fbd92ce975 100644 --- a/third_party/abseil-cpp/FAQ.md +++ b/third_party/abseil-cpp/FAQ.md @@ -27,7 +27,10 @@ compiler, there several ways to do this: file](https://docs.bazel.build/versions/master/guide.html#bazelrc) If you are using CMake as the build system, you'll need to add a line like -`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. See the +`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you +are developing a library designed to be used by other clients, you should +instead leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard +required by each of your library targets via `target_compile_features`. See the [CMake build instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md) for more information. diff --git a/third_party/abseil-cpp/LTS.md b/third_party/abseil-cpp/LTS.md deleted file mode 100644 index 94363b6a36..0000000000 --- a/third_party/abseil-cpp/LTS.md +++ /dev/null @@ -1,15 +0,0 @@ -# Long Term Support (LTS) Branches - -This repository contains periodic snapshots of the Abseil codebase that are -Long Term Support (LTS) branches. An LTS branch allows you to use a known -version of Abseil without interfering with other projects which may also, in -turn, use Abseil. (For more information about our releases, see the -[Abseil Release Management](https://abseil.io/about/releases) guide.) - -## LTS Branches - -The following lists LTS branches and the dates on which they have been released: - -* [LTS Branch December 18, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_12_18/) -* [LTS Branch June 20, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_06_20/) -* [LTS Branch August 8, 2019](https://github.com/abseil/abseil-cpp/tree/lts_2019_08_08/) diff --git a/third_party/abseil-cpp/METADATA b/third_party/abseil-cpp/METADATA index 7ce40fff58..a36d767ad7 100644 --- a/third_party/abseil-cpp/METADATA +++ b/third_party/abseil-cpp/METADATA @@ -10,9 +10,9 @@ third_party { } url { type: GIT - value: "https://github.googlesource.com/abseil/abseil-cpp/+/refs/heads/lts_2020_02_25" + value: "https://github.com/abseil/abseil-cpp" } - version: "fcb104594b0bb4b8ac306cb2f55ecdad40974683" - last_upgrade_date { year: 2020 month: 04 day: 27 } + version: "20211102.0" + last_upgrade_date { year: 2021 month: 12 day: 21 } } diff --git a/third_party/abseil-cpp/README.md b/third_party/abseil-cpp/README.md index 85de569658..db3a7b447a 100644 --- a/third_party/abseil-cpp/README.md +++ b/third_party/abseil-cpp/README.md @@ -9,7 +9,9 @@ standard library. - [About Abseil](#about) - [Quickstart](#quickstart) - [Building Abseil](#build) +- [Support](#support) - [Codemap](#codemap) +- [Releases](#releases) - [License](#license) - [Links](#links) @@ -42,14 +44,22 @@ the Abseil code, running tests, and getting a simple binary working. <a name="build"></a> ## Building Abseil -[Bazel](https://bazel.build) is the official build system for Abseil, -which is supported on most major platforms (Linux, Windows, macOS, for example) -and compilers. See the [quickstart](https://abseil.io/docs/cpp/quickstart) for -more information on building Abseil using the Bazel build system. +[Bazel](https://bazel.build) and [CMake](https://cmake.org/) are the official +build systems for Abseil. -<a name="cmake"></a> -If you require CMake support, please check the -[CMake build instructions](CMake/README.md). +See the [quickstart](https://abseil.io/docs/cpp/quickstart) for more information +on building Abseil using the Bazel build system. + +If you require CMake support, please check the [CMake build +instructions](CMake/README.md) and [CMake +Quickstart](https://abseil.io/docs/cpp/quickstart-cmake). + +## Support + +Abseil is officially supported on many platforms. See the [Abseil +platform support +guide](https://abseil.io/docs/cpp/platforms/platforms) for details on +supported operating systems, compilers, CPUs, etc. ## Codemap @@ -62,6 +72,9 @@ Abseil contains the following C++ library components: * [`algorithm`](absl/algorithm/) <br /> The `algorithm` library contains additions to the C++ `<algorithm>` library and container-based versions of such algorithms. +* [`cleanup`](absl/cleanup/) + <br /> The `cleanup` library contains the control-flow-construct-like type + `absl::Cleanup` which is used for executing a callback on scope exit. * [`container`](absl/container/) <br /> The `container` library contains additional STL-style containers, including Abseil's unordered "Swiss table" containers. @@ -79,6 +92,12 @@ Abseil contains the following C++ library components: available within C++14 and C++17 versions of the C++ `<type_traits>` library. * [`numeric`](absl/numeric/) <br /> The `numeric` library contains C++11-compatible 128-bit integers. +* [`profiling`](absl/profiling/) + <br /> The `profiling` library contains utility code for profiling C++ + entities. It is currently a private dependency of other Abseil libraries. +* [`status`](absl/status/) + <br /> The `status` contains abstractions for error handling, specifically + `absl::Status` and `absl::StatusOr<T>`. * [`strings`](absl/strings/) <br /> The `strings` library contains a variety of strings routines and utilities, including a C++11-compatible version of the C++17 @@ -97,6 +116,15 @@ Abseil contains the following C++ library components: * [`utility`](absl/utility/) <br /> The `utility` library contains utility and helper code. +## Releases + +Abseil recommends users "live-at-head" (update to the latest commit from the +master branch as often as possible). However, we realize this philosophy doesn't +work for every project, so we also provide [Long Term Support +Releases](https://github.com/abseil/abseil-cpp/releases) to which we backport +fixes for severe bugs. See our [release +management](https://abseil.io/about/releases) document for more details. + ## License The Abseil C++ library is licensed under the terms of the Apache diff --git a/third_party/abseil-cpp/WORKSPACE b/third_party/abseil-cpp/WORKSPACE index f2b1046446..c9aa8cafa0 100644 --- a/third_party/abseil-cpp/WORKSPACE +++ b/third_party/abseil-cpp/WORKSPACE @@ -15,31 +15,30 @@ # workspace(name = "com_google_absl") + load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( - name = "com_google_googletest", - urls = ["https://github.com/google/googletest/archive/b6cd405286ed8635ece71c72f118e659f4ade3fb.zip"], # 2019-01-07 - strip_prefix = "googletest-b6cd405286ed8635ece71c72f118e659f4ade3fb", - sha256 = "ff7a82736e158c077e76188232eac77913a15dac0b22508c390ab3f88e6d6d86", + name = "com_google_googletest", # 2021-07-09T13:28:13Z + sha256 = "12ef65654dc01ab40f6f33f9d02c04f2097d2cd9fbe48dc6001b29543583b0ad", + strip_prefix = "googletest-8d51ffdfab10b3fba636ae69bc03da4b54f8c235", + # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh. + urls = ["https://github.com/google/googletest/archive/8d51ffdfab10b3fba636ae69bc03da4b54f8c235.zip"], ) # Google benchmark. http_archive( - name = "com_github_google_benchmark", - urls = ["https://github.com/google/benchmark/archive/16703ff83c1ae6d53e5155df3bb3ab0bc96083be.zip"], - strip_prefix = "benchmark-16703ff83c1ae6d53e5155df3bb3ab0bc96083be", - sha256 = "59f918c8ccd4d74b6ac43484467b500f1d64b40cc1010daa055375b322a43ba3", + name = "com_github_google_benchmark", # 2021-09-20T09:19:51Z + sha256 = "62e2f2e6d8a744d67e4bbc212fcfd06647080de4253c97ad5c6749e09faf2cb0", + strip_prefix = "benchmark-0baacde3618ca617da95375e0af13ce1baadea47", + urls = ["https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.zip"], ) -# C++ rules for Bazel. +# Bazel platform rules. http_archive( - name = "rules_cc", - sha256 = "9a446e9dd9c1bb180c86977a8dc1e9e659550ae732ae58bd2e8fd51e15b2c91d", - strip_prefix = "rules_cc-262ebec3c2296296526740db4aefce68c80de7fa", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip", - "https://github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip", - ], + name = "platforms", + sha256 = "b601beaf841244de5c5a50d2b2eddd34839788000fa1be4260ce6603ca0d8eb7", + strip_prefix = "platforms-98939346da932eef0b54cf808622f5bb0928f00b", + urls = ["https://github.com/bazelbuild/platforms/archive/98939346da932eef0b54cf808622f5bb0928f00b.zip"], ) diff --git a/third_party/abseil-cpp/absl/BUILD.bazel b/third_party/abseil-cpp/absl/BUILD.bazel index 5a03acf87c..d799b7fe51 100644 --- a/third_party/abseil-cpp/absl/BUILD.bazel +++ b/third_party/abseil-cpp/absl/BUILD.bazel @@ -12,48 +12,69 @@ # 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. -# - -load( - ":compiler_config_setting.bzl", - "create_llvm_config", -) package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) -create_llvm_config( - name = "llvm_compiler", +config_setting( + name = "clang_compiler", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "clang", + }, + visibility = [":__subpackages__"], +) + +config_setting( + name = "msvc_compiler", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "msvc-cl", + }, + visibility = [":__subpackages__"], +) + +config_setting( + name = "clang-cl_compiler", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "clang-cl", + }, visibility = [":__subpackages__"], ) config_setting( name = "osx", constraint_values = [ - "@bazel_tools//platforms:osx", + "@platforms//os:osx", ], ) config_setting( name = "ios", constraint_values = [ - "@bazel_tools//platforms:ios", + "@platforms//os:ios", ], ) config_setting( - name = "windows", + name = "ppc", values = { - "cpu": "x64_windows", + "cpu": "ppc", }, visibility = [":__subpackages__"], ) config_setting( - name = "ppc", + name = "wasm", values = { - "cpu": "ppc", + "cpu": "wasm32", + }, + visibility = [":__subpackages__"], +) + +config_setting( + name = "fuchsia", + values = { + "cpu": "fuchsia", }, visibility = [":__subpackages__"], ) diff --git a/third_party/abseil-cpp/absl/CMakeLists.txt b/third_party/abseil-cpp/absl/CMakeLists.txt index fbfa7822b5..b1715846f0 100644 --- a/third_party/abseil-cpp/absl/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(base) add_subdirectory(algorithm) +add_subdirectory(cleanup) add_subdirectory(container) add_subdirectory(debugging) add_subdirectory(flags) @@ -24,6 +25,7 @@ add_subdirectory(hash) add_subdirectory(memory) add_subdirectory(meta) add_subdirectory(numeric) +add_subdirectory(profiling) add_subdirectory(random) add_subdirectory(status) add_subdirectory(strings) diff --git a/third_party/abseil-cpp/absl/abseil.podspec.gen.py b/third_party/abseil-cpp/absl/abseil.podspec.gen.py index 6aefb794df..63752980d0 100644..100755 --- a/third_party/abseil-cpp/absl/abseil.podspec.gen.py +++ b/third_party/abseil-cpp/absl/abseil.podspec.gen.py @@ -40,8 +40,8 @@ Pod::Spec.new do |s| 'USE_HEADERMAP' => 'NO', 'ALWAYS_SEARCH_USER_PATHS' => 'NO', } - s.ios.deployment_target = '7.0' - s.osx.deployment_target = '10.9' + s.ios.deployment_target = '9.0' + s.osx.deployment_target = '10.10' s.tvos.deployment_target = '9.0' s.watchos.deployment_target = '2.0' """ diff --git a/third_party/abseil-cpp/absl/algorithm/BUILD.bazel b/third_party/abseil-cpp/absl/algorithm/BUILD.bazel index 6a96420b96..afc5263998 100644 --- a/third_party/abseil-cpp/absl/algorithm/BUILD.bazel +++ b/third_party/abseil-cpp/absl/algorithm/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,14 +23,16 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "algorithm", hdrs = ["algorithm.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - deps = ["//absl/base:config"], + deps = [ + "//absl/base:config", + ], ) cc_test( diff --git a/third_party/abseil-cpp/absl/algorithm/CMakeLists.txt b/third_party/abseil-cpp/absl/algorithm/CMakeLists.txt index 56cd0fb85b..609d858946 100644 --- a/third_party/abseil-cpp/absl/algorithm/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/algorithm/CMakeLists.txt @@ -35,7 +35,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::algorithm - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -65,5 +65,5 @@ absl_cc_test( absl::core_headers absl::memory absl::span - gmock_main + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/algorithm/container.h b/third_party/abseil-cpp/absl/algorithm/container.h index d72532decf..c38a4a63db 100644 --- a/third_party/abseil-cpp/absl/algorithm/container.h +++ b/third_party/abseil-cpp/absl/algorithm/container.h @@ -90,10 +90,10 @@ using ContainerPointerType = // lookup of std::begin and std::end, i.e. // using std::begin; // using std::end; -// std::foo(begin(c), end(c); +// std::foo(begin(c), end(c)); // becomes // std::foo(container_algorithm_internal::begin(c), -// container_algorithm_internal::end(c)); +// container_algorithm_internal::end(c)); // These are meant for internal use only. template <typename C> @@ -188,7 +188,7 @@ bool c_any_of(const C& c, Pred&& pred) { // c_none_of() // // Container-based version of the <algorithm> `std::none_of()` function to -// test if no elements in a container fulfil a condition. +// test if no elements in a container fulfill a condition. template <typename C, typename Pred> bool c_none_of(const C& c, Pred&& pred) { return std::none_of(container_algorithm_internal::c_begin(c), @@ -340,24 +340,45 @@ container_algorithm_internal::ContainerDifferenceType<const C> c_count_if( // c_mismatch() // // Container-based version of the <algorithm> `std::mismatch()` function to -// return the first element where two ordered containers differ. +// return the first element where two ordered containers differ. Applies `==` to +// the first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2> container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(C1& c1, C2& c2) { - return std::mismatch(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2)); + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + // Negates equality because Cpp17EqualityComparable doesn't require clients + // to overload both `operator==` and `operator!=`. + if (!(*first1 == *first2)) { + break; + } + } + + return std::make_pair(first1, first2); } // Overload of c_mismatch() for using a predicate evaluation other than `==` as -// the function's test condition. +// the function's test condition. Applies `pred`to the first N elements of `c1` +// and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2, typename BinaryPredicate> container_algorithm_internal::ContainerIterPairType<C1, C2> -c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) { - return std::mismatch(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2), - std::forward<BinaryPredicate>(pred)); +c_mismatch(C1& c1, C2& c2, BinaryPredicate pred) { + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + if (!pred(*first1, *first2)) { + break; + } + } + + return std::make_pair(first1, first2); } // c_equal() @@ -539,12 +560,20 @@ BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { // c_swap_ranges() // // Container-based version of the <algorithm> `std::swap_ranges()` function to -// swap a container's elements with another container's elements. +// swap a container's elements with another container's elements. Swaps the +// first N elements of `c1` and `c2`, where N = min(size(c1), size(c2)). template <typename C1, typename C2> container_algorithm_internal::ContainerIter<C2> c_swap_ranges(C1& c1, C2& c2) { - return std::swap_ranges(container_algorithm_internal::c_begin(c1), - container_algorithm_internal::c_end(c1), - container_algorithm_internal::c_begin(c2)); + auto first1 = container_algorithm_internal::c_begin(c1); + auto last1 = container_algorithm_internal::c_end(c1); + auto first2 = container_algorithm_internal::c_begin(c2); + auto last2 = container_algorithm_internal::c_end(c2); + + using std::swap; + for (; first1 != last1 && first2 != last2; ++first1, (void)++first2) { + swap(*first1, *first2); + } + return first2; } // c_transform() @@ -562,16 +591,23 @@ OutputIterator c_transform(const InputSequence& input, OutputIterator output, } // Overload of c_transform() for performing a transformation using a binary -// predicate. +// predicate. Applies `binary_op` to the first N elements of `c1` and `c2`, +// where N = min(size(c1), size(c2)). template <typename InputSequence1, typename InputSequence2, typename OutputIterator, typename BinaryOp> OutputIterator c_transform(const InputSequence1& input1, const InputSequence2& input2, OutputIterator output, BinaryOp&& binary_op) { - return std::transform(container_algorithm_internal::c_begin(input1), - container_algorithm_internal::c_end(input1), - container_algorithm_internal::c_begin(input2), output, - std::forward<BinaryOp>(binary_op)); + auto first1 = container_algorithm_internal::c_begin(input1); + auto last1 = container_algorithm_internal::c_end(input1); + auto first2 = container_algorithm_internal::c_begin(input2); + auto last2 = container_algorithm_internal::c_end(input2); + for (; first1 != last1 && first2 != last2; + ++first1, (void)++first2, ++output) { + *output = binary_op(*first1, *first2); + } + + return output; } // c_replace() @@ -869,11 +905,11 @@ void c_sort(C& c) { // Overload of c_sort() for performing a `comp` comparison other than the // default `operator<`. -template <typename C, typename Compare> -void c_sort(C& c, Compare&& comp) { +template <typename C, typename LessThan> +void c_sort(C& c, LessThan&& comp) { std::sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_stable_sort() @@ -889,11 +925,11 @@ void c_stable_sort(C& c) { // Overload of c_stable_sort() for performing a `comp` comparison other than the // default `operator<`. -template <typename C, typename Compare> -void c_stable_sort(C& c, Compare&& comp) { +template <typename C, typename LessThan> +void c_stable_sort(C& c, LessThan&& comp) { std::stable_sort(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_is_sorted() @@ -908,11 +944,11 @@ bool c_is_sorted(const C& c) { // c_is_sorted() overload for performing a `comp` comparison other than the // default `operator<`. -template <typename C, typename Compare> -bool c_is_sorted(const C& c, Compare&& comp) { +template <typename C, typename LessThan> +bool c_is_sorted(const C& c, LessThan&& comp) { return std::is_sorted(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_partial_sort() @@ -930,22 +966,23 @@ void c_partial_sort( // Overload of c_partial_sort() for performing a `comp` comparison other than // the default `operator<`. -template <typename RandomAccessContainer, typename Compare> +template <typename RandomAccessContainer, typename LessThan> void c_partial_sort( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter<RandomAccessContainer> middle, - Compare&& comp) { + LessThan&& comp) { std::partial_sort(container_algorithm_internal::c_begin(sequence), middle, container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_partial_sort_copy() // // Container-based version of the <algorithm> `std::partial_sort_copy()` -// function to sort elements within a container such that elements before -// `middle` are sorted in ascending order, and return the result within an -// iterator. +// function to sort the elements in the given range `result` within the larger +// `sequence` in ascending order (and using `result` as the output parameter). +// At most min(result.last - result.first, sequence.last - sequence.first) +// elements from the sequence will be stored in the result. template <typename C, typename RandomAccessContainer> container_algorithm_internal::ContainerIter<RandomAccessContainer> c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { @@ -957,15 +994,15 @@ c_partial_sort_copy(const C& sequence, RandomAccessContainer& result) { // Overload of c_partial_sort_copy() for performing a `comp` comparison other // than the default `operator<`. -template <typename C, typename RandomAccessContainer, typename Compare> +template <typename C, typename RandomAccessContainer, typename LessThan> container_algorithm_internal::ContainerIter<RandomAccessContainer> c_partial_sort_copy(const C& sequence, RandomAccessContainer& result, - Compare&& comp) { + LessThan&& comp) { return std::partial_sort_copy(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), container_algorithm_internal::c_begin(result), container_algorithm_internal::c_end(result), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_is_sorted_until() @@ -981,12 +1018,12 @@ container_algorithm_internal::ContainerIter<C> c_is_sorted_until(C& c) { // Overload of c_is_sorted_until() for performing a `comp` comparison other than // the default `operator<`. -template <typename C, typename Compare> +template <typename C, typename LessThan> container_algorithm_internal::ContainerIter<C> c_is_sorted_until( - C& c, Compare&& comp) { + C& c, LessThan&& comp) { return std::is_sorted_until(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_nth_element() @@ -1006,14 +1043,14 @@ void c_nth_element( // Overload of c_nth_element() for performing a `comp` comparison other than // the default `operator<`. -template <typename RandomAccessContainer, typename Compare> +template <typename RandomAccessContainer, typename LessThan> void c_nth_element( RandomAccessContainer& sequence, container_algorithm_internal::ContainerIter<RandomAccessContainer> nth, - Compare&& comp) { + LessThan&& comp) { std::nth_element(container_algorithm_internal::c_begin(sequence), nth, container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } //------------------------------------------------------------------------------ @@ -1035,12 +1072,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_lower_bound( // Overload of c_lower_bound() for performing a `comp` comparison other than // the default `operator<`. -template <typename Sequence, typename T, typename Compare> +template <typename Sequence, typename T, typename LessThan> container_algorithm_internal::ContainerIter<Sequence> c_lower_bound( - Sequence& sequence, T&& value, Compare&& comp) { + Sequence& sequence, T&& value, LessThan&& comp) { return std::lower_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<T>(value), std::forward<Compare>(comp)); + std::forward<T>(value), std::forward<LessThan>(comp)); } // c_upper_bound() @@ -1058,12 +1095,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_upper_bound( // Overload of c_upper_bound() for performing a `comp` comparison other than // the default `operator<`. -template <typename Sequence, typename T, typename Compare> +template <typename Sequence, typename T, typename LessThan> container_algorithm_internal::ContainerIter<Sequence> c_upper_bound( - Sequence& sequence, T&& value, Compare&& comp) { + Sequence& sequence, T&& value, LessThan&& comp) { return std::upper_bound(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<T>(value), std::forward<Compare>(comp)); + std::forward<T>(value), std::forward<LessThan>(comp)); } // c_equal_range() @@ -1081,12 +1118,12 @@ c_equal_range(Sequence& sequence, T&& value) { // Overload of c_equal_range() for performing a `comp` comparison other than // the default `operator<`. -template <typename Sequence, typename T, typename Compare> +template <typename Sequence, typename T, typename LessThan> container_algorithm_internal::ContainerIterPairType<Sequence, Sequence> -c_equal_range(Sequence& sequence, T&& value, Compare&& comp) { +c_equal_range(Sequence& sequence, T&& value, LessThan&& comp) { return std::equal_range(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<T>(value), std::forward<Compare>(comp)); + std::forward<T>(value), std::forward<LessThan>(comp)); } // c_binary_search() @@ -1103,12 +1140,12 @@ bool c_binary_search(Sequence&& sequence, T&& value) { // Overload of c_binary_search() for performing a `comp` comparison other than // the default `operator<`. -template <typename Sequence, typename T, typename Compare> -bool c_binary_search(Sequence&& sequence, T&& value, Compare&& comp) { +template <typename Sequence, typename T, typename LessThan> +bool c_binary_search(Sequence&& sequence, T&& value, LessThan&& comp) { return std::binary_search(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), std::forward<T>(value), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } //------------------------------------------------------------------------------ @@ -1129,14 +1166,14 @@ OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result) { // Overload of c_merge() for performing a `comp` comparison other than // the default `operator<`. -template <typename C1, typename C2, typename OutputIterator, typename Compare> +template <typename C1, typename C2, typename OutputIterator, typename LessThan> OutputIterator c_merge(const C1& c1, const C2& c2, OutputIterator result, - Compare&& comp) { + LessThan&& comp) { return std::merge(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), result, - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_inplace_merge() @@ -1152,13 +1189,13 @@ void c_inplace_merge(C& c, // Overload of c_inplace_merge() for performing a merge using a `comp` other // than `operator<`. -template <typename C, typename Compare> +template <typename C, typename LessThan> void c_inplace_merge(C& c, container_algorithm_internal::ContainerIter<C> middle, - Compare&& comp) { + LessThan&& comp) { std::inplace_merge(container_algorithm_internal::c_begin(c), middle, container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_includes() @@ -1176,13 +1213,13 @@ bool c_includes(const C1& c1, const C2& c2) { // Overload of c_includes() for performing a merge using a `comp` other than // `operator<`. -template <typename C1, typename C2, typename Compare> -bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { +template <typename C1, typename C2, typename LessThan> +bool c_includes(const C1& c1, const C2& c2, LessThan&& comp) { return std::includes(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_set_union() @@ -1206,7 +1243,7 @@ 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 LessThan, typename = typename std::enable_if< !container_algorithm_internal::IsUnorderedContainer<C1>::value, void>::type, @@ -1214,18 +1251,18 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare, !container_algorithm_internal::IsUnorderedContainer<C2>::value, void>::type> OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, - Compare&& comp) { + LessThan&& comp) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_set_intersection() // // 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 sorted containers. template <typename C1, typename C2, typename OutputIterator, typename = typename std::enable_if< !container_algorithm_internal::IsUnorderedContainer<C1>::value, @@ -1235,6 +1272,11 @@ template <typename C1, typename C2, typename OutputIterator, void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using operator<. + assert(absl::c_is_sorted(c1)); + assert(absl::c_is_sorted(c2)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), @@ -1243,7 +1285,7 @@ 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 LessThan, typename = typename std::enable_if< !container_algorithm_internal::IsUnorderedContainer<C1>::value, void>::type, @@ -1251,12 +1293,17 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare, !container_algorithm_internal::IsUnorderedContainer<C2>::value, void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { + OutputIterator output, LessThan&& comp) { + // In debug builds, ensure that both containers are sorted with respect to the + // default comparator. std::set_intersection requires the containers be sorted + // using the same comparator. + assert(absl::c_is_sorted(c1, comp)); + assert(absl::c_is_sorted(c2, comp)); return std::set_intersection(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_set_difference() @@ -1281,7 +1328,7 @@ 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 LessThan, typename = typename std::enable_if< !container_algorithm_internal::IsUnorderedContainer<C1>::value, void>::type, @@ -1289,12 +1336,12 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare, !container_algorithm_internal::IsUnorderedContainer<C2>::value, void>::type> OutputIterator c_set_difference(const C1& c1, const C2& c2, - OutputIterator output, Compare&& comp) { + OutputIterator output, LessThan&& comp) { return std::set_difference(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_set_symmetric_difference() @@ -1320,7 +1367,7 @@ 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 LessThan, typename = typename std::enable_if< !container_algorithm_internal::IsUnorderedContainer<C1>::value, void>::type, @@ -1329,13 +1376,13 @@ template <typename C1, typename C2, typename OutputIterator, typename Compare, void>::type> OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output, - Compare&& comp) { + LessThan&& comp) { return std::set_symmetric_difference( container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), container_algorithm_internal::c_end(c2), output, - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } //------------------------------------------------------------------------------ @@ -1354,11 +1401,11 @@ void c_push_heap(RandomAccessContainer& sequence) { // Overload of c_push_heap() for performing a push operation on a heap using a // `comp` other than `operator<`. -template <typename RandomAccessContainer, typename Compare> -void c_push_heap(RandomAccessContainer& sequence, Compare&& comp) { +template <typename RandomAccessContainer, typename LessThan> +void c_push_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::push_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_pop_heap() @@ -1373,11 +1420,11 @@ void c_pop_heap(RandomAccessContainer& sequence) { // Overload of c_pop_heap() for performing a pop operation on a heap using a // `comp` other than `operator<`. -template <typename RandomAccessContainer, typename Compare> -void c_pop_heap(RandomAccessContainer& sequence, Compare&& comp) { +template <typename RandomAccessContainer, typename LessThan> +void c_pop_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::pop_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_make_heap() @@ -1392,11 +1439,11 @@ void c_make_heap(RandomAccessContainer& sequence) { // Overload of c_make_heap() for performing heap comparisons using a // `comp` other than `operator<` -template <typename RandomAccessContainer, typename Compare> -void c_make_heap(RandomAccessContainer& sequence, Compare&& comp) { +template <typename RandomAccessContainer, typename LessThan> +void c_make_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::make_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_sort_heap() @@ -1411,11 +1458,11 @@ void c_sort_heap(RandomAccessContainer& sequence) { // Overload of c_sort_heap() for performing heap comparisons using a // `comp` other than `operator<` -template <typename RandomAccessContainer, typename Compare> -void c_sort_heap(RandomAccessContainer& sequence, Compare&& comp) { +template <typename RandomAccessContainer, typename LessThan> +void c_sort_heap(RandomAccessContainer& sequence, LessThan&& comp) { std::sort_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_is_heap() @@ -1430,11 +1477,11 @@ bool c_is_heap(const RandomAccessContainer& sequence) { // Overload of c_is_heap() for performing heap comparisons using a // `comp` other than `operator<` -template <typename RandomAccessContainer, typename Compare> -bool c_is_heap(const RandomAccessContainer& sequence, Compare&& comp) { +template <typename RandomAccessContainer, typename LessThan> +bool c_is_heap(const RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_is_heap_until() @@ -1450,12 +1497,12 @@ c_is_heap_until(RandomAccessContainer& sequence) { // Overload of c_is_heap_until() for performing heap comparisons using a // `comp` other than `operator<` -template <typename RandomAccessContainer, typename Compare> +template <typename RandomAccessContainer, typename LessThan> container_algorithm_internal::ContainerIter<RandomAccessContainer> -c_is_heap_until(RandomAccessContainer& sequence, Compare&& comp) { +c_is_heap_until(RandomAccessContainer& sequence, LessThan&& comp) { return std::is_heap_until(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } //------------------------------------------------------------------------------ @@ -1476,12 +1523,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_min_element( // Overload of c_min_element() for performing a `comp` comparison other than // `operator<`. -template <typename Sequence, typename Compare> +template <typename Sequence, typename LessThan> container_algorithm_internal::ContainerIter<Sequence> c_min_element( - Sequence& sequence, Compare&& comp) { + Sequence& sequence, LessThan&& comp) { return std::min_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_max_element() @@ -1498,12 +1545,12 @@ container_algorithm_internal::ContainerIter<Sequence> c_max_element( // Overload of c_max_element() for performing a `comp` comparison other than // `operator<`. -template <typename Sequence, typename Compare> +template <typename Sequence, typename LessThan> container_algorithm_internal::ContainerIter<Sequence> c_max_element( - Sequence& sequence, Compare&& comp) { + Sequence& sequence, LessThan&& comp) { return std::max_element(container_algorithm_internal::c_begin(sequence), container_algorithm_internal::c_end(sequence), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_minmax_element() @@ -1521,12 +1568,12 @@ c_minmax_element(C& c) { // Overload of c_minmax_element() for performing `comp` comparisons other than // `operator<`. -template <typename C, typename Compare> +template <typename C, typename LessThan> container_algorithm_internal::ContainerIterPairType<C, C> -c_minmax_element(C& c, Compare&& comp) { +c_minmax_element(C& c, LessThan&& comp) { return std::minmax_element(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } //------------------------------------------------------------------------------ @@ -1551,15 +1598,15 @@ bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2) { // Overload of c_lexicographical_compare() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template <typename Sequence1, typename Sequence2, typename Compare> +template <typename Sequence1, typename Sequence2, typename LessThan> bool c_lexicographical_compare(Sequence1&& sequence1, Sequence2&& sequence2, - Compare&& comp) { + LessThan&& comp) { return std::lexicographical_compare( container_algorithm_internal::c_begin(sequence1), container_algorithm_internal::c_end(sequence1), container_algorithm_internal::c_begin(sequence2), container_algorithm_internal::c_end(sequence2), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_next_permutation() @@ -1575,11 +1622,11 @@ bool c_next_permutation(C& c) { // Overload of c_next_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template <typename C, typename Compare> -bool c_next_permutation(C& c, Compare&& comp) { +template <typename C, typename LessThan> +bool c_next_permutation(C& c, LessThan&& comp) { return std::next_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } // c_prev_permutation() @@ -1595,11 +1642,11 @@ bool c_prev_permutation(C& c) { // Overload of c_prev_permutation() for performing a lexicographical // comparison using a `comp` operator instead of `operator<`. -template <typename C, typename Compare> -bool c_prev_permutation(C& c, Compare&& comp) { +template <typename C, typename LessThan> +bool c_prev_permutation(C& c, LessThan&& comp) { return std::prev_permutation(container_algorithm_internal::c_begin(c), container_algorithm_internal::c_end(c), - std::forward<Compare>(comp)); + std::forward<LessThan>(comp)); } //------------------------------------------------------------------------------ diff --git a/third_party/abseil-cpp/absl/algorithm/container_test.cc b/third_party/abseil-cpp/absl/algorithm/container_test.cc index 0a4abe9462..605afc8040 100644 --- a/third_party/abseil-cpp/absl/algorithm/container_test.cc +++ b/third_party/abseil-cpp/absl/algorithm/container_test.cc @@ -57,9 +57,7 @@ class NonMutatingTest : public testing::Test { }; struct AccumulateCalls { - void operator()(int value) { - calls.push_back(value); - } + void operator()(int value) { calls.push_back(value); } std::vector<int> calls; }; @@ -68,7 +66,6 @@ bool BinPredicate(int v1, int v2) { return v1 < v2; } bool Equals(int v1, int v2) { return v1 == v2; } bool IsOdd(int x) { return x % 2 != 0; } - TEST_F(NonMutatingTest, Distance) { EXPECT_EQ(container_.size(), absl::c_distance(container_)); EXPECT_EQ(sequence_.size(), absl::c_distance(sequence_)); @@ -151,13 +148,90 @@ TEST_F(NonMutatingTest, CountIf) { } TEST_F(NonMutatingTest, Mismatch) { - absl::c_mismatch(container_, sequence_); - absl::c_mismatch(sequence_, container_); + // Testing necessary as absl::c_mismatch executes logic. + { + auto result = absl::c_mismatch(vector_, sequence_); + EXPECT_EQ(result.first, vector_.end()); + EXPECT_EQ(result.second, sequence_.end()); + } + { + auto result = absl::c_mismatch(sequence_, vector_); + EXPECT_EQ(result.first, sequence_.end()); + EXPECT_EQ(result.second, vector_.end()); + } + + sequence_.back() = 5; + { + auto result = absl::c_mismatch(vector_, sequence_); + EXPECT_EQ(result.first, std::prev(vector_.end())); + EXPECT_EQ(result.second, std::prev(sequence_.end())); + } + { + auto result = absl::c_mismatch(sequence_, vector_); + EXPECT_EQ(result.first, std::prev(sequence_.end())); + EXPECT_EQ(result.second, std::prev(vector_.end())); + } + + sequence_.pop_back(); + { + auto result = absl::c_mismatch(vector_, sequence_); + EXPECT_EQ(result.first, std::prev(vector_.end())); + EXPECT_EQ(result.second, sequence_.end()); + } + { + auto result = absl::c_mismatch(sequence_, vector_); + EXPECT_EQ(result.first, sequence_.end()); + EXPECT_EQ(result.second, std::prev(vector_.end())); + } + { + struct NoNotEquals { + constexpr bool operator==(NoNotEquals) const { return true; } + constexpr bool operator!=(NoNotEquals) const = delete; + }; + std::vector<NoNotEquals> first; + std::list<NoNotEquals> second; + + // Check this still compiles. + absl::c_mismatch(first, second); + } } TEST_F(NonMutatingTest, MismatchWithPredicate) { - absl::c_mismatch(container_, sequence_, BinPredicate); - absl::c_mismatch(sequence_, container_, BinPredicate); + // Testing necessary as absl::c_mismatch executes logic. + { + auto result = absl::c_mismatch(vector_, sequence_, BinPredicate); + EXPECT_EQ(result.first, vector_.begin()); + EXPECT_EQ(result.second, sequence_.begin()); + } + { + auto result = absl::c_mismatch(sequence_, vector_, BinPredicate); + EXPECT_EQ(result.first, sequence_.begin()); + EXPECT_EQ(result.second, vector_.begin()); + } + + sequence_.front() = 0; + { + auto result = absl::c_mismatch(vector_, sequence_, BinPredicate); + EXPECT_EQ(result.first, vector_.begin()); + EXPECT_EQ(result.second, sequence_.begin()); + } + { + auto result = absl::c_mismatch(sequence_, vector_, BinPredicate); + EXPECT_EQ(result.first, std::next(sequence_.begin())); + EXPECT_EQ(result.second, std::next(vector_.begin())); + } + + sequence_.clear(); + { + auto result = absl::c_mismatch(vector_, sequence_, BinPredicate); + EXPECT_EQ(result.first, vector_.begin()); + EXPECT_EQ(result.second, sequence_.end()); + } + { + auto result = absl::c_mismatch(sequence_, vector_, BinPredicate); + EXPECT_EQ(result.first, sequence_.end()); + EXPECT_EQ(result.second, vector_.begin()); + } } TEST_F(NonMutatingTest, Equal) { @@ -519,11 +593,9 @@ TEST_F(SortingTest, IsSortedUntil) { TEST_F(SortingTest, NthElement) { std::vector<int> unsorted = {2, 4, 1, 3}; absl::c_nth_element(unsorted, unsorted.begin() + 2); - EXPECT_THAT(unsorted, - ElementsAre(Lt(3), Lt(3), 3, Gt(3))); + EXPECT_THAT(unsorted, ElementsAre(Lt(3), Lt(3), 3, Gt(3))); absl::c_nth_element(unsorted, unsorted.begin() + 2, std::greater<int>()); - EXPECT_THAT(unsorted, - ElementsAre(Gt(2), Gt(2), 2, Lt(2))); + EXPECT_THAT(unsorted, ElementsAre(Gt(2), Gt(2), 2, Lt(2))); } TEST(MutatingTest, IsPartitioned) { @@ -676,6 +748,15 @@ TEST(MutatingTest, SwapRanges) { absl::c_swap_ranges(odds, evens); EXPECT_THAT(odds, ElementsAre(1, 3, 5)); EXPECT_THAT(evens, ElementsAre(2, 4, 6)); + + odds.pop_back(); + absl::c_swap_ranges(odds, evens); + EXPECT_THAT(odds, ElementsAre(2, 4)); + EXPECT_THAT(evens, ElementsAre(1, 3, 6)); + + absl::c_swap_ranges(evens, odds); + EXPECT_THAT(odds, ElementsAre(1, 3)); + EXPECT_THAT(evens, ElementsAre(2, 4, 6)); } TEST_F(NonMutatingTest, Transform) { @@ -690,6 +771,20 @@ TEST_F(NonMutatingTest, Transform) { EXPECT_EQ(std::vector<int>({1, 5, 4}), z); *end = 7; EXPECT_EQ(std::vector<int>({1, 5, 4, 7}), z); + + z.clear(); + y.pop_back(); + end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>()); + EXPECT_EQ(std::vector<int>({1, 5}), z); + *end = 7; + EXPECT_EQ(std::vector<int>({1, 5, 7}), z); + + z.clear(); + std::swap(x, y); + end = absl::c_transform(x, y, std::back_inserter(z), std::plus<int>()); + EXPECT_EQ(std::vector<int>({1, 5}), z); + *end = 7; + EXPECT_EQ(std::vector<int>({1, 5, 7}), z); } TEST(MutatingTest, Replace) { @@ -755,10 +850,9 @@ MATCHER_P2(IsElement, key, value, "") { TEST(MutatingTest, StableSort) { std::vector<Element> test_vector = {{1, 1}, {2, 1}, {2, 0}, {1, 0}, {2, 2}}; absl::c_stable_sort(test_vector); - EXPECT_THAT( - test_vector, - ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1), - IsElement(2, 0), IsElement(2, 2))); + EXPECT_THAT(test_vector, + ElementsAre(IsElement(1, 1), IsElement(1, 0), IsElement(2, 1), + IsElement(2, 0), IsElement(2, 2))); } TEST(MutatingTest, StableSortWithPredicate) { @@ -766,10 +860,9 @@ TEST(MutatingTest, StableSortWithPredicate) { absl::c_stable_sort(test_vector, [](const Element& e1, const Element& e2) { return e2 < e1; }); - EXPECT_THAT( - test_vector, - ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2), - IsElement(1, 1), IsElement(1, 0))); + EXPECT_THAT(test_vector, + ElementsAre(IsElement(2, 1), IsElement(2, 0), IsElement(2, 2), + IsElement(1, 1), IsElement(1, 0))); } TEST(MutatingTest, ReplaceCopyIf) { diff --git a/third_party/abseil-cpp/absl/base/BUILD.bazel b/third_party/abseil-cpp/absl/base/BUILD.bazel index bae79427ac..4769efda0a 100644 --- a/third_party/abseil-cpp/absl/base/BUILD.bazel +++ b/third_party/abseil-cpp/absl/base/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "atomic_hook", @@ -115,11 +114,18 @@ cc_library( cc_library( name = "dynamic_annotations", - srcs = ["dynamic_annotations.cc"], - hdrs = ["dynamic_annotations.h"], + srcs = [ + "internal/dynamic_annotations.h", + ], + hdrs = [ + "dynamic_annotations.h", + ], copts = ABSL_DEFAULT_COPTS, - defines = ["__CLANG_SUPPORT_DYN_ANNOTATION__"], linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":core_headers", + ], ) cc_library( @@ -153,7 +159,9 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = select({ - "//absl:windows": [], + "//absl:msvc_compiler": [], + "//absl:clang-cl_compiler": [], + "//absl:wasm": [], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, visibility = [ @@ -212,9 +220,13 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = select({ - "//absl:windows": [ + "//absl:msvc_compiler": [ + "-DEFAULTLIB:advapi32.lib", + ], + "//absl:clang-cl_compiler": [ "-DEFAULTLIB:advapi32.lib", ], + "//absl:wasm": [], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, deps = [ @@ -307,6 +319,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":errno_saver", + ":strerror", "@com_google_googletest//:gtest_main", ], ) @@ -405,6 +418,7 @@ cc_library( deps = [ ":base", ":base_internal", + ":config", ":core_headers", "//absl/synchronization", "@com_google_googletest//:gtest", @@ -421,6 +435,7 @@ cc_test( deps = [ ":base", ":base_internal", + ":config", ":core_headers", "//absl/synchronization", "@com_google_googletest//:gtest_main", @@ -451,6 +466,7 @@ cc_binary( testonly = 1, copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ ":spinlock_benchmark_common", @@ -466,6 +482,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":base", ":config", ":core_headers", ], @@ -538,8 +555,13 @@ cc_test( srcs = ["internal/low_level_alloc_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - tags = ["no_test_ios_x86_64"], - deps = [":malloc_internal"], + tags = [ + "no_test_ios_x86_64", + ], + deps = [ + ":malloc_internal", + "//absl/container:node_hash_map", + ], ) cc_test( @@ -571,137 +593,138 @@ cc_test( ) cc_library( - name = "bits", - hdrs = ["internal/bits.h"], + name = "scoped_set_env", + testonly = 1, + srcs = ["internal/scoped_set_env.cc"], + hdrs = ["internal/scoped_set_env.h"], linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], deps = [ ":config", - ":core_headers", + ":raw_logging_internal", ], ) cc_test( - name = "bits_test", + name = "scoped_set_env_test", size = "small", - srcs = ["internal/bits_test.cc"], + srcs = ["internal/scoped_set_env_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":bits", + ":scoped_set_env", "@com_google_googletest//:gtest_main", ], ) -cc_library( - name = "exponential_biased", - srcs = ["internal/exponential_biased.cc"], - hdrs = ["internal/exponential_biased.h"], - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl:__subpackages__", - ], - deps = [ - ":config", - ":core_headers", - ], -) - cc_test( - name = "exponential_biased_test", + name = "log_severity_test", size = "small", - srcs = ["internal/exponential_biased_test.cc"], + srcs = ["log_severity_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], deps = [ - ":exponential_biased", + ":log_severity", + "//absl/flags:flag_internal", + "//absl/flags:marshalling", "//absl/strings", "@com_google_googletest//:gtest_main", ], ) cc_library( - name = "periodic_sampler", - srcs = ["internal/periodic_sampler.cc"], - hdrs = ["internal/periodic_sampler.h"], + name = "strerror", + srcs = ["internal/strerror.cc"], + hdrs = ["internal/strerror.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], deps = [ + ":config", ":core_headers", - ":exponential_biased", + ":errno_saver", ], ) cc_test( - name = "periodic_sampler_test", + name = "strerror_test", size = "small", - srcs = ["internal/periodic_sampler_test.cc"], + srcs = ["internal/strerror_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], deps = [ - ":core_headers", - ":periodic_sampler", + ":strerror", + "//absl/strings", "@com_google_googletest//:gtest_main", ], ) cc_binary( - name = "periodic_sampler_benchmark", + name = "strerror_benchmark", testonly = 1, - srcs = ["internal/periodic_sampler_benchmark.cc"], + srcs = ["internal/strerror_benchmark.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ - ":core_headers", - ":periodic_sampler", + ":strerror", "@com_github_google_benchmark//:benchmark_main", ], ) cc_library( - name = "scoped_set_env", - testonly = 1, - srcs = ["internal/scoped_set_env.cc"], - hdrs = ["internal/scoped_set_env.h"], + name = "fast_type_id", + hdrs = ["internal/fast_type_id.h"], + copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], deps = [ ":config", - ":raw_logging_internal", ], ) cc_test( - name = "scoped_set_env_test", + name = "fast_type_id_test", size = "small", - srcs = ["internal/scoped_set_env_test.cc"], + srcs = ["internal/fast_type_id_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":scoped_set_env", + ":fast_type_id", "@com_google_googletest//:gtest_main", ], ) cc_test( - name = "log_severity_test", + name = "unique_small_name_test", size = "small", - srcs = ["log_severity_test.cc"], + srcs = ["internal/unique_small_name_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + linkstatic = 1, deps = [ - ":log_severity", - "//absl/flags:flag_internal", - "//absl/flags:marshalling", + ":core_headers", "//absl/strings", "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "optimization_test", + size = "small", + srcs = ["optimization_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":core_headers", + "//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/third_party/abseil-cpp/absl/base/CMakeLists.txt b/third_party/abseil-cpp/absl/base/CMakeLists.txt index 14c52eabdf..c7233cb36c 100644 --- a/third_party/abseil-cpp/absl/base/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/base/CMakeLists.txt @@ -105,11 +105,11 @@ absl_cc_library( HDRS "dynamic_annotations.h" SRCS - "dynamic_annotations.cc" + "internal/dynamic_annotations.h" COPTS ${ABSL_DEFAULT_COPTS} - DEFINES - "__CLANG_SUPPORT_DYN_ANNOTATION__" + DEPS + absl::config PUBLIC ) @@ -191,7 +191,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} - $<$<BOOL:${LIBRT}>:${LIBRT}> + $<$<BOOL:${LIBRT}>:-lrt> $<$<BOOL:${MINGW}>:"advapi32"> DEPS absl::atomic_hook @@ -230,7 +230,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::config - gtest + GTest::gtest TESTONLY ) @@ -259,7 +259,7 @@ absl_cc_library( absl::meta absl::strings absl::utility - gtest + GTest::gtest TESTONLY ) @@ -273,7 +273,7 @@ absl_cc_test( DEPS absl::exception_safety_testing absl::memory - gtest_main + GTest::gtest_main ) absl_cc_library( @@ -300,8 +300,8 @@ absl_cc_test( absl::atomic_hook_test_helper absl::atomic_hook absl::core_headers - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -314,7 +314,7 @@ absl_cc_test( DEPS absl::base absl::core_headers - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -326,8 +326,9 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::errno_saver - gmock - gtest_main + absl::strerror + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -341,7 +342,7 @@ absl_cc_test( absl::base absl::config absl::throw_delegate - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -356,7 +357,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base_internal - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -370,8 +371,8 @@ absl_cc_test( absl::base_internal absl::memory absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_library( @@ -383,10 +384,11 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::base + absl::config absl::base_internal absl::core_headers absl::synchronization - gtest + GTest::gtest TESTONLY ) @@ -401,9 +403,10 @@ absl_cc_test( DEPS absl::base absl::base_internal + absl::config absl::core_headers absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_library( @@ -415,6 +418,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::config absl::core_headers PUBLIC @@ -431,7 +435,7 @@ absl_cc_test( absl::base absl::config absl::endian - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -444,7 +448,7 @@ absl_cc_test( DEPS absl::config absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -458,7 +462,7 @@ absl_cc_test( absl::base absl::core_headers absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -471,7 +475,7 @@ absl_cc_test( DEPS absl::raw_logging_internal absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -484,7 +488,7 @@ absl_cc_test( DEPS absl::base absl::synchronization - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -496,6 +500,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::malloc_internal + absl::node_hash_map Threads::Threads ) @@ -511,134 +516,125 @@ absl_cc_test( absl::core_headers absl::synchronization Threads::Threads - gtest_main + GTest::gtest_main ) absl_cc_library( NAME - bits + scoped_set_env + SRCS + "internal/scoped_set_env.cc" HDRS - "internal/bits.h" + "internal/scoped_set_env.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS absl::config - absl::core_headers + absl::raw_logging_internal ) absl_cc_test( NAME - bits_test + scoped_set_env_test SRCS - "internal/bits_test.cc" + "internal/scoped_set_env_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::bits - gtest_main + absl::scoped_set_env + GTest::gtest_main ) -absl_cc_library( +absl_cc_test( NAME - exponential_biased + cmake_thread_test SRCS - "internal/exponential_biased.cc" - HDRS - "internal/exponential_biased.h" + "internal/cmake_thread_test.cc" COPTS - ${ABSL_DEFAULT_COPTS} + ${ABSL_TEST_COPTS} DEPS - absl::config - absl::core_headers + absl::base ) absl_cc_test( NAME - exponential_biased_test + log_severity_test SRCS - "internal/exponential_biased_test.cc" - COPTS - ${ABSL_TEST_COPTS} + "log_severity_test.cc" DEPS - absl::exponential_biased + absl::flags_internal + absl::flags_marshalling + absl::log_severity absl::strings - gmock_main + GTest::gmock + GTest::gtest_main ) absl_cc_library( NAME - periodic_sampler + strerror SRCS - "internal/periodic_sampler.cc" + "internal/strerror.cc" HDRS - "internal/periodic_sampler.h" + "internal/strerror.h" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::config absl::core_headers - absl::exponential_biased + absl::errno_saver ) absl_cc_test( NAME - periodic_sampler_test + strerror_test SRCS - "internal/periodic_sampler_test.cc" + "internal/strerror_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::core_headers - absl::periodic_sampler - gmock_main + absl::strerror + absl::strings + GTest::gmock + GTest::gtest_main ) absl_cc_library( NAME - scoped_set_env - SRCS - "internal/scoped_set_env.cc" + fast_type_id HDRS - "internal/scoped_set_env.h" + "internal/fast_type_id.h" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config - absl::raw_logging_internal ) absl_cc_test( NAME - scoped_set_env_test + fast_type_id_test SRCS - "internal/scoped_set_env_test.cc" + "internal/fast_type_id_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::scoped_set_env - gtest_main + absl::fast_type_id + GTest::gtest_main ) absl_cc_test( NAME - cmake_thread_test + optimization_test SRCS - "internal/cmake_thread_test.cc" + "optimization_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS - absl::base -) - -absl_cc_test( - NAME - log_severity_test - SRCS - "log_severity_test.cc" - DEPS - absl::flags_internal - absl::flags_marshalling - absl::log_severity - absl::strings - gmock - gtest_main + absl::core_headers + absl::optional + GTest::gtest_main ) diff --git a/third_party/abseil-cpp/absl/base/attributes.h b/third_party/abseil-cpp/absl/base/attributes.h index ff138629d1..e3907827d6 100644 --- a/third_party/abseil-cpp/absl/base/attributes.h +++ b/third_party/abseil-cpp/absl/base/attributes.h @@ -18,8 +18,6 @@ // These macros are used within Abseil and allow the compiler to optimize, where // applicable, certain function calls. // -// This file is used for both C and C++! -// // Most macros here are exposing GCC or Clang features, and are stubbed out for // other compilers. // @@ -32,34 +30,12 @@ // of them are not supported in older version of Clang. Thus, we check // `__has_attribute()` first. If the check fails, we check if we are on GCC and // assume the attribute exists on GCC (which is verified on GCC 4.7). -// -// ----------------------------------------------------------------------------- -// Sanitizer Attributes -// ----------------------------------------------------------------------------- -// -// Sanitizer-related attributes are not "defined" in this file (and indeed -// are not defined as such in any file). To utilize the following -// sanitizer-related attributes within your builds, define the following macros -// within your build using a `-D` flag, along with the given value for -// `-fsanitize`: -// -// * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8) -// * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only) -// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+) -// * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+) -// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only) -// -// Example: -// -// // Enable branches in the Abseil code that are tagged for ASan: -// $ bazel build --copt=-DADDRESS_SANITIZER --copt=-fsanitize=address -// --linkopt=-fsanitize=address *target* -// -// Since these macro names are only supported by GCC and Clang, we only check -// for `__GNUC__` (GCC or Clang) and the above macros. + #ifndef ABSL_BASE_ATTRIBUTES_H_ #define ABSL_BASE_ATTRIBUTES_H_ +#include "absl/base/config.h" + // ABSL_HAVE_ATTRIBUTE // // A function-like feature checking macro that is a wrapper around @@ -143,7 +119,7 @@ #if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) -#elif defined(__GNUC__) && !defined(__clang__) +#elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__) #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 #define ABSL_ATTRIBUTE_NO_TAIL_CALL \ __attribute__((optimize("no-optimize-sibling-calls"))) @@ -155,14 +131,14 @@ // ABSL_ATTRIBUTE_WEAK // // Tags a function as weak for the purposes of compilation and linking. -// Weak attributes currently do not work properly in LLVM's Windows backend, -// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// Weak attributes did not work properly in LLVM's Windows backend before +// 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 // for further information. // The MinGW compiler doesn't complain about the weak attribute until the link // step, presumably because Windows doesn't use ELF binaries. #if (ABSL_HAVE_ATTRIBUTE(weak) || \ (defined(__GNUC__) && !defined(__clang__))) && \ - !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__) + (!defined(_WIN32) || __clang_major__ < 9) && !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 @@ -234,7 +210,7 @@ // out of bounds or does other scary things with memory. // NOTE: GCC supports AddressSanitizer(asan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_address) #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS @@ -242,13 +218,13 @@ // ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // -// Tells the MemorySanitizer to relax the handling of a given function. All -// "Use of uninitialized value" warnings from such functions will be suppressed, -// and all values loaded from memory will be considered fully initialized. -// This attribute is similar to the ADDRESS_SANITIZER attribute above, but deals -// with initialized-ness rather than addressability issues. +// Tells the MemorySanitizer to relax the handling of a given function. All "Use +// of uninitialized value" warnings from such functions will be suppressed, and +// all values loaded from memory will be considered fully initialized. This +// attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute +// above, but deals with initialized-ness rather than addressability issues. // NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. -#if defined(__clang__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory) #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY @@ -259,7 +235,7 @@ // Tells the ThreadSanitizer to not instrument a given function. // NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread) #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD @@ -271,8 +247,10 @@ // where certain behavior (eg. division by zero) is being used intentionally. // NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9. // https://gcc.gnu.org/gcc-4.9/changes.html -#if defined(__GNUC__) && \ - (defined(UNDEFINED_BEHAVIOR_SANITIZER) || defined(ADDRESS_SANITIZER)) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined) +#define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ + __attribute__((no_sanitize_undefined)) +#elif ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ __attribute__((no_sanitize("undefined"))) #else @@ -283,7 +261,7 @@ // // Tells the ControlFlowIntegrity sanitizer to not instrument a given function. // See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. -#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI @@ -293,7 +271,7 @@ // // Tells the SafeStack to not instrument a given function. // See https://clang.llvm.org/docs/SafeStack.html for details. -#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER) +#if ABSL_HAVE_ATTRIBUTE(no_sanitize) #define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ __attribute__((no_sanitize("safe-stack"))) #else @@ -303,10 +281,7 @@ // ABSL_ATTRIBUTE_RETURNS_NONNULL // // Tells the compiler that a particular function never returns a null pointer. -#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) || \ - (defined(__GNUC__) && \ - (__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ - !defined(__clang__)) +#if ABSL_HAVE_ATTRIBUTE(returns_nonnull) #define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) #else #define ABSL_ATTRIBUTE_RETURNS_NONNULL @@ -343,8 +318,16 @@ // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. // This functionality is supported by GNU linker. #ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE +#ifdef _AIX +// __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo +// op which includes an additional integer as part of its syntax indcating +// alignment. If data fall under different alignments then you might get a +// compilation error indicating a `Section type conflict`. +#define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) +#else #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) #endif +#endif // ABSL_DECLARE_ATTRIBUTE_SECTION_VARS // @@ -507,8 +490,10 @@ // packages/targets, as this may lead to conflicting definitions of functions at // link-time. // +// XRay isn't currently supported on Android: +// https://github.com/android/ndk/issues/368 #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \ - !defined(ABSL_NO_XRAY_ATTRIBUTES) + !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__) #define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]] #define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) @@ -544,6 +529,13 @@ // ABSL_ATTRIBUTE_UNUSED // // Prevents the compiler from complaining about variables that appear unused. +// +// For code or headers that are assured to only build with C++17 and up, prefer +// just using the standard '[[maybe_unused]]' directly over this macro. +// +// Due to differences in positioning requirements between the old, compiler +// specific __attribute__ syntax and the now standard [[maybe_unused]], this +// macro does not attempt to take advantage of '[[maybe_unused]]'. #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) #undef ABSL_ATTRIBUTE_UNUSED #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) @@ -564,13 +556,19 @@ // ABSL_ATTRIBUTE_PACKED // // Instructs the compiler not to use natural alignment for a tagged data -// structure, but instead to reduce its alignment to 1. This attribute can -// either be applied to members of a structure or to a structure in its -// entirety. Applying this attribute (judiciously) to a structure in its -// entirety to optimize the memory footprint of very commonly-used structs is -// fine. Do not apply this attribute to a structure in its entirety if the -// purpose is to control the offsets of the members in the structure. Instead, -// apply this attribute only to structure members that need it. +// structure, but instead to reduce its alignment to 1. +// +// Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing +// so can cause atomic variables to be mis-aligned and silently violate +// atomicity on x86. +// +// This attribute can either be applied to members of a structure or to a +// structure in its entirety. Applying this attribute (judiciously) to a +// structure in its entirety to optimize the memory footprint of very +// commonly-used structs is fine. Do not apply this attribute to a structure in +// its entirety if the purpose is to control the offsets of the members in the +// structure. Instead, apply this attribute only to structure members that need +// it. // // When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the // natural alignment of structure members not annotated is preserved. Aligned @@ -592,6 +590,79 @@ #define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) #endif +// ABSL_FALLTHROUGH_INTENDED +// +// Annotates implicit fall-through between switch labels, allowing a case to +// indicate intentional fallthrough and turn off warnings about any lack of a +// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by +// a semicolon and can be used in most places where `break` can, provided that +// no statements exist between it and the next switch label. +// +// Example: +// +// switch (x) { +// case 40: +// case 41: +// if (truth_is_out_there) { +// ++x; +// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations +// // in comments +// } else { +// return x; +// } +// case 42: +// ... +// +// Notes: When supported, GCC and Clang can issue a warning on switch labels +// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See +// clang documentation on language extensions for details: +// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough +// +// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has +// no effect on diagnostics. In any case this macro has no effect on runtime +// behavior and performance of code. + +#ifdef ABSL_FALLTHROUGH_INTENDED +#error "ABSL_FALLTHROUGH_INTENDED should not be defined." +#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] +#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough) +#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] +#else +#define ABSL_FALLTHROUGH_INTENDED \ + do { \ + } while (0) +#endif + +// ABSL_DEPRECATED() +// +// Marks a deprecated class, struct, enum, function, method and variable +// declarations. The macro argument is used as a custom diagnostic message (e.g. +// suggestion of a better alternative). +// +// Examples: +// +// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; +// +// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} +// +// template <typename T> +// ABSL_DEPRECATED("Use DoThat() instead") +// void DoThis(); +// +// Every usage of a deprecated entity will trigger a warning when compiled with +// clang's `-Wdeprecated-declarations` option. This option is turned off by +// default, but the warnings will be reported by clang-tidy. +#if defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L +#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) +#endif + +#ifndef ABSL_DEPRECATED +#define ABSL_DEPRECATED(message) +#endif + // ABSL_CONST_INIT // // A variable declaration annotated with the `ABSL_CONST_INIT` attribute will @@ -618,4 +689,47 @@ #define ABSL_CONST_INIT #endif // ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) +// ABSL_ATTRIBUTE_PURE_FUNCTION +// +// ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure" +// functions. A function is pure if its return value is only a function of its +// arguments. The pure attribute prohibits a function from modifying the state +// of the program that is observable by means other than inspecting the +// function's return value. Declaring such functions with the pure attribute +// allows the compiler to avoid emitting some calls in repeated invocations of +// the function with the same argument values. +// +// Example: +// +// ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d); +#if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]] +#elif ABSL_HAVE_ATTRIBUTE(pure) +#define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure)) +#else +#define ABSL_ATTRIBUTE_PURE_FUNCTION +#endif + +// ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function +// parameter or implicit object parameter is retained by the return value of the +// annotated function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] +#elif ABSL_HAVE_ATTRIBUTE(lifetimebound) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) +#else +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + #endif // ABSL_BASE_ATTRIBUTES_H_ diff --git a/third_party/abseil-cpp/absl/base/call_once.h b/third_party/abseil-cpp/absl/base/call_once.h index bc5ec93704..96109f537c 100644 --- a/third_party/abseil-cpp/absl/base/call_once.h +++ b/third_party/abseil-cpp/absl/base/call_once.h @@ -175,17 +175,10 @@ void CallOnceImpl(std::atomic<uint32_t>* control, std::memory_order_relaxed) || base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, scheduling_mode) == kOnceInit) { - base_internal::Invoke(std::forward<Callable>(fn), + base_internal::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...); - // The call to SpinLockWake below is an optimization, because the waiter - // in SpinLockWait is waiting with a short timeout. The atomic load/store - // sequence is slightly faster than an atomic exchange: - // old_control = control->exchange(base_internal::kOnceDone, - // std::memory_order_release); - // We opt for a slightly faster case when there are no waiters, in spite - // of longer tail latency when there are waiters. - old_control = control->load(std::memory_order_relaxed); - control->store(base_internal::kOnceDone, std::memory_order_release); + old_control = + control->exchange(base_internal::kOnceDone, std::memory_order_release); if (old_control == base_internal::kOnceWaiter) { base_internal::SpinLockWake(control, true); } diff --git a/third_party/abseil-cpp/absl/base/casts.h b/third_party/abseil-cpp/absl/base/casts.h index 322cc1d243..83c691265f 100644 --- a/third_party/abseil-cpp/absl/base/casts.h +++ b/third_party/abseil-cpp/absl/base/casts.h @@ -159,16 +159,19 @@ inline Dest bit_cast(const Source& source) { return dest; } -// NOTE: This overload is only picked if the requirements of bit_cast are not -// met. It is therefore UB, but is provided temporarily as previous versions of -// this function template were unchecked. Do not use this in new code. +// NOTE: This overload is only picked if the requirements of bit_cast are +// not met. It is therefore UB, but is provided temporarily as previous +// versions of this function template were unchecked. Do not use this in +// new code. template < typename Dest, typename Source, typename std::enable_if< - !internal_casts::is_bitcastable<Dest, Source>::value, int>::type = 0> + !internal_casts::is_bitcastable<Dest, Source>::value, + int>::type = 0> ABSL_DEPRECATED( - "absl::bit_cast type requirements were violated. Update the types being " - "used such that they are the same size and are both TriviallyCopyable.") + "absl::bit_cast type requirements were violated. Update the types " + "being used such that they are the same size and are both " + "TriviallyCopyable.") inline Dest bit_cast(const Source& source) { static_assert(sizeof(Dest) == sizeof(Source), "Source and destination types should have equal sizes."); diff --git a/third_party/abseil-cpp/absl/base/config.h b/third_party/abseil-cpp/absl/base/config.h index ee99f94629..585485c359 100644 --- a/third_party/abseil-cpp/absl/base/config.h +++ b/third_party/abseil-cpp/absl/base/config.h @@ -66,6 +66,35 @@ #include "absl/base/options.h" #include "absl/base/policy_checks.h" +// Abseil long-term support (LTS) releases will define +// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the +// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the +// integer representing the patch-level for that release. +// +// For example, for LTS release version "20300401.2", this would give us +// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2 +// +// These symbols will not be defined in non-LTS code. +// +// Abseil recommends that clients live-at-head. Therefore, if you are using +// these symbols to assert a minimum version requirement, we recommend you do it +// as +// +// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401 +// #error Project foo requires Abseil LTS version >= 20300401 +// #endif +// +// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes +// live-at-head clients from the minimum version assertion. +// +// See https://abseil.io/about/releases for more information on Abseil release +// management. +// +// LTS releases can be obtained from +// https://github.com/abseil/abseil-cpp/releases. +#define ABSL_LTS_RELEASE_VERSION 20211102 +#define ABSL_LTS_RELEASE_PATCH_LEVEL 0 + // Helper macro to convert a CPP variable to a string literal. #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x #define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) @@ -121,10 +150,16 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 #define ABSL_NAMESPACE_BEGIN #define ABSL_NAMESPACE_END +#define ABSL_INTERNAL_C_SYMBOL(x) x #elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1 #define ABSL_NAMESPACE_BEGIN \ inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME { #define ABSL_NAMESPACE_END } +#define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v +#define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) +#define ABSL_INTERNAL_C_SYMBOL(x) \ + ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME) #else #error options.h is misconfigured. #endif @@ -154,6 +189,28 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_INTERNAL_HAS_KEYWORD(x) 0 #endif +#ifdef __has_feature +#define ABSL_HAVE_FEATURE(f) __has_feature(f) +#else +#define ABSL_HAVE_FEATURE(f) 0 +#endif + +// Portable check for GCC minimum version: +// https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \ + (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0 +#endif + +#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \ + (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y)) +#else +#define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0 +#endif + // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. // We assume __thread is supported on Linux when compiled with Clang or compiled // against libstdc++ with _GLIBCXX_HAVE_TLS defined. @@ -171,10 +228,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // gcc >= 4.8.1 using libstdc++, and Visual Studio. #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE #error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set -#elif defined(_LIBCPP_VERSION) || \ - (!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \ - defined(_MSC_VER) +#elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \ + (!defined(__clang__) && defined(__GLIBCXX__) && \ + ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8)) #define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 #endif @@ -187,16 +243,17 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // // Checks whether `std::is_trivially_copy_assignable<T>` is supported. -// Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with -// either libc++ or libstdc++, and Visual Studio (but not NVCC). +// Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with +// libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC). #if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set -#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ - (!defined(__clang__) && defined(__GNUC__) && \ - (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ +#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ + (!defined(__clang__) && \ + ((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \ + (ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) && \ + defined(_LIBCPP_VERSION)))) || \ (defined(_MSC_VER) && !defined(__NVCC__)) #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 @@ -210,6 +267,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) #define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 +#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) +#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 #endif #endif @@ -226,11 +285,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // * 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. +// making ABSL_HAVE_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 ABSL_HAVE_FEATURE(cxx_thread_local) && \ !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) #define ABSL_HAVE_THREAD_LOCAL 1 #endif @@ -262,13 +319,6 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #endif // defined(__ANDROID__) && defined(__clang__) -// Emscripten doesn't yet support `thread_local` or `__thread`. -// https://github.com/emscripten-core/emscripten/issues/3502 -#if defined(__EMSCRIPTEN__) -#undef ABSL_HAVE_TLS -#undef ABSL_HAVE_THREAD_LOCAL -#endif // defined(__EMSCRIPTEN__) - // ABSL_HAVE_INTRINSIC_INT128 // // Checks whether the __int128 compiler extension for a 128-bit integral type is @@ -314,25 +364,21 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // For further details, consult the compiler's documentation. #ifdef ABSL_HAVE_EXCEPTIONS #error ABSL_HAVE_EXCEPTIONS cannot be directly set. - -#elif defined(__clang__) - -#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) +#elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6) // Clang >= 3.6 -#if __has_feature(cxx_exceptions) +#if ABSL_HAVE_FEATURE(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 -#endif // __has_feature(cxx_exceptions) -#else +#endif // ABSL_HAVE_FEATURE(cxx_exceptions) +#elif defined(__clang__) // Clang < 3.6 // http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro -#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) +#if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) #define ABSL_HAVE_EXCEPTIONS 1 -#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions) -#endif // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6) - +#endif // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) // Handle remaining special cases and default to exceptions being supported. -#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ - !(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \ +#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ + !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) && \ + !defined(__cpp_exceptions)) && \ !(defined(_MSC_VER) && !defined(_CPPUNWIND)) #define ABSL_HAVE_EXCEPTIONS 1 #endif @@ -364,10 +410,11 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // POSIX.1-2001. #ifdef ABSL_HAVE_MMAP #error ABSL_HAVE_MMAP cannot be directly set -#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ - defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \ - defined(__ASYLO__) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ + defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ + defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ + defined(__HAIKU__) #define ABSL_HAVE_MMAP 1 #endif @@ -378,10 +425,19 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__ros__) + defined(_AIX) || defined(__ros__) #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 #endif +// ABSL_HAVE_SCHED_GETCPU +// +// Checks whether sched_getcpu is available. +#ifdef ABSL_HAVE_SCHED_GETCPU +#error ABSL_HAVE_SCHED_GETCPU cannot be directly set +#elif defined(__linux__) +#define ABSL_HAVE_SCHED_GETCPU 1 +#endif + // ABSL_HAVE_SCHED_YIELD // // Checks whether the platform implements sched_yield(2) as defined in @@ -477,9 +533,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 50000)) + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)) #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 #else #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 @@ -493,7 +549,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #ifdef __has_include -#if __has_include(<any>) && __cplusplus >= 201703L && \ +#if __has_include(<any>) && defined(__cplusplus) && __cplusplus >= 201703L && \ !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_ANY 1 #endif @@ -507,8 +563,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #ifdef __has_include -#if __has_include(<optional>) && __cplusplus >= 201703L && \ - !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#if __has_include(<optional>) && defined(__cplusplus) && \ + __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_OPTIONAL 1 #endif #endif @@ -521,8 +577,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #ifdef __has_include -#if __has_include(<variant>) && __cplusplus >= 201703L && \ - !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE +#if __has_include(<variant>) && defined(__cplusplus) && \ + __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_VARIANT 1 #endif #endif @@ -535,7 +591,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #endif #ifdef __has_include -#if __has_include(<string_view>) && __cplusplus >= 201703L +#if __has_include(<string_view>) && defined(__cplusplus) && \ + __cplusplus >= 201703L #define ABSL_HAVE_STD_STRING_VIEW 1 #endif #endif @@ -547,8 +604,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || // not correctly set by MSVC, so we use `_MSVC_LANG` to check the language // version. // TODO(zhangxy): fix tests before enabling aliasing for `std::any`. -#if defined(_MSC_VER) && _MSC_VER >= 1910 && \ - ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402) +#if defined(_MSC_VER) && _MSC_VER >= 1910 && \ + ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \ + (defined(__cplusplus) && __cplusplus > 201402)) // #define ABSL_HAVE_STD_ANY 1 #define ABSL_HAVE_STD_OPTIONAL 1 #define ABSL_HAVE_STD_VARIANT 1 @@ -668,4 +726,47 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || #define ABSL_DLL #endif // defined(_MSC_VER) +// ABSL_HAVE_MEMORY_SANITIZER +// +// MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of +// a compiler instrumentation module and a run-time library. +#ifdef ABSL_HAVE_MEMORY_SANITIZER +#error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_MEMORY__) +#define ABSL_HAVE_MEMORY_SANITIZER 1 +#elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer) +#define ABSL_HAVE_MEMORY_SANITIZER 1 +#endif + +// ABSL_HAVE_THREAD_SANITIZER +// +// ThreadSanitizer (TSan) is a fast data race detector. +#ifdef ABSL_HAVE_THREAD_SANITIZER +#error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_THREAD__) +#define ABSL_HAVE_THREAD_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(thread_sanitizer) +#define ABSL_HAVE_THREAD_SANITIZER 1 +#endif + +// ABSL_HAVE_ADDRESS_SANITIZER +// +// AddressSanitizer (ASan) is a fast memory error detector. +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set." +#elif defined(__SANITIZE_ADDRESS__) +#define ABSL_HAVE_ADDRESS_SANITIZER 1 +#elif ABSL_HAVE_FEATURE(address_sanitizer) +#define ABSL_HAVE_ADDRESS_SANITIZER 1 +#endif + +// ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +// +// Class template argument deduction is a language feature added in C++17. +#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION +#error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set." +#elif defined(__cpp_deduction_guides) +#define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 +#endif + #endif // ABSL_BASE_CONFIG_H_ diff --git a/third_party/abseil-cpp/absl/base/dynamic_annotations.cc b/third_party/abseil-cpp/absl/base/dynamic_annotations.cc deleted file mode 100644 index 21e822e53c..0000000000 --- a/third_party/abseil-cpp/absl/base/dynamic_annotations.cc +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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 <stdlib.h> -#include <string.h> - -#include "absl/base/dynamic_annotations.h" - -#ifndef __has_feature -#define __has_feature(x) 0 -#endif - -/* Compiler-based ThreadSanitizer defines - DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL = 1 - and provides its own definitions of the functions. */ - -#ifndef DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL -# define DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL 0 -#endif - -/* Each function is empty and called (via a macro) only in debug mode. - The arguments are captured by dynamic tools at runtime. */ - -#if DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 && !defined(__native_client__) - -#if __has_feature(memory_sanitizer) -#include <sanitizer/msan_interface.h> -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void AnnotateRWLockCreate(const char *, int, - const volatile void *){} -void AnnotateRWLockDestroy(const char *, int, - const volatile void *){} -void AnnotateRWLockAcquired(const char *, int, - const volatile void *, long){} -void AnnotateRWLockReleased(const char *, int, - const volatile void *, long){} -void AnnotateBenignRace(const char *, int, - const volatile void *, - const char *){} -void AnnotateBenignRaceSized(const char *, int, - const volatile void *, - size_t, - const char *) {} -void AnnotateThreadName(const char *, int, - const char *){} -void AnnotateIgnoreReadsBegin(const char *, int){} -void AnnotateIgnoreReadsEnd(const char *, int){} -void AnnotateIgnoreWritesBegin(const char *, int){} -void AnnotateIgnoreWritesEnd(const char *, int){} -void AnnotateEnableRaceDetection(const char *, int, int){} -void AnnotateMemoryIsInitialized(const char *, int, - const volatile void *mem, size_t size) { -#if __has_feature(memory_sanitizer) - __msan_unpoison(mem, size); -#else - (void)mem; - (void)size; -#endif -} - -void AnnotateMemoryIsUninitialized(const char *, int, - const volatile void *mem, size_t size) { -#if __has_feature(memory_sanitizer) - __msan_allocated_memory(mem, size); -#else - (void)mem; - (void)size; -#endif -} - -static int GetRunningOnValgrind(void) { -#ifdef RUNNING_ON_VALGRIND - if (RUNNING_ON_VALGRIND) return 1; -#endif - char *running_on_valgrind_str = getenv("RUNNING_ON_VALGRIND"); - if (running_on_valgrind_str) { - return strcmp(running_on_valgrind_str, "0") != 0; - } - return 0; -} - -/* See the comments in dynamic_annotations.h */ -int RunningOnValgrind(void) { - static volatile int running_on_valgrind = -1; - int local_running_on_valgrind = running_on_valgrind; - /* C doesn't have thread-safe initialization of statics, and we - don't want to depend on pthread_once here, so hack it. */ - ANNOTATE_BENIGN_RACE(&running_on_valgrind, "safe hack"); - if (local_running_on_valgrind == -1) - running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind(); - return local_running_on_valgrind; -} - -/* See the comments in dynamic_annotations.h */ -double ValgrindSlowdown(void) { - /* Same initialization hack as in RunningOnValgrind(). */ - static volatile double slowdown = 0.0; - double local_slowdown = slowdown; - ANNOTATE_BENIGN_RACE(&slowdown, "safe hack"); - if (RunningOnValgrind() == 0) { - return 1.0; - } - if (local_slowdown == 0.0) { - char *env = getenv("VALGRIND_SLOWDOWN"); - slowdown = local_slowdown = env ? atof(env) : 50.0; - } - return local_slowdown; -} - -#ifdef __cplusplus -} // extern "C" -#endif -#endif /* DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 */ diff --git a/third_party/abseil-cpp/absl/base/dynamic_annotations.h b/third_party/abseil-cpp/absl/base/dynamic_annotations.h index 65a54b447f..3ea7c1568c 100644 --- a/third_party/abseil-cpp/absl/base/dynamic_annotations.h +++ b/third_party/abseil-cpp/absl/base/dynamic_annotations.h @@ -1,389 +1,471 @@ -/* - * Copyright 2017 The Abseil Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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. - */ -/* This file defines dynamic annotations for use with dynamic analysis - tool such as valgrind, PIN, etc. - - Dynamic annotation is a source code annotation that affects - the generated code (that is, the annotation is not a comment). - Each such annotation is attached to a particular - instruction and/or to a particular object (address) in the program. - - The annotations that should be used by users are macros in all upper-case - (e.g., ANNOTATE_THREAD_NAME). - - Actual implementation of these macros may differ depending on the - dynamic analysis tool being used. - - This file supports the following configurations: - - Dynamic Annotations enabled (with static thread-safety warnings disabled). - In this case, macros expand to functions implemented by Thread Sanitizer, - when building with TSan. When not provided an external implementation, - dynamic_annotations.cc provides no-op implementations. - - - Static Clang thread-safety warnings enabled. - When building with a Clang compiler that supports thread-safety warnings, - a subset of annotations can be statically-checked at compile-time. We - expand these macros to static-inline functions that can be analyzed for - thread-safety, but afterwards elided when building the final binary. - - - All annotations are disabled. - If neither Dynamic Annotations nor Clang thread-safety warnings are - enabled, then all annotation-macros expand to empty. */ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +// This file defines dynamic annotations for use with dynamic analysis tool +// such as valgrind, PIN, etc. +// +// Dynamic annotation is a source code annotation that affects the generated +// code (that is, the annotation is not a comment). Each such annotation is +// attached to a particular instruction and/or to a particular object (address) +// in the program. +// +// The annotations that should be used by users are macros in all upper-case +// (e.g., ABSL_ANNOTATE_THREAD_NAME). +// +// Actual implementation of these macros may differ depending on the dynamic +// analysis tool being used. +// +// This file supports the following configurations: +// - Dynamic Annotations enabled (with static thread-safety warnings disabled). +// In this case, macros expand to functions implemented by Thread Sanitizer, +// when building with TSan. When not provided an external implementation, +// dynamic_annotations.cc provides no-op implementations. +// +// - Static Clang thread-safety warnings enabled. +// When building with a Clang compiler that supports thread-safety warnings, +// a subset of annotations can be statically-checked at compile-time. We +// expand these macros to static-inline functions that can be analyzed for +// thread-safety, but afterwards elided when building the final binary. +// +// - All annotations are disabled. +// If neither Dynamic Annotations nor Clang thread-safety warnings are +// enabled, then all annotation-macros expand to empty. #ifndef ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ #define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ -#ifndef DYNAMIC_ANNOTATIONS_ENABLED -# define DYNAMIC_ANNOTATIONS_ENABLED 0 -#endif +#include <stddef.h> -#if DYNAMIC_ANNOTATIONS_ENABLED != 0 - - /* ------------------------------------------------------------- - Annotations that suppress errors. It is usually better to express the - program's synchronization using the other annotations, but these can - be used when all else fails. */ - - /* Report that we may have a benign race at "pointer", with size - "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the - point where "pointer" has been allocated, preferably close to the point - where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */ - #define ANNOTATE_BENIGN_RACE(pointer, description) \ - AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \ - sizeof(*(pointer)), description) - - /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to - the memory range [address, address+size). */ - #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ - AnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) - - /* Enable (enable!=0) or disable (enable==0) race detection for all threads. - This annotation could be useful if you want to skip expensive race analysis - during some period of program execution, e.g. during initialization. */ - #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \ - AnnotateEnableRaceDetection(__FILE__, __LINE__, enable) - - /* ------------------------------------------------------------- - Annotations useful for debugging. */ - - /* Report the current thread name to a race detector. */ - #define ANNOTATE_THREAD_NAME(name) \ - AnnotateThreadName(__FILE__, __LINE__, name) - - /* ------------------------------------------------------------- - Annotations useful when implementing locks. They are not - normally needed by modules that merely use locks. - The "lock" argument is a pointer to the lock object. */ - - /* Report that a lock has been created at address "lock". */ - #define ANNOTATE_RWLOCK_CREATE(lock) \ - AnnotateRWLockCreate(__FILE__, __LINE__, lock) - - /* Report that a linker initialized lock has been created at address "lock". - */ -#ifdef THREAD_SANITIZER - #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ - AnnotateRWLockCreateStatic(__FILE__, __LINE__, lock) -#else - #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock) +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#ifdef __cplusplus +#include "absl/base/macros.h" #endif - /* Report that the lock at address "lock" is about to be destroyed. */ - #define ANNOTATE_RWLOCK_DESTROY(lock) \ - AnnotateRWLockDestroy(__FILE__, __LINE__, lock) +// TODO(rogeeff): Remove after the backward compatibility period. +#include "absl/base/internal/dynamic_annotations.h" // IWYU pragma: export - /* Report that the lock at address "lock" has been acquired. - is_w=1 for writer lock, is_w=0 for reader lock. */ - #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ - AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w) +// ------------------------------------------------------------------------- +// Decide which features are enabled. - /* Report that the lock at address "lock" is about to be released. */ - #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ - AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w) +#ifdef ABSL_HAVE_THREAD_SANITIZER -#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1 - #define ANNOTATE_RWLOCK_CREATE(lock) /* empty */ - #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) /* empty */ - #define ANNOTATE_RWLOCK_DESTROY(lock) /* empty */ - #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */ - #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */ - #define ANNOTATE_BENIGN_RACE(address, description) /* empty */ - #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */ - #define ANNOTATE_THREAD_NAME(name) /* empty */ - #define ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */ +#else -#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0 -/* These annotations are also made available to LLVM's Memory Sanitizer */ -#if DYNAMIC_ANNOTATIONS_ENABLED == 1 || defined(MEMORY_SANITIZER) - #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ - AnnotateMemoryIsInitialized(__FILE__, __LINE__, address, size) +// Clang provides limited support for static thread-safety analysis through a +// feature called Annotalysis. We configure macro-definitions according to +// whether Annotalysis support is available. When running in opt-mode, GCC +// will issue a warning, if these attributes are compiled. Only include them +// when compiling using Clang. - #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ - AnnotateMemoryIsUninitialized(__FILE__, __LINE__, address, size) +#if defined(__clang__) +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 1 +#if !defined(SWIG) +#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1 +#endif #else - #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */ - #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */ -#endif /* DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */ - -/* TODO(delesley) -- Replace __CLANG_SUPPORT_DYN_ANNOTATION__ with the - appropriate feature ID. */ -#if defined(__clang__) && (!defined(SWIG)) \ - && defined(__CLANG_SUPPORT_DYN_ANNOTATION__) - - #if DYNAMIC_ANNOTATIONS_ENABLED == 0 - #define ANNOTALYSIS_ENABLED - #endif - - /* When running in opt-mode, GCC will issue a warning, if these attributes are - compiled. Only include them when compiling using Clang. */ - #define ATTRIBUTE_IGNORE_READS_BEGIN \ - __attribute((exclusive_lock_function("*"))) - #define ATTRIBUTE_IGNORE_READS_END \ - __attribute((unlock_function("*"))) +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 +#endif + +// Read/write annotations are enabled in Annotalysis mode; disabled otherwise. +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \ + ABSL_INTERNAL_ANNOTALYSIS_ENABLED + +#endif // ABSL_HAVE_THREAD_SANITIZER + +#ifdef __cplusplus +#define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" { +#define ABSL_INTERNAL_END_EXTERN_C } // extern "C" +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F +#define ABSL_INTERNAL_STATIC_INLINE inline #else - #define ATTRIBUTE_IGNORE_READS_BEGIN /* empty */ - #define ATTRIBUTE_IGNORE_READS_END /* empty */ -#endif /* defined(__clang__) && ... */ +#define ABSL_INTERNAL_BEGIN_EXTERN_C // empty +#define ABSL_INTERNAL_END_EXTERN_C // empty +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) F +#define ABSL_INTERNAL_STATIC_INLINE static inline +#endif -#if (DYNAMIC_ANNOTATIONS_ENABLED != 0) || defined(ANNOTALYSIS_ENABLED) - #define ANNOTATIONS_ENABLED +// ------------------------------------------------------------------------- +// Define race annotations. + +#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1 +// Some of the symbols used in this section (e.g. AnnotateBenignRaceSized) are +// defined by the compiler-based santizer implementation, not by the Abseil +// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. + +// ------------------------------------------------------------- +// Annotations that suppress errors. It is usually better to express the +// program's synchronization using the other annotations, but these can be used +// when all else fails. + +// Report that we may have a benign race at `pointer`, with size +// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the +// point where `pointer` has been allocated, preferably close to the point +// where the race happens. See also ABSL_ANNOTATE_BENIGN_RACE_STATIC. +#define ABSL_ANNOTATE_BENIGN_RACE(pointer, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +// Same as ABSL_ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to +// the memory range [`address`, `address`+`size`). +#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, address, size, description) + +// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads. +// This annotation could be useful if you want to skip expensive race analysis +// during some period of program execution, e.g. during initialization. +#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \ + (__FILE__, __LINE__, enable) + +// ------------------------------------------------------------- +// Annotations useful for debugging. + +// Report the current thread `name` to a race detector. +#define ABSL_ANNOTATE_THREAD_NAME(name) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name) + +// ------------------------------------------------------------- +// Annotations useful when implementing locks. They are not normally needed by +// modules that merely use locks. The `lock` argument is a pointer to the lock +// object. + +// Report that a lock has been created at address `lock`. +#define ABSL_ANNOTATE_RWLOCK_CREATE(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock) + +// Report that a linker initialized lock has been created at address `lock`. +#ifdef ABSL_HAVE_THREAD_SANITIZER +#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \ + (__FILE__, __LINE__, lock) +#else +#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ + ABSL_ANNOTATE_RWLOCK_CREATE(lock) #endif -#if (DYNAMIC_ANNOTATIONS_ENABLED != 0) +// Report that the lock at address `lock` is about to be destroyed. +#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock) + +// Report that the lock at address `lock` has been acquired. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \ + (__FILE__, __LINE__, lock, is_w) + +// Report that the lock at address `lock` is about to be released. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \ + (__FILE__, __LINE__, lock, is_w) + +// Apply ABSL_ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`. +#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ + namespace { \ + class static_var##_annotator { \ + public: \ + static_var##_annotator() { \ + ABSL_ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \ + #static_var ": " description); \ + } \ + }; \ + static static_var##_annotator the##static_var##_annotator; \ + } // namespace + +// Function prototypes of annotations provided by the compiler-based sanitizer +// implementation. +ABSL_INTERNAL_BEGIN_EXTERN_C +void AnnotateRWLockCreate(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockCreateStatic(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockDestroy(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockAcquired(const char* file, int line, + const volatile void* lock, long is_w); // NOLINT +void AnnotateRWLockReleased(const char* file, int line, + const volatile void* lock, long is_w); // NOLINT +void AnnotateBenignRace(const char* file, int line, + const volatile void* address, const char* description); +void AnnotateBenignRaceSized(const char* file, int line, + const volatile void* address, size_t size, + const char* description); +void AnnotateThreadName(const char* file, int line, const char* name); +void AnnotateEnableRaceDetection(const char* file, int line, int enable); +ABSL_INTERNAL_END_EXTERN_C + +#else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0 + +#define ABSL_ANNOTATE_RWLOCK_CREATE(lock) // empty +#define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty +#define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) // empty +#define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty +#define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty +#define ABSL_ANNOTATE_BENIGN_RACE(address, description) // empty +#define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty +#define ABSL_ANNOTATE_THREAD_NAME(name) // empty +#define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty +#define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty + +#endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED + +// ------------------------------------------------------------------------- +// Define memory annotations. + +#ifdef ABSL_HAVE_MEMORY_SANITIZER + +#include <sanitizer/msan_interface.h> + +#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + __msan_unpoison(address, size) + +#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + __msan_allocated_memory(address, size) + +#else // !defined(ABSL_HAVE_MEMORY_SANITIZER) + +// TODO(rogeeff): remove this branch +#ifdef ABSL_HAVE_THREAD_SANITIZER +#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#else + +#define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty +#define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty + +#endif + +#endif // ABSL_HAVE_MEMORY_SANITIZER + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END attributes. + +#if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \ + __attribute((exclusive_lock_function("*"))) +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \ + __attribute((unlock_function("*"))) + +#else // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty + +#endif // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END annotations. - /* Request the analysis tool to ignore all reads in the current thread - until ANNOTATE_IGNORE_READS_END is called. - Useful to ignore intentional racey reads, while still checking - other reads and all writes. - See also ANNOTATE_UNPROTECTED_READ. */ - #define ANNOTATE_IGNORE_READS_BEGIN() \ - AnnotateIgnoreReadsBegin(__FILE__, __LINE__) +#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1 +// Some of the symbols used in this section (e.g. AnnotateIgnoreReadsBegin) are +// defined by the compiler-based implementation, not by the Abseil +// library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. - /* Stop ignoring reads. */ - #define ANNOTATE_IGNORE_READS_END() \ - AnnotateIgnoreReadsEnd(__FILE__, __LINE__) +// Request the analysis tool to ignore all reads in the current thread until +// ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey +// reads, while still checking other reads and all writes. +// See also ABSL_ANNOTATE_UNPROTECTED_READ. +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin) \ + (__FILE__, __LINE__) - /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. */ - #define ANNOTATE_IGNORE_WRITES_BEGIN() \ - AnnotateIgnoreWritesBegin(__FILE__, __LINE__) +// Stop ignoring reads. +#define ABSL_ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd) \ + (__FILE__, __LINE__) - /* Stop ignoring writes. */ - #define ANNOTATE_IGNORE_WRITES_END() \ - AnnotateIgnoreWritesEnd(__FILE__, __LINE__) +// Function prototypes of annotations provided by the compiler-based sanitizer +// implementation. +ABSL_INTERNAL_BEGIN_EXTERN_C +void AnnotateIgnoreReadsBegin(const char* file, int line) + ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE; +void AnnotateIgnoreReadsEnd(const char* file, + int line) ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE; +ABSL_INTERNAL_END_EXTERN_C -/* Clang provides limited support for static thread-safety analysis - through a feature called Annotalysis. We configure macro-definitions - according to whether Annotalysis support is available. */ -#elif defined(ANNOTALYSIS_ENABLED) +#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED) - #define ANNOTATE_IGNORE_READS_BEGIN() \ - StaticAnnotateIgnoreReadsBegin(__FILE__, __LINE__) +// When Annotalysis is enabled without Dynamic Annotations, the use of +// static-inline functions allows the annotations to be read at compile-time, +// while still letting the compiler elide the functions from the final build. +// +// TODO(delesley) -- The exclusive lock here ignores writes as well, but +// allows IGNORE_READS_AND_WRITES to work properly. - #define ANNOTATE_IGNORE_READS_END() \ - StaticAnnotateIgnoreReadsEnd(__FILE__, __LINE__) +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED( \ + ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsBegin)) \ + () - #define ANNOTATE_IGNORE_WRITES_BEGIN() \ - StaticAnnotateIgnoreWritesBegin(__FILE__, __LINE__) +#define ABSL_ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED( \ + ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsEnd)) \ + () - #define ANNOTATE_IGNORE_WRITES_END() \ - StaticAnnotateIgnoreWritesEnd(__FILE__, __LINE__) +ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( + AbslInternalAnnotateIgnoreReadsBegin)() + ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE {} + +ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( + AbslInternalAnnotateIgnoreReadsEnd)() + ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE {} #else - #define ANNOTATE_IGNORE_READS_BEGIN() /* empty */ - #define ANNOTATE_IGNORE_READS_END() /* empty */ - #define ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */ - #define ANNOTATE_IGNORE_WRITES_END() /* empty */ + +#define ABSL_ANNOTATE_IGNORE_READS_BEGIN() // empty +#define ABSL_ANNOTATE_IGNORE_READS_END() // empty + #endif -/* Implement the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more - primitive annotations defined above. */ -#if defined(ANNOTATIONS_ENABLED) +// ------------------------------------------------------------------------- +// Define IGNORE_WRITES_BEGIN/_END annotations. - /* Start ignoring all memory accesses (both reads and writes). */ - #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ - do { \ - ANNOTATE_IGNORE_READS_BEGIN(); \ - ANNOTATE_IGNORE_WRITES_BEGIN(); \ - }while (0) +#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1 - /* Stop ignoring both reads and writes. */ - #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ - do { \ - ANNOTATE_IGNORE_WRITES_END(); \ - ANNOTATE_IGNORE_READS_END(); \ - }while (0) +// Similar to ABSL_ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. +#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__) + +// Stop ignoring writes. +#define ABSL_ANNOTATE_IGNORE_WRITES_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__) + +// Function prototypes of annotations provided by the compiler-based sanitizer +// implementation. +ABSL_INTERNAL_BEGIN_EXTERN_C +void AnnotateIgnoreWritesBegin(const char* file, int line); +void AnnotateIgnoreWritesEnd(const char* file, int line); +ABSL_INTERNAL_END_EXTERN_C #else - #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */ - #define ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */ -#endif -/* Use the macros above rather than using these functions directly. */ -#include <stddef.h> -#ifdef __cplusplus -extern "C" { -#endif -void AnnotateRWLockCreate(const char *file, int line, - const volatile void *lock); -void AnnotateRWLockCreateStatic(const char *file, int line, - const volatile void *lock); -void AnnotateRWLockDestroy(const char *file, int line, - const volatile void *lock); -void AnnotateRWLockAcquired(const char *file, int line, - const volatile void *lock, long is_w); /* NOLINT */ -void AnnotateRWLockReleased(const char *file, int line, - const volatile void *lock, long is_w); /* NOLINT */ -void AnnotateBenignRace(const char *file, int line, - const volatile void *address, - const char *description); -void AnnotateBenignRaceSized(const char *file, int line, - const volatile void *address, - size_t size, - const char *description); -void AnnotateThreadName(const char *file, int line, - const char *name); -void AnnotateEnableRaceDetection(const char *file, int line, int enable); -void AnnotateMemoryIsInitialized(const char *file, int line, - const volatile void *mem, size_t size); -void AnnotateMemoryIsUninitialized(const char *file, int line, - const volatile void *mem, size_t size); - -/* Annotations expand to these functions, when Dynamic Annotations are enabled. - These functions are either implemented as no-op calls, if no Sanitizer is - attached, or provided with externally-linked implementations by a library - like ThreadSanitizer. */ -void AnnotateIgnoreReadsBegin(const char *file, int line) - ATTRIBUTE_IGNORE_READS_BEGIN; -void AnnotateIgnoreReadsEnd(const char *file, int line) - ATTRIBUTE_IGNORE_READS_END; -void AnnotateIgnoreWritesBegin(const char *file, int line); -void AnnotateIgnoreWritesEnd(const char *file, int line); - -#if defined(ANNOTALYSIS_ENABLED) -/* When Annotalysis is enabled without Dynamic Annotations, the use of - static-inline functions allows the annotations to be read at compile-time, - while still letting the compiler elide the functions from the final build. - - TODO(delesley) -- The exclusive lock here ignores writes as well, but - allows IGNORE_READS_AND_WRITES to work properly. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -static inline void StaticAnnotateIgnoreReadsBegin(const char *file, int line) - ATTRIBUTE_IGNORE_READS_BEGIN { (void)file; (void)line; } -static inline void StaticAnnotateIgnoreReadsEnd(const char *file, int line) - ATTRIBUTE_IGNORE_READS_END { (void)file; (void)line; } -static inline void StaticAnnotateIgnoreWritesBegin( - const char *file, int line) { (void)file; (void)line; } -static inline void StaticAnnotateIgnoreWritesEnd( - const char *file, int line) { (void)file; (void)line; } -#pragma GCC diagnostic pop +#define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() // empty +#define ABSL_ANNOTATE_IGNORE_WRITES_END() // empty + #endif -/* Return non-zero value if running under valgrind. - - If "valgrind.h" is included into dynamic_annotations.cc, - the regular valgrind mechanism will be used. - See http://valgrind.org/docs/manual/manual-core-adv.html about - RUNNING_ON_VALGRIND and other valgrind "client requests". - The file "valgrind.h" may be obtained by doing - svn co svn://svn.valgrind.org/valgrind/trunk/include - - If for some reason you can't use "valgrind.h" or want to fake valgrind, - there are two ways to make this function return non-zero: - - Use environment variable: export RUNNING_ON_VALGRIND=1 - - Make your tool intercept the function RunningOnValgrind() and - change its return value. - */ -int RunningOnValgrind(void); - -/* ValgrindSlowdown returns: - * 1.0, if (RunningOnValgrind() == 0) - * 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") == NULL) - * atof(getenv("VALGRIND_SLOWDOWN")) otherwise - This function can be used to scale timeout values: - EXAMPLE: - for (;;) { - DoExpensiveBackgroundTask(); - SleepForSeconds(5 * ValgrindSlowdown()); - } - */ -double ValgrindSlowdown(void); +// ------------------------------------------------------------------------- +// Define the ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more +// primitive annotations defined above. +// +// Instead of doing +// ABSL_ANNOTATE_IGNORE_READS_BEGIN(); +// ... = x; +// ABSL_ANNOTATE_IGNORE_READS_END(); +// one can use +// ... = ABSL_ANNOTATE_UNPROTECTED_READ(x); + +#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED) + +// Start ignoring all memory accesses (both reads and writes). +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ + do { \ + ABSL_ANNOTATE_IGNORE_READS_BEGIN(); \ + ABSL_ANNOTATE_IGNORE_WRITES_BEGIN(); \ + } while (0) + +// Stop ignoring both reads and writes. +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() \ + do { \ + ABSL_ANNOTATE_IGNORE_WRITES_END(); \ + ABSL_ANNOTATE_IGNORE_READS_END(); \ + } while (0) #ifdef __cplusplus -} -#endif +// ABSL_ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. +#define ABSL_ANNOTATE_UNPROTECTED_READ(x) \ + absl::base_internal::AnnotateUnprotectedRead(x) -/* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { - Instead of doing - ANNOTATE_IGNORE_READS_BEGIN(); - ... = x; - ANNOTATE_IGNORE_READS_END(); - one can use - ... = ANNOTATE_UNPROTECTED_READ(x); */ -#if defined(__cplusplus) && defined(ANNOTATIONS_ENABLED) template <typename T> -inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) { /* NOLINT */ - ANNOTATE_IGNORE_READS_BEGIN(); +inline T AnnotateUnprotectedRead(const volatile T& x) { // NOLINT + ABSL_ANNOTATE_IGNORE_READS_BEGIN(); T res = x; - ANNOTATE_IGNORE_READS_END(); + ABSL_ANNOTATE_IGNORE_READS_END(); return res; - } +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl +#endif + #else - #define ANNOTATE_UNPROTECTED_READ(x) (x) + +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty +#define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty +#define ABSL_ANNOTATE_UNPROTECTED_READ(x) (x) + #endif -#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus) - /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */ - #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ - namespace { \ - class static_var ## _annotator { \ - public: \ - static_var ## _annotator() { \ - ANNOTATE_BENIGN_RACE_SIZED(&static_var, \ - sizeof(static_var), \ - # static_var ": " description); \ - } \ - }; \ - static static_var ## _annotator the ## static_var ## _annotator;\ - } // namespace -#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ - #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */ -#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ - -#ifdef ADDRESS_SANITIZER -/* Describe the current state of a contiguous container such as e.g. - * std::vector or std::string. For more details see - * sanitizer/common_interface_defs.h, which is provided by the compiler. */ +// ------------------------------------------------------------------------- +// Address sanitizer annotations + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +// Describe the current state of a contiguous container such as e.g. +// std::vector or std::string. For more details see +// sanitizer/common_interface_defs.h, which is provided by the compiler. #include <sanitizer/common_interface_defs.h> -#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ + +#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) -#define ADDRESS_SANITIZER_REDZONE(name) \ - struct { char x[8] __attribute__ ((aligned (8))); } name +#define ABSL_ADDRESS_SANITIZER_REDZONE(name) \ + struct { \ + alignas(8) char x[8]; \ + } name + #else -#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) -#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") -#endif // ADDRESS_SANITIZER -/* Undefine the macros intended only in this file. */ -#undef ANNOTALYSIS_ENABLED -#undef ANNOTATIONS_ENABLED -#undef ATTRIBUTE_IGNORE_READS_BEGIN -#undef ATTRIBUTE_IGNORE_READS_END +#define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) // empty +#define ABSL_ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") + +#endif // ABSL_HAVE_ADDRESS_SANITIZER + +// ------------------------------------------------------------------------- +// Undefine the macros intended only for this file. + +#undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED +#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_BEGIN_EXTERN_C +#undef ABSL_INTERNAL_END_EXTERN_C +#undef ABSL_INTERNAL_STATIC_INLINE -#endif /* ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ */ +#endif // ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/bits.h b/third_party/abseil-cpp/absl/base/internal/bits.h deleted file mode 100644 index 8b03453c18..0000000000 --- a/third_party/abseil-cpp/absl/base/internal/bits.h +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. - -#ifndef ABSL_BASE_INTERNAL_BITS_H_ -#define ABSL_BASE_INTERNAL_BITS_H_ - -// This file contains bitwise ops which are implementation details of various -// absl libraries. - -#include <cstdint> - -#include "absl/base/config.h" - -// Clang on Windows has __builtin_clzll; otherwise we need to use the -// windows intrinsic functions. -#if defined(_MSC_VER) -#include <intrin.h> -#if defined(_M_X64) -#pragma intrinsic(_BitScanReverse64) -#pragma intrinsic(_BitScanForward64) -#endif -#pragma intrinsic(_BitScanReverse) -#pragma intrinsic(_BitScanForward) -#endif - -#include "absl/base/attributes.h" - -#if defined(_MSC_VER) -// We can achieve something similar to attribute((always_inline)) with MSVC by -// using the __forceinline keyword, however this is not perfect. MSVC is -// much less aggressive about inlining, and even with the __forceinline keyword. -#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline -#else -// Use default attribute inline. -#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE -#endif - - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { - int zeroes = 60; - if (n >> 32) { - zeroes -= 32; - n >>= 32; - } - if (n >> 16) { - zeroes -= 16; - n >>= 16; - } - if (n >> 8) { - zeroes -= 8; - n >>= 8; - } - if (n >> 4) { - zeroes -= 4; - n >>= 4; - } - return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { -#if defined(_MSC_VER) && defined(_M_X64) - // MSVC does not have __buitin_clzll. Use _BitScanReverse64. - unsigned long result = 0; // NOLINT(runtime/int) - if (_BitScanReverse64(&result, n)) { - return 63 - result; - } - return 64; -#elif defined(_MSC_VER) - // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse - unsigned long result = 0; // NOLINT(runtime/int) - if ((n >> 32) && _BitScanReverse(&result, n >> 32)) { - return 31 - result; - } - if (_BitScanReverse(&result, n)) { - return 63 - result; - } - return 64; -#elif defined(__GNUC__) - // Use __builtin_clzll, which uses the following instructions: - // x86: bsr - // ARM64: clz - // PPC: cntlzd - static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) - "__builtin_clzll does not take 64-bit arg"); - - // Handle 0 as a special case because __builtin_clzll(0) is undefined. - if (n == 0) { - return 64; - } - return __builtin_clzll(n); -#else - return CountLeadingZeros64Slow(n); -#endif -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { - int zeroes = 28; - if (n >> 16) { - zeroes -= 16; - n >>= 16; - } - if (n >> 8) { - zeroes -= 8; - n >>= 8; - } - if (n >> 4) { - zeroes -= 4; - n >>= 4; - } - return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { -#if defined(_MSC_VER) - unsigned long result = 0; // NOLINT(runtime/int) - if (_BitScanReverse(&result, n)) { - return 31 - result; - } - return 32; -#elif defined(__GNUC__) - // Use __builtin_clz, which uses the following instructions: - // x86: bsr - // ARM64: clz - // PPC: cntlzd - static_assert(sizeof(int) == sizeof(n), - "__builtin_clz does not take 32-bit arg"); - - // Handle 0 as a special case because __builtin_clz(0) is undefined. - if (n == 0) { - return 32; - } - return __builtin_clz(n); -#else - return CountLeadingZeros32Slow(n); -#endif -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) { - int c = 63; - n &= ~n + 1; - if (n & 0x00000000FFFFFFFF) c -= 32; - if (n & 0x0000FFFF0000FFFF) c -= 16; - if (n & 0x00FF00FF00FF00FF) c -= 8; - if (n & 0x0F0F0F0F0F0F0F0F) c -= 4; - if (n & 0x3333333333333333) c -= 2; - if (n & 0x5555555555555555) c -= 1; - return c; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { -#if defined(_MSC_VER) && defined(_M_X64) - unsigned long result = 0; // NOLINT(runtime/int) - _BitScanForward64(&result, n); - return result; -#elif defined(_MSC_VER) - unsigned long result = 0; // NOLINT(runtime/int) - if (static_cast<uint32_t>(n) == 0) { - _BitScanForward(&result, n >> 32); - return result + 32; - } - _BitScanForward(&result, n); - return result; -#elif defined(__GNUC__) - static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) - "__builtin_ctzll does not take 64-bit arg"); - return __builtin_ctzll(n); -#else - return CountTrailingZerosNonZero64Slow(n); -#endif -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) { - int c = 31; - n &= ~n + 1; - if (n & 0x0000FFFF) c -= 16; - if (n & 0x00FF00FF) c -= 8; - if (n & 0x0F0F0F0F) c -= 4; - if (n & 0x33333333) c -= 2; - if (n & 0x55555555) c -= 1; - return c; -} - -ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { -#if defined(_MSC_VER) - unsigned long result = 0; // NOLINT(runtime/int) - _BitScanForward(&result, n); - return result; -#elif defined(__GNUC__) - static_assert(sizeof(int) == sizeof(n), - "__builtin_ctz does not take 32-bit arg"); - return __builtin_ctz(n); -#else - return CountTrailingZerosNonZero32Slow(n); -#endif -} - -#undef ABSL_BASE_INTERNAL_FORCEINLINE - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_BASE_INTERNAL_BITS_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/bits_test.cc b/third_party/abseil-cpp/absl/base/internal/bits_test.cc deleted file mode 100644 index 7855fa6297..0000000000 --- a/third_party/abseil-cpp/absl/base/internal/bits_test.cc +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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/base/internal/bits.h" - -#include "gtest/gtest.h" - -namespace { - -int CLZ64(uint64_t n) { - int fast = absl::base_internal::CountLeadingZeros64(n); - int slow = absl::base_internal::CountLeadingZeros64Slow(n); - EXPECT_EQ(fast, slow) << n; - return fast; -} - -TEST(BitsTest, CountLeadingZeros64) { - EXPECT_EQ(64, CLZ64(uint64_t{})); - EXPECT_EQ(0, CLZ64(~uint64_t{})); - - for (int index = 0; index < 64; index++) { - uint64_t x = static_cast<uint64_t>(1) << index; - const auto cnt = 63 - index; - ASSERT_EQ(cnt, CLZ64(x)) << index; - ASSERT_EQ(cnt, CLZ64(x + x - 1)) << index; - } -} - -int CLZ32(uint32_t n) { - int fast = absl::base_internal::CountLeadingZeros32(n); - int slow = absl::base_internal::CountLeadingZeros32Slow(n); - EXPECT_EQ(fast, slow) << n; - return fast; -} - -TEST(BitsTest, CountLeadingZeros32) { - EXPECT_EQ(32, CLZ32(uint32_t{})); - EXPECT_EQ(0, CLZ32(~uint32_t{})); - - for (int index = 0; index < 32; index++) { - uint32_t x = static_cast<uint32_t>(1) << index; - const auto cnt = 31 - index; - ASSERT_EQ(cnt, CLZ32(x)) << index; - ASSERT_EQ(cnt, CLZ32(x + x - 1)) << index; - ASSERT_EQ(CLZ64(x), CLZ32(x) + 32); - } -} - -int CTZ64(uint64_t n) { - int fast = absl::base_internal::CountTrailingZerosNonZero64(n); - int slow = absl::base_internal::CountTrailingZerosNonZero64Slow(n); - EXPECT_EQ(fast, slow) << n; - return fast; -} - -TEST(BitsTest, CountTrailingZerosNonZero64) { - EXPECT_EQ(0, CTZ64(~uint64_t{})); - - for (int index = 0; index < 64; index++) { - uint64_t x = static_cast<uint64_t>(1) << index; - const auto cnt = index; - ASSERT_EQ(cnt, CTZ64(x)) << index; - ASSERT_EQ(cnt, CTZ64(~(x - 1))) << index; - } -} - -int CTZ32(uint32_t n) { - int fast = absl::base_internal::CountTrailingZerosNonZero32(n); - int slow = absl::base_internal::CountTrailingZerosNonZero32Slow(n); - EXPECT_EQ(fast, slow) << n; - return fast; -} - -TEST(BitsTest, CountTrailingZerosNonZero32) { - EXPECT_EQ(0, CTZ32(~uint32_t{})); - - for (int index = 0; index < 32; index++) { - uint32_t x = static_cast<uint32_t>(1) << index; - const auto cnt = index; - ASSERT_EQ(cnt, CTZ32(x)) << index; - ASSERT_EQ(cnt, CTZ32(~(x - 1))) << index; - } -} - - -} // namespace diff --git a/third_party/abseil-cpp/absl/base/internal/direct_mmap.h b/third_party/abseil-cpp/absl/base/internal/direct_mmap.h index 5618867ba0..274054cd5a 100644 --- a/third_party/abseil-cpp/absl/base/internal/direct_mmap.h +++ b/third_party/abseil-cpp/absl/base/internal/direct_mmap.h @@ -61,6 +61,10 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); #endif #endif // __BIONIC__ +#if defined(__NR_mmap2) && !defined(SYS_mmap2) +#define SYS_mmap2 __NR_mmap2 +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { @@ -70,9 +74,13 @@ namespace base_internal { inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, off64_t offset) noexcept { #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + defined(__m68k__) || defined(__sh__) || \ + (defined(__hppa__) && !defined(__LP64__)) || \ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ (defined(__PPC__) && !defined(__PPC64__)) || \ - (defined(__s390__) && !defined(__s390x__)) + (defined(__riscv) && __riscv_xlen == 32) || \ + (defined(__s390__) && !defined(__s390x__)) || \ + (defined(__sparc__) && !defined(__arch64__)) // On these architectures, implement mmap with mmap2. static int pagesize = 0; if (pagesize == 0) { diff --git a/third_party/abseil-cpp/absl/base/internal/dynamic_annotations.h b/third_party/abseil-cpp/absl/base/internal/dynamic_annotations.h new file mode 100644 index 0000000000..b23c5ec1c4 --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/dynamic_annotations.h @@ -0,0 +1,398 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +// This file defines dynamic annotations for use with dynamic analysis tool +// such as valgrind, PIN, etc. +// +// Dynamic annotation is a source code annotation that affects the generated +// code (that is, the annotation is not a comment). Each such annotation is +// attached to a particular instruction and/or to a particular object (address) +// in the program. +// +// The annotations that should be used by users are macros in all upper-case +// (e.g., ANNOTATE_THREAD_NAME). +// +// Actual implementation of these macros may differ depending on the dynamic +// analysis tool being used. +// +// This file supports the following configurations: +// - Dynamic Annotations enabled (with static thread-safety warnings disabled). +// In this case, macros expand to functions implemented by Thread Sanitizer, +// when building with TSan. When not provided an external implementation, +// dynamic_annotations.cc provides no-op implementations. +// +// - Static Clang thread-safety warnings enabled. +// When building with a Clang compiler that supports thread-safety warnings, +// a subset of annotations can be statically-checked at compile-time. We +// expand these macros to static-inline functions that can be analyzed for +// thread-safety, but afterwards elided when building the final binary. +// +// - All annotations are disabled. +// If neither Dynamic Annotations nor Clang thread-safety warnings are +// enabled, then all annotation-macros expand to empty. + +#ifndef ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ +#define ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ + +#include <stddef.h> + +#include "absl/base/config.h" + +// ------------------------------------------------------------------------- +// Decide which features are enabled + +#ifndef DYNAMIC_ANNOTATIONS_ENABLED +#define DYNAMIC_ANNOTATIONS_ENABLED 0 +#endif + +#if defined(__clang__) && !defined(SWIG) +#define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1 +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 + +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1 +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1 + +#else + +#define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0 +#define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0 + +// Clang provides limited support for static thread-safety analysis through a +// feature called Annotalysis. We configure macro-definitions according to +// whether Annotalysis support is available. When running in opt-mode, GCC +// will issue a warning, if these attributes are compiled. Only include them +// when compiling using Clang. + +// ANNOTALYSIS_ENABLED == 1 when IGNORE_READ_ATTRIBUTE_ENABLED == 1 +#define ABSL_INTERNAL_ANNOTALYSIS_ENABLED \ + defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) +// Read/write annotations are enabled in Annotalysis mode; disabled otherwise. +#define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \ + ABSL_INTERNAL_ANNOTALYSIS_ENABLED +#endif + +// Memory annotations are also made available to LLVM's Memory Sanitizer +#if defined(ABSL_HAVE_MEMORY_SANITIZER) && !defined(__native_client__) +#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 1 +#endif + +#ifndef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED +#define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 0 +#endif + +#ifdef __cplusplus +#define ABSL_INTERNAL_BEGIN_EXTERN_C extern "C" { +#define ABSL_INTERNAL_END_EXTERN_C } // extern "C" +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F +#define ABSL_INTERNAL_STATIC_INLINE inline +#else +#define ABSL_INTERNAL_BEGIN_EXTERN_C // empty +#define ABSL_INTERNAL_END_EXTERN_C // empty +#define ABSL_INTERNAL_GLOBAL_SCOPED(F) F +#define ABSL_INTERNAL_STATIC_INLINE static inline +#endif + +// ------------------------------------------------------------------------- +// Define race annotations. + +#if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1 + +// ------------------------------------------------------------- +// Annotations that suppress errors. It is usually better to express the +// program's synchronization using the other annotations, but these can be used +// when all else fails. + +// Report that we may have a benign race at `pointer`, with size +// "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the +// point where `pointer` has been allocated, preferably close to the point +// where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. +#define ANNOTATE_BENIGN_RACE(pointer, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) + +// Same as ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to +// the memory range [`address`, `address`+`size`). +#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ + (__FILE__, __LINE__, address, size, description) + +// Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads. +// This annotation could be useful if you want to skip expensive race analysis +// during some period of program execution, e.g. during initialization. +#define ANNOTATE_ENABLE_RACE_DETECTION(enable) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \ + (__FILE__, __LINE__, enable) + +// ------------------------------------------------------------- +// Annotations useful for debugging. + +// Report the current thread `name` to a race detector. +#define ANNOTATE_THREAD_NAME(name) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName)(__FILE__, __LINE__, name) + +// ------------------------------------------------------------- +// Annotations useful when implementing locks. They are not normally needed by +// modules that merely use locks. The `lock` argument is a pointer to the lock +// object. + +// Report that a lock has been created at address `lock`. +#define ANNOTATE_RWLOCK_CREATE(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate)(__FILE__, __LINE__, lock) + +// Report that a linker initialized lock has been created at address `lock`. +#ifdef ABSL_HAVE_THREAD_SANITIZER +#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \ + (__FILE__, __LINE__, lock) +#else +#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock) +#endif + +// Report that the lock at address `lock` is about to be destroyed. +#define ANNOTATE_RWLOCK_DESTROY(lock) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock) + +// Report that the lock at address `lock` has been acquired. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \ + (__FILE__, __LINE__, lock, is_w) + +// Report that the lock at address `lock` is about to be released. +// `is_w`=1 for writer lock, `is_w`=0 for reader lock. +#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \ + (__FILE__, __LINE__, lock, is_w) + +// Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`. +#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ + namespace { \ + class static_var##_annotator { \ + public: \ + static_var##_annotator() { \ + ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \ + #static_var ": " description); \ + } \ + }; \ + static static_var##_annotator the##static_var##_annotator; \ + } // namespace + +#else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0 + +#define ANNOTATE_RWLOCK_CREATE(lock) // empty +#define ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty +#define ANNOTATE_RWLOCK_DESTROY(lock) // empty +#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty +#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty +#define ANNOTATE_BENIGN_RACE(address, description) // empty +#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty +#define ANNOTATE_THREAD_NAME(name) // empty +#define ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty +#define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty + +#endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED + +// ------------------------------------------------------------------------- +// Define memory annotations. + +#if ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 1 + +#include <sanitizer/msan_interface.h> + +#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + __msan_unpoison(address, size) + +#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + __msan_allocated_memory(address, size) + +#else // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 0 + +#if DYNAMIC_ANNOTATIONS_ENABLED == 1 +#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ + do { \ + (void)(address); \ + (void)(size); \ + } while (0) +#else +#define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty +#define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty +#endif + +#endif // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END attributes. + +#if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \ + __attribute((exclusive_lock_function("*"))) +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \ + __attribute((unlock_function("*"))) + +#else // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +#define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty +#define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty + +#endif // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) + +// ------------------------------------------------------------------------- +// Define IGNORE_READS_BEGIN/_END annotations. + +#if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1 + +// Request the analysis tool to ignore all reads in the current thread until +// ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey +// reads, while still checking other reads and all writes. +// See also ANNOTATE_UNPROTECTED_READ. +#define ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__) + +// Stop ignoring reads. +#define ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__) + +#elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED) + +// When Annotalysis is enabled without Dynamic Annotations, the use of +// static-inline functions allows the annotations to be read at compile-time, +// while still letting the compiler elide the functions from the final build. +// +// TODO(delesley) -- The exclusive lock here ignores writes as well, but +// allows IGNORE_READS_AND_WRITES to work properly. + +#define ANNOTATE_IGNORE_READS_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin)() + +#define ANNOTATE_IGNORE_READS_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd)() + +#else + +#define ANNOTATE_IGNORE_READS_BEGIN() // empty +#define ANNOTATE_IGNORE_READS_END() // empty + +#endif + +// ------------------------------------------------------------------------- +// Define IGNORE_WRITES_BEGIN/_END annotations. + +#if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1 + +// Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. +#define ANNOTATE_IGNORE_WRITES_BEGIN() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__) + +// Stop ignoring writes. +#define ANNOTATE_IGNORE_WRITES_END() \ + ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__) + +#else + +#define ANNOTATE_IGNORE_WRITES_BEGIN() // empty +#define ANNOTATE_IGNORE_WRITES_END() // empty + +#endif + +// ------------------------------------------------------------------------- +// Define the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more +// primitive annotations defined above. +// +// Instead of doing +// ANNOTATE_IGNORE_READS_BEGIN(); +// ... = x; +// ANNOTATE_IGNORE_READS_END(); +// one can use +// ... = ANNOTATE_UNPROTECTED_READ(x); + +#if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED) + +// Start ignoring all memory accesses (both reads and writes). +#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ + do { \ + ANNOTATE_IGNORE_READS_BEGIN(); \ + ANNOTATE_IGNORE_WRITES_BEGIN(); \ + } while (0) + +// Stop ignoring both reads and writes. +#define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ + do { \ + ANNOTATE_IGNORE_WRITES_END(); \ + ANNOTATE_IGNORE_READS_END(); \ + } while (0) + +#ifdef __cplusplus +// ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. +#define ANNOTATE_UNPROTECTED_READ(x) \ + absl::base_internal::AnnotateUnprotectedRead(x) + +#endif + +#else + +#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty +#define ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty +#define ANNOTATE_UNPROTECTED_READ(x) (x) + +#endif + +// ------------------------------------------------------------------------- +// Address sanitizer annotations + +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +// Describe the current state of a contiguous container such as e.g. +// std::vector or std::string. For more details see +// sanitizer/common_interface_defs.h, which is provided by the compiler. +#include <sanitizer/common_interface_defs.h> + +#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ + __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) +#define ADDRESS_SANITIZER_REDZONE(name) \ + struct { \ + char x[8] __attribute__((aligned(8))); \ + } name + +#else + +#define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) +#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") + +#endif // ABSL_HAVE_ADDRESS_SANITIZER + +// ------------------------------------------------------------------------- +// Undefine the macros intended only for this file. + +#undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED +#undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED +#undef ABSL_INTERNAL_BEGIN_EXTERN_C +#undef ABSL_INTERNAL_END_EXTERN_C +#undef ABSL_INTERNAL_STATIC_INLINE + +#endif // ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/endian.h b/third_party/abseil-cpp/absl/base/internal/endian.h index 9677530e8d..dad0e9aeb0 100644 --- a/third_party/abseil-cpp/absl/base/internal/endian.h +++ b/third_party/abseil-cpp/absl/base/internal/endian.h @@ -26,6 +26,7 @@ #endif #include <cstdint> +#include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" @@ -173,6 +174,36 @@ inline constexpr bool IsLittleEndian() { return false; } #endif /* ENDIAN */ +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x))); +} + // Functions to do unaligned loads and stores in little-endian order. inline uint16_t Load16(const void *p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); @@ -233,6 +264,36 @@ inline constexpr bool IsLittleEndian() { return false; } #endif /* ENDIAN */ +inline uint8_t FromHost(uint8_t x) { return x; } +inline uint16_t FromHost(uint16_t x) { return FromHost16(x); } +inline uint32_t FromHost(uint32_t x) { return FromHost32(x); } +inline uint64_t FromHost(uint64_t x) { return FromHost64(x); } +inline uint8_t ToHost(uint8_t x) { return x; } +inline uint16_t ToHost(uint16_t x) { return ToHost16(x); } +inline uint32_t ToHost(uint32_t x) { return ToHost32(x); } +inline uint64_t ToHost(uint64_t x) { return ToHost64(x); } + +inline int8_t FromHost(int8_t x) { return x; } +inline int16_t FromHost(int16_t x) { + return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x))); +} +inline int32_t FromHost(int32_t x) { + return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x))); +} +inline int64_t FromHost(int64_t x) { + return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x))); +} +inline int8_t ToHost(int8_t x) { return x; } +inline int16_t ToHost(int16_t x) { + return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x))); +} +inline int32_t ToHost(int32_t x) { + return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x))); +} +inline int64_t ToHost(int64_t x) { + return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x))); +} + // Functions to do unaligned loads and stores in big-endian order. inline uint16_t Load16(const void *p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); diff --git a/third_party/abseil-cpp/absl/base/internal/endian_test.cc b/third_party/abseil-cpp/absl/base/internal/endian_test.cc index aa6b849690..a1691b1f82 100644 --- a/third_party/abseil-cpp/absl/base/internal/endian_test.cc +++ b/third_party/abseil-cpp/absl/base/internal/endian_test.cc @@ -54,24 +54,22 @@ const uint32_t k32ValueBE{0x67452301}; const uint16_t k16ValueBE{0x2301}; #endif -template<typename T> -std::vector<T> GenerateAllValuesForType() { - std::vector<T> result; - T next = std::numeric_limits<T>::min(); - while (true) { - result.push_back(next); - if (next == std::numeric_limits<T>::max()) { - return result; - } - ++next; +std::vector<uint16_t> GenerateAllUint16Values() { + std::vector<uint16_t> result; + result.reserve(size_t{1} << (sizeof(uint16_t) * 8)); + for (uint32_t i = std::numeric_limits<uint16_t>::min(); + i <= std::numeric_limits<uint16_t>::max(); ++i) { + result.push_back(static_cast<uint16_t>(i)); } + return result; } template<typename T> -std::vector<T> GenerateRandomIntegers(size_t numValuesToTest) { +std::vector<T> GenerateRandomIntegers(size_t num_values_to_test) { std::vector<T> result; + result.reserve(num_values_to_test); std::mt19937_64 rng(kRandomSeed); - for (size_t i = 0; i < numValuesToTest; ++i) { + for (size_t i = 0; i < num_values_to_test; ++i) { result.push_back(rng()); } return result; @@ -148,7 +146,7 @@ void Swap64(char* bytes) { } TEST(EndianessTest, Uint16) { - GBSwapHelper(GenerateAllValuesForType<uint16_t>(), &Swap16); + GBSwapHelper(GenerateAllUint16Values(), &Swap16); } TEST(EndianessTest, Uint32) { diff --git a/third_party/abseil-cpp/absl/base/internal/errno_saver_test.cc b/third_party/abseil-cpp/absl/base/internal/errno_saver_test.cc index b845e2dd1a..e9b742c588 100644 --- a/third_party/abseil-cpp/absl/base/internal/errno_saver_test.cc +++ b/third_party/abseil-cpp/absl/base/internal/errno_saver_test.cc @@ -18,6 +18,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/strerror.h" namespace { using ::testing::Eq; @@ -26,7 +27,7 @@ struct ErrnoPrinter { int no; }; std::ostream &operator<<(std::ostream &os, ErrnoPrinter ep) { - return os << strerror(ep.no) << " [" << ep.no << "]"; + return os << absl::base_internal::StrError(ep.no) << " [" << ep.no << "]"; } bool operator==(ErrnoPrinter one, ErrnoPrinter two) { return one.no == two.no; } diff --git a/third_party/abseil-cpp/absl/base/internal/exception_safety_testing.h b/third_party/abseil-cpp/absl/base/internal/exception_safety_testing.h index 6ba89d05df..77a5aec642 100644 --- a/third_party/abseil-cpp/absl/base/internal/exception_safety_testing.h +++ b/third_party/abseil-cpp/absl/base/internal/exception_safety_testing.h @@ -536,7 +536,22 @@ class ThrowingValue : private exceptions_internal::TrackedObject { } // Memory management operators - // Args.. allows us to overload regular and placement new in one shot + static void* operator new(size_t s) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new(s); + } + + static void* operator new[](size_t s) noexcept( + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); + } + return ::operator new[](s); + } + template <typename... Args> static void* operator new(size_t s, Args&&... args) noexcept( IsSpecified(TypeSpec::kNoThrowNew)) { @@ -557,12 +572,6 @@ class ThrowingValue : private exceptions_internal::TrackedObject { // Abseil doesn't support throwing overloaded operator delete. These are // provided so a throwing operator-new can clean up after itself. - // - // We provide both regular and templated operator delete because if only the - // templated version is provided as we did with operator new, the compiler has - // no way of knowing which overload of operator delete to call. See - // https://en.cppreference.com/w/cpp/memory/new/operator_delete and - // https://en.cppreference.com/w/cpp/language/delete for the gory details. void operator delete(void* p) noexcept { ::operator delete(p); } template <typename... Args> @@ -726,9 +735,8 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { ThrowingAllocator select_on_container_copy_construction() noexcept( IsSpecified(AllocSpec::kNoThrowAllocate)) { - auto& out = *this; ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); - return out; + return *this; } template <typename U> diff --git a/third_party/abseil-cpp/absl/base/internal/fast_type_id.h b/third_party/abseil-cpp/absl/base/internal/fast_type_id.h new file mode 100644 index 0000000000..3db59e8374 --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/fast_type_id.h @@ -0,0 +1,48 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// + +#ifndef ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ +#define ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +template <typename Type> +struct FastTypeTag { + constexpr static char dummy_var = 0; +}; + +template <typename Type> +constexpr char FastTypeTag<Type>::dummy_var; + +// FastTypeId<Type>() evaluates at compile/link-time to a unique pointer for the +// passed-in type. These are meant to be good match for keys into maps or +// straight up comparisons. +using FastTypeIdType = const void*; + +template <typename Type> +constexpr inline FastTypeIdType FastTypeId() { + return &FastTypeTag<Type>::dummy_var; +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/fast_type_id_test.cc b/third_party/abseil-cpp/absl/base/internal/fast_type_id_test.cc new file mode 100644 index 0000000000..16f3c1458b --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/fast_type_id_test.cc @@ -0,0 +1,123 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/base/internal/fast_type_id.h" + +#include <cstdint> +#include <map> +#include <vector> + +#include "gtest/gtest.h" + +namespace { +namespace bi = absl::base_internal; + +// NOLINTNEXTLINE +#define PRIM_TYPES(A) \ + A(bool) \ + A(short) \ + A(unsigned short) \ + A(int) \ + A(unsigned int) \ + A(long) \ + A(unsigned long) \ + A(long long) \ + A(unsigned long long) \ + A(float) \ + A(double) \ + A(long double) + +TEST(FastTypeIdTest, PrimitiveTypes) { + bi::FastTypeIdType type_ids[] = { +#define A(T) bi::FastTypeId<T>(), + PRIM_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const T>(), + PRIM_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<volatile T>(), + PRIM_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const volatile T>(), + PRIM_TYPES(A) +#undef A + }; + size_t total_type_ids = sizeof(type_ids) / sizeof(bi::FastTypeIdType); + + for (int i = 0; i < total_type_ids; ++i) { + EXPECT_EQ(type_ids[i], type_ids[i]); + for (int j = 0; j < i; ++j) { + EXPECT_NE(type_ids[i], type_ids[j]); + } + } +} + +#define FIXED_WIDTH_TYPES(A) \ + A(int8_t) \ + A(uint8_t) \ + A(int16_t) \ + A(uint16_t) \ + A(int32_t) \ + A(uint32_t) \ + A(int64_t) \ + A(uint64_t) + +TEST(FastTypeIdTest, FixedWidthTypes) { + bi::FastTypeIdType type_ids[] = { +#define A(T) bi::FastTypeId<T>(), + FIXED_WIDTH_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const T>(), + FIXED_WIDTH_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<volatile T>(), + FIXED_WIDTH_TYPES(A) +#undef A +#define A(T) bi::FastTypeId<const volatile T>(), + FIXED_WIDTH_TYPES(A) +#undef A + }; + size_t total_type_ids = sizeof(type_ids) / sizeof(bi::FastTypeIdType); + + for (int i = 0; i < total_type_ids; ++i) { + EXPECT_EQ(type_ids[i], type_ids[i]); + for (int j = 0; j < i; ++j) { + EXPECT_NE(type_ids[i], type_ids[j]); + } + } +} + +TEST(FastTypeIdTest, AliasTypes) { + using int_alias = int; + EXPECT_EQ(bi::FastTypeId<int_alias>(), bi::FastTypeId<int>()); +} + +TEST(FastTypeIdTest, TemplateSpecializations) { + EXPECT_NE(bi::FastTypeId<std::vector<int>>(), + bi::FastTypeId<std::vector<long>>()); + + EXPECT_NE((bi::FastTypeId<std::map<int, float>>()), + (bi::FastTypeId<std::map<int, double>>())); +} + +struct Base {}; +struct Derived : Base {}; +struct PDerived : private Base {}; + +TEST(FastTypeIdTest, Inheritance) { + EXPECT_NE(bi::FastTypeId<Base>(), bi::FastTypeId<Derived>()); + EXPECT_NE(bi::FastTypeId<Base>(), bi::FastTypeId<PDerived>()); +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/base/internal/invoke.h b/third_party/abseil-cpp/absl/base/internal/invoke.h index c4eceebd7c..5c71f32823 100644 --- a/third_party/abseil-cpp/absl/base/internal/invoke.h +++ b/third_party/abseil-cpp/absl/base/internal/invoke.h @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// absl::base_internal::Invoke(f, args...) is an implementation of +// absl::base_internal::invoke(f, args...) is an implementation of // INVOKE(f, args...) from section [func.require] of the C++ standard. // // [func.require] @@ -29,7 +29,7 @@ // is not one of the types described in the previous item; // 5. f(t1, t2, ..., tN) in all other cases. // -// The implementation is SFINAE-friendly: substitution failure within Invoke() +// The implementation is SFINAE-friendly: substitution failure within invoke() // isn't an error. #ifndef ABSL_BASE_INTERNAL_INVOKE_H_ @@ -170,13 +170,13 @@ struct Invoker { // The result type of Invoke<F, Args...>. template <typename F, typename... Args> -using InvokeT = decltype(Invoker<F, Args...>::type::Invoke( +using invoke_result_t = decltype(Invoker<F, Args...>::type::Invoke( std::declval<F>(), std::declval<Args>()...)); // Invoke(f, args...) is an implementation of INVOKE(f, args...) from section // [func.require] of the C++ standard. template <typename F, typename... Args> -InvokeT<F, Args...> Invoke(F&& f, Args&&... args) { +invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) { return Invoker<F, Args...>::type::Invoke(std::forward<F>(f), std::forward<Args>(args)...); } diff --git a/third_party/abseil-cpp/absl/base/internal/low_level_alloc.cc b/third_party/abseil-cpp/absl/base/internal/low_level_alloc.cc index 1bf94438d6..229ab9162d 100644 --- a/third_party/abseil-cpp/absl/base/internal/low_level_alloc.cc +++ b/third_party/abseil-cpp/absl/base/internal/low_level_alloc.cc @@ -598,7 +598,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { section.Leave(); result = &s->levels; } - ANNOTATE_MEMORY_IS_UNINITIALIZED(result, request); + ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(result, request); return result; } diff --git a/third_party/abseil-cpp/absl/base/internal/low_level_alloc_test.cc b/third_party/abseil-cpp/absl/base/internal/low_level_alloc_test.cc index 7abbbf9c59..31abb888a6 100644 --- a/third_party/abseil-cpp/absl/base/internal/low_level_alloc_test.cc +++ b/third_party/abseil-cpp/absl/base/internal/low_level_alloc_test.cc @@ -21,6 +21,12 @@ #include <unordered_map> #include <utility> +#ifdef __EMSCRIPTEN__ +#include <emscripten.h> +#endif + +#include "absl/container/node_hash_map.h" + namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { @@ -75,7 +81,7 @@ static bool using_low_level_alloc = false; // allocations and deallocations are reported via the MallocHook // interface. static void Test(bool use_new_arena, bool call_malloc_hook, int n) { - typedef std::unordered_map<int, BlockDesc> AllocMap; + typedef absl::node_hash_map<int, BlockDesc> AllocMap; AllocMap allocated; AllocMap::iterator it; BlockDesc block_desc; @@ -156,5 +162,20 @@ ABSL_NAMESPACE_END int main(int argc, char *argv[]) { // The actual test runs in the global constructor of `before_main`. printf("PASS\n"); +#ifdef __EMSCRIPTEN__ + // clang-format off +// This is JS here. Don't try to format it. + MAIN_THREAD_EM_ASM({ + if (ENVIRONMENT_IS_WEB) { + if (typeof TEST_FINISH === 'function') { + TEST_FINISH($0); + } else { + console.error('Attempted to exit with status ' + $0); + console.error('But TEST_FINSIHED is not a function.'); + } + } + }, 0); +// clang-format on +#endif return 0; } diff --git a/third_party/abseil-cpp/absl/base/internal/low_level_scheduling.h b/third_party/abseil-cpp/absl/base/internal/low_level_scheduling.h index 961cc981b8..9baccc0659 100644 --- a/third_party/abseil-cpp/absl/base/internal/low_level_scheduling.h +++ b/third_party/abseil-cpp/absl/base/internal/low_level_scheduling.h @@ -18,6 +18,7 @@ #ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ #define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ +#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scheduling_mode.h" #include "absl/base/macros.h" @@ -29,6 +30,13 @@ extern "C" void __google_enable_rescheduling(bool disable_result); namespace absl { ABSL_NAMESPACE_BEGIN +class CondVar; +class Mutex; + +namespace synchronization_internal { +int MutexDelay(int32_t c, int mode); +} // namespace synchronization_internal + namespace base_internal { class SchedulingHelper; // To allow use of SchedulingGuard. @@ -53,6 +61,8 @@ class SchedulingGuard { public: // Returns true iff the calling thread may be cooperatively rescheduled. static bool ReschedulingIsAllowed(); + SchedulingGuard(const SchedulingGuard&) = delete; + SchedulingGuard& operator=(const SchedulingGuard&) = delete; private: // Disable cooperative rescheduling of the calling thread. It may still @@ -76,12 +86,23 @@ class SchedulingGuard { bool disabled; }; - // Access to SchedulingGuard is explicitly white-listed. + // A scoped helper to enable rescheduling temporarily. + // REQUIRES: destructor must run in same thread as constructor. + class ScopedEnable { + public: + ScopedEnable(); + ~ScopedEnable(); + + private: + int scheduling_disabled_depth_; + }; + + // Access to SchedulingGuard is explicitly permitted. + friend class absl::CondVar; + friend class absl::Mutex; friend class SchedulingHelper; friend class SpinLock; - - SchedulingGuard(const SchedulingGuard&) = delete; - SchedulingGuard& operator=(const SchedulingGuard&) = delete; + friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode); }; //------------------------------------------------------------------------------ @@ -100,6 +121,12 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { return; } +inline SchedulingGuard::ScopedEnable::ScopedEnable() + : scheduling_disabled_depth_(0) {} +inline SchedulingGuard::ScopedEnable::~ScopedEnable() { + ABSL_RAW_CHECK(scheduling_disabled_depth_ == 0, "disable unused warning"); +} + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/raw_logging.cc b/third_party/abseil-cpp/absl/base/internal/raw_logging.cc index 40cea55061..074e026adb 100644 --- a/third_party/abseil-cpp/absl/base/internal/raw_logging.cc +++ b/third_party/abseil-cpp/absl/base/internal/raw_logging.cc @@ -67,28 +67,32 @@ #undef ABSL_HAVE_RAW_IO #endif +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace raw_logging_internal { +namespace { + // TODO(gfalcon): We want raw-logging to work on as many platforms as possible. -// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a -// whitelisted set of platforms for which we expect not to be able to raw log. +// Explicitly `#error` out when not `ABSL_LOW_LEVEL_WRITE_SUPPORTED`, except for +// a selected set of platforms for which we expect not to be able to raw log. -ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook< - absl::raw_logging_internal::LogPrefixHook> - log_prefix_hook; -ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook< - absl::raw_logging_internal::AbortHook> - abort_hook; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES + absl::base_internal::AtomicHook<LogPrefixHook> + log_prefix_hook; +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES + absl::base_internal::AtomicHook<AbortHook> + abort_hook; #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED -static const char kTruncated[] = " ... (message truncated)\n"; +constexpr char kTruncated[] = " ... (message truncated)\n"; // sprintf the format to the buffer, adjusting *buf and *size to reflect the // consumed bytes, and return whether the message fit without truncation. If // truncation occurred, if possible leave room in the buffer for the message // kTruncated[]. -inline static bool VADoRawLog(char** buf, int* size, const char* format, - va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0); -inline static bool VADoRawLog(char** buf, int* size, - const char* format, va_list ap) { +bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) + ABSL_PRINTF_ATTRIBUTE(3, 0); +bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) { int n = vsnprintf(*buf, *size, format, ap); bool result = true; if (n < 0 || n > *size) { @@ -96,7 +100,7 @@ inline static bool VADoRawLog(char** buf, int* size, if (static_cast<size_t>(*size) > sizeof(kTruncated)) { n = *size - sizeof(kTruncated); // room for truncation message } else { - n = 0; // no room for truncation message + n = 0; // no room for truncation message } } *size -= n; @@ -105,9 +109,7 @@ inline static bool VADoRawLog(char** buf, int* size, } #endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED -static constexpr int kLogBufSize = 3000; - -namespace { +constexpr int kLogBufSize = 3000; // CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths // that invoke malloc() and getenv() that might acquire some locks. @@ -166,7 +168,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } else { DoRawLog(&buf, &size, "%s", kTruncated); } - absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer)); + SafeWriteToStderr(buffer, strlen(buffer)); } #else static_cast<void>(format); @@ -181,11 +183,18 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } } +// Non-formatting version of RawLog(). +// +// TODO(gfalcon): When string_view no longer depends on base, change this +// interface to take its message as a string_view instead. +void DefaultInternalLog(absl::LogSeverity severity, const char* file, int line, + const std::string& message) { + RawLog(severity, file, line, "%.*s", static_cast<int>(message.size()), + message.data()); +} + } // namespace -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace raw_logging_internal { void SafeWriteToStderr(const char *s, size_t len) { #if defined(ABSL_HAVE_SYSCALL_WRITE) syscall(SYS_write, STDERR_FILENO, s, len); @@ -201,8 +210,6 @@ void SafeWriteToStderr(const char *s, size_t len) { } void RawLog(absl::LogSeverity severity, const char* file, int line, - const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); -void RawLog(absl::LogSeverity severity, const char* file, int line, const char* format, ...) { va_list ap; va_start(ap, format); @@ -210,15 +217,6 @@ void RawLog(absl::LogSeverity severity, const char* file, int line, va_end(ap); } -// Non-formatting version of RawLog(). -// -// TODO(gfalcon): When string_view no longer depends on base, change this -// interface to take its message as a string_view instead. -static void DefaultInternalLog(absl::LogSeverity severity, const char* file, - int line, const std::string& message) { - RawLog(severity, file, line, "%s", message.c_str()); -} - bool RawLoggingFullySupported() { #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED return true; @@ -227,10 +225,14 @@ bool RawLoggingFullySupported() { #endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED } -ABSL_DLL ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL absl::base_internal::AtomicHook<InternalLogFunction> internal_log_function(DefaultInternalLog); +void RegisterLogPrefixHook(LogPrefixHook func) { log_prefix_hook.Store(func); } + +void RegisterAbortHook(AbortHook func) { abort_hook.Store(func); } + void RegisterInternalLogFunction(InternalLogFunction func) { internal_log_function.Store(func); } diff --git a/third_party/abseil-cpp/absl/base/internal/raw_logging.h b/third_party/abseil-cpp/absl/base/internal/raw_logging.h index 418d6c856f..2bf7aabac1 100644 --- a/third_party/abseil-cpp/absl/base/internal/raw_logging.h +++ b/third_party/abseil-cpp/absl/base/internal/raw_logging.h @@ -72,10 +72,14 @@ // // The API is a subset of the above: each macro only takes two arguments. Use // StrCat if you need to build a richer message. -#define ABSL_INTERNAL_LOG(severity, message) \ - do { \ - ::absl::raw_logging_internal::internal_log_function( \ - ABSL_RAW_LOGGING_INTERNAL_##severity, __FILE__, __LINE__, message); \ +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + constexpr const char* absl_raw_logging_internal_filename = __FILE__; \ + ::absl::raw_logging_internal::internal_log_function( \ + ABSL_RAW_LOGGING_INTERNAL_##severity, \ + absl_raw_logging_internal_filename, __LINE__, message); \ + if (ABSL_RAW_LOGGING_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \ + ABSL_INTERNAL_UNREACHABLE; \ } while (0) #define ABSL_INTERNAL_CHECK(condition, message) \ @@ -170,10 +174,18 @@ using InternalLogFunction = void (*)(absl::LogSeverity severity, const char* file, int line, const std::string& message); -ABSL_DLL ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES extern base_internal::AtomicHook< +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook< InternalLogFunction> internal_log_function; +// Registers hooks of the above types. Only a single hook of each type may be +// registered. It is an error to call these functions multiple times with +// different input arguments. +// +// These functions are safe to call at any point during initialization; they do +// not block or malloc, and are async-signal safe. +void RegisterLogPrefixHook(LogPrefixHook func); +void RegisterAbortHook(AbortHook func); void RegisterInternalLogFunction(InternalLogFunction func); } // namespace raw_logging_internal diff --git a/third_party/abseil-cpp/absl/base/internal/spinlock.cc b/third_party/abseil-cpp/absl/base/internal/spinlock.cc index 830d4729af..35c0696a34 100644 --- a/third_party/abseil-cpp/absl/base/internal/spinlock.cc +++ b/third_party/abseil-cpp/absl/base/internal/spinlock.cc @@ -66,35 +66,19 @@ void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock, submit_profile_data.Store(fn); } +// Static member variable definitions. +constexpr uint32_t SpinLock::kSpinLockHeld; +constexpr uint32_t SpinLock::kSpinLockCooperative; +constexpr uint32_t SpinLock::kSpinLockDisabledScheduling; +constexpr uint32_t SpinLock::kSpinLockSleeper; +constexpr uint32_t SpinLock::kWaitTimeMask; + // Uncommon constructors. SpinLock::SpinLock(base_internal::SchedulingMode mode) : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) { ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); } -SpinLock::SpinLock(base_internal::LinkerInitialized, - base_internal::SchedulingMode mode) { - ABSL_TSAN_MUTEX_CREATE(this, 0); - if (IsCooperative(mode)) { - InitLinkerInitializedAndCooperative(); - } - // Otherwise, lockword_ is already initialized. -} - -// Static (linker initialized) spinlocks always start life as functional -// non-cooperative locks. When their static constructor does run, it will call -// this initializer to augment the lockword with the cooperative bit. By -// actually taking the lock when we do this we avoid the need for an atomic -// operation in the regular unlock path. -// -// SlowLock() must be careful to re-test for this bit so that any outstanding -// waiters may be upgraded to cooperative status. -void SpinLock::InitLinkerInitializedAndCooperative() { - Lock(); - lockword_.fetch_or(kSpinLockCooperative, std::memory_order_relaxed); - Unlock(); -} - // Monitor the lock to see if its value changes within some time period // (adaptive_spin_count loop iterations). The last value read from the lock // is returned from the method. @@ -121,6 +105,14 @@ void SpinLock::SlowLock() { if ((lock_value & kSpinLockHeld) == 0) { return; } + + base_internal::SchedulingMode scheduling_mode; + if ((lock_value & kSpinLockCooperative) != 0) { + scheduling_mode = base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; + } else { + scheduling_mode = base_internal::SCHEDULE_KERNEL_ONLY; + } + // The lock was not obtained initially, so this thread needs to wait for // it. Record the current timestamp in the local variable wait_start_time // so the total wait time can be stored in the lockword once this thread @@ -133,8 +125,9 @@ void SpinLock::SlowLock() { // it as having a sleeper. if ((lock_value & kWaitTimeMask) == 0) { // Here, just "mark" that the thread is going to sleep. Don't store the - // lock wait time in the lock as that will cause the current lock - // owner to think it experienced contention. + // lock wait time in the lock -- the lock word stores the amount of time + // that the current holder waited before acquiring the lock, not the wait + // time of any thread currently waiting to acquire it. if (lockword_.compare_exchange_strong( lock_value, lock_value | kSpinLockSleeper, std::memory_order_relaxed, std::memory_order_relaxed)) { @@ -148,15 +141,17 @@ void SpinLock::SlowLock() { // this thread obtains the lock. lock_value = TryLockInternal(lock_value, wait_cycles); continue; // Skip the delay at the end of the loop. + } else if ((lock_value & kWaitTimeMask) == 0) { + // The lock is still held, without a waiter being marked, but something + // else about the lock word changed, causing our CAS to fail. For + // example, a new lock holder may have acquired the lock with + // kSpinLockDisabledScheduling set, whereas the previous holder had not + // set that flag. In this case, attempt again to mark ourselves as a + // waiter. + continue; } } - base_internal::SchedulingMode scheduling_mode; - if ((lock_value & kSpinLockCooperative) != 0) { - scheduling_mode = base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; - } else { - scheduling_mode = base_internal::SCHEDULE_KERNEL_ONLY; - } // SpinLockDelay() calls into fiber scheduler, we need to see // synchronization there to avoid false positives. ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); @@ -190,30 +185,32 @@ void SpinLock::SlowUnlock(uint32_t lock_value) { // We use the upper 29 bits of the lock word to store the time spent waiting to // acquire this lock. This is reported by contentionz profiling. Since the // lower bits of the cycle counter wrap very quickly on high-frequency -// processors we divide to reduce the granularity to 2^PROFILE_TIMESTAMP_SHIFT +// processors we divide to reduce the granularity to 2^kProfileTimestampShift // sized units. On a 4Ghz machine this will lose track of wait times greater // than (2^29/4 Ghz)*128 =~ 17.2 seconds. Such waits should be extremely rare. -enum { PROFILE_TIMESTAMP_SHIFT = 7 }; -enum { LOCKWORD_RESERVED_SHIFT = 3 }; // We currently reserve the lower 3 bits. +static constexpr int kProfileTimestampShift = 7; + +// We currently reserve the lower 3 bits. +static constexpr int kLockwordReservedShift = 3; uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time, int64_t wait_end_time) { static const int64_t kMaxWaitTime = - std::numeric_limits<uint32_t>::max() >> LOCKWORD_RESERVED_SHIFT; + std::numeric_limits<uint32_t>::max() >> kLockwordReservedShift; int64_t scaled_wait_time = - (wait_end_time - wait_start_time) >> PROFILE_TIMESTAMP_SHIFT; + (wait_end_time - wait_start_time) >> kProfileTimestampShift; // Return a representation of the time spent waiting that can be stored in // the lock word's upper bits. uint32_t clamped = static_cast<uint32_t>( - std::min(scaled_wait_time, kMaxWaitTime) << LOCKWORD_RESERVED_SHIFT); + std::min(scaled_wait_time, kMaxWaitTime) << kLockwordReservedShift); if (clamped == 0) { return kSpinLockSleeper; // Just wake waiters, but don't record contention. } // Bump up value if necessary to avoid returning kSpinLockSleeper. const uint32_t kMinWaitTime = - kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT); + kSpinLockSleeper + (1 << kLockwordReservedShift); if (clamped == kSpinLockSleeper) { return kMinWaitTime; } @@ -224,8 +221,7 @@ uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { // Cast to uint32_t first to ensure bits [63:32] are cleared. const uint64_t scaled_wait_time = static_cast<uint32_t>(lock_value & kWaitTimeMask); - return scaled_wait_time - << (PROFILE_TIMESTAMP_SHIFT - LOCKWORD_RESERVED_SHIFT); + return scaled_wait_time << (kProfileTimestampShift - kLockwordReservedShift); } } // namespace base_internal diff --git a/third_party/abseil-cpp/absl/base/internal/spinlock.h b/third_party/abseil-cpp/absl/base/internal/spinlock.h index 24e2e9a6f8..ac40daff12 100644 --- a/third_party/abseil-cpp/absl/base/internal/spinlock.h +++ b/third_party/abseil-cpp/absl/base/internal/spinlock.h @@ -15,17 +15,16 @@ // // Most users requiring mutual exclusion should use Mutex. -// SpinLock is provided for use in three situations: -// - for use in code that Mutex itself depends on -// - to get a faster fast-path release under low contention (without an -// atomic read-modify-write) In return, SpinLock has worse behaviour under -// contention, which is why Mutex is preferred in most situations. +// SpinLock is provided for use in two situations: +// - for use by Abseil internal code that Mutex itself depends on // - for async signal safety (see below) // SpinLock is async signal safe. If a spinlock is used within a signal // handler, all code that acquires the lock must ensure that the signal cannot // arrive while they are holding the lock. Typically, this is done by blocking // the signal. +// +// Threads waiting on a SpinLock may be woken in an arbitrary order. #ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_ #define ABSL_BASE_INTERNAL_SPINLOCK_H_ @@ -36,6 +35,7 @@ #include <atomic> #include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/internal/low_level_scheduling.h" #include "absl/base/internal/raw_logging.h" @@ -55,29 +55,22 @@ class ABSL_LOCKABLE SpinLock { ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); } - // Special constructor for use with static SpinLock objects. E.g., - // - // static SpinLock lock(base_internal::kLinkerInitialized); - // - // When initialized using this constructor, we depend on the fact - // that the linker has already initialized the memory appropriately. The lock - // is initialized in non-cooperative mode. - // - // A SpinLock constructed like this can be freely used from global - // initializers without worrying about the order in which global - // initializers run. - explicit SpinLock(base_internal::LinkerInitialized) { - // Does nothing; lockword_ is already initialized - ABSL_TSAN_MUTEX_CREATE(this, 0); - } - // Constructors that allow non-cooperative spinlocks to be created for use // inside thread schedulers. Normal clients should not use these. explicit SpinLock(base_internal::SchedulingMode mode); - SpinLock(base_internal::LinkerInitialized, - base_internal::SchedulingMode mode); + // Constructor for global SpinLock instances. See absl/base/const_init.h. + constexpr SpinLock(absl::ConstInitType, base_internal::SchedulingMode mode) + : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {} + + // For global SpinLock instances prefer trivial destructor when possible. + // Default but non-trivial destructor in some build configurations causes an + // extra static initializer. +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE ~SpinLock() { ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); } +#else + ~SpinLock() = default; +#endif // Acquire this SpinLock. inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { @@ -146,14 +139,27 @@ class ABSL_LOCKABLE SpinLock { // // bit[0] encodes whether a lock is being held. // bit[1] encodes whether a lock uses cooperative scheduling. - // bit[2] encodes whether a lock disables scheduling. + // bit[2] encodes whether the current lock holder disabled scheduling when + // acquiring the lock. Only set when kSpinLockHeld is also set. // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int. - enum { kSpinLockHeld = 1 }; - enum { kSpinLockCooperative = 2 }; - enum { kSpinLockDisabledScheduling = 4 }; - enum { kSpinLockSleeper = 8 }; - enum { kWaitTimeMask = // Includes kSpinLockSleeper. - ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling) }; + // This is set by the lock holder to indicate how long it waited on + // the lock before eventually acquiring it. The number of cycles is + // encoded as a 29-bit unsigned int, or in the case that the current + // holder did not wait but another waiter is queued, the LSB + // (kSpinLockSleeper) is set. The implementation does not explicitly + // track the number of queued waiters beyond this. It must always be + // assumed that waiters may exist if the current holder was required to + // queue. + // + // Invariant: if the lock is not held, the value is either 0 or + // kSpinLockCooperative. + static constexpr uint32_t kSpinLockHeld = 1; + static constexpr uint32_t kSpinLockCooperative = 2; + static constexpr uint32_t kSpinLockDisabledScheduling = 4; + static constexpr uint32_t kSpinLockSleeper = 8; + // Includes kSpinLockSleeper. + static constexpr uint32_t kWaitTimeMask = + ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling); // Returns true if the provided scheduling mode is cooperative. static constexpr bool IsCooperative( @@ -162,7 +168,6 @@ class ABSL_LOCKABLE SpinLock { } uint32_t TryLockInternal(uint32_t lock_value, uint32_t wait_cycles); - void InitLinkerInitializedAndCooperative(); void SlowLock() ABSL_ATTRIBUTE_COLD; void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD; uint32_t SpinLoop(); diff --git a/third_party/abseil-cpp/absl/base/internal/spinlock_akaros.inc b/third_party/abseil-cpp/absl/base/internal/spinlock_akaros.inc index bc468940fc..7b0cada4f1 100644 --- a/third_party/abseil-cpp/absl/base/internal/spinlock_akaros.inc +++ b/third_party/abseil-cpp/absl/base/internal/spinlock_akaros.inc @@ -20,7 +20,7 @@ extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int /* loop */, absl::base_internal::SchedulingMode /* mode */) { // In Akaros, one must take care not to call anything that could cause a @@ -29,7 +29,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( // arbitrary code. } -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( std::atomic<uint32_t>* /* lock_word */, bool /* all */) {} } // extern "C" diff --git a/third_party/abseil-cpp/absl/base/internal/spinlock_linux.inc b/third_party/abseil-cpp/absl/base/internal/spinlock_linux.inc index 323edd62f4..202f7cdfc8 100644 --- a/third_party/abseil-cpp/absl/base/internal/spinlock_linux.inc +++ b/third_party/abseil-cpp/absl/base/internal/spinlock_linux.inc @@ -46,9 +46,17 @@ static_assert(sizeof(std::atomic<uint32_t>) == sizeof(int), #endif #endif +#if defined(__NR_futex_time64) && !defined(SYS_futex_time64) +#define SYS_futex_time64 __NR_futex_time64 +#endif + +#if defined(SYS_futex_time64) && !defined(SYS_futex) +#define SYS_futex SYS_futex_time64 +#endif + extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( std::atomic<uint32_t> *w, uint32_t value, int loop, absl::base_internal::SchedulingMode) { absl::base_internal::ErrnoSaver errno_saver; @@ -58,8 +66,8 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); } -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w, - bool all) { +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic<uint32_t> *w, bool all) { syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0); } diff --git a/third_party/abseil-cpp/absl/base/internal/spinlock_posix.inc b/third_party/abseil-cpp/absl/base/internal/spinlock_posix.inc index fcd21b151b..4f6f887d99 100644 --- a/third_party/abseil-cpp/absl/base/internal/spinlock_posix.inc +++ b/third_party/abseil-cpp/absl/base/internal/spinlock_posix.inc @@ -25,7 +25,7 @@ extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop, absl::base_internal::SchedulingMode /* mode */) { absl::base_internal::ErrnoSaver errno_saver; @@ -40,7 +40,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( } } -ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( std::atomic<uint32_t>* /* lock_word */, bool /* all */) {} } // extern "C" diff --git a/third_party/abseil-cpp/absl/base/internal/spinlock_wait.h b/third_party/abseil-cpp/absl/base/internal/spinlock_wait.h index 169bc749fb..9a1adcda5e 100644 --- a/third_party/abseil-cpp/absl/base/internal/spinlock_wait.h +++ b/third_party/abseil-cpp/absl/base/internal/spinlock_wait.h @@ -39,22 +39,22 @@ struct SpinLockWaitTransition { // satisfying 0<=i<n && trans[i].done, atomically make the transition, // then return the old value of *w. Make any other atomic transitions // where !trans[i].done, but continue waiting. +// +// Wakeups for threads blocked on SpinLockWait do not respect priorities. uint32_t SpinLockWait(std::atomic<uint32_t> *w, int n, const SpinLockWaitTransition trans[], SchedulingMode scheduling_mode); -// If possible, wake some thread that has called SpinLockDelay(w, ...). If -// "all" is true, wake all such threads. This call is a hint, and on some -// systems it may be a no-op; threads calling SpinLockDelay() will always wake -// eventually even if SpinLockWake() is never called. +// If possible, wake some thread that has called SpinLockDelay(w, ...). If `all` +// is true, wake all such threads. On some systems, this may be a no-op; on +// those systems, threads calling SpinLockDelay() will always wake eventually +// even if SpinLockWake() is never called. void SpinLockWake(std::atomic<uint32_t> *w, bool all); // Wait for an appropriate spin delay on iteration "loop" of a // spin loop on location *w, whose previously observed value was "value". // SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick, -// or may wait for a delay that can be truncated by a call to SpinLockWake(w). -// In all cases, it must return in bounded time even if SpinLockWake() is not -// called. +// or may wait for a call to SpinLockWake(w). void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop, base_internal::SchedulingMode scheduling_mode); @@ -73,21 +73,23 @@ ABSL_NAMESPACE_END // By changing our extension points to be extern "C", we dodge this // check. extern "C" { -void AbslInternalSpinLockWake(std::atomic<uint32_t> *w, bool all); -void AbslInternalSpinLockDelay( +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(std::atomic<uint32_t> *w, + bool all); +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( std::atomic<uint32_t> *w, uint32_t value, int loop, absl::base_internal::SchedulingMode scheduling_mode); } inline void absl::base_internal::SpinLockWake(std::atomic<uint32_t> *w, bool all) { - AbslInternalSpinLockWake(w, all); + ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(w, all); } inline void absl::base_internal::SpinLockDelay( std::atomic<uint32_t> *w, uint32_t value, int loop, absl::base_internal::SchedulingMode scheduling_mode) { - AbslInternalSpinLockDelay(w, value, loop, scheduling_mode); + ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay) + (w, value, loop, scheduling_mode); } #endif // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/spinlock_win32.inc b/third_party/abseil-cpp/absl/base/internal/spinlock_win32.inc index 78654b5b59..9d224813a5 100644 --- a/third_party/abseil-cpp/absl/base/internal/spinlock_win32.inc +++ b/third_party/abseil-cpp/absl/base/internal/spinlock_win32.inc @@ -20,9 +20,9 @@ extern "C" { -void AbslInternalSpinLockDelay(std::atomic<uint32_t>* /* lock_word */, - uint32_t /* value */, int loop, - absl::base_internal::SchedulingMode /* mode */) { +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( + std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop, + absl::base_internal::SchedulingMode /* mode */) { if (loop == 0) { } else if (loop == 1) { Sleep(0); @@ -31,7 +31,7 @@ void AbslInternalSpinLockDelay(std::atomic<uint32_t>* /* lock_word */, } } -void AbslInternalSpinLockWake(std::atomic<uint32_t>* /* lock_word */, - bool /* all */) {} +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( + std::atomic<uint32_t>* /* lock_word */, bool /* all */) {} } // extern "C" diff --git a/third_party/abseil-cpp/absl/base/internal/strerror.cc b/third_party/abseil-cpp/absl/base/internal/strerror.cc new file mode 100644 index 0000000000..0d6226fd0a --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/strerror.cc @@ -0,0 +1,88 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/base/internal/strerror.h" + +#include <array> +#include <cerrno> +#include <cstddef> +#include <cstdio> +#include <cstring> +#include <string> +#include <type_traits> + +#include "absl/base/internal/errno_saver.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { +namespace { + +const char* StrErrorAdaptor(int errnum, char* buf, size_t buflen) { +#if defined(_WIN32) + int rc = strerror_s(buf, buflen, errnum); + buf[buflen - 1] = '\0'; // guarantee NUL termination + if (rc == 0 && strncmp(buf, "Unknown error", buflen) == 0) *buf = '\0'; + return buf; +#else + // The type of `ret` is platform-specific; both of these branches must compile + // either way but only one will execute on any given platform: + auto ret = strerror_r(errnum, buf, buflen); + if (std::is_same<decltype(ret), int>::value) { + // XSI `strerror_r`; `ret` is `int`: + if (ret) *buf = '\0'; + return buf; + } else { + // GNU `strerror_r`; `ret` is `char *`: + return reinterpret_cast<const char*>(ret); + } +#endif +} + +std::string StrErrorInternal(int errnum) { + char buf[100]; + const char* str = StrErrorAdaptor(errnum, buf, sizeof buf); + if (*str == '\0') { + snprintf(buf, sizeof buf, "Unknown error %d", errnum); + str = buf; + } + return str; +} + +// kSysNerr is the number of errors from a recent glibc. `StrError()` falls back +// to `StrErrorAdaptor()` if the value is larger than this. +constexpr int kSysNerr = 135; + +std::array<std::string, kSysNerr>* NewStrErrorTable() { + auto* table = new std::array<std::string, kSysNerr>; + for (int i = 0; i < static_cast<int>(table->size()); ++i) { + (*table)[i] = StrErrorInternal(i); + } + return table; +} + +} // namespace + +std::string StrError(int errnum) { + absl::base_internal::ErrnoSaver errno_saver; + static const auto* table = NewStrErrorTable(); + if (errnum >= 0 && errnum < static_cast<int>(table->size())) { + return (*table)[errnum]; + } + return StrErrorInternal(errnum); +} + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/strerror.h b/third_party/abseil-cpp/absl/base/internal/strerror.h new file mode 100644 index 0000000000..350097366e --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/strerror.h @@ -0,0 +1,39 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_BASE_INTERNAL_STRERROR_H_ +#define ABSL_BASE_INTERNAL_STRERROR_H_ + +#include <string> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace base_internal { + +// A portable and thread-safe alternative to C89's `strerror`. +// +// The C89 specification of `strerror` is not suitable for use in a +// multi-threaded application as the returned string may be changed by calls to +// `strerror` from another thread. The many non-stdlib alternatives differ +// enough in their names, availability, and semantics to justify this wrapper +// around them. `errno` will not be modified by a call to `absl::StrError`. +std::string StrError(int errnum); + +} // namespace base_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_STRERROR_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/strerror_benchmark.cc b/third_party/abseil-cpp/absl/base/internal/strerror_benchmark.cc new file mode 100644 index 0000000000..c9ab14a89d --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/strerror_benchmark.cc @@ -0,0 +1,29 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <cerrno> +#include <cstdio> +#include <string> + +#include "absl/base/internal/strerror.h" +#include "benchmark/benchmark.h" + +namespace { +void BM_AbslStrError(benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(absl::base_internal::StrError(ERANGE)); + } +} +BENCHMARK(BM_AbslStrError); +} // namespace diff --git a/third_party/abseil-cpp/absl/base/internal/strerror_test.cc b/third_party/abseil-cpp/absl/base/internal/strerror_test.cc new file mode 100644 index 0000000000..e32d5b5c9b --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/strerror_test.cc @@ -0,0 +1,88 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/base/internal/strerror.h" + +#include <atomic> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <string> +#include <thread> // NOLINT(build/c++11) +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/match.h" + +namespace { +using ::testing::AnyOf; +using ::testing::Eq; + +TEST(StrErrorTest, ValidErrorCode) { + errno = ERANGE; + EXPECT_THAT(absl::base_internal::StrError(EDOM), Eq(strerror(EDOM))); + EXPECT_THAT(errno, Eq(ERANGE)); +} + +TEST(StrErrorTest, InvalidErrorCode) { + errno = ERANGE; + EXPECT_THAT(absl::base_internal::StrError(-1), + AnyOf(Eq("No error information"), Eq("Unknown error -1"))); + EXPECT_THAT(errno, Eq(ERANGE)); +} + +TEST(StrErrorTest, MultipleThreads) { + // In this test, we will start up 2 threads and have each one call + // StrError 1000 times, each time with a different errnum. We + // expect that StrError(errnum) will return a string equal to the + // one returned by strerror(errnum), if the code is known. Since + // strerror is known to be thread-hostile, collect all the expected + // strings up front. + const int kNumCodes = 1000; + std::vector<std::string> expected_strings(kNumCodes); + for (int i = 0; i < kNumCodes; ++i) { + expected_strings[i] = strerror(i); + } + + std::atomic_int counter(0); + auto thread_fun = [&]() { + for (int i = 0; i < kNumCodes; ++i) { + ++counter; + errno = ERANGE; + const std::string value = absl::base_internal::StrError(i); + // EXPECT_* could change errno. Stash it first. + int check_err = errno; + EXPECT_THAT(check_err, Eq(ERANGE)); + // Only the GNU implementation is guaranteed to provide the + // string "Unknown error nnn". POSIX doesn't say anything. + if (!absl::StartsWith(value, "Unknown error ")) { + EXPECT_THAT(value, Eq(expected_strings[i])); + } + } + }; + + const int kNumThreads = 100; + std::vector<std::thread> threads; + for (int i = 0; i < kNumThreads; ++i) { + threads.push_back(std::thread(thread_fun)); + } + for (auto& thread : threads) { + thread.join(); + } + + EXPECT_THAT(counter, Eq(kNumThreads * kNumCodes)); +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/base/internal/sysinfo.cc b/third_party/abseil-cpp/absl/base/internal/sysinfo.cc index a0930e9781..a7cfb461f3 100644 --- a/third_party/abseil-cpp/absl/base/internal/sysinfo.cc +++ b/third_party/abseil-cpp/absl/base/internal/sysinfo.cc @@ -39,6 +39,7 @@ #endif #include <string.h> + #include <cassert> #include <cstdint> #include <cstdio> @@ -50,17 +51,88 @@ #include <vector> #include "absl/base/call_once.h" +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/spinlock.h" #include "absl/base/internal/unscaledcycleclock.h" +#include "absl/base/thread_annotations.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { +namespace { + +#if defined(_WIN32) + +// Returns number of bits set in `bitMask` +DWORD Win32CountSetBits(ULONG_PTR bitMask) { + for (DWORD bitSetCount = 0; ; ++bitSetCount) { + if (bitMask == 0) return bitSetCount; + bitMask &= bitMask - 1; + } +} + +// Returns the number of logical CPUs using GetLogicalProcessorInformation(), or +// 0 if the number of processors is not available or can not be computed. +// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformation +int Win32NumCPUs() { +#pragma comment(lib, "kernel32.lib") + using Info = SYSTEM_LOGICAL_PROCESSOR_INFORMATION; + + DWORD info_size = sizeof(Info); + Info* info(static_cast<Info*>(malloc(info_size))); + if (info == nullptr) return 0; + + bool success = GetLogicalProcessorInformation(info, &info_size); + if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + free(info); + info = static_cast<Info*>(malloc(info_size)); + if (info == nullptr) return 0; + success = GetLogicalProcessorInformation(info, &info_size); + } + + DWORD logicalProcessorCount = 0; + if (success) { + Info* ptr = info; + DWORD byteOffset = 0; + while (byteOffset + sizeof(Info) <= info_size) { + switch (ptr->Relationship) { + case RelationProcessorCore: + logicalProcessorCount += Win32CountSetBits(ptr->ProcessorMask); + break; + + case RelationNumaNode: + case RelationCache: + case RelationProcessorPackage: + // Ignore other entries + break; + + default: + // Ignore unknown entries + break; + } + byteOffset += sizeof(Info); + ptr++; + } + } + free(info); + return logicalProcessorCount; +} + +#endif + +} // namespace + + static int GetNumCPUs() { #if defined(__myriad2__) return 1; +#elif defined(_WIN32) + const unsigned hardware_concurrency = Win32NumCPUs(); + return hardware_concurrency ? hardware_concurrency : 1; +#elif defined(_AIX) + return sysconf(_SC_NPROCESSORS_ONLN); #else // Other possibilities: // - Read /sys/devices/system/cpu/online and use cpumask_parse() @@ -72,6 +144,12 @@ static int GetNumCPUs() { #if defined(_WIN32) static double GetNominalCPUFrequency() { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + // UWP apps don't have access to the registry and currently don't provide an + // API informing about CPU nominal frequency. + return 1.0; +#else #pragma comment(lib, "advapi32.lib") // For Reg* functions. HKEY key; // Use the Reg* functions rather than the SH functions because shlwapi.dll @@ -91,6 +169,7 @@ static double GetNominalCPUFrequency() { } } return 1.0; +#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP } #elif defined(CTL_HW) && defined(HW_CPU_FREQ) @@ -336,15 +415,16 @@ pid_t GetTID() { #else // Fallback implementation of GetTID using pthread_getspecific. -static once_flag tid_once; -static pthread_key_t tid_key; -static absl::base_internal::SpinLock tid_lock( - absl::base_internal::kLinkerInitialized); +ABSL_CONST_INIT static once_flag tid_once; +ABSL_CONST_INIT static pthread_key_t tid_key; +ABSL_CONST_INIT static absl::base_internal::SpinLock tid_lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); // We set a bit per thread in this array to indicate that an ID is in // use. ID 0 is unused because it is the default value returned by // pthread_getspecific(). -static std::vector<uint32_t>* tid_array GUARDED_BY(tid_lock) = nullptr; +ABSL_CONST_INIT static std::vector<uint32_t> *tid_array + ABSL_GUARDED_BY(tid_lock) = nullptr; static constexpr int kBitsPerWord = 32; // tid_array is uint32_t. // Returns the TID to tid_array. @@ -411,6 +491,18 @@ pid_t GetTID() { #endif +// GetCachedTID() caches the thread ID in thread-local storage (which is a +// userspace construct) to avoid unnecessary system calls. Without this caching, +// it can take roughly 98ns, while it takes roughly 1ns with this caching. +pid_t GetCachedTID() { +#ifdef ABSL_HAVE_THREAD_LOCAL + static thread_local pid_t thread_id = GetTID(); + return thread_id; +#else + return GetTID(); +#endif // ABSL_HAVE_THREAD_LOCAL +} + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/sysinfo.h b/third_party/abseil-cpp/absl/base/internal/sysinfo.h index 7246d5dd95..119cf1f0e8 100644 --- a/third_party/abseil-cpp/absl/base/internal/sysinfo.h +++ b/third_party/abseil-cpp/absl/base/internal/sysinfo.h @@ -30,6 +30,7 @@ #include <cstdint> +#include "absl/base/config.h" #include "absl/base/port.h" namespace absl { @@ -59,6 +60,13 @@ using pid_t = uint32_t; #endif pid_t GetTID(); +// Like GetTID(), but caches the result in thread-local storage in order +// to avoid unnecessary system calls. Note that there are some cases where +// one must call through to GetTID directly, which is why this exists as a +// separate function. For example, GetCachedTID() is not safe to call in +// an asynchronous signal-handling context nor right after a call to fork(). +pid_t GetCachedTID(); + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/sysinfo_test.cc b/third_party/abseil-cpp/absl/base/internal/sysinfo_test.cc index fa8b88b1dc..5f9e45f6aa 100644 --- a/third_party/abseil-cpp/absl/base/internal/sysinfo_test.cc +++ b/third_party/abseil-cpp/absl/base/internal/sysinfo_test.cc @@ -37,17 +37,28 @@ TEST(SysinfoTest, NumCPUs) { << "NumCPUs() should not have the default value of 0"; } +// Ensure that NominalCPUFrequency returns a reasonable value, or 1.00 on +// platforms where the CPU frequency is not available through sysfs. +// +// POWER is particularly problematic here; some Linux kernels expose the CPU +// frequency, while others do not. Since we can't predict a priori what a given +// machine is going to do, just disable this test on POWER on Linux. +#if !(defined(__linux) && (defined(__ppc64__) || defined(__PPC64__))) TEST(SysinfoTest, NominalCPUFrequency) { -#if !(defined(__aarch64__) && defined(__linux__)) && !defined(__EMSCRIPTEN__) - EXPECT_GE(NominalCPUFrequency(), 1000.0) - << "NominalCPUFrequency() did not return a reasonable value"; -#else - // Aarch64 cannot read the CPU frequency from sysfs, so we get back 1.0. - // Emscripten does not have a sysfs to read from at all. + // Linux only exposes the CPU frequency on certain architectures, and + // Emscripten doesn't expose it at all. +#if defined(__linux__) && \ + (defined(__aarch64__) || defined(__hppa__) || defined(__mips__) || \ + defined(__riscv) || defined(__s390x__)) || \ + defined(__EMSCRIPTEN__) EXPECT_EQ(NominalCPUFrequency(), 1.0) << "CPU frequency detection was fixed! Please update unittest."; +#else + EXPECT_GE(NominalCPUFrequency(), 1000.0) + << "NominalCPUFrequency() did not return a reasonable value"; #endif } +#endif TEST(SysinfoTest, GetTID) { EXPECT_EQ(GetTID(), GetTID()); // Basic compile and equality test. diff --git a/third_party/abseil-cpp/absl/base/internal/thread_identity.cc b/third_party/abseil-cpp/absl/base/internal/thread_identity.cc index d63a04ae91..9950e63a79 100644 --- a/third_party/abseil-cpp/absl/base/internal/thread_identity.cc +++ b/third_party/abseil-cpp/absl/base/internal/thread_identity.cc @@ -23,6 +23,7 @@ #include <cassert> #include <memory> +#include "absl/base/attributes.h" #include "absl/base/call_once.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/spinlock.h" @@ -53,9 +54,11 @@ void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) { // exist within a process (via dlopen() or similar), references to // thread_identity_ptr from each instance of the code will refer to // *different* instances of this ptr. -#ifdef __GNUC__ +// Apple platforms have the visibility attribute, but issue a compile warning +// that protected visibility is unsupported. +#if ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) __attribute__((visibility("protected"))) -#endif // __GNUC__ +#endif // ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) #if ABSL_PER_THREAD_TLS // Prefer __thread to thread_local as benchmarks indicate it is a bit faster. ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr; @@ -117,10 +120,10 @@ void SetCurrentThreadIdentity( ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 // Please see the comment on `CurrentThreadIdentityIfPresent` in -// thread_identity.h. Because DLLs cannot expose thread_local variables in -// headers, we opt for the correct-but-slower option of placing the definition -// of this function only in a translation unit inside DLL. -#if defined(ABSL_BUILD_DLL) || defined(ABSL_CONSUME_DLL) +// thread_identity.h. When we cannot expose thread_local variables in +// headers, we opt for the correct-but-slower option of not inlining this +// function. +#ifndef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } #endif #endif diff --git a/third_party/abseil-cpp/absl/base/internal/thread_identity.h b/third_party/abseil-cpp/absl/base/internal/thread_identity.h index ceb109b41c..659694b326 100644 --- a/third_party/abseil-cpp/absl/base/internal/thread_identity.h +++ b/third_party/abseil-cpp/absl/base/internal/thread_identity.h @@ -32,6 +32,7 @@ #include "absl/base/config.h" #include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -69,30 +70,28 @@ struct PerThreadSynch { // is using this PerThreadSynch as a terminator. Its // skip field must not be filled in because the loop // might then skip over the terminator. - - // The wait parameters of the current wait. waitp is null if the - // thread is not waiting. Transitions from null to non-null must - // occur before the enqueue commit point (state = kQueued in - // Enqueue() and CondVarEnqueue()). Transitions from non-null to - // null must occur after the wait is finished (state = kAvailable in - // Mutex::Block() and CondVar::WaitCommon()). This field may be - // changed only by the thread that describes this PerThreadSynch. A - // special case is Fer(), which calls Enqueue() on another thread, - // but with an identical SynchWaitParams pointer, thus leaving the - // pointer unchanged. - SynchWaitParams *waitp; - - bool suppress_fatal_errors; // If true, try to proceed even in the face of - // broken invariants. This is used within fatal - // signal handlers to improve the chances of - // debug logging information being output - // successfully. - - intptr_t readers; // Number of readers in mutex. - int priority; // Priority of thread (updated every so often). - - // When priority will next be read (cycles). - int64_t next_priority_read_cycles; + bool wake; // This thread is to be woken from a Mutex. + // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the + // waiter is waiting on the mutex as part of a CV Wait or Mutex Await. + // + // The value of "x->cond_waiter" is meaningless if "x" is not on a + // Mutex waiter list. + bool cond_waiter; + bool maybe_unlocking; // Valid at head of Mutex waiter queue; + // true if UnlockSlow could be searching + // for a waiter to wake. Used for an optimization + // in Enqueue(). true is always a valid value. + // Can be reset to false when the unlocker or any + // writer releases the lock, or a reader fully + // releases the lock. It may not be set to false + // by a reader that decrements the count to + // non-zero. protected by mutex spinlock + bool suppress_fatal_errors; // If true, try to proceed even in the face + // of broken invariants. This is used within + // fatal signal handlers to improve the + // chances of debug logging information being + // output successfully. + int priority; // Priority of thread (updated every so often). // State values: // kAvailable: This PerThreadSynch is available. @@ -111,30 +110,30 @@ struct PerThreadSynch { }; std::atomic<State> state; - bool maybe_unlocking; // Valid at head of Mutex waiter queue; - // true if UnlockSlow could be searching - // for a waiter to wake. Used for an optimization - // in Enqueue(). true is always a valid value. - // Can be reset to false when the unlocker or any - // writer releases the lock, or a reader fully releases - // the lock. It may not be set to false by a reader - // that decrements the count to non-zero. - // protected by mutex spinlock + // The wait parameters of the current wait. waitp is null if the + // thread is not waiting. Transitions from null to non-null must + // occur before the enqueue commit point (state = kQueued in + // Enqueue() and CondVarEnqueue()). Transitions from non-null to + // null must occur after the wait is finished (state = kAvailable in + // Mutex::Block() and CondVar::WaitCommon()). This field may be + // changed only by the thread that describes this PerThreadSynch. A + // special case is Fer(), which calls Enqueue() on another thread, + // but with an identical SynchWaitParams pointer, thus leaving the + // pointer unchanged. + SynchWaitParams* waitp; - bool wake; // This thread is to be woken from a Mutex. + intptr_t readers; // Number of readers in mutex. - // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the - // waiter is waiting on the mutex as part of a CV Wait or Mutex Await. - // - // The value of "x->cond_waiter" is meaningless if "x" is not on a - // Mutex waiter list. - bool cond_waiter; + // When priority will next be read (cycles). + int64_t next_priority_read_cycles; // Locks held; used during deadlock detection. // Allocated in Synch_GetAllLocks() and freed in ReclaimThreadIdentity(). SynchLocksHeld *all_locks; }; +// The instances of this class are allocated in NewThreadIdentity() with an +// alignment of PerThreadSynch::kAlignment. struct ThreadIdentity { // Must be the first member. The Mutex implementation requires that // the PerThreadSynch object associated with each thread is @@ -144,7 +143,7 @@ struct ThreadIdentity { // Private: Reserved for absl::synchronization_internal::Waiter. struct WaiterState { - char data[128]; + alignas(void*) char data[128]; } waiter_state; // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter(). @@ -189,30 +188,32 @@ void ClearCurrentThreadIdentity(); // May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE=<mode // index> #ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC -#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0 #endif #ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS -#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1 #endif #ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11 -#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be directly set #else #define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2 #endif #ifdef ABSL_THREAD_IDENTITY_MODE -#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set +#error ABSL_THREAD_IDENTITY_MODE cannot be directly set #elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE) #define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE #elif defined(_WIN32) && !defined(__MINGW32__) #define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 -#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \ +#elif defined(__APPLE__) && defined(ABSL_HAVE_THREAD_LOCAL) +#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 +#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \ (__GOOGLE_GRTE_VERSION__ >= 20140228L) // Support for async-safe TLS was specifically added in GRTEv4. It's not // present in the upstream eglibc. @@ -235,13 +236,18 @@ ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; #error Thread-local storage not detected on this platform #endif -// thread_local variables cannot be in headers exposed by DLLs. However, it is -// important for performance reasons in general that -// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a -// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note +// thread_local variables cannot be in headers exposed by DLLs or in certain +// build configurations on Apple platforms. However, it is important for +// performance reasons in general that `CurrentThreadIdentityIfPresent` be +// inlined. In the other cases we opt to have the function not be inlined. Note // that `CurrentThreadIdentityIfPresent` is declared above so we can exclude -// this entire inline definition when compiling as a DLL. -#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) +// this entire inline definition. +#if !defined(__APPLE__) && !defined(ABSL_BUILD_DLL) && \ + !defined(ABSL_CONSUME_DLL) +#define ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT 1 +#endif + +#ifdef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT inline ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } diff --git a/third_party/abseil-cpp/absl/base/internal/thread_identity_test.cc b/third_party/abseil-cpp/absl/base/internal/thread_identity_test.cc index 3685779ce8..46a6f74346 100644 --- a/third_party/abseil-cpp/absl/base/internal/thread_identity_test.cc +++ b/third_party/abseil-cpp/absl/base/internal/thread_identity_test.cc @@ -21,6 +21,7 @@ #include "absl/base/attributes.h" #include "absl/base/internal/spinlock.h" #include "absl/base/macros.h" +#include "absl/base/thread_annotations.h" #include "absl/synchronization/internal/per_thread_sem.h" #include "absl/synchronization/mutex.h" @@ -29,10 +30,9 @@ ABSL_NAMESPACE_BEGIN namespace base_internal { namespace { -// protects num_identities_reused -static absl::base_internal::SpinLock map_lock( - absl::base_internal::kLinkerInitialized); -static int num_identities_reused; +ABSL_CONST_INIT static absl::base_internal::SpinLock map_lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static int num_identities_reused ABSL_GUARDED_BY(map_lock); static const void* const kCheckNoIdentity = reinterpret_cast<void*>(1); @@ -75,7 +75,7 @@ TEST(ThreadIdentityTest, BasicIdentityWorksThreaded) { // - If a thread implementation chooses to recycle threads, that // correct re-initialization occurs. static const int kNumLoops = 3; - static const int kNumThreads = 400; + static const int kNumThreads = 32; for (int iter = 0; iter < kNumLoops; iter++) { std::vector<std::thread> threads; for (int i = 0; i < kNumThreads; ++i) { @@ -90,6 +90,7 @@ TEST(ThreadIdentityTest, BasicIdentityWorksThreaded) { // We should have recycled ThreadIdentity objects above; while (external) // library threads allocating their own identities may preclude some // reuse, we should have sufficient repetitions to exclude this. + absl::base_internal::SpinLockHolder l(&map_lock); EXPECT_LT(kNumThreads, num_identities_reused); } diff --git a/third_party/abseil-cpp/absl/base/internal/throw_delegate.cc b/third_party/abseil-cpp/absl/base/internal/throw_delegate.cc index c055f75d9d..c260ff1eed 100644 --- a/third_party/abseil-cpp/absl/base/internal/throw_delegate.cc +++ b/third_party/abseil-cpp/absl/base/internal/throw_delegate.cc @@ -18,6 +18,7 @@ #include <functional> #include <new> #include <stdexcept> + #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" @@ -25,83 +26,186 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { +// NOTE: The various STL exception throwing functions are placed within the +// #ifdef blocks so the symbols aren't exposed on platforms that don't support +// them, such as the Android NDK. For example, ANGLE fails to link when building +// within AOSP without them, since the STL functions don't exist. namespace { +#ifdef ABSL_HAVE_EXCEPTIONS template <typename T> [[noreturn]] void Throw(const T& error) { -#ifdef ABSL_HAVE_EXCEPTIONS throw error; -#else - ABSL_RAW_LOG(FATAL, "%s", error.what()); - std::abort(); -#endif } +#endif } // namespace void ThrowStdLogicError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::logic_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdLogicError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::logic_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdInvalidArgument(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::invalid_argument(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdInvalidArgument(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::invalid_argument(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdDomainError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::domain_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdDomainError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::domain_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdLengthError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::length_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdLengthError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::length_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdOutOfRange(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::out_of_range(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdOutOfRange(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::out_of_range(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdRuntimeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::runtime_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdRuntimeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::runtime_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdRangeError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::range_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdRangeError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::range_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdOverflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::overflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdOverflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::overflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } void ThrowStdUnderflowError(const std::string& what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::underflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg.c_str()); + std::abort(); +#endif } void ThrowStdUnderflowError(const char* what_arg) { +#ifdef ABSL_HAVE_EXCEPTIONS Throw(std::underflow_error(what_arg)); +#else + ABSL_RAW_LOG(FATAL, "%s", what_arg); + std::abort(); +#endif } -void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); } +void ThrowStdBadFunctionCall() { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::bad_function_call()); +#else + std::abort(); +#endif +} -void ThrowStdBadAlloc() { Throw(std::bad_alloc()); } +void ThrowStdBadAlloc() { +#ifdef ABSL_HAVE_EXCEPTIONS + Throw(std::bad_alloc()); +#else + std::abort(); +#endif +} } // namespace base_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/base/internal/tsan_mutex_interface.h b/third_party/abseil-cpp/absl/base/internal/tsan_mutex_interface.h index 2a510603bc..39207d8a5c 100644 --- a/third_party/abseil-cpp/absl/base/internal/tsan_mutex_interface.h +++ b/third_party/abseil-cpp/absl/base/internal/tsan_mutex_interface.h @@ -19,6 +19,8 @@ #ifndef ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ #define ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ +#include "absl/base/config.h" + // ABSL_INTERNAL_HAVE_TSAN_INTERFACE // Macro intended only for internal use. // @@ -28,7 +30,7 @@ #error "ABSL_INTERNAL_HAVE_TSAN_INTERFACE cannot be directly set." #endif -#if defined(THREAD_SANITIZER) && defined(__has_include) +#if defined(ABSL_HAVE_THREAD_SANITIZER) && defined(__has_include) #if __has_include(<sanitizer/tsan_interface.h>) #define ABSL_INTERNAL_HAVE_TSAN_INTERFACE 1 #endif diff --git a/third_party/abseil-cpp/absl/base/internal/unaligned_access.h b/third_party/abseil-cpp/absl/base/internal/unaligned_access.h index 6be56c865b..093dd9b499 100644 --- a/third_party/abseil-cpp/absl/base/internal/unaligned_access.h +++ b/third_party/abseil-cpp/absl/base/internal/unaligned_access.h @@ -31,80 +31,6 @@ // The unaligned API is C++ only. The declarations use C++ features // (namespaces, inline) which are absent or incompatible in C. #if defined(__cplusplus) - -#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||\ - defined(MEMORY_SANITIZER) -// Consider we have an unaligned load/store of 4 bytes from address 0x...05. -// AddressSanitizer will treat it as a 3-byte access to the range 05:07 and -// will miss a bug if 08 is the first unaddressable byte. -// ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will -// miss a race between this access and some other accesses to 08. -// MemorySanitizer will correctly propagate the shadow on unaligned stores -// and correctly report bugs on unaligned loads, but it may not properly -// update and report the origin of the uninitialized memory. -// For all three tools, replacing an unaligned access with a tool-specific -// callback solves the problem. - -// Make sure uint16_t/uint32_t/uint64_t are defined. -#include <stdint.h> - -extern "C" { -uint16_t __sanitizer_unaligned_load16(const void *p); -uint32_t __sanitizer_unaligned_load32(const void *p); -uint64_t __sanitizer_unaligned_load64(const void *p); -void __sanitizer_unaligned_store16(void *p, uint16_t v); -void __sanitizer_unaligned_store32(void *p, uint32_t v); -void __sanitizer_unaligned_store64(void *p, uint64_t v); -} // extern "C" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { - -inline uint16_t UnalignedLoad16(const void *p) { - return __sanitizer_unaligned_load16(p); -} - -inline uint32_t UnalignedLoad32(const void *p) { - return __sanitizer_unaligned_load32(p); -} - -inline uint64_t UnalignedLoad64(const void *p) { - return __sanitizer_unaligned_load64(p); -} - -inline void UnalignedStore16(void *p, uint16_t v) { - __sanitizer_unaligned_store16(p, v); -} - -inline void UnalignedStore32(void *p, uint32_t v) { - __sanitizer_unaligned_store32(p, v); -} - -inline void UnalignedStore64(void *p, uint64_t v) { - __sanitizer_unaligned_store64(p, v); -} - -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - (absl::base_internal::UnalignedLoad16(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - (absl::base_internal::UnalignedLoad32(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (absl::base_internal::UnalignedLoad64(_p)) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - (absl::base_internal::UnalignedStore16(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (absl::base_internal::UnalignedStore32(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::base_internal::UnalignedStore64(_p, _val)) - -#else - namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { @@ -151,8 +77,6 @@ ABSL_NAMESPACE_END #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ (absl::base_internal::UnalignedStore64(_p, _val)) -#endif - #endif // defined(__cplusplus), end of unaligned API #endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/unique_small_name_test.cc b/third_party/abseil-cpp/absl/base/internal/unique_small_name_test.cc new file mode 100644 index 0000000000..ff8c2b3fb4 --- /dev/null +++ b/third_party/abseil-cpp/absl/base/internal/unique_small_name_test.cc @@ -0,0 +1,77 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 "gtest/gtest.h" +#include "absl/base/optimization.h" +#include "absl/strings/string_view.h" + +// This test by itself does not do anything fancy, but it serves as binary I can +// query in shell test. + +namespace { + +template <class T> +void DoNotOptimize(const T& var) { +#ifdef __GNUC__ + asm volatile("" : "+m"(const_cast<T&>(var))); +#else + std::cout << (void*)&var; +#endif +} + +int very_long_int_variable_name ABSL_INTERNAL_UNIQUE_SMALL_NAME() = 0; +char very_long_str_variable_name[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = "abc"; + +TEST(UniqueSmallName, NonAutomaticVar) { + EXPECT_EQ(very_long_int_variable_name, 0); + EXPECT_EQ(absl::string_view(very_long_str_variable_name), "abc"); +} + +int VeryLongFreeFunctionName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); + +TEST(UniqueSmallName, FreeFunction) { + DoNotOptimize(&VeryLongFreeFunctionName); + + EXPECT_EQ(VeryLongFreeFunctionName(), 456); +} + +int VeryLongFreeFunctionName() { return 456; } + +struct VeryLongStructName { + explicit VeryLongStructName(int i); + + int VeryLongMethodName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); + + static int VeryLongStaticMethodName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); + + private: + int fld; +}; + +TEST(UniqueSmallName, Struct) { + VeryLongStructName var(10); + + DoNotOptimize(var); + DoNotOptimize(&VeryLongStructName::VeryLongMethodName); + DoNotOptimize(&VeryLongStructName::VeryLongStaticMethodName); + + EXPECT_EQ(var.VeryLongMethodName(), 10); + EXPECT_EQ(VeryLongStructName::VeryLongStaticMethodName(), 123); +} + +VeryLongStructName::VeryLongStructName(int i) : fld(i) {} +int VeryLongStructName::VeryLongMethodName() { return fld; } +int VeryLongStructName::VeryLongStaticMethodName() { return 123; } + +} // namespace diff --git a/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.cc b/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.cc index f1e7bbef84..4d352bd110 100644 --- a/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.cc +++ b/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.cc @@ -87,6 +87,10 @@ int64_t UnscaledCycleClock::Now() { double UnscaledCycleClock::Frequency() { #ifdef __GLIBC__ return __ppc_get_timebase_freq(); +#elif defined(_AIX) + // This is the same constant value as returned by + // __ppc_get_timebase_freq(). + return static_cast<double>(512000000); #elif defined(__FreeBSD__) static once_flag init_timebase_frequency_once; static double timebase_frequency = 0.0; @@ -119,13 +123,23 @@ double UnscaledCycleClock::Frequency() { return aarch64_timer_frequency; } +#elif defined(__riscv) + +int64_t UnscaledCycleClock::Now() { + int64_t virtual_timer_value; + asm volatile("rdcycle %0" : "=r"(virtual_timer_value)); + return virtual_timer_value; +} + +double UnscaledCycleClock::Frequency() { + return base_internal::NominalCPUFrequency(); +} + #elif defined(_M_IX86) || defined(_M_X64) #pragma intrinsic(__rdtsc) -int64_t UnscaledCycleClock::Now() { - return __rdtsc(); -} +int64_t UnscaledCycleClock::Now() { return __rdtsc(); } double UnscaledCycleClock::Frequency() { return base_internal::NominalCPUFrequency(); diff --git a/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.h b/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.h index cdce9bf8a8..681ff8f996 100644 --- a/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.h +++ b/third_party/abseil-cpp/absl/base/internal/unscaledcycleclock.h @@ -15,8 +15,8 @@ // UnscaledCycleClock // An UnscaledCycleClock yields the value and frequency of a cycle counter // that increments at a rate that is approximately constant. -// This class is for internal / whitelisted use only, you should consider -// using CycleClock instead. +// This class is for internal use only, you should consider using CycleClock +// instead. // // Notes: // The cycle counter frequency is not necessarily the core clock frequency. @@ -46,8 +46,8 @@ // The following platforms have an implementation of a hardware counter. #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ - defined(__powerpc__) || defined(__ppc__) || \ - defined(_M_IX86) || defined(_M_X64) + defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ + defined(_M_IX86) || defined(_M_X64) #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 #else #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 @@ -80,8 +80,8 @@ // This macro can be used to test if UnscaledCycleClock::Frequency() // is NominalCPUFrequency() on a particular platform. -#if (defined(__i386__) || defined(__x86_64__) || \ - defined(_M_IX86) || defined(_M_X64)) +#if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || \ + defined(_M_IX86) || defined(_M_X64)) #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY #endif @@ -109,7 +109,7 @@ class UnscaledCycleClock { // value. static double Frequency(); - // Whitelisted friends. + // Allowed users friend class base_internal::CycleClock; friend class time_internal::UnscaledCycleClockWrapperForGetCurrentTime; friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency; diff --git a/third_party/abseil-cpp/absl/base/invoke_test.cc b/third_party/abseil-cpp/absl/base/invoke_test.cc index 6aa613c913..bcdef36c3b 100644 --- a/third_party/abseil-cpp/absl/base/invoke_test.cc +++ b/third_party/abseil-cpp/absl/base/invoke_test.cc @@ -86,71 +86,73 @@ struct FlipFlop { int member; }; -// CallMaybeWithArg(f) resolves either to Invoke(f) or Invoke(f, 42), depending +// CallMaybeWithArg(f) resolves either to invoke(f) or invoke(f, 42), depending // on which one is valid. template <typename F> -decltype(Invoke(std::declval<const F&>())) CallMaybeWithArg(const F& f) { - return Invoke(f); +decltype(base_internal::invoke(std::declval<const F&>())) CallMaybeWithArg( + const F& f) { + return base_internal::invoke(f); } template <typename F> -decltype(Invoke(std::declval<const F&>(), 42)) CallMaybeWithArg(const F& f) { - return Invoke(f, 42); +decltype(base_internal::invoke(std::declval<const F&>(), 42)) CallMaybeWithArg( + const F& f) { + return base_internal::invoke(f, 42); } TEST(InvokeTest, Function) { - EXPECT_EQ(1, Invoke(Function, 3, 2)); - EXPECT_EQ(1, Invoke(&Function, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(Function, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Function, 3, 2)); } TEST(InvokeTest, NonCopyableArgument) { - EXPECT_EQ(42, Invoke(Sink, make_unique<int>(42))); + EXPECT_EQ(42, base_internal::invoke(Sink, make_unique<int>(42))); } TEST(InvokeTest, NonCopyableResult) { - EXPECT_THAT(Invoke(Factory, 42), ::testing::Pointee(42)); + EXPECT_THAT(base_internal::invoke(Factory, 42), ::testing::Pointee(42)); } -TEST(InvokeTest, VoidResult) { - Invoke(NoOp); -} +TEST(InvokeTest, VoidResult) { base_internal::invoke(NoOp); } TEST(InvokeTest, ConstFunctor) { - EXPECT_EQ(1, Invoke(ConstFunctor(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(ConstFunctor(), 3, 2)); } TEST(InvokeTest, MutableFunctor) { MutableFunctor f; - EXPECT_EQ(1, Invoke(f, 3, 2)); - EXPECT_EQ(1, Invoke(MutableFunctor(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(f, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(MutableFunctor(), 3, 2)); } TEST(InvokeTest, EphemeralFunctor) { EphemeralFunctor f; - EXPECT_EQ(1, Invoke(std::move(f), 3, 2)); - EXPECT_EQ(1, Invoke(EphemeralFunctor(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::move(f), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(EphemeralFunctor(), 3, 2)); } TEST(InvokeTest, OverloadedFunctor) { OverloadedFunctor f; const OverloadedFunctor& cf = f; - EXPECT_EQ("&", Invoke(f)); - EXPECT_EQ("& 42", Invoke(f, " 42")); + EXPECT_EQ("&", base_internal::invoke(f)); + EXPECT_EQ("& 42", base_internal::invoke(f, " 42")); + + EXPECT_EQ("const&", base_internal::invoke(cf)); + EXPECT_EQ("const& 42", base_internal::invoke(cf, " 42")); - EXPECT_EQ("const&", Invoke(cf)); - EXPECT_EQ("const& 42", Invoke(cf, " 42")); + EXPECT_EQ("&&", base_internal::invoke(std::move(f))); - EXPECT_EQ("&&", Invoke(std::move(f))); - EXPECT_EQ("&& 42", Invoke(std::move(f), " 42")); + OverloadedFunctor f2; + EXPECT_EQ("&& 42", base_internal::invoke(std::move(f2), " 42")); } TEST(InvokeTest, ReferenceWrapper) { ConstFunctor cf; MutableFunctor mf; - EXPECT_EQ(1, Invoke(std::cref(cf), 3, 2)); - EXPECT_EQ(1, Invoke(std::ref(cf), 3, 2)); - EXPECT_EQ(1, Invoke(std::ref(mf), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::cref(cf), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::ref(cf), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(std::ref(mf), 3, 2)); } TEST(InvokeTest, MemberFunction) { @@ -158,58 +160,62 @@ TEST(InvokeTest, MemberFunction) { std::unique_ptr<const Class> cp(new Class); std::unique_ptr<volatile Class> vp(new Class); - EXPECT_EQ(1, Invoke(&Class::Method, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::Method, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::Method, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefMethod, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::RefRefMethod, std::move(*p), 3, 2)); // NOLINT - EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, *p, 3, 2)); - - EXPECT_EQ(1, Invoke(&Class::ConstMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, *p, 3, 2)); - - EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, *cp, 3, 2)); - - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *p, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp, 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp.get(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *vp, 3, 2)); - - EXPECT_EQ(1, Invoke(&Class::Method, make_unique<Class>(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique<Class>(), 3, 2)); - EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique<const Class>(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::Method, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::Method, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::Method, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefMethod, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::RefRefMethod, std::move(*p), 3, + 2)); // NOLINT + EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::NoExceptMethod, *p, 3, 2)); + + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, *p, 3, 2)); + + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, cp, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, cp.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, *cp, 3, 2)); + + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, p.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, *p, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, vp, 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, vp.get(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::VolatileMethod, *vp, 3, 2)); + + EXPECT_EQ(1, + base_internal::invoke(&Class::Method, make_unique<Class>(), 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, make_unique<Class>(), + 3, 2)); + EXPECT_EQ(1, base_internal::invoke(&Class::ConstMethod, + make_unique<const Class>(), 3, 2)); } TEST(InvokeTest, DataMember) { std::unique_ptr<Class> p(new Class{42}); std::unique_ptr<const Class> cp(new Class{42}); - EXPECT_EQ(42, Invoke(&Class::member, p)); - EXPECT_EQ(42, Invoke(&Class::member, *p)); - EXPECT_EQ(42, Invoke(&Class::member, p.get())); + EXPECT_EQ(42, base_internal::invoke(&Class::member, p)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, *p)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, p.get())); - Invoke(&Class::member, p) = 42; - Invoke(&Class::member, p.get()) = 42; + base_internal::invoke(&Class::member, p) = 42; + base_internal::invoke(&Class::member, p.get()) = 42; - EXPECT_EQ(42, Invoke(&Class::member, cp)); - EXPECT_EQ(42, Invoke(&Class::member, *cp)); - EXPECT_EQ(42, Invoke(&Class::member, cp.get())); + EXPECT_EQ(42, base_internal::invoke(&Class::member, cp)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, *cp)); + EXPECT_EQ(42, base_internal::invoke(&Class::member, cp.get())); } TEST(InvokeTest, FlipFlop) { FlipFlop obj = {42}; // This call could resolve to (obj.*&FlipFlop::ConstMethod)() or // ((*obj).*&FlipFlop::ConstMethod)(). We verify that it's the former. - EXPECT_EQ(42, Invoke(&FlipFlop::ConstMethod, obj)); - EXPECT_EQ(42, Invoke(&FlipFlop::member, obj)); + EXPECT_EQ(42, base_internal::invoke(&FlipFlop::ConstMethod, obj)); + EXPECT_EQ(42, base_internal::invoke(&FlipFlop::member, obj)); } TEST(InvokeTest, SfinaeFriendly) { diff --git a/third_party/abseil-cpp/absl/base/log_severity.h b/third_party/abseil-cpp/absl/base/log_severity.h index 65a3b16672..2236422462 100644 --- a/third_party/abseil-cpp/absl/base/log_severity.h +++ b/third_party/abseil-cpp/absl/base/log_severity.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ -#define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#ifndef ABSL_BASE_LOG_SEVERITY_H_ +#define ABSL_BASE_LOG_SEVERITY_H_ #include <array> #include <ostream> @@ -36,7 +36,7 @@ ABSL_NAMESPACE_BEGIN // such values to a defined severity level, however in some cases values other // than the defined levels are useful for comparison. // -// Exmaple: +// Example: // // // Effectively disables all logging: // SetMinLogLevel(static_cast<absl::LogSeverity>(100)); @@ -118,4 +118,4 @@ std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ +#endif // ABSL_BASE_LOG_SEVERITY_H_ diff --git a/third_party/abseil-cpp/absl/base/log_severity_test.cc b/third_party/abseil-cpp/absl/base/log_severity_test.cc index 2302aa1208..55b26d1774 100644 --- a/third_party/abseil-cpp/absl/base/log_severity_test.cc +++ b/third_party/abseil-cpp/absl/base/log_severity_test.cc @@ -52,9 +52,9 @@ TEST(StreamTest, Works) { Eq("absl::LogSeverity(4)")); } -static_assert( - absl::flags_internal::IsAtomicFlagTypeTrait<absl::LogSeverity>::value, - "Flags of type absl::LogSeverity ought to be lock-free."); +static_assert(absl::flags_internal::FlagUseValueAndInitBitStorage< + absl::LogSeverity>::value, + "Flags of type absl::LogSeverity ought to be lock-free."); using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>; INSTANTIATE_TEST_SUITE_P( diff --git a/third_party/abseil-cpp/absl/base/macros.h b/third_party/abseil-cpp/absl/base/macros.h index 547f93bafb..3e085a916b 100644 --- a/third_party/abseil-cpp/absl/base/macros.h +++ b/third_party/abseil-cpp/absl/base/macros.h @@ -32,6 +32,7 @@ #include <cstddef> #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/optimization.h" #include "absl/base/port.h" @@ -54,115 +55,6 @@ auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; ABSL_NAMESPACE_END } // namespace absl -// kLinkerInitialized -// -// An enum used only as a constructor argument to indicate that a variable has -// static storage duration, and that the constructor should do nothing to its -// state. Use of this macro indicates to the reader that it is legal to -// declare a static instance of the class, provided the constructor is given -// the absl::base_internal::kLinkerInitialized argument. -// -// Normally, it is unsafe to declare a static variable that has a constructor or -// a destructor because invocation order is undefined. However, if the type can -// be zero-initialized (which the loader does for static variables) into a valid -// state and the type's destructor does not affect storage, then a constructor -// for static initialization can be declared. -// -// Example: -// // Declaration -// explicit MyClass(absl::base_internal:LinkerInitialized x) {} -// -// // Invocation -// static MyClass my_global(absl::base_internal::kLinkerInitialized); -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace base_internal { -enum LinkerInitialized { - kLinkerInitialized = 0, -}; -} // namespace base_internal -ABSL_NAMESPACE_END -} // namespace absl - -// ABSL_FALLTHROUGH_INTENDED -// -// Annotates implicit fall-through between switch labels, allowing a case to -// indicate intentional fallthrough and turn off warnings about any lack of a -// `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by -// a semicolon and can be used in most places where `break` can, provided that -// no statements exist between it and the next switch label. -// -// Example: -// -// switch (x) { -// case 40: -// case 41: -// if (truth_is_out_there) { -// ++x; -// ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations -// // in comments -// } else { -// return x; -// } -// case 42: -// ... -// -// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED -// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed -// when performing switch labels fall-through diagnostic -// (`-Wimplicit-fallthrough`). See clang documentation on language extensions -// for details: -// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough -// -// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro -// has no effect on diagnostics. In any case this macro has no effect on runtime -// behavior and performance of code. -#ifdef ABSL_FALLTHROUGH_INTENDED -#error "ABSL_FALLTHROUGH_INTENDED should not be defined." -#endif - -// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported. -#if defined(__clang__) && defined(__has_warning) -#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") -#define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] -#endif -#elif defined(__GNUC__) && __GNUC__ >= 7 -#define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] -#endif - -#ifndef ABSL_FALLTHROUGH_INTENDED -#define ABSL_FALLTHROUGH_INTENDED \ - do { \ - } while (0) -#endif - -// ABSL_DEPRECATED() -// -// Marks a deprecated class, struct, enum, function, method and variable -// declarations. The macro argument is used as a custom diagnostic message (e.g. -// suggestion of a better alternative). -// -// Examples: -// -// class ABSL_DEPRECATED("Use Bar instead") Foo {...}; -// -// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} -// -// template <typename T> -// ABSL_DEPRECATED("Use DoThat() instead") -// void DoThis(); -// -// Every usage of a deprecated entity will trigger a warning when compiled with -// clang's `-Wdeprecated-declarations` option. This option is turned off by -// default, but the warnings will be reported by clang-tidy. -#if defined(__clang__) && __cplusplus >= 201103L -#define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) -#endif - -#ifndef ABSL_DEPRECATED -#define ABSL_DEPRECATED(message) -#endif - // ABSL_BAD_CALL_IF() // // Used on a function overload to trap bad calls: any call that matches the @@ -207,6 +99,41 @@ ABSL_NAMESPACE_END : [] { assert(false && #expr); }()) // NOLINT #endif +// `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` +// aborts the program in release mode (when NDEBUG is defined). The +// implementation should abort the program as quickly as possible and ideally it +// should not be possible to ignore the abort request. +#if (ABSL_HAVE_BUILTIN(__builtin_trap) && \ + ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_HARDENING_ABORT() \ + do { \ + __builtin_trap(); \ + __builtin_unreachable(); \ + } while (false) +#else +#define ABSL_INTERNAL_HARDENING_ABORT() abort() +#endif + +// ABSL_HARDENING_ASSERT() +// +// `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement +// runtime assertions that should be enabled in hardened builds even when +// `NDEBUG` is defined. +// +// When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to +// `ABSL_ASSERT()`. +// +// See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on +// hardened mode. +#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) +#define ABSL_HARDENING_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \ + : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) +#else +#define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) +#endif + #ifdef ABSL_HAVE_EXCEPTIONS #define ABSL_INTERNAL_TRY try #define ABSL_INTERNAL_CATCH_ANY catch (...) @@ -217,4 +144,15 @@ ABSL_NAMESPACE_END #define ABSL_INTERNAL_RETHROW do {} while (false) #endif // ABSL_HAVE_EXCEPTIONS +// `ABSL_INTERNAL_UNREACHABLE` is an unreachable statement. A program which +// reaches one has undefined behavior, and the compiler may optimize +// accordingly. +#if defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_INTERNAL_UNREACHABLE __builtin_unreachable() +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_UNREACHABLE __assume(0) +#else +#define ABSL_INTERNAL_UNREACHABLE +#endif + #endif // ABSL_BASE_MACROS_H_ diff --git a/third_party/abseil-cpp/absl/base/optimization.h b/third_party/abseil-cpp/absl/base/optimization.h index 646523b346..d090be1286 100644 --- a/third_party/abseil-cpp/absl/base/optimization.h +++ b/third_party/abseil-cpp/absl/base/optimization.h @@ -22,13 +22,15 @@ #ifndef ABSL_BASE_OPTIMIZATION_H_ #define ABSL_BASE_OPTIMIZATION_H_ +#include <assert.h> + #include "absl/base/config.h" // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION // -// Instructs the compiler to avoid optimizing tail-call recursion. Use of this -// macro is useful when you wish to preserve the existing function order within -// a stack trace for logging, debugging, or profiling purposes. +// Instructs the compiler to avoid optimizing tail-call recursion. This macro is +// useful when you wish to preserve the existing function order within a stack +// trace for logging, debugging, or profiling purposes. // // Example: // @@ -104,9 +106,10 @@ // Cacheline aligning objects properly allows constructive memory sharing and // prevents destructive (or "false") memory sharing. // -// NOTE: this macro should be replaced with usage of `alignas()` using +// NOTE: callers should replace uses of this macro with `alignas()` using // `std::hardware_constructive_interference_size` and/or -// `std::hardware_destructive_interference_size` when available within C++17. +// `std::hardware_destructive_interference_size` when C++17 becomes available to +// them. // // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html // for more information. @@ -171,11 +174,71 @@ // to yield performance improvements. #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ (defined(__GNUC__) && !defined(__clang__)) -#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define ABSL_PREDICT_FALSE(x) (__builtin_expect(false || (x), false)) #define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) #else #define ABSL_PREDICT_FALSE(x) (x) #define ABSL_PREDICT_TRUE(x) (x) #endif +// ABSL_INTERNAL_ASSUME(cond) +// Informs the compiler that a condition is always true and that it can assume +// it to be true for optimization purposes. The call has undefined behavior if +// the condition is false. +// In !NDEBUG mode, the condition is checked with an assert(). +// NOTE: The expression must not have side effects, as it will only be evaluated +// in some compilation modes and not others. +// +// Example: +// +// int x = ...; +// ABSL_INTERNAL_ASSUME(x >= 0); +// // The compiler can optimize the division to a simple right shift using the +// // assumption specified above. +// int y = x / 16; +// +#if !defined(NDEBUG) +#define ABSL_INTERNAL_ASSUME(cond) assert(cond) +#elif ABSL_HAVE_BUILTIN(__builtin_assume) +#define ABSL_INTERNAL_ASSUME(cond) __builtin_assume(cond) +#elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) +#define ABSL_INTERNAL_ASSUME(cond) \ + do { \ + if (!(cond)) __builtin_unreachable(); \ + } while (0) +#elif defined(_MSC_VER) +#define ABSL_INTERNAL_ASSUME(cond) __assume(cond) +#else +#define ABSL_INTERNAL_ASSUME(cond) \ + do { \ + static_cast<void>(false && (cond)); \ + } while (0) +#endif + +// ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) +// This macro forces small unique name on a static file level symbols like +// static local variables or static functions. This is intended to be used in +// macro definitions to optimize the cost of generated code. Do NOT use it on +// symbols exported from translation unit since it may cause a link time +// conflict. +// +// Example: +// +// #define MY_MACRO(txt) +// namespace { +// char VeryVeryLongVarName[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = txt; +// const char* VeryVeryLongFuncName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); +// const char* VeryVeryLongFuncName() { return txt; } +// } +// + +#if defined(__GNUC__) +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) #x +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME1(x) ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME() \ + asm(ABSL_INTERNAL_UNIQUE_SMALL_NAME1(.absl.__COUNTER__)) +#else +#define ABSL_INTERNAL_UNIQUE_SMALL_NAME() +#endif + #endif // ABSL_BASE_OPTIMIZATION_H_ diff --git a/third_party/abseil-cpp/absl/base/optimization_test.cc b/third_party/abseil-cpp/absl/base/optimization_test.cc new file mode 100644 index 0000000000..e83369f322 --- /dev/null +++ b/third_party/abseil-cpp/absl/base/optimization_test.cc @@ -0,0 +1,129 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/base/optimization.h" + +#include "gtest/gtest.h" +#include "absl/types/optional.h" + +namespace { + +// Tests for the ABSL_PREDICT_TRUE and ABSL_PREDICT_FALSE macros. +// The tests only verify that the macros are functionally correct - i.e. code +// behaves as if they weren't used. They don't try to check their impact on +// optimization. + +TEST(PredictTest, PredictTrue) { + EXPECT_TRUE(ABSL_PREDICT_TRUE(true)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(false)); + EXPECT_TRUE(ABSL_PREDICT_TRUE(1 == 1)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(1 == 2)); + + if (ABSL_PREDICT_TRUE(false)) ADD_FAILURE(); + if (!ABSL_PREDICT_TRUE(true)) ADD_FAILURE(); + + EXPECT_TRUE(ABSL_PREDICT_TRUE(true) && true); + EXPECT_TRUE(ABSL_PREDICT_TRUE(true) || false); +} + +TEST(PredictTest, PredictFalse) { + EXPECT_TRUE(ABSL_PREDICT_FALSE(true)); + EXPECT_FALSE(ABSL_PREDICT_FALSE(false)); + EXPECT_TRUE(ABSL_PREDICT_FALSE(1 == 1)); + EXPECT_FALSE(ABSL_PREDICT_FALSE(1 == 2)); + + if (ABSL_PREDICT_FALSE(false)) ADD_FAILURE(); + if (!ABSL_PREDICT_FALSE(true)) ADD_FAILURE(); + + EXPECT_TRUE(ABSL_PREDICT_FALSE(true) && true); + EXPECT_TRUE(ABSL_PREDICT_FALSE(true) || false); +} + +TEST(PredictTest, OneEvaluation) { + // Verify that the expression is only evaluated once. + int x = 0; + if (ABSL_PREDICT_TRUE((++x) == 0)) ADD_FAILURE(); + EXPECT_EQ(x, 1); + if (ABSL_PREDICT_FALSE((++x) == 0)) ADD_FAILURE(); + EXPECT_EQ(x, 2); +} + +TEST(PredictTest, OperatorOrder) { + // Verify that operator order inside and outside the macro behaves well. + // These would fail for a naive '#define ABSL_PREDICT_TRUE(x) x' + EXPECT_TRUE(ABSL_PREDICT_TRUE(1 && 2) == true); + EXPECT_TRUE(ABSL_PREDICT_FALSE(1 && 2) == true); + EXPECT_TRUE(!ABSL_PREDICT_TRUE(1 == 2)); + EXPECT_TRUE(!ABSL_PREDICT_FALSE(1 == 2)); +} + +TEST(PredictTest, Pointer) { + const int x = 3; + const int *good_intptr = &x; + const int *null_intptr = nullptr; + EXPECT_TRUE(ABSL_PREDICT_TRUE(good_intptr)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(null_intptr)); + EXPECT_TRUE(ABSL_PREDICT_FALSE(good_intptr)); + EXPECT_FALSE(ABSL_PREDICT_FALSE(null_intptr)); +} + +TEST(PredictTest, Optional) { + // Note: An optional's truth value is the value's existence, not its truth. + absl::optional<bool> has_value(false); + absl::optional<bool> no_value; + EXPECT_TRUE(ABSL_PREDICT_TRUE(has_value)); + EXPECT_FALSE(ABSL_PREDICT_TRUE(no_value)); + EXPECT_TRUE(ABSL_PREDICT_FALSE(has_value)); + EXPECT_FALSE(ABSL_PREDICT_FALSE(no_value)); +} + +class ImplictlyConvertibleToBool { + public: + explicit ImplictlyConvertibleToBool(bool value) : value_(value) {} + operator bool() const { // NOLINT(google-explicit-constructor) + return value_; + } + + private: + bool value_; +}; + +TEST(PredictTest, ImplicitBoolConversion) { + const ImplictlyConvertibleToBool is_true(true); + const ImplictlyConvertibleToBool is_false(false); + if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE(); + if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE(); + if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE(); + if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE(); +} + +class ExplictlyConvertibleToBool { + public: + explicit ExplictlyConvertibleToBool(bool value) : value_(value) {} + explicit operator bool() const { return value_; } + + private: + bool value_; +}; + +TEST(PredictTest, ExplicitBoolConversion) { + const ExplictlyConvertibleToBool is_true(true); + const ExplictlyConvertibleToBool is_false(false); + if (!ABSL_PREDICT_TRUE(is_true)) ADD_FAILURE(); + if (ABSL_PREDICT_TRUE(is_false)) ADD_FAILURE(); + if (!ABSL_PREDICT_FALSE(is_true)) ADD_FAILURE(); + if (ABSL_PREDICT_FALSE(is_false)) ADD_FAILURE(); +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/base/options.h b/third_party/abseil-cpp/absl/base/options.h index 50f26e24b4..56b4e36ee0 100644 --- a/third_party/abseil-cpp/absl/base/options.h +++ b/third_party/abseil-cpp/absl/base/options.h @@ -1,6 +1,3 @@ -#ifndef ABSL_BASE_OPTIONS_H_ -#define ABSL_BASE_OPTIONS_H_ - // Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -67,6 +64,9 @@ // proper Abseil implementation at compile-time, which will not be sufficient // to guarantee ABI stability to package managers. +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + // Include a standard library header to allow configuration based on the // standard library in use. #ifdef __cplusplus @@ -206,6 +206,33 @@ // allowed. #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 -#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_2020_02_25 +#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20211102 + +// ABSL_OPTION_HARDENED +// +// This option enables a "hardened" build in release mode (in this context, +// release mode is defined as a build where the `NDEBUG` macro is defined). +// +// A value of 0 means that "hardened" mode is not enabled. +// +// A value of 1 means that "hardened" mode is enabled. +// +// Hardened builds have additional security checks enabled when `NDEBUG` is +// defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a +// no-op, as well as disabling other bespoke program consistency checks. By +// defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in +// release mode. These checks guard against programming errors that may lead to +// security vulnerabilities. In release mode, when one of these programming +// errors is encountered, the program will immediately abort, possibly without +// any attempt at logging. +// +// The checks enabled by this option are not free; they do incur runtime cost. +// +// The checks enabled by this option are always active when `NDEBUG` is not +// defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The +// checks enabled by this option may abort the program in a different way and +// log additional information when `NDEBUG` is not defined. + +#define ABSL_OPTION_HARDENED 0 #endif // ABSL_BASE_OPTIONS_H_ diff --git a/third_party/abseil-cpp/absl/base/policy_checks.h b/third_party/abseil-cpp/absl/base/policy_checks.h index 4dfa49e54a..06b3243916 100644 --- a/third_party/abseil-cpp/absl/base/policy_checks.h +++ b/third_party/abseil-cpp/absl/base/policy_checks.h @@ -41,7 +41,7 @@ #endif // ----------------------------------------------------------------------------- -// Compiler Check +// Toolchain Check // ----------------------------------------------------------------------------- // We support MSVC++ 14.0 update 2 and later. diff --git a/third_party/abseil-cpp/absl/base/port.h b/third_party/abseil-cpp/absl/base/port.h index 6c28068d4f..5bc4d6cd95 100644 --- a/third_party/abseil-cpp/absl/base/port.h +++ b/third_party/abseil-cpp/absl/base/port.h @@ -14,7 +14,6 @@ // // This files is a forwarding header for other headers containing various // portability macros and functions. -// This file is used for both C and C++! #ifndef ABSL_BASE_PORT_H_ #define ABSL_BASE_PORT_H_ diff --git a/third_party/abseil-cpp/absl/base/spinlock_test_common.cc b/third_party/abseil-cpp/absl/base/spinlock_test_common.cc index 08f61ba86a..2b572c5b3f 100644 --- a/third_party/abseil-cpp/absl/base/spinlock_test_common.cc +++ b/third_party/abseil-cpp/absl/base/spinlock_test_common.cc @@ -20,10 +20,12 @@ #include <limits> #include <random> #include <thread> // NOLINT(build/c++11) +#include <type_traits> #include <vector> #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/low_level_scheduling.h" #include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/spinlock.h" @@ -56,12 +58,10 @@ namespace { static constexpr int kArrayLength = 10; static uint32_t values[kArrayLength]; -static SpinLock static_spinlock(base_internal::kLinkerInitialized); -static SpinLock static_cooperative_spinlock( - base_internal::kLinkerInitialized, - base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL); -static SpinLock static_noncooperative_spinlock( - base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static SpinLock static_cooperative_spinlock( + absl::kConstInit, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL); +ABSL_CONST_INIT static SpinLock static_noncooperative_spinlock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); // Simple integer hash function based on the public domain lookup2 hash. // http://burtleburtle.net/bob/c/lookup2.c @@ -92,6 +92,7 @@ static void TestFunction(int thread_salt, SpinLock* spinlock) { static void ThreadedTest(SpinLock* spinlock) { std::vector<std::thread> threads; + threads.reserve(kNumThreads); for (int i = 0; i < kNumThreads; ++i) { threads.push_back(std::thread(TestFunction, i, spinlock)); } @@ -105,6 +106,10 @@ static void ThreadedTest(SpinLock* spinlock) { } } +#ifndef ABSL_HAVE_THREAD_SANITIZER +static_assert(std::is_trivially_destructible<SpinLock>(), ""); +#endif + TEST(SpinLock, StackNonCooperativeDisablesScheduling) { SpinLock spinlock(base_internal::SCHEDULE_KERNEL_ONLY); spinlock.Lock(); @@ -191,10 +196,6 @@ TEST(SpinLock, WaitCyclesEncoding) { EXPECT_GT(expected_max_value_decoded, before_max_value_decoded); } -TEST(SpinLockWithThreads, StaticSpinLock) { - ThreadedTest(&static_spinlock); -} - TEST(SpinLockWithThreads, StackSpinLock) { SpinLock spinlock; ThreadedTest(&spinlock); diff --git a/third_party/abseil-cpp/absl/base/thread_annotations.h b/third_party/abseil-cpp/absl/base/thread_annotations.h index 5f51c0c2d2..9695f6de67 100644 --- a/third_party/abseil-cpp/absl/base/thread_annotations.h +++ b/third_party/abseil-cpp/absl/base/thread_annotations.h @@ -34,16 +34,11 @@ #ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_ #define ABSL_BASE_THREAD_ANNOTATIONS_H_ +#include "absl/base/attributes.h" #include "absl/base/config.h" // TODO(mbonadei): Remove after the backward compatibility period. #include "absl/base/internal/thread_annotations.h" // IWYU pragma: export -#if defined(__clang__) -#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) __attribute__((x)) -#else -#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) // no-op -#endif - // ABSL_GUARDED_BY() // // Documents if a shared field or global variable needs to be protected by a @@ -61,8 +56,11 @@ // int p1_ ABSL_GUARDED_BY(mu_); // ... // }; -#define ABSL_GUARDED_BY(x) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(guarded_by(x)) +#if ABSL_HAVE_ATTRIBUTE(guarded_by) +#define ABSL_GUARDED_BY(x) __attribute__((guarded_by(x))) +#else +#define ABSL_GUARDED_BY(x) +#endif // ABSL_PT_GUARDED_BY() // @@ -84,8 +82,11 @@ // // `q_`, guarded by `mu1_`, points to a shared memory location that is // // guarded by `mu2_`: // int *q_ ABSL_GUARDED_BY(mu1_) ABSL_PT_GUARDED_BY(mu2_); -#define ABSL_PT_GUARDED_BY(x) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(pt_guarded_by(x)) +#if ABSL_HAVE_ATTRIBUTE(pt_guarded_by) +#define ABSL_PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x))) +#else +#define ABSL_PT_GUARDED_BY(x) +#endif // ABSL_ACQUIRED_AFTER() / ABSL_ACQUIRED_BEFORE() // @@ -102,11 +103,17 @@ // // Mutex m1_; // Mutex m2_ ABSL_ACQUIRED_AFTER(m1_); -#define ABSL_ACQUIRED_AFTER(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_after(__VA_ARGS__)) +#if ABSL_HAVE_ATTRIBUTE(acquired_after) +#define ABSL_ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__))) +#else +#define ABSL_ACQUIRED_AFTER(...) +#endif -#define ABSL_ACQUIRED_BEFORE(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_before(__VA_ARGS__)) +#if ABSL_HAVE_ATTRIBUTE(acquired_before) +#define ABSL_ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__))) +#else +#define ABSL_ACQUIRED_BEFORE(...) +#endif // ABSL_EXCLUSIVE_LOCKS_REQUIRED() / ABSL_SHARED_LOCKS_REQUIRED() // @@ -131,33 +138,50 @@ // // void foo() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } // void bar() const ABSL_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } -#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ - exclusive_locks_required(__VA_ARGS__)) +#if ABSL_HAVE_ATTRIBUTE(exclusive_locks_required) +#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \ + __attribute__((exclusive_locks_required(__VA_ARGS__))) +#else +#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) +#endif +#if ABSL_HAVE_ATTRIBUTE(shared_locks_required) #define ABSL_SHARED_LOCKS_REQUIRED(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_locks_required(__VA_ARGS__)) + __attribute__((shared_locks_required(__VA_ARGS__))) +#else +#define ABSL_SHARED_LOCKS_REQUIRED(...) +#endif // ABSL_LOCKS_EXCLUDED() // // Documents the locks acquired in the body of the function. These locks // cannot be held when calling this function (as Abseil's `Mutex` locks are // non-reentrant). -#define ABSL_LOCKS_EXCLUDED(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(locks_excluded(__VA_ARGS__)) +#if ABSL_HAVE_ATTRIBUTE(locks_excluded) +#define ABSL_LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) +#else +#define ABSL_LOCKS_EXCLUDED(...) +#endif // ABSL_LOCK_RETURNED() // // Documents a function that returns a mutex without acquiring it. For example, // a public getter method that returns a pointer to a private mutex should // be annotated with ABSL_LOCK_RETURNED. -#define ABSL_LOCK_RETURNED(x) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lock_returned(x)) +#if ABSL_HAVE_ATTRIBUTE(lock_returned) +#define ABSL_LOCK_RETURNED(x) __attribute__((lock_returned(x))) +#else +#define ABSL_LOCK_RETURNED(x) +#endif // ABSL_LOCKABLE // // Documents if a class/type is a lockable type (such as the `Mutex` class). -#define ABSL_LOCKABLE ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lockable) +#if ABSL_HAVE_ATTRIBUTE(lockable) +#define ABSL_LOCKABLE __attribute__((lockable)) +#else +#define ABSL_LOCKABLE +#endif // ABSL_SCOPED_LOCKABLE // @@ -166,30 +190,43 @@ // acquired, and the destructor should use `UNLOCK_FUNCTION()` with no // arguments; the analysis will assume that the destructor unlocks whatever the // constructor locked. -#define ABSL_SCOPED_LOCKABLE \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(scoped_lockable) +#if ABSL_HAVE_ATTRIBUTE(scoped_lockable) +#define ABSL_SCOPED_LOCKABLE __attribute__((scoped_lockable)) +#else +#define ABSL_SCOPED_LOCKABLE +#endif // ABSL_EXCLUSIVE_LOCK_FUNCTION() // // Documents functions that acquire a lock in the body of a function, and do // not release it. -#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ - exclusive_lock_function(__VA_ARGS__)) +#if ABSL_HAVE_ATTRIBUTE(exclusive_lock_function) +#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \ + __attribute__((exclusive_lock_function(__VA_ARGS__))) +#else +#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) +#endif // ABSL_SHARED_LOCK_FUNCTION() // // Documents functions that acquire a shared (reader) lock in the body of a // function, and do not release it. +#if ABSL_HAVE_ATTRIBUTE(shared_lock_function) #define ABSL_SHARED_LOCK_FUNCTION(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_lock_function(__VA_ARGS__)) + __attribute__((shared_lock_function(__VA_ARGS__))) +#else +#define ABSL_SHARED_LOCK_FUNCTION(...) +#endif // ABSL_UNLOCK_FUNCTION() // // Documents functions that expect a lock to be held on entry to the function, // and release it in the body of the function. -#define ABSL_UNLOCK_FUNCTION(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(unlock_function(__VA_ARGS__)) +#if ABSL_HAVE_ATTRIBUTE(unlock_function) +#define ABSL_UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__))) +#else +#define ABSL_UNLOCK_FUNCTION(...) +#endif // ABSL_EXCLUSIVE_TRYLOCK_FUNCTION() / ABSL_SHARED_TRYLOCK_FUNCTION() // @@ -199,31 +236,49 @@ // success, or `false` for functions that return `false` on success. The second // argument specifies the mutex that is locked on success. If unspecified, this // mutex is assumed to be `this`. +#if ABSL_HAVE_ATTRIBUTE(exclusive_trylock_function) #define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ - exclusive_trylock_function(__VA_ARGS__)) + __attribute__((exclusive_trylock_function(__VA_ARGS__))) +#else +#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) +#endif -#define ABSL_SHARED_TRYLOCK_FUNCTION(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ - shared_trylock_function(__VA_ARGS__)) +#if ABSL_HAVE_ATTRIBUTE(shared_trylock_function) +#define ABSL_SHARED_TRYLOCK_FUNCTION(...) \ + __attribute__((shared_trylock_function(__VA_ARGS__))) +#else +#define ABSL_SHARED_TRYLOCK_FUNCTION(...) +#endif // ABSL_ASSERT_EXCLUSIVE_LOCK() / ABSL_ASSERT_SHARED_LOCK() // // Documents functions that dynamically check to see if a lock is held, and fail // if it is not held. +#if ABSL_HAVE_ATTRIBUTE(assert_exclusive_lock) #define ABSL_ASSERT_EXCLUSIVE_LOCK(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_exclusive_lock(__VA_ARGS__)) + __attribute__((assert_exclusive_lock(__VA_ARGS__))) +#else +#define ABSL_ASSERT_EXCLUSIVE_LOCK(...) +#endif +#if ABSL_HAVE_ATTRIBUTE(assert_shared_lock) #define ABSL_ASSERT_SHARED_LOCK(...) \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_shared_lock(__VA_ARGS__)) + __attribute__((assert_shared_lock(__VA_ARGS__))) +#else +#define ABSL_ASSERT_SHARED_LOCK(...) +#endif // ABSL_NO_THREAD_SAFETY_ANALYSIS // // Turns off thread safety checking within the body of a particular function. // This annotation is used to mark functions that are known to be correct, but // the locking behavior is more complicated than the analyzer can handle. +#if ABSL_HAVE_ATTRIBUTE(no_thread_safety_analysis) #define ABSL_NO_THREAD_SAFETY_ANALYSIS \ - ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) + __attribute__((no_thread_safety_analysis)) +#else +#define ABSL_NO_THREAD_SAFETY_ANALYSIS +#endif //------------------------------------------------------------------------------ // Tool-Supplied Annotations @@ -262,7 +317,7 @@ namespace base_internal { // Takes a reference to a guarded data member, and returns an unguarded // reference. -// Do not used this function directly, use ABSL_TS_UNCHECKED_READ instead. +// Do not use this function directly, use ABSL_TS_UNCHECKED_READ instead. template <typename T> inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { return v; diff --git a/third_party/abseil-cpp/absl/cleanup/BUILD.bazel b/third_party/abseil-cpp/absl/cleanup/BUILD.bazel new file mode 100644 index 0000000000..2154d9f1a3 --- /dev/null +++ b/third_party/abseil-cpp/absl/cleanup/BUILD.bazel @@ -0,0 +1,65 @@ +# Copyright 2021 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +cc_library( + name = "cleanup_internal", + hdrs = ["internal/cleanup.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:base_internal", + "//absl/base:core_headers", + "//absl/utility", + ], +) + +cc_library( + name = "cleanup", + hdrs = [ + "cleanup.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":cleanup_internal", + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "cleanup_test", + size = "small", + srcs = [ + "cleanup_test.cc", + ], + copts = ABSL_TEST_COPTS, + deps = [ + ":cleanup", + "//absl/base:config", + "//absl/utility", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/third_party/abseil-cpp/absl/cleanup/CMakeLists.txt b/third_party/abseil-cpp/absl/cleanup/CMakeLists.txt new file mode 100644 index 0000000000..26a6d0dce3 --- /dev/null +++ b/third_party/abseil-cpp/absl/cleanup/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright 2021 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +absl_cc_library( + NAME + cleanup_internal + HDRS + "internal/cleanup.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base_internal + absl::core_headers + absl::utility + PUBLIC +) + +absl_cc_library( + NAME + cleanup + HDRS + "cleanup.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::cleanup_internal + absl::config + absl::core_headers + PUBLIC +) + +absl_cc_test( + NAME + cleanup_test + SRCS + "cleanup_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::cleanup + absl::config + absl::utility + GTest::gmock_main +) diff --git a/third_party/abseil-cpp/absl/cleanup/cleanup.h b/third_party/abseil-cpp/absl/cleanup/cleanup.h new file mode 100644 index 0000000000..960ccd080e --- /dev/null +++ b/third_party/abseil-cpp/absl/cleanup/cleanup.h @@ -0,0 +1,140 @@ +// Copyright 2021 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// File: cleanup.h +// ----------------------------------------------------------------------------- +// +// `absl::Cleanup` implements the scope guard idiom, invoking the contained +// callback's `operator()() &&` on scope exit. +// +// Example: +// +// ``` +// absl::Status CopyGoodData(const char* source_path, const char* sink_path) { +// FILE* source_file = fopen(source_path, "r"); +// if (source_file == nullptr) { +// return absl::NotFoundError("No source file"); // No cleanups execute +// } +// +// // C++17 style cleanup using class template argument deduction +// absl::Cleanup source_closer = [source_file] { fclose(source_file); }; +// +// FILE* sink_file = fopen(sink_path, "w"); +// if (sink_file == nullptr) { +// return absl::NotFoundError("No sink file"); // First cleanup executes +// } +// +// // C++11 style cleanup using the factory function +// auto sink_closer = absl::MakeCleanup([sink_file] { fclose(sink_file); }); +// +// Data data; +// while (ReadData(source_file, &data)) { +// if (!data.IsGood()) { +// absl::Status result = absl::FailedPreconditionError("Read bad data"); +// return result; // Both cleanups execute +// } +// SaveData(sink_file, &data); +// } +// +// return absl::OkStatus(); // Both cleanups execute +// } +// ``` +// +// Methods: +// +// `std::move(cleanup).Cancel()` will prevent the callback from executing. +// +// `std::move(cleanup).Invoke()` will execute the callback early, before +// destruction, and prevent the callback from executing in the destructor. +// +// Usage: +// +// `absl::Cleanup` is not an interface type. It is only intended to be used +// within the body of a function. It is not a value type and instead models a +// control flow construct. Check out `defer` in Golang for something similar. + +#ifndef ABSL_CLEANUP_CLEANUP_H_ +#define ABSL_CLEANUP_CLEANUP_H_ + +#include <utility> + +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/cleanup/internal/cleanup.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +template <typename Arg, typename Callback = void()> +class ABSL_MUST_USE_RESULT Cleanup final { + static_assert(cleanup_internal::WasDeduced<Arg>(), + "Explicit template parameters are not supported."); + + static_assert(cleanup_internal::ReturnsVoid<Callback>(), + "Callbacks that return values are not supported."); + + public: + Cleanup(Callback callback) : storage_(std::move(callback)) {} // NOLINT + + Cleanup(Cleanup&& other) = default; + + void Cancel() && { + ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); + storage_.DestroyCallback(); + } + + void Invoke() && { + ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); + storage_.InvokeCallback(); + storage_.DestroyCallback(); + } + + ~Cleanup() { + if (storage_.IsCallbackEngaged()) { + storage_.InvokeCallback(); + storage_.DestroyCallback(); + } + } + + private: + cleanup_internal::Storage<Callback> storage_; +}; + +// `absl::Cleanup c = /* callback */;` +// +// C++17 type deduction API for creating an instance of `absl::Cleanup` +#if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) +template <typename Callback> +Cleanup(Callback callback) -> Cleanup<cleanup_internal::Tag, Callback>; +#endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) + +// `auto c = absl::MakeCleanup(/* callback */);` +// +// C++11 type deduction API for creating an instance of `absl::Cleanup` +template <typename... Args, typename Callback> +absl::Cleanup<cleanup_internal::Tag, Callback> MakeCleanup(Callback callback) { + static_assert(cleanup_internal::WasDeduced<cleanup_internal::Tag, Args...>(), + "Explicit template parameters are not supported."); + + static_assert(cleanup_internal::ReturnsVoid<Callback>(), + "Callbacks that return values are not supported."); + + return {std::move(callback)}; +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CLEANUP_CLEANUP_H_ diff --git a/third_party/abseil-cpp/absl/cleanup/cleanup_test.cc b/third_party/abseil-cpp/absl/cleanup/cleanup_test.cc new file mode 100644 index 0000000000..46b885899c --- /dev/null +++ b/third_party/abseil-cpp/absl/cleanup/cleanup_test.cc @@ -0,0 +1,311 @@ +// Copyright 2021 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/cleanup/cleanup.h" + +#include <functional> +#include <type_traits> +#include <utility> + +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/utility/utility.h" + +namespace { + +using Tag = absl::cleanup_internal::Tag; + +template <typename Type1, typename Type2> +constexpr bool IsSame() { + return (std::is_same<Type1, Type2>::value); +} + +struct IdentityFactory { + template <typename Callback> + static Callback AsCallback(Callback callback) { + return Callback(std::move(callback)); + } +}; + +// `FunctorClass` is a type used for testing `absl::Cleanup`. It is intended to +// represent users that make their own move-only callback types outside of +// `std::function` and lambda literals. +class FunctorClass { + using Callback = std::function<void()>; + + public: + explicit FunctorClass(Callback callback) : callback_(std::move(callback)) {} + + FunctorClass(FunctorClass&& other) + : callback_(absl::exchange(other.callback_, Callback())) {} + + FunctorClass(const FunctorClass&) = delete; + + FunctorClass& operator=(const FunctorClass&) = delete; + + FunctorClass& operator=(FunctorClass&&) = delete; + + void operator()() const& = delete; + + void operator()() && { + ASSERT_TRUE(callback_); + callback_(); + callback_ = nullptr; + } + + private: + Callback callback_; +}; + +struct FunctorClassFactory { + template <typename Callback> + static FunctorClass AsCallback(Callback callback) { + return FunctorClass(std::move(callback)); + } +}; + +struct StdFunctionFactory { + template <typename Callback> + static std::function<void()> AsCallback(Callback callback) { + return std::function<void()>(std::move(callback)); + } +}; + +using CleanupTestParams = + ::testing::Types<IdentityFactory, FunctorClassFactory, StdFunctionFactory>; +template <typename> +struct CleanupTest : public ::testing::Test {}; +TYPED_TEST_SUITE(CleanupTest, CleanupTestParams); + +bool fn_ptr_called = false; +void FnPtrFunction() { fn_ptr_called = true; } + +TYPED_TEST(CleanupTest, FactoryProducesCorrectType) { + { + auto callback = TypeParam::AsCallback([] {}); + auto cleanup = absl::MakeCleanup(std::move(callback)); + + static_assert( + IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(), + ""); + } + + { + auto cleanup = absl::MakeCleanup(&FnPtrFunction); + + static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(), + ""); + } + + { + auto cleanup = absl::MakeCleanup(FnPtrFunction); + + static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(), + ""); + } +} + +#if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) +TYPED_TEST(CleanupTest, CTADProducesCorrectType) { + { + auto callback = TypeParam::AsCallback([] {}); + absl::Cleanup cleanup = std::move(callback); + + static_assert( + IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(), + ""); + } + + { + absl::Cleanup cleanup = &FnPtrFunction; + + static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(), + ""); + } + + { + absl::Cleanup cleanup = FnPtrFunction; + + static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(), + ""); + } +} + +TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) { + { + auto callback = IdentityFactory::AsCallback([] {}); + auto factory_cleanup = absl::MakeCleanup(callback); + absl::Cleanup deduction_cleanup = callback; + + static_assert( + IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); + } + + { + auto factory_cleanup = + absl::MakeCleanup(FunctorClassFactory::AsCallback([] {})); + absl::Cleanup deduction_cleanup = FunctorClassFactory::AsCallback([] {}); + + static_assert( + IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); + } + + { + auto factory_cleanup = + absl::MakeCleanup(StdFunctionFactory::AsCallback([] {})); + absl::Cleanup deduction_cleanup = StdFunctionFactory::AsCallback([] {}); + + static_assert( + IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); + } + + { + auto factory_cleanup = absl::MakeCleanup(&FnPtrFunction); + absl::Cleanup deduction_cleanup = &FnPtrFunction; + + static_assert( + IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); + } + + { + auto factory_cleanup = absl::MakeCleanup(FnPtrFunction); + absl::Cleanup deduction_cleanup = FnPtrFunction; + + static_assert( + IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); + } +} +#endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) + +TYPED_TEST(CleanupTest, BasicUsage) { + bool called = false; + + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); + EXPECT_FALSE(called); // Constructor shouldn't invoke the callback + } + + EXPECT_TRUE(called); // Destructor should invoke the callback +} + +TYPED_TEST(CleanupTest, BasicUsageWithFunctionPointer) { + fn_ptr_called = false; + + { + auto cleanup = absl::MakeCleanup(TypeParam::AsCallback(&FnPtrFunction)); + EXPECT_FALSE(fn_ptr_called); // Constructor shouldn't invoke the callback + } + + EXPECT_TRUE(fn_ptr_called); // Destructor should invoke the callback +} + +TYPED_TEST(CleanupTest, Cancel) { + bool called = false; + + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); + EXPECT_FALSE(called); // Constructor shouldn't invoke the callback + + std::move(cleanup).Cancel(); + EXPECT_FALSE(called); // Cancel shouldn't invoke the callback + } + + EXPECT_FALSE(called); // Destructor shouldn't invoke the callback +} + +TYPED_TEST(CleanupTest, Invoke) { + bool called = false; + + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); + EXPECT_FALSE(called); // Constructor shouldn't invoke the callback + + std::move(cleanup).Invoke(); + EXPECT_TRUE(called); // Invoke should invoke the callback + + called = false; // Reset tracker before destructor runs + } + + EXPECT_FALSE(called); // Destructor shouldn't invoke the callback +} + +TYPED_TEST(CleanupTest, Move) { + bool called = false; + + { + auto moved_from_cleanup = + absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); + EXPECT_FALSE(called); // Constructor shouldn't invoke the callback + + { + auto moved_to_cleanup = std::move(moved_from_cleanup); + EXPECT_FALSE(called); // Move shouldn't invoke the callback + } + + EXPECT_TRUE(called); // Destructor should invoke the callback + + called = false; // Reset tracker before destructor runs + } + + EXPECT_FALSE(called); // Destructor shouldn't invoke the callback +} + +int DestructionCount = 0; + +struct DestructionCounter { + void operator()() {} + + ~DestructionCounter() { ++DestructionCount; } +}; + +TYPED_TEST(CleanupTest, DestructorDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + } + + EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys +} + +TYPED_TEST(CleanupTest, CancelDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + + std::move(cleanup).Cancel(); + EXPECT_EQ(DestructionCount, 1); // Cancel destroys + } + + EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy +} + +TYPED_TEST(CleanupTest, InvokeDestroys) { + { + auto cleanup = + absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); + DestructionCount = 0; + + std::move(cleanup).Invoke(); + EXPECT_EQ(DestructionCount, 1); // Invoke destroys + } + + EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/cleanup/internal/cleanup.h b/third_party/abseil-cpp/absl/cleanup/internal/cleanup.h new file mode 100644 index 0000000000..2783fcb7c1 --- /dev/null +++ b/third_party/abseil-cpp/absl/cleanup/internal/cleanup.h @@ -0,0 +1,100 @@ +// Copyright 2021 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_ +#define ABSL_CLEANUP_INTERNAL_CLEANUP_H_ + +#include <new> +#include <type_traits> +#include <utility> + +#include "absl/base/internal/invoke.h" +#include "absl/base/macros.h" +#include "absl/base/thread_annotations.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace cleanup_internal { + +struct Tag {}; + +template <typename Arg, typename... Args> +constexpr bool WasDeduced() { + return (std::is_same<cleanup_internal::Tag, Arg>::value) && + (sizeof...(Args) == 0); +} + +template <typename Callback> +constexpr bool ReturnsVoid() { + return (std::is_same<base_internal::invoke_result_t<Callback>, void>::value); +} + +template <typename Callback> +class Storage { + public: + Storage() = delete; + + explicit Storage(Callback callback) { + // Placement-new into a character buffer is used for eager destruction when + // the cleanup is invoked or cancelled. To ensure this optimizes well, the + // behavior is implemented locally instead of using an absl::optional. + ::new (GetCallbackBuffer()) Callback(std::move(callback)); + is_callback_engaged_ = true; + } + + Storage(Storage&& other) { + ABSL_HARDENING_ASSERT(other.IsCallbackEngaged()); + + ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback())); + is_callback_engaged_ = true; + + other.DestroyCallback(); + } + + Storage(const Storage& other) = delete; + + Storage& operator=(Storage&& other) = delete; + + Storage& operator=(const Storage& other) = delete; + + void* GetCallbackBuffer() { return static_cast<void*>(+callback_buffer_); } + + Callback& GetCallback() { + return *reinterpret_cast<Callback*>(GetCallbackBuffer()); + } + + bool IsCallbackEngaged() const { return is_callback_engaged_; } + + void DestroyCallback() { + is_callback_engaged_ = false; + GetCallback().~Callback(); + } + + void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS { + std::move(GetCallback())(); + } + + private: + bool is_callback_engaged_; + alignas(Callback) char callback_buffer_[sizeof(Callback)]; +}; + +} // namespace cleanup_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_CLEANUP_INTERNAL_CLEANUP_H_ diff --git a/third_party/abseil-cpp/absl/compiler_config_setting.bzl b/third_party/abseil-cpp/absl/compiler_config_setting.bzl deleted file mode 100644 index 66962294d0..0000000000 --- a/third_party/abseil-cpp/absl/compiler_config_setting.bzl +++ /dev/null @@ -1,38 +0,0 @@ -# -# Copyright 2018 The Abseil Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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. - -"""Creates config_setting that allows selecting based on 'compiler' value.""" - -def create_llvm_config(name, visibility): - # The "do_not_use_tools_cpp_compiler_present" attribute exists to - # distinguish between older versions of Bazel that do not support - # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do. - # In the future, the only way to select on the compiler will be through - # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can - # be removed. - if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"): - native.config_setting( - name = name, - flag_values = { - "@bazel_tools//tools/cpp:compiler": "llvm", - }, - visibility = visibility, - ) - else: - native.config_setting( - name = name, - values = {"compiler": "llvm"}, - visibility = visibility, - ) diff --git a/third_party/abseil-cpp/absl/container/BUILD.bazel b/third_party/abseil-cpp/absl/container/BUILD.bazel index f221714027..ffaee19cdb 100644 --- a/third_party/abseil-cpp/absl/container/BUILD.bazel +++ b/third_party/abseil-cpp/absl/container/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "compressed_tuple", @@ -60,6 +59,7 @@ cc_library( deps = [ ":compressed_tuple", "//absl/algorithm", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:dynamic_annotations", "//absl/base:throw_delegate", @@ -73,7 +73,9 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":counting_allocator", ":fixed_array", + "//absl/base:config", "//absl/base:exception_testing", "//absl/hash:hash_testing", "//absl/memory", @@ -153,6 +155,7 @@ cc_test( ":counting_allocator", ":inlined_vector", ":test_instance_tracker", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:exception_testing", "//absl/base:raw_logging_internal", @@ -255,6 +258,7 @@ cc_test( ":unordered_map_lookup_test", ":unordered_map_members_test", ":unordered_map_modifiers_test", + "//absl/base:raw_logging_internal", "//absl/types:any", "@com_google_googletest//:gtest_main", ], @@ -288,6 +292,7 @@ cc_test( ":unordered_set_lookup_test", ":unordered_set_members_test", ":unordered_set_modifiers_test", + "//absl/base:raw_logging_internal", "//absl/memory", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -363,7 +368,9 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + "//absl/base:config", "//absl/memory", + "//absl/meta:type_traits", "//absl/utility", ], ) @@ -376,6 +383,7 @@ cc_test( tags = NOTEST_TAGS_NONMOBILE, deps = [ ":container_memory", + ":test_instance_tracker", "//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -390,6 +398,7 @@ cc_library( "//absl/base:config", "//absl/hash", "//absl/strings", + "//absl/strings:cord", ], ) @@ -402,7 +411,10 @@ cc_test( deps = [ ":hash_function_defaults", "//absl/hash", + "//absl/random", "//absl/strings", + "//absl/strings:cord", + "//absl/strings:cord_test_helpers", "@com_google_googletest//:gtest_main", ], ) @@ -497,9 +509,10 @@ cc_library( ":have_sse", "//absl/base", "//absl/base:core_headers", - "//absl/base:exponential_biased", "//absl/debugging:stacktrace", "//absl/memory", + "//absl/profiling:exponential_biased", + "//absl/profiling:sample_recorder", "//absl/synchronization", "//absl/utility", ], @@ -513,6 +526,7 @@ cc_test( ":hashtablez_sampler", ":have_sse", "//absl/base:core_headers", + "//absl/profiling:sample_recorder", "//absl/synchronization", "//absl/synchronization:thread_pool", "//absl/time", @@ -585,13 +599,12 @@ cc_library( ":hashtable_debug_hooks", ":hashtablez_sampler", ":have_sse", - ":layout", - "//absl/base:bits", "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", "//absl/memory", "//absl/meta:type_traits", + "//absl/numeric:bits", "//absl/utility", ], ) @@ -609,6 +622,7 @@ cc_test( ":hashtable_debug", ":raw_hash_set", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/strings", @@ -616,6 +630,45 @@ cc_test( ], ) +cc_binary( + name = "raw_hash_set_benchmark", + testonly = 1, + srcs = ["internal/raw_hash_set_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":hash_function_defaults", + ":raw_hash_set", + "//absl/base:raw_logging_internal", + "//absl/strings:str_format", + "@com_github_google_benchmark//:benchmark_main", + ], +) + +cc_binary( + name = "raw_hash_set_probe_benchmark", + testonly = 1, + srcs = ["internal/raw_hash_set_probe_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = select({ + "//conditions:default": [], + }) + ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":flat_hash_map", + ":hash_function_defaults", + ":hashtable_debug", + ":raw_hash_set", + "//absl/random", + "//absl/random:distributions", + "//absl/strings", + "//absl/strings:str_format", + ], +) + cc_test( name = "raw_hash_set_allocator_test", size = "small", @@ -636,6 +689,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + "//absl/base:config", "//absl/base:core_headers", "//absl/meta:type_traits", "//absl/strings", @@ -654,6 +708,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":layout", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/types:span", @@ -661,6 +716,22 @@ cc_test( ], ) +cc_binary( + name = "layout_benchmark", + testonly = 1, + srcs = ["internal/layout_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":layout", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_library( name = "tracked", testonly = 1, @@ -805,6 +876,22 @@ cc_test( ], ) +cc_test( + name = "sample_element_size_test", + srcs = ["sample_element_size_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = NOTEST_TAGS_NONMOBILE, + visibility = ["//visibility:private"], + deps = [ + ":flat_hash_map", + ":flat_hash_set", + ":node_hash_map", + ":node_hash_set", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "btree", srcs = [ @@ -828,6 +915,7 @@ cc_library( "//absl/memory", "//absl/meta:type_traits", "//absl/strings", + "//absl/strings:cord", "//absl/types:compare", "//absl/utility", ], @@ -844,6 +932,7 @@ cc_library( ":btree", ":flat_hash_set", "//absl/strings", + "//absl/strings:cord", "//absl/time", ], ) @@ -895,6 +984,7 @@ cc_binary( "//absl/flags:flag", "//absl/hash", "//absl/memory", + "//absl/strings:cord", "//absl/strings:str_format", "//absl/time", "@com_github_google_benchmark//:benchmark_main", diff --git a/third_party/abseil-cpp/absl/container/CMakeLists.txt b/third_party/abseil-cpp/absl/container/CMakeLists.txt index e702ba8576..78584d2cef 100644 --- a/third_party/abseil-cpp/absl/container/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/container/CMakeLists.txt @@ -14,15 +14,6 @@ # limitations under the License. # -# This is deprecated and will be removed in the future. It also doesn't do -# anything anyways. Prefer to use the library associated with the API you are -# using. -absl_cc_library( - NAME - container - PUBLIC -) - absl_cc_library( NAME btree @@ -40,6 +31,7 @@ absl_cc_library( absl::compare absl::compressed_tuple absl::container_memory + absl::cord absl::core_headers absl::layout absl::memory @@ -60,6 +52,7 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::btree + absl::cord absl::flat_hash_set absl::strings absl::time @@ -87,7 +80,7 @@ absl_cc_test( absl::strings absl::test_instance_tracker absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -116,7 +109,7 @@ absl_cc_test( absl::optional absl::test_instance_tracker absl::utility - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -129,6 +122,7 @@ absl_cc_library( DEPS absl::compressed_tuple absl::algorithm + absl::config absl::core_headers absl::dynamic_annotations absl::throw_delegate @@ -145,10 +139,12 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::fixed_array + absl::counting_allocator + absl::config absl::exception_testing absl::hash_testing absl::memory - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -162,7 +158,7 @@ absl_cc_test( absl::fixed_array absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -219,13 +215,14 @@ absl_cc_test( absl::counting_allocator absl::inlined_vector absl::test_instance_tracker + absl::config absl::core_headers absl::exception_testing absl::hash_testing absl::memory absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -239,7 +236,7 @@ absl_cc_test( absl::inlined_vector absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -265,7 +262,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::test_instance_tracker - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -299,7 +296,8 @@ absl_cc_test( absl::unordered_map_members_test absl::unordered_map_modifiers_test absl::any - gmock_main + absl::raw_logging_internal + GTest::gmock_main ) absl_cc_library( @@ -335,8 +333,9 @@ absl_cc_test( absl::unordered_set_members_test absl::unordered_set_modifiers_test absl::memory + absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -371,7 +370,7 @@ absl_cc_test( absl::unordered_map_lookup_test absl::unordered_map_members_test absl::unordered_map_modifiers_test - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -405,7 +404,7 @@ absl_cc_test( absl::unordered_set_lookup_test absl::unordered_set_members_test absl::unordered_set_modifiers_test - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -416,7 +415,9 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::memory + absl::type_traits absl::utility PUBLIC ) @@ -431,7 +432,8 @@ absl_cc_test( DEPS absl::container_memory absl::strings - gmock_main + absl::test_instance_tracker + GTest::gmock_main ) absl_cc_library( @@ -443,6 +445,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::config + absl::cord absl::hash absl::strings PUBLIC @@ -456,10 +459,13 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::cord + absl::cord_test_helpers absl::hash_function_defaults absl::hash + absl::random_random absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -501,7 +507,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::hash_policy_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -525,7 +531,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::hash_policy_traits - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -542,6 +548,7 @@ absl_cc_library( absl::base absl::exponential_biased absl::have_sse + absl::sample_recorder absl::synchronization ) @@ -555,7 +562,7 @@ absl_cc_test( DEPS absl::hashtablez_sampler absl::have_sse - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -612,7 +619,7 @@ absl_cc_test( DEPS absl::hash_policy_traits absl::node_hash_policy - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -660,7 +667,6 @@ absl_cc_library( absl::hash_policy_traits absl::hashtable_debug_hooks absl::have_sse - absl::layout absl::memory absl::meta absl::optional @@ -683,10 +689,11 @@ absl_cc_test( absl::hashtable_debug absl::raw_hash_set absl::base + absl::config absl::core_headers absl::raw_logging_internal absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -700,7 +707,7 @@ absl_cc_test( absl::raw_hash_set absl::tracked absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -711,6 +718,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::core_headers absl::meta absl::strings @@ -728,10 +736,11 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::layout + absl::config absl::core_headers absl::raw_logging_internal absl::span - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -756,7 +765,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -770,7 +779,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -783,7 +792,7 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::type_traits - gmock + GTest::gmock TESTONLY ) @@ -797,7 +806,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -811,7 +820,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -825,7 +834,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -838,7 +847,7 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::type_traits - gmock + GTest::gmock TESTONLY ) @@ -852,7 +861,7 @@ absl_cc_library( DEPS absl::hash_generator_testing absl::hash_policy_testing - gmock + GTest::gmock TESTONLY ) @@ -868,7 +877,7 @@ absl_cc_test( absl::unordered_set_lookup_test absl::unordered_set_members_test absl::unordered_set_modifiers_test - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -883,5 +892,20 @@ absl_cc_test( absl::unordered_map_lookup_test absl::unordered_map_members_test absl::unordered_map_modifiers_test - gmock_main + GTest::gmock_main +) + +absl_cc_test( + NAME + sample_element_size_test + SRCS + "sample_element_size_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flat_hash_map + absl::flat_hash_set + absl::node_hash_map + absl::node_hash_set + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/container/btree_benchmark.cc b/third_party/abseil-cpp/absl/container/btree_benchmark.cc index 4af92f9fd8..65b6790b71 100644 --- a/third_party/abseil-cpp/absl/container/btree_benchmark.cc +++ b/third_party/abseil-cpp/absl/container/btree_benchmark.cc @@ -26,6 +26,7 @@ #include <unordered_set> #include <vector> +#include "benchmark/benchmark.h" #include "absl/base/internal/raw_logging.h" #include "absl/container/btree_map.h" #include "absl/container/btree_set.h" @@ -36,9 +37,9 @@ #include "absl/flags/flag.h" #include "absl/hash/hash.h" #include "absl/memory/memory.h" +#include "absl/strings/cord.h" #include "absl/strings/str_format.h" #include "absl/time/time.h" -#include "benchmark/benchmark.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -100,36 +101,24 @@ void BM_InsertSorted(benchmark::State& state) { BM_InsertImpl<T>(state, true); } -// container::insert sometimes returns a pair<iterator, bool> and sometimes -// returns an iterator (for multi- containers). -template <typename Iter> -Iter GetIterFromInsert(const std::pair<Iter, bool>& pair) { - return pair.first; -} -template <typename Iter> -Iter GetIterFromInsert(const Iter iter) { - return iter; -} - -// Benchmark insertion of values into a container at the end. +// Benchmark inserting the first few elements in a container. In b-tree, this is +// when the root node grows. template <typename T> -void BM_InsertEnd(benchmark::State& state) { +void BM_InsertSmall(benchmark::State& state) { using V = typename remove_pair_const<typename T::value_type>::type; - typename KeyOfValue<typename T::key_type, V>::type key_of_value; + const int kSize = 8; + std::vector<V> values = GenerateValues<V>(kSize); T container; - const int kSize = 10000; - for (int i = 0; i < kSize; ++i) { - container.insert(Generator<V>(kSize)(i)); - } - V v = Generator<V>(kSize)(kSize - 1); - typename T::key_type k = key_of_value(v); - auto it = container.find(k); - while (state.KeepRunning()) { - // Repeatedly removing then adding v. - container.erase(it); - it = GetIterFromInsert(container.insert(v)); + while (state.KeepRunningBatch(kSize)) { + for (int i = 0; i < kSize; ++i) { + benchmark::DoNotOptimize(container.insert(values[i])); + } + state.PauseTiming(); + // Do not measure the time it takes to clear the container. + container.clear(); + state.ResumeTiming(); } } @@ -438,6 +427,7 @@ using StdString = std::string; STL_ORDERED_TYPES(int32_t); STL_ORDERED_TYPES(int64_t); STL_ORDERED_TYPES(StdString); +STL_ORDERED_TYPES(Cord); STL_ORDERED_TYPES(Time); #define STL_UNORDERED_TYPES(value) \ @@ -458,6 +448,8 @@ STL_ORDERED_TYPES(Time); using stl_unordered_multimap_##value = \ std::unordered_multimap<value, intptr_t, hash> +STL_UNORDERED_TYPES_CUSTOM_HASH(Cord, absl::Hash<absl::Cord>); + STL_UNORDERED_TYPES(int32_t); STL_UNORDERED_TYPES(int64_t); STL_UNORDERED_TYPES(StdString); @@ -478,6 +470,7 @@ STL_UNORDERED_TYPES_CUSTOM_HASH(Time, absl::Hash<absl::Time>); BTREE_TYPES(int32_t); BTREE_TYPES(int64_t); BTREE_TYPES(StdString); +BTREE_TYPES(Cord); BTREE_TYPES(Time); #define MY_BENCHMARK4(type, func) \ @@ -487,7 +480,7 @@ BTREE_TYPES(Time); #define MY_BENCHMARK3(type) \ MY_BENCHMARK4(type, Insert); \ MY_BENCHMARK4(type, InsertSorted); \ - MY_BENCHMARK4(type, InsertEnd); \ + MY_BENCHMARK4(type, InsertSmall); \ MY_BENCHMARK4(type, Lookup); \ MY_BENCHMARK4(type, FullLookup); \ MY_BENCHMARK4(type, Delete); \ @@ -526,6 +519,7 @@ BTREE_TYPES(Time); MY_BENCHMARK(int32_t); MY_BENCHMARK(int64_t); MY_BENCHMARK(StdString); +MY_BENCHMARK(Cord); MY_BENCHMARK(Time); // Define a type whose size and cost of moving are independently customizable. @@ -538,19 +532,19 @@ struct BigType { BigType() : BigType(0) {} explicit BigType(int x) { std::iota(values.begin(), values.end(), x); } - void Copy(const BigType& x) { - for (int i = 0; i < Size && i < Copies; ++i) values[i] = x.values[i]; + void Copy(const BigType& other) { + for (int i = 0; i < Size && i < Copies; ++i) values[i] = other.values[i]; // If Copies > Size, do extra copies. for (int i = Size, idx = 0; i < Copies; ++i) { - int64_t tmp = x.values[idx]; + int64_t tmp = other.values[idx]; benchmark::DoNotOptimize(tmp); idx = idx + 1 == Size ? 0 : idx + 1; } } - BigType(const BigType& x) { Copy(x); } - BigType& operator=(const BigType& x) { - Copy(x); + BigType(const BigType& other) { Copy(other); } + BigType& operator=(const BigType& other) { + Copy(other); return *this; } @@ -641,14 +635,14 @@ struct BigTypePtr { explicit BigTypePtr(int x) { ptr = absl::make_unique<BigType<Size, Size>>(x); } - BigTypePtr(const BigTypePtr& x) { - ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); + BigTypePtr(const BigTypePtr& other) { + ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr); } - BigTypePtr(BigTypePtr&& x) noexcept = default; - BigTypePtr& operator=(const BigTypePtr& x) { - ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); + BigTypePtr(BigTypePtr&& other) noexcept = default; + BigTypePtr& operator=(const BigTypePtr& other) { + ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr); } - BigTypePtr& operator=(BigTypePtr&& x) noexcept = default; + BigTypePtr& operator=(BigTypePtr&& other) noexcept = default; bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; } bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; } diff --git a/third_party/abseil-cpp/absl/container/btree_map.h b/third_party/abseil-cpp/absl/container/btree_map.h index d23f4ee5e6..f0a8d4a6a4 100644 --- a/third_party/abseil-cpp/absl/container/btree_map.h +++ b/third_party/abseil-cpp/absl/container/btree_map.h @@ -185,7 +185,7 @@ class btree_map // template <typename K> size_type erase(const K& key): // // Erases the element with the matching key, if it exists, returning the - // number of elements erased. + // number of elements erased (0 or 1). using Base::erase; // btree_map::insert() @@ -318,13 +318,18 @@ class btree_map // Extracts the element at the indicated position and returns a node handle // owning that extracted data. // - // template <typename K> node_type extract(const K& x): + // template <typename K> node_type extract(const K& k): // // Extracts the element with the key matching the passed key value and // returns a node handle owning that extracted data. If the `btree_map` // does not contain an element with a matching key, this function returns an // empty node handle. // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). + // // NOTE: In this context, `node_type` refers to the C++17 concept of a // move-only type that owns and provides access to the elements in associative // containers (https://en.cppreference.com/w/cpp/container/node_handle). @@ -361,8 +366,8 @@ class btree_map // Determines whether an element comparing equal to the given `key` exists // within the `btree_map`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::contains; // btree_map::count() @@ -373,15 +378,14 @@ class btree_map // the `btree_map`. Note that this function will return either `1` or `0` // since duplicate elements are not allowed within a `btree_map`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::count; // btree_map::equal_range() // - // Returns a closed range [first, last], defined by a `std::pair` of two - // iterators, containing all elements with the passed key in the - // `btree_map`. + // Returns a half-open range [first, last), defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the `btree_map`. using Base::equal_range; // btree_map::find() @@ -391,10 +395,34 @@ class btree_map // // Finds an element with the passed `key` within the `btree_map`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::find; + // btree_map::lower_bound() + // + // template <typename K> iterator lower_bound(const K& key): + // template <typename K> const_iterator lower_bound(const K& key) const: + // + // Finds the first element with a key that is not less than `key` within the + // `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_map::upper_bound() + // + // template <typename K> iterator upper_bound(const K& key): + // template <typename K> const_iterator upper_bound(const K& key) const: + // + // Finds the first element with a key that is greater than `key` within the + // `btree_map`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_map::operator[]() // // Returns a reference to the value mapped to the passed key within the @@ -645,13 +673,18 @@ class btree_multimap // Extracts the element at the indicated position and returns a node handle // owning that extracted data. // - // template <typename K> node_type extract(const K& x): + // template <typename K> node_type extract(const K& k): // // Extracts the element with the key matching the passed key value and // returns a node handle owning that extracted data. If the `btree_multimap` // does not contain an element with a matching key, this function returns an // empty node handle. // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). + // // NOTE: In this context, `node_type` refers to the C++17 concept of a // move-only type that owns and provides access to the elements in associative // containers (https://en.cppreference.com/w/cpp/container/node_handle). @@ -660,9 +693,8 @@ class btree_multimap // btree_multimap::merge() // - // Extracts elements from a given `source` btree_multimap into this - // `btree_multimap`. If the destination `btree_multimap` already contains an - // element with an equivalent key, that element is not extracted. + // Extracts all elements from a given `source` btree_multimap into this + // `btree_multimap`. using Base::merge; // btree_multimap::swap(btree_multimap& other) @@ -682,8 +714,8 @@ class btree_multimap // Determines whether an element comparing equal to the given `key` exists // within the `btree_multimap`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::contains; // btree_multimap::count() @@ -693,13 +725,13 @@ class btree_multimap // Returns the number of elements comparing equal to the given `key` within // the `btree_multimap`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::count; // btree_multimap::equal_range() // - // Returns a closed range [first, last], defined by a `std::pair` of two + // Returns a half-open range [first, last), defined by a `std::pair` of two // iterators, containing all elements with the passed key in the // `btree_multimap`. using Base::equal_range; @@ -711,10 +743,34 @@ class btree_multimap // // Finds an element with the passed `key` within the `btree_multimap`. // - // Supports heterogeneous lookup, provided that the map is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. using Base::find; + // btree_multimap::lower_bound() + // + // template <typename K> iterator lower_bound(const K& key): + // template <typename K> const_iterator lower_bound(const K& key) const: + // + // Finds the first element with a key that is not less than `key` within the + // `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_multimap::upper_bound() + // + // template <typename K> iterator upper_bound(const K& key): + // template <typename K> const_iterator upper_bound(const K& key) const: + // + // Finds the first element with a key that is greater than `key` within the + // `btree_multimap`. + // + // Supports heterogeneous lookup, provided that the map has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_multimap::get_allocator() // // Returns the allocator function associated with this `btree_multimap`. diff --git a/third_party/abseil-cpp/absl/container/btree_set.h b/third_party/abseil-cpp/absl/container/btree_set.h index 127fb940d4..8973900693 100644 --- a/third_party/abseil-cpp/absl/container/btree_set.h +++ b/third_party/abseil-cpp/absl/container/btree_set.h @@ -183,7 +183,7 @@ class btree_set // template <typename K> size_type erase(const K& key): // // Erases the element with the matching key, if it exists, returning the - // number of elements erased. + // number of elements erased (0 or 1). using Base::erase; // btree_set::insert() @@ -263,7 +263,7 @@ class btree_set // Extracts the element at the indicated position and returns a node handle // owning that extracted data. // - // template <typename K> node_type extract(const K& x): + // template <typename K> node_type extract(const K& k): // // Extracts the element with the key matching the passed key value and // returns a node handle owning that extracted data. If the `btree_set` @@ -300,8 +300,8 @@ class btree_set // Determines whether an element comparing equal to the given `key` exists // within the `btree_set`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::contains; // btree_set::count() @@ -312,8 +312,8 @@ class btree_set // the `btree_set`. Note that this function will return either `1` or `0` // since duplicate elements are not allowed within a `btree_set`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::count; // btree_set::equal_range() @@ -330,10 +330,32 @@ class btree_set // // Finds an element with the passed `key` within the `btree_set`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::find; + // btree_set::lower_bound() + // + // template <typename K> iterator lower_bound(const K& key): + // template <typename K> const_iterator lower_bound(const K& key) const: + // + // Finds the first element that is not less than `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_set::upper_bound() + // + // template <typename K> iterator upper_bound(const K& key): + // template <typename K> const_iterator upper_bound(const K& key) const: + // + // Finds the first element that is greater than `key` within the `btree_set`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_set::get_allocator() // // Returns the allocator function associated with this `btree_set`. @@ -567,7 +589,7 @@ class btree_multiset // Extracts the element at the indicated position and returns a node handle // owning that extracted data. // - // template <typename K> node_type extract(const K& x): + // template <typename K> node_type extract(const K& k): // // Extracts the element with the key matching the passed key value and // returns a node handle owning that extracted data. If the `btree_multiset` @@ -582,9 +604,8 @@ class btree_multiset // btree_multiset::merge() // - // Extracts elements from a given `source` btree_multiset into this - // `btree_multiset`. If the destination `btree_multiset` already contains an - // element with an equivalent key, that element is not extracted. + // Extracts all elements from a given `source` btree_multiset into this + // `btree_multiset`. using Base::merge; // btree_multiset::swap(btree_multiset& other) @@ -604,8 +625,8 @@ class btree_multiset // Determines whether an element comparing equal to the given `key` exists // within the `btree_multiset`, returning `true` if so or `false` otherwise. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::contains; // btree_multiset::count() @@ -615,8 +636,8 @@ class btree_multiset // Returns the number of elements comparing equal to the given `key` within // the `btree_multiset`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::count; // btree_multiset::equal_range() @@ -633,10 +654,34 @@ class btree_multiset // // Finds an element with the passed `key` within the `btree_multiset`. // - // Supports heterogeneous lookup, provided that the set is provided a - // compatible heterogeneous comparator. + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. using Base::find; + // btree_multiset::lower_bound() + // + // template <typename K> iterator lower_bound(const K& key): + // template <typename K> const_iterator lower_bound(const K& key) const: + // + // Finds the first element that is not less than `key` within the + // `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::lower_bound; + + // btree_multiset::upper_bound() + // + // template <typename K> iterator upper_bound(const K& key): + // template <typename K> const_iterator upper_bound(const K& key) const: + // + // Finds the first element that is greater than `key` within the + // `btree_multiset`. + // + // Supports heterogeneous lookup, provided that the set has a compatible + // heterogeneous comparator. + using Base::upper_bound; + // btree_multiset::get_allocator() // // Returns the allocator function associated with this `btree_multiset`. diff --git a/third_party/abseil-cpp/absl/container/btree_test.cc b/third_party/abseil-cpp/absl/container/btree_test.cc index 9edf38f9d0..d27cf27105 100644 --- a/third_party/abseil-cpp/absl/container/btree_test.cc +++ b/third_party/abseil-cpp/absl/container/btree_test.cc @@ -15,6 +15,7 @@ #include "absl/container/btree_test.h" #include <cstdint> +#include <limits> #include <map> #include <memory> #include <stdexcept> @@ -52,7 +53,9 @@ using ::absl::test_internal::MovableOnlyInstance; using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::IsEmpty; +using ::testing::IsNull; using ::testing::Pair; +using ::testing::SizeIs; template <typename T, typename U> void CheckPairEquals(const T &x, const U &y) { @@ -89,8 +92,8 @@ class base_checker { public: base_checker() : const_tree_(tree_) {} - base_checker(const base_checker &x) - : tree_(x.tree_), const_tree_(tree_), checker_(x.checker_) {} + base_checker(const base_checker &other) + : tree_(other.tree_), const_tree_(tree_), checker_(other.checker_) {} template <typename InputIterator> base_checker(InputIterator b, InputIterator e) : tree_(b, e), const_tree_(tree_), checker_(b, e) {} @@ -124,11 +127,11 @@ class base_checker { } return tree_iter; } - void value_check(const value_type &x) { + void value_check(const value_type &v) { typename KeyOfValue<typename TreeType::key_type, typename TreeType::value_type>::type key_of_value; - const key_type &key = key_of_value(x); - CheckPairEquals(*find(key), x); + const key_type &key = key_of_value(v); + CheckPairEquals(*find(key), v); lower_bound(key); upper_bound(key); equal_range(key); @@ -187,9 +190,9 @@ class base_checker { return res; } - base_checker &operator=(const base_checker &x) { - tree_ = x.tree_; - checker_ = x.checker_; + base_checker &operator=(const base_checker &other) { + tree_ = other.tree_; + checker_ = other.checker_; return *this; } @@ -250,9 +253,9 @@ class base_checker { tree_.clear(); checker_.clear(); } - void swap(base_checker &x) { - tree_.swap(x.tree_); - checker_.swap(x.checker_); + void swap(base_checker &other) { + tree_.swap(other.tree_); + checker_.swap(other.checker_); } void verify() const { @@ -323,28 +326,28 @@ class unique_checker : public base_checker<TreeType, CheckerType> { public: unique_checker() : super_type() {} - unique_checker(const unique_checker &x) : super_type(x) {} + unique_checker(const unique_checker &other) : super_type(other) {} template <class InputIterator> unique_checker(InputIterator b, InputIterator e) : super_type(b, e) {} unique_checker &operator=(const unique_checker &) = default; // Insertion routines. - std::pair<iterator, bool> insert(const value_type &x) { + std::pair<iterator, bool> insert(const value_type &v) { int size = this->tree_.size(); std::pair<typename CheckerType::iterator, bool> checker_res = - this->checker_.insert(x); - std::pair<iterator, bool> tree_res = this->tree_.insert(x); + this->checker_.insert(v); + std::pair<iterator, bool> tree_res = this->tree_.insert(v); CheckPairEquals(*tree_res.first, *checker_res.first); EXPECT_EQ(tree_res.second, checker_res.second); EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), size + tree_res.second); return tree_res; } - iterator insert(iterator position, const value_type &x) { + iterator insert(iterator position, const value_type &v) { int size = this->tree_.size(); std::pair<typename CheckerType::iterator, bool> checker_res = - this->checker_.insert(x); - iterator tree_res = this->tree_.insert(position, x); + this->checker_.insert(v); + iterator tree_res = this->tree_.insert(position, v); CheckPairEquals(*tree_res, *checker_res.first); EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), size + checker_res.second); @@ -371,25 +374,25 @@ class multi_checker : public base_checker<TreeType, CheckerType> { public: multi_checker() : super_type() {} - multi_checker(const multi_checker &x) : super_type(x) {} + multi_checker(const multi_checker &other) : super_type(other) {} template <class InputIterator> multi_checker(InputIterator b, InputIterator e) : super_type(b, e) {} multi_checker &operator=(const multi_checker &) = default; // Insertion routines. - iterator insert(const value_type &x) { + iterator insert(const value_type &v) { int size = this->tree_.size(); - auto checker_res = this->checker_.insert(x); - iterator tree_res = this->tree_.insert(x); + auto checker_res = this->checker_.insert(v); + iterator tree_res = this->tree_.insert(v); CheckPairEquals(*tree_res, *checker_res); EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), size + 1); return tree_res; } - iterator insert(iterator position, const value_type &x) { + iterator insert(iterator position, const value_type &v) { int size = this->tree_.size(); - auto checker_res = this->checker_.insert(x); - iterator tree_res = this->tree_.insert(position, x); + auto checker_res = this->checker_.insert(v); + iterator tree_res = this->tree_.insert(position, v); CheckPairEquals(*tree_res, *checker_res); EXPECT_EQ(this->tree_.size(), this->checker_.size()); EXPECT_EQ(this->tree_.size(), size + 1); @@ -592,7 +595,7 @@ void BtreeTest() { using V = typename remove_pair_const<typename T::value_type>::type; const std::vector<V> random_values = GenerateValuesWithSeed<V>( absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values), - testing::GTEST_FLAG(random_seed)); + GTEST_FLAG_GET(random_seed)); unique_checker<T, C> container; @@ -616,7 +619,7 @@ void BtreeMultiTest() { using V = typename remove_pair_const<typename T::value_type>::type; const std::vector<V> random_values = GenerateValuesWithSeed<V>( absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values), - testing::GTEST_FLAG(random_seed)); + GTEST_FLAG_GET(random_seed)); multi_checker<T, C> container; @@ -812,10 +815,12 @@ void MapTest() { TEST(Btree, set_int32) { SetTest<int32_t>(); } TEST(Btree, set_int64) { SetTest<int64_t>(); } TEST(Btree, set_string) { SetTest<std::string>(); } +TEST(Btree, set_cord) { SetTest<absl::Cord>(); } TEST(Btree, set_pair) { SetTest<std::pair<int, int>>(); } TEST(Btree, map_int32) { MapTest<int32_t>(); } TEST(Btree, map_int64) { MapTest<int64_t>(); } TEST(Btree, map_string) { MapTest<std::string>(); } +TEST(Btree, map_cord) { MapTest<absl::Cord>(); } TEST(Btree, map_pair) { MapTest<std::pair<int, int>>(); } template <typename K, int N = 256> @@ -847,10 +852,12 @@ void MultiMapTest() { TEST(Btree, multiset_int32) { MultiSetTest<int32_t>(); } TEST(Btree, multiset_int64) { MultiSetTest<int64_t>(); } TEST(Btree, multiset_string) { MultiSetTest<std::string>(); } +TEST(Btree, multiset_cord) { MultiSetTest<absl::Cord>(); } TEST(Btree, multiset_pair) { MultiSetTest<std::pair<int, int>>(); } TEST(Btree, multimap_int32) { MultiMapTest<int32_t>(); } TEST(Btree, multimap_int64) { MultiMapTest<int64_t>(); } TEST(Btree, multimap_string) { MultiMapTest<std::string>(); } +TEST(Btree, multimap_cord) { MultiMapTest<absl::Cord>(); } TEST(Btree, multimap_pair) { MultiMapTest<std::pair<int, int>>(); } struct CompareIntToString { @@ -1176,6 +1183,103 @@ TEST(Btree, RangeCtorSanity) { EXPECT_EQ(1, tmap.size()); } +} // namespace + +class BtreeNodePeer { + public: + // Yields the size of a leaf node with a specific number of values. + template <typename ValueType> + constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) { + return btree_node< + set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>, + /*TargetNodeSize=*/256, // This parameter isn't used here. + /*Multi=*/false>>::SizeWithNSlots(target_values_per_node); + } + + // Yields the number of slots in a (non-root) leaf node for this btree. + template <typename Btree> + constexpr static size_t GetNumSlotsPerNode() { + return btree_node<typename Btree::params_type>::kNodeSlots; + } + + template <typename Btree> + constexpr static size_t GetMaxFieldType() { + return std::numeric_limits< + typename btree_node<typename Btree::params_type>::field_type>::max(); + } + + template <typename Btree> + constexpr static bool UsesLinearNodeSearch() { + return btree_node<typename Btree::params_type>::use_linear_search::value; + } +}; + +namespace { + +class BtreeMapTest : public ::testing::Test { + public: + struct Key {}; + struct Cmp { + template <typename T> + bool operator()(T, T) const { + return false; + } + }; + + struct KeyLin { + using absl_btree_prefer_linear_node_search = std::true_type; + }; + struct CmpLin : Cmp { + using absl_btree_prefer_linear_node_search = std::true_type; + }; + + struct KeyBin { + using absl_btree_prefer_linear_node_search = std::false_type; + }; + struct CmpBin : Cmp { + using absl_btree_prefer_linear_node_search = std::false_type; + }; + + template <typename K, typename C> + static bool IsLinear() { + return BtreeNodePeer::UsesLinearNodeSearch<absl::btree_map<K, int, C>>(); + } +}; + +TEST_F(BtreeMapTest, TestLinearSearchPreferredForKeyLinearViaAlias) { + // Test requesting linear search by directly exporting an alias. + EXPECT_FALSE((IsLinear<Key, Cmp>())); + EXPECT_TRUE((IsLinear<KeyLin, Cmp>())); + EXPECT_TRUE((IsLinear<Key, CmpLin>())); + EXPECT_TRUE((IsLinear<KeyLin, CmpLin>())); +} + +TEST_F(BtreeMapTest, LinearChoiceTree) { + // Cmp has precedence, and is forcing binary + EXPECT_FALSE((IsLinear<Key, CmpBin>())); + EXPECT_FALSE((IsLinear<KeyLin, CmpBin>())); + EXPECT_FALSE((IsLinear<KeyBin, CmpBin>())); + EXPECT_FALSE((IsLinear<int, CmpBin>())); + EXPECT_FALSE((IsLinear<std::string, CmpBin>())); + // Cmp has precedence, and is forcing linear + EXPECT_TRUE((IsLinear<Key, CmpLin>())); + EXPECT_TRUE((IsLinear<KeyLin, CmpLin>())); + EXPECT_TRUE((IsLinear<KeyBin, CmpLin>())); + EXPECT_TRUE((IsLinear<int, CmpLin>())); + EXPECT_TRUE((IsLinear<std::string, CmpLin>())); + // Cmp has no preference, Key determines linear vs binary. + EXPECT_FALSE((IsLinear<Key, Cmp>())); + EXPECT_TRUE((IsLinear<KeyLin, Cmp>())); + EXPECT_FALSE((IsLinear<KeyBin, Cmp>())); + // arithmetic key w/ std::less or std::greater: linear + EXPECT_TRUE((IsLinear<int, std::less<int>>())); + EXPECT_TRUE((IsLinear<double, std::greater<double>>())); + // arithmetic key w/ custom compare: binary + EXPECT_FALSE((IsLinear<int, Cmp>())); + // non-arithmetic key: binary + EXPECT_FALSE((IsLinear<std::string, std::less<std::string>>())); +} + TEST(Btree, BtreeMapCanHoldMoveOnlyTypes) { absl::btree_map<std::string, std::unique_ptr<std::string>> m; @@ -1268,6 +1372,8 @@ TEST(Btree, KeyCompareToAdapter) { AssertKeyCompareToAdapted<std::less<absl::string_view>, absl::string_view>(); AssertKeyCompareToAdapted<std::greater<absl::string_view>, absl::string_view>(); + AssertKeyCompareToAdapted<std::less<absl::Cord>, absl::Cord>(); + AssertKeyCompareToAdapted<std::greater<absl::Cord>, absl::Cord>(); AssertKeyCompareToNotAdapted<std::less<int>, int>(); AssertKeyCompareToNotAdapted<std::greater<int>, int>(); } @@ -1319,28 +1425,6 @@ TEST(Btree, RValueInsert) { EXPECT_EQ(tracker.swaps(), 0); } -} // namespace - -class BtreeNodePeer { - public: - // Yields the size of a leaf node with a specific number of values. - template <typename ValueType> - constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) { - return btree_node< - set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>, - /*TargetNodeSize=*/256, // This parameter isn't used here. - /*Multi=*/false>>::SizeWithNValues(target_values_per_node); - } - - // Yields the number of values in a (non-root) leaf node for this set. - template <typename Set> - constexpr static size_t GetNumValuesPerNode() { - return btree_node<typename Set::params_type>::kNodeValues; - } -}; - -namespace { - // A btree set with a specific number of values per node. template <typename Key, int TargetValuesPerNode, typename Cmp = std::less<Key>> class SizedBtreeSet @@ -1374,7 +1458,7 @@ void ExpectOperationCounts(const int expected_moves, TEST(Btree, MovesComparisonsCopiesSwapsTracking) { InstanceTracker tracker; // Note: this is minimum number of values per node. - SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3> set3; + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4> set4; // Note: this is the default number of values per node for a set of int32s // (with 64-bit pointers). SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61> set61; @@ -1385,28 +1469,28 @@ TEST(Btree, MovesComparisonsCopiesSwapsTracking) { std::vector<int> values = GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23); - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3); - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61); - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set4)>(), 4); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100); if (sizeof(void *) == 8) { - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(), - BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>()); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(), + BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>()); } // Test key insertion/deletion in random order. - ExpectOperationCounts(45281, 132551, values, &tracker, &set3); + ExpectOperationCounts(56540, 134212, values, &tracker, &set4); ExpectOperationCounts(386718, 129807, values, &tracker, &set61); ExpectOperationCounts(586761, 130310, values, &tracker, &set100); // Test key insertion/deletion in sorted order. std::sort(values.begin(), values.end()); - ExpectOperationCounts(26638, 92134, values, &tracker, &set3); + ExpectOperationCounts(24972, 85563, values, &tracker, &set4); ExpectOperationCounts(20208, 87757, values, &tracker, &set61); ExpectOperationCounts(20124, 96583, values, &tracker, &set100); // Test key insertion/deletion in reverse sorted order. std::reverse(values.begin(), values.end()); - ExpectOperationCounts(49951, 119325, values, &tracker, &set3); + ExpectOperationCounts(54949, 127531, values, &tracker, &set4); ExpectOperationCounts(338813, 118266, values, &tracker, &set61); ExpectOperationCounts(534529, 125279, values, &tracker, &set100); } @@ -1423,9 +1507,9 @@ struct MovableOnlyInstanceThreeWayCompare { TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) { InstanceTracker tracker; // Note: this is minimum number of values per node. - SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3, + SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4, MovableOnlyInstanceThreeWayCompare> - set3; + set4; // Note: this is the default number of values per node for a set of int32s // (with 64-bit pointers). SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61, @@ -1440,28 +1524,28 @@ TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) { std::vector<int> values = GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23); - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3); - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61); - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set4)>(), 4); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100); if (sizeof(void *) == 8) { - EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(), - BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>()); + EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(), + BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>()); } // Test key insertion/deletion in random order. - ExpectOperationCounts(45281, 122560, values, &tracker, &set3); + ExpectOperationCounts(56540, 124221, values, &tracker, &set4); ExpectOperationCounts(386718, 119816, values, &tracker, &set61); ExpectOperationCounts(586761, 120319, values, &tracker, &set100); // Test key insertion/deletion in sorted order. std::sort(values.begin(), values.end()); - ExpectOperationCounts(26638, 92134, values, &tracker, &set3); + ExpectOperationCounts(24972, 85563, values, &tracker, &set4); ExpectOperationCounts(20208, 87757, values, &tracker, &set61); ExpectOperationCounts(20124, 96583, values, &tracker, &set100); // Test key insertion/deletion in reverse sorted order. std::reverse(values.begin(), values.end()); - ExpectOperationCounts(49951, 109326, values, &tracker, &set3); + ExpectOperationCounts(54949, 117532, values, &tracker, &set4); ExpectOperationCounts(338813, 108267, values, &tracker, &set61); ExpectOperationCounts(534529, 115280, values, &tracker, &set100); } @@ -1537,7 +1621,7 @@ TEST(Btree, MapAt) { #ifdef ABSL_HAVE_EXCEPTIONS EXPECT_THROW(map.at(3), std::out_of_range); #else - EXPECT_DEATH(map.at(3), "absl::btree_map::at"); + EXPECT_DEATH_IF_SUPPORTED(map.at(3), "absl::btree_map::at"); #endif } @@ -1624,10 +1708,25 @@ TEST(Btree, StrSplitCompatible) { EXPECT_EQ(split_set, expected_set); } -// We can't use EXPECT_EQ/etc. to compare absl::weak_ordering because they -// convert literal 0 to int and absl::weak_ordering can only be compared with -// literal 0. Defining this function allows for avoiding ClangTidy warnings. -bool Identity(const bool b) { return b; } +TEST(Btree, KeyComp) { + absl::btree_set<int> s; + EXPECT_TRUE(s.key_comp()(1, 2)); + EXPECT_FALSE(s.key_comp()(2, 2)); + EXPECT_FALSE(s.key_comp()(2, 1)); + + absl::btree_map<int, int> m1; + EXPECT_TRUE(m1.key_comp()(1, 2)); + EXPECT_FALSE(m1.key_comp()(2, 2)); + EXPECT_FALSE(m1.key_comp()(2, 1)); + + // Even though we internally adapt the comparator of `m2` to be three-way and + // heterogeneous, the comparator we expose through key_comp() is the original + // unadapted comparator. + absl::btree_map<std::string, int> m2; + EXPECT_TRUE(m2.key_comp()("a", "b")); + EXPECT_FALSE(m2.key_comp()("b", "b")); + EXPECT_FALSE(m2.key_comp()("b", "a")); +} TEST(Btree, ValueComp) { absl::btree_set<int> s; @@ -1640,13 +1739,13 @@ TEST(Btree, ValueComp) { EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(2, 0))); EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(1, 0))); + // Even though we internally adapt the comparator of `m2` to be three-way and + // heterogeneous, the comparator we expose through value_comp() is based on + // the original unadapted comparator. absl::btree_map<std::string, int> m2; - EXPECT_TRUE(Identity( - m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)) < 0)); - EXPECT_TRUE(Identity( - m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)) == 0)); - EXPECT_TRUE(Identity( - m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)) > 0)); + EXPECT_TRUE(m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0))); + EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0))); + EXPECT_FALSE(m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0))); } TEST(Btree, DefaultConstruction) { @@ -1954,6 +2053,30 @@ TEST(Btree, ExtractAndInsertNodeHandleMultiMap) { EXPECT_EQ(res, ++other.begin()); } +TEST(Btree, ExtractMultiMapEquivalentKeys) { + // Note: using string keys means a three-way comparator. + absl::btree_multimap<std::string, int> map; + for (int i = 0; i < 100; ++i) { + for (int j = 0; j < 100; ++j) { + map.insert({absl::StrCat(i), j}); + } + } + + for (int i = 0; i < 100; ++i) { + const std::string key = absl::StrCat(i); + auto node_handle = map.extract(key); + EXPECT_EQ(node_handle.key(), key); + EXPECT_EQ(node_handle.mapped(), 0) << i; + } + + for (int i = 0; i < 100; ++i) { + const std::string key = absl::StrCat(i); + auto node_handle = map.extract(key); + EXPECT_EQ(node_handle.key(), key); + EXPECT_EQ(node_handle.mapped(), 1) << i; + } +} + // For multisets, insert with hint also affects correctness because we need to // insert immediately before the hint if possible. struct InsertMultiHintData { @@ -2095,6 +2218,31 @@ TEST(Btree, MergeIntoMultiMapsWithDifferentComparators) { Pair(4, 1), Pair(4, 4), Pair(5, 5))); } +TEST(Btree, MergeIntoSetMovableOnly) { + absl::btree_set<MovableOnlyInstance> src; + src.insert(MovableOnlyInstance(1)); + absl::btree_multiset<MovableOnlyInstance> dst1; + dst1.insert(MovableOnlyInstance(2)); + absl::btree_set<MovableOnlyInstance> dst2; + + // Test merge into multiset. + dst1.merge(src); + + EXPECT_TRUE(src.empty()); + // ElementsAre/ElementsAreArray don't work with move-only types. + ASSERT_THAT(dst1, SizeIs(2)); + EXPECT_EQ(*dst1.begin(), MovableOnlyInstance(1)); + EXPECT_EQ(*std::next(dst1.begin()), MovableOnlyInstance(2)); + + // Test merge into set. + dst2.merge(dst1); + + EXPECT_TRUE(dst1.empty()); + ASSERT_THAT(dst2, SizeIs(2)); + EXPECT_EQ(*dst2.begin(), MovableOnlyInstance(1)); + EXPECT_EQ(*std::next(dst2.begin()), MovableOnlyInstance(2)); +} + struct KeyCompareToWeakOrdering { template <typename T> absl::weak_ordering operator()(const T &a, const T &b) const { @@ -2126,11 +2274,11 @@ TEST(Btree, UserProvidedKeyCompareToComparators) { TEST(Btree, TryEmplaceBasicTest) { absl::btree_map<int, std::string> m; - // Should construct a std::string from the literal. + // Should construct a string from the literal. m.try_emplace(1, "one"); EXPECT_EQ(1, m.size()); - // Try other std::string constructors and const lvalue key. + // Try other string constructors and const lvalue key. const int key(42); m.try_emplace(key, 3, 'a'); m.try_emplace(2, std::string("two")); @@ -2398,6 +2546,408 @@ TEST(Btree, BitfieldArgument) { m[n]; } +TEST(Btree, SetRangeConstructorAndInsertSupportExplicitConversionComparable) { + const absl::string_view names[] = {"n1", "n2"}; + + absl::btree_set<std::string> name_set1{std::begin(names), std::end(names)}; + EXPECT_THAT(name_set1, ElementsAreArray(names)); + + absl::btree_set<std::string> name_set2; + name_set2.insert(std::begin(names), std::end(names)); + EXPECT_THAT(name_set2, ElementsAreArray(names)); +} + +// A type that is explicitly convertible from int and counts constructor calls. +struct ConstructorCounted { + explicit ConstructorCounted(int i) : i(i) { ++constructor_calls; } + bool operator==(int other) const { return i == other; } + + int i; + static int constructor_calls; +}; +int ConstructorCounted::constructor_calls = 0; + +struct ConstructorCountedCompare { + bool operator()(int a, const ConstructorCounted &b) const { return a < b.i; } + bool operator()(const ConstructorCounted &a, int b) const { return a.i < b; } + bool operator()(const ConstructorCounted &a, + const ConstructorCounted &b) const { + return a.i < b.i; + } + using is_transparent = void; +}; + +TEST(Btree, + SetRangeConstructorAndInsertExplicitConvComparableLimitConstruction) { + const int i[] = {0, 1, 1}; + ConstructorCounted::constructor_calls = 0; + + absl::btree_set<ConstructorCounted, ConstructorCountedCompare> set{ + std::begin(i), std::end(i)}; + EXPECT_THAT(set, ElementsAre(0, 1)); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); + + set.insert(std::begin(i), std::end(i)); + EXPECT_THAT(set, ElementsAre(0, 1)); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); +} + +TEST(Btree, + SetRangeConstructorAndInsertSupportExplicitConversionNonComparable) { + const int i[] = {0, 1}; + + absl::btree_set<std::vector<void *>> s1{std::begin(i), std::end(i)}; + EXPECT_THAT(s1, ElementsAre(IsEmpty(), ElementsAre(IsNull()))); + + absl::btree_set<std::vector<void *>> s2; + s2.insert(std::begin(i), std::end(i)); + EXPECT_THAT(s2, ElementsAre(IsEmpty(), ElementsAre(IsNull()))); +} + +// libstdc++ included with GCC 4.9 has a bug in the std::pair constructors that +// prevents explicit conversions between pair types. +// We only run this test for the libstdc++ from GCC 7 or newer because we can't +// reliably check the libstdc++ version prior to that release. +#if !defined(__GLIBCXX__) || \ + (defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 7) +TEST(Btree, MapRangeConstructorAndInsertSupportExplicitConversionComparable) { + const std::pair<absl::string_view, int> names[] = {{"n1", 1}, {"n2", 2}}; + + absl::btree_map<std::string, int> name_map1{std::begin(names), + std::end(names)}; + EXPECT_THAT(name_map1, ElementsAre(Pair("n1", 1), Pair("n2", 2))); + + absl::btree_map<std::string, int> name_map2; + name_map2.insert(std::begin(names), std::end(names)); + EXPECT_THAT(name_map2, ElementsAre(Pair("n1", 1), Pair("n2", 2))); +} + +TEST(Btree, + MapRangeConstructorAndInsertExplicitConvComparableLimitConstruction) { + const std::pair<int, int> i[] = {{0, 1}, {1, 2}, {1, 3}}; + ConstructorCounted::constructor_calls = 0; + + absl::btree_map<ConstructorCounted, int, ConstructorCountedCompare> map{ + std::begin(i), std::end(i)}; + EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2))); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); + + map.insert(std::begin(i), std::end(i)); + EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2))); + EXPECT_EQ(ConstructorCounted::constructor_calls, 2); +} + +TEST(Btree, + MapRangeConstructorAndInsertSupportExplicitConversionNonComparable) { + const std::pair<int, int> i[] = {{0, 1}, {1, 2}}; + + absl::btree_map<std::vector<void *>, int> m1{std::begin(i), std::end(i)}; + EXPECT_THAT(m1, + ElementsAre(Pair(IsEmpty(), 1), Pair(ElementsAre(IsNull()), 2))); + + absl::btree_map<std::vector<void *>, int> m2; + m2.insert(std::begin(i), std::end(i)); + EXPECT_THAT(m2, + ElementsAre(Pair(IsEmpty(), 1), Pair(ElementsAre(IsNull()), 2))); +} + +TEST(Btree, HeterogeneousTryEmplace) { + absl::btree_map<std::string, int> m; + std::string s = "key"; + absl::string_view sv = s; + m.try_emplace(sv, 1); + EXPECT_EQ(m[s], 1); + + m.try_emplace(m.end(), sv, 2); + EXPECT_EQ(m[s], 1); +} + +TEST(Btree, HeterogeneousOperatorMapped) { + absl::btree_map<std::string, int> m; + std::string s = "key"; + absl::string_view sv = s; + m[sv] = 1; + EXPECT_EQ(m[s], 1); + + m[sv] = 2; + EXPECT_EQ(m[s], 2); +} + +TEST(Btree, HeterogeneousInsertOrAssign) { + absl::btree_map<std::string, int> m; + std::string s = "key"; + absl::string_view sv = s; + m.insert_or_assign(sv, 1); + EXPECT_EQ(m[s], 1); + + m.insert_or_assign(m.end(), sv, 2); + EXPECT_EQ(m[s], 2); +} +#endif + +// This test requires std::launder for mutable key access in node handles. +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 +TEST(Btree, NodeHandleMutableKeyAccess) { + { + absl::btree_map<std::string, std::string> map; + + map["key1"] = "mapped"; + + auto nh = map.extract(map.begin()); + nh.key().resize(3); + map.insert(std::move(nh)); + + EXPECT_THAT(map, ElementsAre(Pair("key", "mapped"))); + } + // Also for multimap. + { + absl::btree_multimap<std::string, std::string> map; + + map.emplace("key1", "mapped"); + + auto nh = map.extract(map.begin()); + nh.key().resize(3); + map.insert(std::move(nh)); + + EXPECT_THAT(map, ElementsAre(Pair("key", "mapped"))); + } +} +#endif + +struct MultiKey { + int i1; + int i2; +}; + +bool operator==(const MultiKey a, const MultiKey b) { + return a.i1 == b.i1 && a.i2 == b.i2; +} + +// A heterogeneous comparator that has different equivalence classes for +// different lookup types. +struct MultiKeyComp { + using is_transparent = void; + bool operator()(const MultiKey a, const MultiKey b) const { + if (a.i1 != b.i1) return a.i1 < b.i1; + return a.i2 < b.i2; + } + bool operator()(const int a, const MultiKey b) const { return a < b.i1; } + bool operator()(const MultiKey a, const int b) const { return a.i1 < b; } +}; + +// A heterogeneous, three-way comparator that has different equivalence classes +// for different lookup types. +struct MultiKeyThreeWayComp { + using is_transparent = void; + absl::weak_ordering operator()(const MultiKey a, const MultiKey b) const { + if (a.i1 < b.i1) return absl::weak_ordering::less; + if (a.i1 > b.i1) return absl::weak_ordering::greater; + if (a.i2 < b.i2) return absl::weak_ordering::less; + if (a.i2 > b.i2) return absl::weak_ordering::greater; + return absl::weak_ordering::equivalent; + } + absl::weak_ordering operator()(const int a, const MultiKey b) const { + if (a < b.i1) return absl::weak_ordering::less; + if (a > b.i1) return absl::weak_ordering::greater; + return absl::weak_ordering::equivalent; + } + absl::weak_ordering operator()(const MultiKey a, const int b) const { + if (a.i1 < b) return absl::weak_ordering::less; + if (a.i1 > b) return absl::weak_ordering::greater; + return absl::weak_ordering::equivalent; + } +}; + +template <typename Compare> +class BtreeMultiKeyTest : public ::testing::Test {}; +using MultiKeyComps = ::testing::Types<MultiKeyComp, MultiKeyThreeWayComp>; +TYPED_TEST_SUITE(BtreeMultiKeyTest, MultiKeyComps); + +TYPED_TEST(BtreeMultiKeyTest, EqualRange) { + absl::btree_set<MultiKey, TypeParam> set; + for (int i = 0; i < 100; ++i) { + for (int j = 0; j < 100; ++j) { + set.insert({i, j}); + } + } + + for (int i = 0; i < 100; ++i) { + auto equal_range = set.equal_range(i); + EXPECT_EQ(equal_range.first->i1, i); + EXPECT_EQ(equal_range.first->i2, 0) << i; + EXPECT_EQ(std::distance(equal_range.first, equal_range.second), 100) << i; + } +} + +TYPED_TEST(BtreeMultiKeyTest, Extract) { + absl::btree_set<MultiKey, TypeParam> set; + for (int i = 0; i < 100; ++i) { + for (int j = 0; j < 100; ++j) { + set.insert({i, j}); + } + } + + for (int i = 0; i < 100; ++i) { + auto node_handle = set.extract(i); + EXPECT_EQ(node_handle.value().i1, i); + EXPECT_EQ(node_handle.value().i2, 0) << i; + } + + for (int i = 0; i < 100; ++i) { + auto node_handle = set.extract(i); + EXPECT_EQ(node_handle.value().i1, i); + EXPECT_EQ(node_handle.value().i2, 1) << i; + } +} + +TYPED_TEST(BtreeMultiKeyTest, Erase) { + absl::btree_set<MultiKey, TypeParam> set = { + {1, 1}, {2, 1}, {2, 2}, {3, 1}}; + EXPECT_EQ(set.erase(2), 2); + EXPECT_THAT(set, ElementsAre(MultiKey{1, 1}, MultiKey{3, 1})); +} + +TYPED_TEST(BtreeMultiKeyTest, Count) { + const absl::btree_set<MultiKey, TypeParam> set = { + {1, 1}, {2, 1}, {2, 2}, {3, 1}}; + EXPECT_EQ(set.count(2), 2); +} + +TEST(Btree, AllocConstructor) { + using Alloc = CountingAllocator<int>; + using Set = absl::btree_set<int, std::less<int>, Alloc>; + int64_t bytes_used = 0; + Alloc alloc(&bytes_used); + Set set(alloc); + + set.insert({1, 2, 3}); + + EXPECT_THAT(set, ElementsAre(1, 2, 3)); + EXPECT_GT(bytes_used, set.size() * sizeof(int)); +} + +TEST(Btree, AllocInitializerListConstructor) { + using Alloc = CountingAllocator<int>; + using Set = absl::btree_set<int, std::less<int>, Alloc>; + int64_t bytes_used = 0; + Alloc alloc(&bytes_used); + Set set({1, 2, 3}, alloc); + + EXPECT_THAT(set, ElementsAre(1, 2, 3)); + EXPECT_GT(bytes_used, set.size() * sizeof(int)); +} + +TEST(Btree, AllocRangeConstructor) { + using Alloc = CountingAllocator<int>; + using Set = absl::btree_set<int, std::less<int>, Alloc>; + int64_t bytes_used = 0; + Alloc alloc(&bytes_used); + std::vector<int> v = {1, 2, 3}; + Set set(v.begin(), v.end(), alloc); + + EXPECT_THAT(set, ElementsAre(1, 2, 3)); + EXPECT_GT(bytes_used, set.size() * sizeof(int)); +} + +TEST(Btree, AllocCopyConstructor) { + using Alloc = CountingAllocator<int>; + using Set = absl::btree_set<int, std::less<int>, Alloc>; + int64_t bytes_used1 = 0; + Alloc alloc1(&bytes_used1); + Set set1(alloc1); + + set1.insert({1, 2, 3}); + + int64_t bytes_used2 = 0; + Alloc alloc2(&bytes_used2); + Set set2(set1, alloc2); + + EXPECT_THAT(set1, ElementsAre(1, 2, 3)); + EXPECT_THAT(set2, ElementsAre(1, 2, 3)); + EXPECT_GT(bytes_used1, set1.size() * sizeof(int)); + EXPECT_EQ(bytes_used1, bytes_used2); +} + +TEST(Btree, AllocMoveConstructor_SameAlloc) { + using Alloc = CountingAllocator<int>; + using Set = absl::btree_set<int, std::less<int>, Alloc>; + int64_t bytes_used = 0; + Alloc alloc(&bytes_used); + Set set1(alloc); + + set1.insert({1, 2, 3}); + + const int64_t original_bytes_used = bytes_used; + EXPECT_GT(original_bytes_used, set1.size() * sizeof(int)); + + Set set2(std::move(set1), alloc); + + EXPECT_THAT(set2, ElementsAre(1, 2, 3)); + EXPECT_EQ(bytes_used, original_bytes_used); +} + +TEST(Btree, AllocMoveConstructor_DifferentAlloc) { + using Alloc = CountingAllocator<int>; + using Set = absl::btree_set<int, std::less<int>, Alloc>; + int64_t bytes_used1 = 0; + Alloc alloc1(&bytes_used1); + Set set1(alloc1); + + set1.insert({1, 2, 3}); + + const int64_t original_bytes_used = bytes_used1; + EXPECT_GT(original_bytes_used, set1.size() * sizeof(int)); + + int64_t bytes_used2 = 0; + Alloc alloc2(&bytes_used2); + Set set2(std::move(set1), alloc2); + + EXPECT_THAT(set2, ElementsAre(1, 2, 3)); + // We didn't free these bytes allocated by `set1` yet. + EXPECT_EQ(bytes_used1, original_bytes_used); + EXPECT_EQ(bytes_used2, original_bytes_used); +} + +bool IntCmp(const int a, const int b) { return a < b; } + +TEST(Btree, SupportsFunctionPtrComparator) { + absl::btree_set<int, decltype(IntCmp) *> set(IntCmp); + set.insert({1, 2, 3}); + EXPECT_THAT(set, ElementsAre(1, 2, 3)); + EXPECT_TRUE(set.key_comp()(1, 2)); + EXPECT_TRUE(set.value_comp()(1, 2)); + + absl::btree_map<int, int, decltype(IntCmp) *> map(&IntCmp); + map[1] = 1; + EXPECT_THAT(map, ElementsAre(Pair(1, 1))); + EXPECT_TRUE(map.key_comp()(1, 2)); + EXPECT_TRUE(map.value_comp()(std::make_pair(1, 1), std::make_pair(2, 2))); +} + +template <typename Compare> +struct TransparentPassThroughComp { + using is_transparent = void; + + // This will fail compilation if we attempt a comparison that Compare does not + // support, and the failure will happen inside the function implementation so + // it can't be avoided by using SFINAE on this comparator. + template <typename T, typename U> + bool operator()(const T &lhs, const U &rhs) const { + return Compare()(lhs, rhs); + } +}; + +TEST(Btree, + SupportsTransparentComparatorThatDoesNotImplementAllVisibleOperators) { + absl::btree_set<MultiKey, TransparentPassThroughComp<MultiKeyComp>> set; + set.insert(MultiKey{1, 2}); + EXPECT_TRUE(set.contains(1)); +} + +TEST(Btree, ConstructImplicitlyWithUnadaptedComparator) { + absl::btree_set<MultiKey, MultiKeyComp> set = {{}, MultiKeyComp{}}; +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/btree_test.h b/third_party/abseil-cpp/absl/container/btree_test.h index 218ba41dc2..624908072d 100644 --- a/third_party/abseil-cpp/absl/container/btree_test.h +++ b/third_party/abseil-cpp/absl/container/btree_test.h @@ -25,6 +25,7 @@ #include "absl/container/btree_map.h" #include "absl/container/btree_set.h" #include "absl/container/flat_hash_set.h" +#include "absl/strings/cord.h" #include "absl/time/time.h" namespace absl { @@ -100,6 +101,16 @@ struct Generator<std::string> { } }; +template <> +struct Generator<Cord> { + int maxval; + explicit Generator(int m) : maxval(m) {} + Cord operator()(int i) const { + char buf[16]; + return Cord(GenerateDigits(buf, i, maxval)); + } +}; + template <typename T, typename U> struct Generator<std::pair<T, U> > { Generator<typename remove_pair_const<T>::type> tgen; diff --git a/third_party/abseil-cpp/absl/container/fixed_array.h b/third_party/abseil-cpp/absl/container/fixed_array.h index a9ce99bafd..839ba0bc16 100644 --- a/third_party/abseil-cpp/absl/container/fixed_array.h +++ b/third_party/abseil-cpp/absl/container/fixed_array.h @@ -41,6 +41,7 @@ #include <type_traits> #include "absl/algorithm/algorithm.h" +#include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" @@ -72,11 +73,6 @@ constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); // uninitialized (e.g. int, int[4], double), and others default-constructed. // This matches the behavior of c-style arrays and `std::array`, but not // `std::vector`. -// -// Note that `FixedArray` does not provide a public allocator; if it requires a -// heap allocation, it will do so with global `::operator new[]()` and -// `::operator delete[]()`, even if T provides class-scope overrides for these -// operators. template <typename T, size_t N = kFixedArrayUseDefault, typename A = std::allocator<T>> class FixedArray { @@ -106,13 +102,13 @@ class FixedArray { public: using allocator_type = typename AllocatorTraits::allocator_type; - using value_type = typename allocator_type::value_type; - using pointer = typename allocator_type::pointer; - using const_pointer = typename allocator_type::const_pointer; - using reference = typename allocator_type::reference; - using const_reference = typename allocator_type::const_reference; - using size_type = typename allocator_type::size_type; - using difference_type = typename allocator_type::difference_type; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator<iterator>; @@ -217,7 +213,7 @@ class FixedArray { // Returns a reference the ith element of the fixed array. // REQUIRES: 0 <= i < size() reference operator[](size_type i) { - assert(i < size()); + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } @@ -225,14 +221,14 @@ class FixedArray { // ith element of the fixed array. // REQUIRES: 0 <= i < size() const_reference operator[](size_type i) const { - assert(i < size()); + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } // FixedArray::at // - // Bounds-checked access. Returns a reference to the ith element of the - // fiexed array, or throws std::out_of_range + // Bounds-checked access. Returns a reference to the ith element of the fixed + // array, or throws std::out_of_range reference at(size_type i) { if (ABSL_PREDICT_FALSE(i >= size())) { base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); @@ -252,20 +248,32 @@ class FixedArray { // FixedArray::front() // // Returns a reference to the first element of the fixed array. - reference front() { return *begin(); } + reference front() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } // Overload of FixedArray::front() to return a reference to the first element // of a fixed array of const values. - const_reference front() const { return *begin(); } + const_reference front() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; + } // FixedArray::back() // // Returns a reference to the last element of the fixed array. - reference back() { return *(end() - 1); } + reference back() { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } // Overload of FixedArray::back() to return a reference to the last element // of a fixed array of const values. - const_reference back() const { return *(end() - 1); } + const_reference back() const { + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; + } // FixedArray::begin() // @@ -410,15 +418,15 @@ class FixedArray { void AnnotateConstruct(size_type n); void AnnotateDestruct(size_type n); -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER void* RedzoneBegin() { return &redzone_begin_; } void* RedzoneEnd() { return &redzone_end_ + 1; } -#endif // ADDRESS_SANITIZER +#endif // ABSL_HAVE_ADDRESS_SANITIZER private: - ADDRESS_SANITIZER_REDZONE(redzone_begin_); + ABSL_ADDRESS_SANITIZER_REDZONE(redzone_begin_); alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])]; - ADDRESS_SANITIZER_REDZONE(redzone_end_); + ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_); }; class EmptyInlinedStorage { @@ -491,22 +499,26 @@ constexpr typename FixedArray<T, N, A>::size_type template <typename T, size_t N, typename A> void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct( typename FixedArray<T, N, A>::size_type n) { -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER if (!n) return; - ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n); - ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin()); -#endif // ADDRESS_SANITIZER + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), + data() + n); + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), + RedzoneBegin()); +#endif // ABSL_HAVE_ADDRESS_SANITIZER static_cast<void>(n); // Mark used when not in asan mode } template <typename T, size_t N, typename A> void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct( typename FixedArray<T, N, A>::size_type n) { -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER if (!n) return; - ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd()); - ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data()); -#endif // ADDRESS_SANITIZER + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, + RedzoneEnd()); + ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), + data()); +#endif // ABSL_HAVE_ADDRESS_SANITIZER static_cast<void>(n); // Mark used when not in asan mode } ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/fixed_array_exception_safety_test.cc b/third_party/abseil-cpp/absl/container/fixed_array_exception_safety_test.cc index a5bb009d98..e5f59299b3 100644 --- a/third_party/abseil-cpp/absl/container/fixed_array_exception_safety_test.cc +++ b/third_party/abseil-cpp/absl/container/fixed_array_exception_safety_test.cc @@ -150,8 +150,7 @@ TEST(FixedArrayExceptionSafety, InitListConstructorWithAlloc) { template <typename FixedArrT> testing::AssertionResult ReadMemory(FixedArrT* fixed_arr) { - // Marked volatile to prevent optimization. Used for running asan tests. - volatile int sum = 0; + int sum = 0; for (const auto& thrower : *fixed_arr) { sum += thrower.Get(); } diff --git a/third_party/abseil-cpp/absl/container/fixed_array_test.cc b/third_party/abseil-cpp/absl/container/fixed_array_test.cc index c960fe51c1..49598e7a05 100644 --- a/third_party/abseil-cpp/absl/container/fixed_array_test.cc +++ b/third_party/abseil-cpp/absl/container/fixed_array_test.cc @@ -27,7 +27,10 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/options.h" +#include "absl/container/internal/counting_allocator.h" #include "absl/hash/hash_testing.h" #include "absl/memory/memory.h" @@ -188,6 +191,21 @@ TEST(FixedArrayTest, AtThrows) { "failed bounds check"); } +TEST(FixedArrayTest, Hardened) { +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::FixedArray<int> a = {1, 2, 3}; + EXPECT_EQ(a[2], 3); + EXPECT_DEATH_IF_SUPPORTED(a[3], ""); + EXPECT_DEATH_IF_SUPPORTED(a[-1], ""); + + absl::FixedArray<int> empty(0); + EXPECT_DEATH_IF_SUPPORTED(empty[0], ""); + EXPECT_DEATH_IF_SUPPORTED(empty[-1], ""); + EXPECT_DEATH_IF_SUPPORTED(empty.front(), ""); + EXPECT_DEATH_IF_SUPPORTED(empty.back(), ""); +#endif +} + TEST(FixedArrayRelationalsTest, EqualArrays) { for (int i = 0; i < 10; ++i) { absl::FixedArray<int, 5> a1(i); @@ -622,70 +640,9 @@ TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) { } #endif // __GNUC__ -// This is a stateful allocator, but the state lives outside of the -// allocator (in whatever test is using the allocator). This is odd -// but helps in tests where the allocator is propagated into nested -// containers - that chain of allocators uses the same state and is -// thus easier to query for aggregate allocation information. -template <typename T> -class CountingAllocator : public std::allocator<T> { - public: - using Alloc = std::allocator<T>; - using pointer = typename Alloc::pointer; - using size_type = typename Alloc::size_type; - - CountingAllocator() : bytes_used_(nullptr), instance_count_(nullptr) {} - explicit CountingAllocator(int64_t* b) - : bytes_used_(b), instance_count_(nullptr) {} - CountingAllocator(int64_t* b, int64_t* a) - : bytes_used_(b), instance_count_(a) {} - - template <typename U> - explicit CountingAllocator(const CountingAllocator<U>& x) - : Alloc(x), - bytes_used_(x.bytes_used_), - instance_count_(x.instance_count_) {} - - pointer allocate(size_type n, const void* const hint = nullptr) { - assert(bytes_used_ != nullptr); - *bytes_used_ += n * sizeof(T); - return Alloc::allocate(n, hint); - } - - void deallocate(pointer p, size_type n) { - Alloc::deallocate(p, n); - assert(bytes_used_ != nullptr); - *bytes_used_ -= n * sizeof(T); - } - - template <typename... Args> - void construct(pointer p, Args&&... args) { - Alloc::construct(p, absl::forward<Args>(args)...); - if (instance_count_) { - *instance_count_ += 1; - } - } - - void destroy(pointer p) { - Alloc::destroy(p); - if (instance_count_) { - *instance_count_ -= 1; - } - } - - template <typename U> - class rebind { - public: - using other = CountingAllocator<U>; - }; - - int64_t* bytes_used_; - int64_t* instance_count_; -}; - TEST(AllocatorSupportTest, CountInlineAllocations) { constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; + using Alloc = absl::container_internal::CountingAllocator<int>; using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>; int64_t allocated = 0; @@ -706,7 +663,7 @@ TEST(AllocatorSupportTest, CountInlineAllocations) { TEST(AllocatorSupportTest, CountOutoflineAllocations) { constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; + using Alloc = absl::container_internal::CountingAllocator<int>; using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>; int64_t allocated = 0; @@ -727,7 +684,7 @@ TEST(AllocatorSupportTest, CountOutoflineAllocations) { TEST(AllocatorSupportTest, CountCopyInlineAllocations) { constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; + using Alloc = absl::container_internal::CountingAllocator<int>; using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>; int64_t allocated1 = 0; @@ -755,7 +712,7 @@ TEST(AllocatorSupportTest, CountCopyInlineAllocations) { TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) { constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; + using Alloc = absl::container_internal::CountingAllocator<int>; using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>; int64_t allocated1 = 0; @@ -787,7 +744,7 @@ TEST(AllocatorSupportTest, SizeValAllocConstructor) { using testing::SizeIs; constexpr size_t inlined_size = 4; - using Alloc = CountingAllocator<int>; + using Alloc = absl::container_internal::CountingAllocator<int>; using AllocFxdArr = absl::FixedArray<int, inlined_size, Alloc>; { @@ -811,16 +768,16 @@ TEST(AllocatorSupportTest, SizeValAllocConstructor) { } } -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER TEST(FixedArrayTest, AddressSanitizerAnnotations1) { absl::FixedArray<int, 32> a(10); int* raw = a.data(); raw[0] = 0; raw[9] = 0; - EXPECT_DEATH(raw[-2] = 0, "container-overflow"); - EXPECT_DEATH(raw[-1] = 0, "container-overflow"); - EXPECT_DEATH(raw[10] = 0, "container-overflow"); - EXPECT_DEATH(raw[31] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[-2] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[-1] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[10] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[31] = 0, "container-overflow"); } TEST(FixedArrayTest, AddressSanitizerAnnotations2) { @@ -828,10 +785,10 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations2) { char* raw = a.data(); raw[0] = 0; raw[11] = 0; - EXPECT_DEATH(raw[-7] = 0, "container-overflow"); - EXPECT_DEATH(raw[-1] = 0, "container-overflow"); - EXPECT_DEATH(raw[12] = 0, "container-overflow"); - EXPECT_DEATH(raw[17] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[-7] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[-1] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[12] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[17] = 0, "container-overflow"); } TEST(FixedArrayTest, AddressSanitizerAnnotations3) { @@ -839,8 +796,8 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations3) { uint64_t* raw = a.data(); raw[0] = 0; raw[19] = 0; - EXPECT_DEATH(raw[-1] = 0, "container-overflow"); - EXPECT_DEATH(raw[20] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[-1] = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[20] = 0, "container-overflow"); } TEST(FixedArrayTest, AddressSanitizerAnnotations4) { @@ -852,13 +809,13 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations4) { // there is only a 8-byte red zone before the container range, so we only // access the last 4 bytes of the struct to make sure it stays within the red // zone. - EXPECT_DEATH(raw[-1].z_ = 0, "container-overflow"); - EXPECT_DEATH(raw[10] = ThreeInts(), "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[-1].z_ = 0, "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[10] = ThreeInts(), "container-overflow"); // The actual size of storage is kDefaultBytes=256, 21*12 = 252, // so reading raw[21] should still trigger the correct warning. - EXPECT_DEATH(raw[21] = ThreeInts(), "container-overflow"); + EXPECT_DEATH_IF_SUPPORTED(raw[21] = ThreeInts(), "container-overflow"); } -#endif // ADDRESS_SANITIZER +#endif // ABSL_HAVE_ADDRESS_SANITIZER TEST(FixedArrayTest, AbslHashValueWorks) { using V = absl::FixedArray<int>; diff --git a/third_party/abseil-cpp/absl/container/flat_hash_map.h b/third_party/abseil-cpp/absl/container/flat_hash_map.h index fcb70d861f..74def0df0e 100644 --- a/third_party/abseil-cpp/absl/container/flat_hash_map.h +++ b/third_party/abseil-cpp/absl/container/flat_hash_map.h @@ -234,7 +234,8 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // // size_type erase(const key_type& key): // - // Erases the element with the matching key, if it exists. + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). using Base::erase; // flat_hash_map::insert() @@ -383,6 +384,11 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // key value and returns a node handle owning that extracted data. If the // `flat_hash_map` does not contain an element with a matching key, this // function returns an empty node handle. + // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). using Base::extract; // flat_hash_map::merge() diff --git a/third_party/abseil-cpp/absl/container/flat_hash_map_test.cc b/third_party/abseil-cpp/absl/container/flat_hash_map_test.cc index 728b693a07..8dda1d3539 100644 --- a/third_party/abseil-cpp/absl/container/flat_hash_map_test.cc +++ b/third_party/abseil-cpp/absl/container/flat_hash_map_test.cc @@ -16,6 +16,7 @@ #include <memory> +#include "absl/base/internal/raw_logging.h" #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" @@ -34,6 +35,19 @@ using ::testing::IsEmpty; using ::testing::Pair; using ::testing::UnorderedElementsAre; +// Check that absl::flat_hash_map works in a global constructor. +struct BeforeMain { + BeforeMain() { + absl::flat_hash_map<int, int> x; + x.insert({1, 1}); + ABSL_RAW_CHECK(x.find(0) == x.end(), "x should not contain 0"); + auto it = x.find(1); + ABSL_RAW_CHECK(it != x.end(), "x should contain 1"); + ABSL_RAW_CHECK(it->second, "1 should map to 1"); + } +}; +const BeforeMain before_main; + template <class K, class V> using Map = flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual, Alloc<std::pair<const K, V>>>; @@ -253,6 +267,47 @@ TEST(FlatHashMap, EraseIf) { } } +// This test requires std::launder for mutable key access in node handles. +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 +TEST(FlatHashMap, NodeHandleMutableKeyAccess) { + flat_hash_map<std::string, std::string> map; + + map["key1"] = "mapped"; + + auto nh = map.extract(map.begin()); + nh.key().resize(3); + map.insert(std::move(nh)); + + EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped"))); +} +#endif + +TEST(FlatHashMap, Reserve) { + // Verify that if we reserve(size() + n) then we can perform n insertions + // without a rehash, i.e., without invalidating any references. + for (size_t trial = 0; trial < 20; ++trial) { + for (size_t initial = 3; initial < 100; ++initial) { + // Fill in `initial` entries, then erase 2 of them, then reserve space for + // two inserts and check for reference stability while doing the inserts. + flat_hash_map<size_t, size_t> map; + for (size_t i = 0; i < initial; ++i) { + map[i] = i; + } + map.erase(0); + map.erase(1); + map.reserve(map.size() + 2); + size_t& a2 = map[2]; + // In the event of a failure, asan will complain in one of these two + // assignments. + map[initial] = a2; + map[initial + 1] = a2; + // Fail even when not under asan: + size_t& a2new = map[2]; + EXPECT_EQ(&a2, &a2new); + } + } +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/flat_hash_set.h b/third_party/abseil-cpp/absl/container/flat_hash_set.h index 94be6e3d13..6b89da6571 100644 --- a/third_party/abseil-cpp/absl/container/flat_hash_set.h +++ b/third_party/abseil-cpp/absl/container/flat_hash_set.h @@ -227,7 +227,8 @@ class flat_hash_set // // size_type erase(const key_type& key): // - // Erases the element with the matching key, if it exists. + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). using Base::erase; // flat_hash_set::insert() @@ -323,7 +324,7 @@ class flat_hash_set // flat_hash_set::merge() // - // Extracts elements from a given `source` flat hash map into this + // Extracts elements from a given `source` flat hash set into this // `flat_hash_set`. If the destination `flat_hash_set` already contains an // element with an equivalent key, that element is not extracted. using Base::merge; diff --git a/third_party/abseil-cpp/absl/container/flat_hash_set_test.cc b/third_party/abseil-cpp/absl/container/flat_hash_set_test.cc index 40d7f85c5d..8f6f9944ca 100644 --- a/third_party/abseil-cpp/absl/container/flat_hash_set_test.cc +++ b/third_party/abseil-cpp/absl/container/flat_hash_set_test.cc @@ -16,6 +16,7 @@ #include <vector> +#include "absl/base/internal/raw_logging.h" #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/unordered_set_constructor_test.h" #include "absl/container/internal/unordered_set_lookup_test.h" @@ -36,6 +37,17 @@ using ::testing::Pointee; using ::testing::UnorderedElementsAre; using ::testing::UnorderedElementsAreArray; +// Check that absl::flat_hash_set works in a global constructor. +struct BeforeMain { + BeforeMain() { + absl::flat_hash_set<int> x; + x.insert(1); + ABSL_RAW_CHECK(!x.contains(0), "x should not contain 0"); + ABSL_RAW_CHECK(x.contains(1), "x should contain 1"); + } +}; +const BeforeMain before_main; + template <class T> using Set = absl::flat_hash_set<T, StatefulTestingHash, StatefulTestingEqual, Alloc<T>>; diff --git a/third_party/abseil-cpp/absl/container/inlined_vector.h b/third_party/abseil-cpp/absl/container/inlined_vector.h index 2388d471dc..df9e09917d 100644 --- a/third_party/abseil-cpp/absl/container/inlined_vector.h +++ b/third_party/abseil-cpp/absl/container/inlined_vector.h @@ -48,6 +48,7 @@ #include "absl/algorithm/algorithm.h" #include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/base/port.h" #include "absl/container/internal/inlined_vector.h" @@ -63,7 +64,7 @@ ABSL_NAMESPACE_BEGIN // `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 // 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 +// 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 { @@ -71,37 +72,43 @@ class InlinedVector { using Storage = inlined_vector_internal::Storage<T, N, A>; - using AllocatorTraits = typename Storage::AllocatorTraits; - using RValueReference = typename Storage::RValueReference; - using MoveIterator = typename Storage::MoveIterator; - using IsMemcpyOk = typename Storage::IsMemcpyOk; + template <typename TheA> + using AllocatorTraits = inlined_vector_internal::AllocatorTraits<TheA>; + template <typename TheA> + using MoveIterator = inlined_vector_internal::MoveIterator<TheA>; + template <typename TheA> + using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<TheA>; - template <typename Iterator> + template <typename TheA, typename Iterator> using IteratorValueAdapter = - typename Storage::template IteratorValueAdapter<Iterator>; - using CopyValueAdapter = typename Storage::CopyValueAdapter; - using DefaultValueAdapter = typename Storage::DefaultValueAdapter; + inlined_vector_internal::IteratorValueAdapter<TheA, Iterator>; + template <typename TheA> + using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter<TheA>; + template <typename TheA> + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter<TheA>; template <typename Iterator> using EnableIfAtLeastForwardIterator = absl::enable_if_t< - inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>; + inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>; template <typename Iterator> using DisableIfAtLeastForwardIterator = absl::enable_if_t< - !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>; + !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>; public: - using allocator_type = typename Storage::allocator_type; - using value_type = typename Storage::value_type; - using pointer = typename Storage::pointer; - using const_pointer = typename Storage::const_pointer; - using size_type = typename Storage::size_type; - using difference_type = typename Storage::difference_type; - using reference = typename Storage::reference; - using const_reference = typename Storage::const_reference; - using iterator = typename Storage::iterator; - using const_iterator = typename Storage::const_iterator; - using reverse_iterator = typename Storage::reverse_iterator; - using const_reverse_iterator = typename Storage::const_reverse_iterator; + using allocator_type = A; + using value_type = inlined_vector_internal::ValueType<A>; + using pointer = inlined_vector_internal::Pointer<A>; + using const_pointer = inlined_vector_internal::ConstPointer<A>; + using size_type = inlined_vector_internal::SizeType<A>; + using difference_type = inlined_vector_internal::DifferenceType<A>; + using reference = inlined_vector_internal::Reference<A>; + using const_reference = inlined_vector_internal::ConstReference<A>; + using iterator = inlined_vector_internal::Iterator<A>; + using const_iterator = inlined_vector_internal::ConstIterator<A>; + using reverse_iterator = inlined_vector_internal::ReverseIterator<A>; + using const_reverse_iterator = + inlined_vector_internal::ConstReverseIterator<A>; // --------------------------------------------------------------------------- // InlinedVector Constructors and Destructor @@ -110,28 +117,28 @@ class InlinedVector { // Creates an empty inlined vector with a value-initialized allocator. InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} - // Creates an empty inlined vector with a copy of `alloc`. - explicit InlinedVector(const allocator_type& alloc) noexcept - : storage_(alloc) {} + // Creates an empty inlined vector with a copy of `allocator`. + explicit InlinedVector(const allocator_type& allocator) noexcept + : storage_(allocator) {} // Creates an inlined vector with `n` copies of `value_type()`. explicit InlinedVector(size_type n, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { - storage_.Initialize(DefaultValueAdapter(), n); + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(DefaultValueAdapter<A>(), n); } // Creates an inlined vector with `n` copies of `v`. InlinedVector(size_type n, const_reference v, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { - storage_.Initialize(CopyValueAdapter(v), n); + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(CopyValueAdapter<A>(std::addressof(v)), n); } // Creates an inlined vector with copies of the elements of `list`. InlinedVector(std::initializer_list<value_type> list, - const allocator_type& alloc = allocator_type()) - : InlinedVector(list.begin(), list.end(), alloc) {} + const allocator_type& allocator = allocator_type()) + : InlinedVector(list.begin(), list.end(), allocator) {} // Creates an inlined vector with elements constructed from the provided // forward iterator range [`first`, `last`). @@ -140,37 +147,40 @@ class InlinedVector { // this constructor with two integral arguments and a call to the above // `InlinedVector(size_type, const_reference)` constructor. template <typename ForwardIterator, - EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> + EnableIfAtLeastForwardIterator<ForwardIterator> = 0> InlinedVector(ForwardIterator first, ForwardIterator last, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { - storage_.Initialize(IteratorValueAdapter<ForwardIterator>(first), + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { + storage_.Initialize(IteratorValueAdapter<A, ForwardIterator>(first), std::distance(first, last)); } // Creates an inlined vector with elements constructed from the provided input // iterator range [`first`, `last`). template <typename InputIterator, - DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> + DisableIfAtLeastForwardIterator<InputIterator> = 0> InlinedVector(InputIterator first, InputIterator last, - const allocator_type& alloc = allocator_type()) - : storage_(alloc) { + const allocator_type& allocator = allocator_type()) + : storage_(allocator) { std::copy(first, last, std::back_inserter(*this)); } // Creates an inlined vector by copying the contents of `other` using // `other`'s allocator. InlinedVector(const InlinedVector& other) - : InlinedVector(other, *other.storage_.GetAllocPtr()) {} - - // Creates an inlined vector by copying the contents of `other` using `alloc`. - InlinedVector(const InlinedVector& other, const allocator_type& alloc) - : storage_(alloc) { - if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { + : InlinedVector(other, other.storage_.GetAllocator()) {} + + // Creates an inlined vector by copying the contents of `other` using the + // provided `allocator`. + InlinedVector(const InlinedVector& other, const allocator_type& allocator) + : storage_(allocator) { + if (other.empty()) { + // Empty; nothing to do. + } else if (IsMemcpyOk<A>::value && !other.storage_.GetIsAllocated()) { + // Memcpy-able and do not need allocation. storage_.MemcpyFrom(other.storage_); } else { - storage_.Initialize(IteratorValueAdapter<const_pointer>(other.data()), - other.size()); + storage_.InitFrom(other.storage_); } } @@ -191,23 +201,23 @@ class InlinedVector { InlinedVector(InlinedVector&& other) noexcept( absl::allocator_is_nothrow<allocator_type>::value || std::is_nothrow_move_constructible<value_type>::value) - : storage_(*other.storage_.GetAllocPtr()) { - if (IsMemcpyOk::value) { + : storage_(other.storage_.GetAllocator()) { + if (IsMemcpyOk<A>::value) { storage_.MemcpyFrom(other.storage_); other.storage_.SetInlinedSize(0); } else if (other.storage_.GetIsAllocated()) { - storage_.SetAllocatedData(other.storage_.GetAllocatedData(), - other.storage_.GetAllocatedCapacity()); + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); storage_.SetAllocatedSize(other.storage_.GetSize()); other.storage_.SetInlinedSize(0); } else { - IteratorValueAdapter<MoveIterator> other_values( - MoveIterator(other.storage_.GetInlinedData())); + IteratorValueAdapter<A, MoveIterator<A>> other_values( + MoveIterator<A>(other.storage_.GetInlinedData())); - inlined_vector_internal::ConstructElements( - storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values, + inlined_vector_internal::ConstructElements<A>( + storage_.GetAllocator(), storage_.GetInlinedData(), other_values, other.storage_.GetSize()); storage_.SetInlinedSize(other.storage_.GetSize()); @@ -215,30 +225,32 @@ class InlinedVector { } // Creates an inlined vector by moving in the contents of `other` with a copy - // of `alloc`. + // of `allocator`. // - // NOTE: if `other`'s allocator is not equal to `alloc`, even if `other` + // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other` // contains allocated memory, this move constructor will still allocate. Since // allocation is performed, this constructor can only be `noexcept` if the // specified allocator is also `noexcept`. - InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept( - absl::allocator_is_nothrow<allocator_type>::value) - : storage_(alloc) { - if (IsMemcpyOk::value) { + InlinedVector( + InlinedVector&& other, + const allocator_type& allocator) + noexcept(absl::allocator_is_nothrow<allocator_type>::value) + : storage_(allocator) { + if (IsMemcpyOk<A>::value) { storage_.MemcpyFrom(other.storage_); other.storage_.SetInlinedSize(0); - } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) && + } else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) && other.storage_.GetIsAllocated()) { - storage_.SetAllocatedData(other.storage_.GetAllocatedData(), - other.storage_.GetAllocatedCapacity()); + storage_.SetAllocation({other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()}); storage_.SetAllocatedSize(other.storage_.GetSize()); other.storage_.SetInlinedSize(0); } else { - storage_.Initialize( - IteratorValueAdapter<MoveIterator>(MoveIterator(other.data())), - other.size()); + storage_.Initialize(IteratorValueAdapter<A, MoveIterator<A>>( + MoveIterator<A>(other.data())), + other.size()); } } @@ -307,16 +319,14 @@ class InlinedVector { // // Returns a `reference` to the `i`th element of the inlined vector. reference operator[](size_type i) { - assert(i < size()); - + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } // Overload of `InlinedVector::operator[](...)` that returns a // `const_reference` to the `i`th element of the inlined vector. const_reference operator[](size_type i) const { - assert(i < size()); - + ABSL_HARDENING_ASSERT(i < size()); return data()[i]; } @@ -331,7 +341,6 @@ class InlinedVector { base_internal::ThrowStdOutOfRange( "`InlinedVector::at(size_type)` failed bounds check"); } - return data()[i]; } @@ -345,7 +354,6 @@ class InlinedVector { base_internal::ThrowStdOutOfRange( "`InlinedVector::at(size_type) const` failed bounds check"); } - return data()[i]; } @@ -353,34 +361,30 @@ class InlinedVector { // // Returns a `reference` to the first element of the inlined vector. reference front() { - assert(!empty()); - - return at(0); + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; } // Overload of `InlinedVector::front()` that returns a `const_reference` to // the first element of the inlined vector. const_reference front() const { - assert(!empty()); - - return at(0); + ABSL_HARDENING_ASSERT(!empty()); + return data()[0]; } // `InlinedVector::back()` // // Returns a `reference` to the last element of the inlined vector. reference back() { - assert(!empty()); - - return at(size() - 1); + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; } // Overload of `InlinedVector::back()` that returns a `const_reference` to the // last element of the inlined vector. const_reference back() const { - assert(!empty()); - - return at(size() - 1); + ABSL_HARDENING_ASSERT(!empty()); + return data()[size() - 1]; } // `InlinedVector::begin()` @@ -447,7 +451,7 @@ class InlinedVector { // `InlinedVector::get_allocator()` // // Returns a copy of the inlined vector's allocator. - allocator_type get_allocator() const { return *storage_.GetAllocPtr(); } + allocator_type get_allocator() const { return storage_.GetAllocator(); } // --------------------------------------------------------------------------- // InlinedVector Member Mutators @@ -481,16 +485,16 @@ class InlinedVector { // unspecified state. InlinedVector& operator=(InlinedVector&& other) { if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { - if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { - inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), - size()); + if (IsMemcpyOk<A>::value || other.storage_.GetIsAllocated()) { + inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(), + data(), size()); storage_.DeallocateIfAllocated(); storage_.MemcpyFrom(other.storage_); other.storage_.SetInlinedSize(0); } else { - storage_.Assign(IteratorValueAdapter<MoveIterator>( - MoveIterator(other.storage_.GetInlinedData())), + storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>( + MoveIterator<A>(other.storage_.GetInlinedData())), other.size()); } } @@ -502,7 +506,7 @@ class InlinedVector { // // Replaces the contents of the inlined vector with `n` copies of `v`. void assign(size_type n, const_reference v) { - storage_.Assign(CopyValueAdapter(v), n); + storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n); } // Overload of `InlinedVector::assign(...)` that replaces the contents of the @@ -516,9 +520,9 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "forward" category or better. template <typename ForwardIterator, - EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> + EnableIfAtLeastForwardIterator<ForwardIterator> = 0> void assign(ForwardIterator first, ForwardIterator last) { - storage_.Assign(IteratorValueAdapter<ForwardIterator>(first), + storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first), std::distance(first, last)); } @@ -527,11 +531,11 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "input" category. template <typename InputIterator, - DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> + DisableIfAtLeastForwardIterator<InputIterator> = 0> void assign(InputIterator first, InputIterator last) { size_type i = 0; for (; i < size() && first != last; ++i, static_cast<void>(++first)) { - at(i) = *first; + data()[i] = *first; } erase(data() + i, data() + size()); @@ -542,9 +546,12 @@ class InlinedVector { // // Resizes the inlined vector to contain `n` elements. // - // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n` // is larger than `size()`, new elements are value-initialized. - void resize(size_type n) { storage_.Resize(DefaultValueAdapter(), n); } + void resize(size_type n) { + ABSL_HARDENING_ASSERT(n <= max_size()); + storage_.Resize(DefaultValueAdapter<A>(), n); + } // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to // contain `n` elements. @@ -552,7 +559,8 @@ class InlinedVector { // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` // is larger than `size()`, new elements are copied-constructed from `v`. void resize(size_type n, const_reference v) { - storage_.Resize(CopyValueAdapter(v), n); + ABSL_HARDENING_ASSERT(n <= max_size()); + storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n); } // `InlinedVector::insert(...)` @@ -565,7 +573,7 @@ class InlinedVector { // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using // move semantics, returning an `iterator` to the newly inserted element. - iterator insert(const_iterator pos, RValueReference v) { + iterator insert(const_iterator pos, value_type&& v) { return emplace(pos, std::move(v)); } @@ -573,12 +581,13 @@ class InlinedVector { // of `v` starting at `pos`, returning an `iterator` pointing to the first of // the newly inserted elements. iterator insert(const_iterator pos, size_type n, const_reference v) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); if (ABSL_PREDICT_TRUE(n != 0)) { value_type dealias = v; - return storage_.Insert(pos, CopyValueAdapter(dealias), n); + return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)), + n); } else { return const_cast<iterator>(pos); } @@ -597,14 +606,15 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "forward" category or better. template <typename ForwardIterator, - EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> + EnableIfAtLeastForwardIterator<ForwardIterator> = 0> iterator insert(const_iterator pos, ForwardIterator first, ForwardIterator last) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); if (ABSL_PREDICT_TRUE(first != last)) { - return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first), + return storage_.Insert(pos, + IteratorValueAdapter<A, ForwardIterator>(first), std::distance(first, last)); } else { return const_cast<iterator>(pos); @@ -617,10 +627,10 @@ class InlinedVector { // // NOTE: this overload is for iterators that are "input" category. template <typename InputIterator, - DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> + DisableIfAtLeastForwardIterator<InputIterator> = 0> iterator insert(const_iterator pos, InputIterator first, InputIterator last) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); size_type index = std::distance(cbegin(), pos); for (size_type i = index; first != last; ++i, static_cast<void>(++first)) { @@ -636,13 +646,13 @@ class InlinedVector { // `pos`, returning an `iterator` pointing to the newly emplaced element. template <typename... Args> iterator emplace(const_iterator pos, Args&&... args) { - assert(pos >= begin()); - assert(pos <= end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos <= end()); value_type dealias(std::forward<Args>(args)...); return storage_.Insert(pos, - IteratorValueAdapter<MoveIterator>( - MoveIterator(std::addressof(dealias))), + IteratorValueAdapter<A, MoveIterator<A>>( + MoveIterator<A>(std::addressof(dealias))), 1); } @@ -662,7 +672,7 @@ class InlinedVector { // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` // using move semantics. - void push_back(RValueReference v) { + void push_back(value_type&& v) { static_cast<void>(emplace_back(std::move(v))); } @@ -670,9 +680,9 @@ class InlinedVector { // // Destroys the element at `back()`, reducing the size by `1`. void pop_back() noexcept { - assert(!empty()); + ABSL_HARDENING_ASSERT(!empty()); - AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); + AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1)); storage_.SubtractSize(1); } @@ -683,8 +693,8 @@ class InlinedVector { // // NOTE: may return `end()`, which is not dereferencable. iterator erase(const_iterator pos) { - assert(pos >= begin()); - assert(pos < end()); + ABSL_HARDENING_ASSERT(pos >= begin()); + ABSL_HARDENING_ASSERT(pos < end()); return storage_.Erase(pos, pos + 1); } @@ -695,9 +705,9 @@ class InlinedVector { // // NOTE: may return `end()`, which is not dereferencable. iterator erase(const_iterator from, const_iterator to) { - assert(from >= begin()); - assert(from <= to); - assert(to <= end()); + ABSL_HARDENING_ASSERT(from >= begin()); + ABSL_HARDENING_ASSERT(from <= to); + ABSL_HARDENING_ASSERT(to <= end()); if (ABSL_PREDICT_TRUE(from != to)) { return storage_.Erase(from, to); @@ -711,8 +721,8 @@ class InlinedVector { // Destroys all elements in the inlined vector, setting the size to `0` and // deallocating any held memory. void clear() noexcept { - inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), - size()); + inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(), data(), + size()); storage_.DeallocateIfAllocated(); storage_.SetInlinedSize(0); @@ -725,15 +735,12 @@ class InlinedVector { // `InlinedVector::shrink_to_fit()` // - // Reduces memory usage by freeing unused memory. After being called, calls to - // `capacity()` will be equal to `max(N, size())`. - // - // If `size() <= N` and the inlined vector contains allocated memory, the - // elements will all be moved to the inlined space and the allocated memory - // will be deallocated. + // Attempts to reduce memory usage by moving elements to (or keeping elements + // in) the smallest available buffer sufficient for containing `size()` + // elements. // - // If `size() > N` and `size() < capacity()`, the elements will be moved to a - // smaller allocation. + // If `size()` is sufficiently small, the elements will be moved into (or kept + // in) the inlined space. void shrink_to_fit() { if (storage_.GetIsAllocated()) { storage_.ShrinkToFit(); diff --git a/third_party/abseil-cpp/absl/container/inlined_vector_benchmark.cc b/third_party/abseil-cpp/absl/container/inlined_vector_benchmark.cc index 3f2b4ed28a..e256fad60f 100644 --- a/third_party/abseil-cpp/absl/container/inlined_vector_benchmark.cc +++ b/third_party/abseil-cpp/absl/container/inlined_vector_benchmark.cc @@ -83,7 +83,7 @@ int GetNonShortStringOptimizationSize() { } ABSL_RAW_LOG( FATAL, - "Failed to find a std::string larger than the short std::string optimization"); + "Failed to find a string larger than the short string optimization"); return -1; } @@ -534,6 +534,28 @@ void BM_ConstructFromMove(benchmark::State& state) { ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType); ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType); +// Measure cost of copy-constructor+destructor. +void BM_CopyTrivial(benchmark::State& state) { + const int n = state.range(0); + InlVec<int64_t> src(n); + for (auto s : state) { + InlVec<int64_t> copy(src); + benchmark::DoNotOptimize(copy); + } +} +BENCHMARK(BM_CopyTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize); + +// Measure cost of copy-constructor+destructor. +void BM_CopyNonTrivial(benchmark::State& state) { + const int n = state.range(0); + InlVec<InlVec<int64_t>> src(n); + for (auto s : state) { + InlVec<InlVec<int64_t>> copy(src); + benchmark::DoNotOptimize(copy); + } +} +BENCHMARK(BM_CopyNonTrivial)->Arg(0)->Arg(1)->Arg(kLargeSize); + template <typename T, size_t FromSize, size_t ToSize> void BM_AssignSizeRef(benchmark::State& state) { auto size = ToSize; diff --git a/third_party/abseil-cpp/absl/container/inlined_vector_test.cc b/third_party/abseil-cpp/absl/container/inlined_vector_test.cc index 2c9b0d0e03..98aff33498 100644 --- a/third_party/abseil-cpp/absl/container/inlined_vector_test.cc +++ b/third_party/abseil-cpp/absl/container/inlined_vector_test.cc @@ -30,6 +30,7 @@ #include "absl/base/internal/exception_testing.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" +#include "absl/base/options.h" #include "absl/container/internal/counting_allocator.h" #include "absl/container/internal/test_instance_tracker.h" #include "absl/hash/hash_testing.h" @@ -247,6 +248,16 @@ TEST(IntVec, Erase) { } } +TEST(IntVec, Hardened) { + IntVec v; + Fill(&v, 10); + EXPECT_EQ(v[9], 9); +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + EXPECT_DEATH_IF_SUPPORTED(v[10], ""); + EXPECT_DEATH_IF_SUPPORTED(v[-1], ""); +#endif +} + // At the end of this test loop, the elements between [erase_begin, erase_end) // should have reference counts == 0, and all others elements should have // reference counts == 1. @@ -725,22 +736,26 @@ TEST(OverheadTest, Storage) { // In particular, ensure that std::allocator doesn't cost anything to store. // The union should be absorbing some of the allocation bookkeeping overhead // in the larger vectors, leaving only the size_ field as overhead. - EXPECT_EQ(2 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 1>) - 1 * sizeof(int*)); - EXPECT_EQ(1 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 2>) - 2 * sizeof(int*)); - EXPECT_EQ(1 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 3>) - 3 * sizeof(int*)); - EXPECT_EQ(1 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 4>) - 4 * sizeof(int*)); - EXPECT_EQ(1 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 5>) - 5 * sizeof(int*)); - EXPECT_EQ(1 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 6>) - 6 * sizeof(int*)); - EXPECT_EQ(1 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 7>) - 7 * sizeof(int*)); - EXPECT_EQ(1 * sizeof(int*), - sizeof(absl::InlinedVector<int*, 8>) - 8 * sizeof(int*)); + + struct T { void* val; }; + size_t expected_overhead = sizeof(T); + + EXPECT_EQ((2 * expected_overhead), + sizeof(absl::InlinedVector<T, 1>) - sizeof(T[1])); + EXPECT_EQ(expected_overhead, + sizeof(absl::InlinedVector<T, 2>) - sizeof(T[2])); + EXPECT_EQ(expected_overhead, + sizeof(absl::InlinedVector<T, 3>) - sizeof(T[3])); + EXPECT_EQ(expected_overhead, + sizeof(absl::InlinedVector<T, 4>) - sizeof(T[4])); + EXPECT_EQ(expected_overhead, + sizeof(absl::InlinedVector<T, 5>) - sizeof(T[5])); + EXPECT_EQ(expected_overhead, + sizeof(absl::InlinedVector<T, 6>) - sizeof(T[6])); + EXPECT_EQ(expected_overhead, + sizeof(absl::InlinedVector<T, 7>) - sizeof(T[7])); + EXPECT_EQ(expected_overhead, + sizeof(absl::InlinedVector<T, 8>) - sizeof(T[8])); } TEST(IntVec, Clear) { @@ -780,7 +795,7 @@ TEST(IntVec, Reserve) { TEST(StringVec, SelfRefPushBack) { std::vector<std::string> std_v; absl::InlinedVector<std::string, 4> v; - const std::string s = "A quite long std::string to ensure heap."; + const std::string s = "A quite long string to ensure heap."; std_v.push_back(s); v.push_back(s); for (int i = 0; i < 20; ++i) { @@ -795,7 +810,7 @@ TEST(StringVec, SelfRefPushBack) { TEST(StringVec, SelfRefPushBackWithMove) { std::vector<std::string> std_v; absl::InlinedVector<std::string, 4> v; - const std::string s = "A quite long std::string to ensure heap."; + const std::string s = "A quite long string to ensure heap."; std_v.push_back(s); v.push_back(s); for (int i = 0; i < 20; ++i) { @@ -808,7 +823,7 @@ TEST(StringVec, SelfRefPushBackWithMove) { } TEST(StringVec, SelfMove) { - const std::string s = "A quite long std::string to ensure heap."; + const std::string s = "A quite long string to ensure heap."; for (int len = 0; len < 20; len++) { SCOPED_TRACE(len); absl::InlinedVector<std::string, 8> v; diff --git a/third_party/abseil-cpp/absl/container/internal/btree.h b/third_party/abseil-cpp/absl/container/internal/btree.h index fd5c0e7aba..f636c5fc73 100644 --- a/third_party/abseil-cpp/absl/container/internal/btree.h +++ b/third_party/abseil-cpp/absl/container/internal/btree.h @@ -65,6 +65,7 @@ #include "absl/container/internal/layout.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" +#include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "absl/types/compare.h" #include "absl/utility/utility.h" @@ -87,12 +88,30 @@ struct StringBtreeDefaultLess { // Compatibility constructor. StringBtreeDefaultLess(std::less<std::string>) {} // NOLINT - StringBtreeDefaultLess(std::less<string_view>) {} // NOLINT + StringBtreeDefaultLess(std::less<absl::string_view>) {} // NOLINT + + // Allow converting to std::less for use in key_comp()/value_comp(). + explicit operator std::less<std::string>() const { return {}; } + explicit operator std::less<absl::string_view>() const { return {}; } + explicit operator std::less<absl::Cord>() const { return {}; } absl::weak_ordering operator()(absl::string_view lhs, absl::string_view rhs) const { return compare_internal::compare_result_as_ordering(lhs.compare(rhs)); } + StringBtreeDefaultLess(std::less<absl::Cord>) {} // NOLINT + absl::weak_ordering operator()(const absl::Cord &lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(lhs.Compare(rhs)); + } + absl::weak_ordering operator()(const absl::Cord &lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(lhs.Compare(rhs)); + } + absl::weak_ordering operator()(absl::string_view lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(-rhs.Compare(lhs)); + } }; struct StringBtreeDefaultGreater { @@ -101,23 +120,41 @@ struct StringBtreeDefaultGreater { StringBtreeDefaultGreater() = default; StringBtreeDefaultGreater(std::greater<std::string>) {} // NOLINT - StringBtreeDefaultGreater(std::greater<string_view>) {} // NOLINT + StringBtreeDefaultGreater(std::greater<absl::string_view>) {} // NOLINT + + // Allow converting to std::greater for use in key_comp()/value_comp(). + explicit operator std::greater<std::string>() const { return {}; } + explicit operator std::greater<absl::string_view>() const { return {}; } + explicit operator std::greater<absl::Cord>() const { return {}; } absl::weak_ordering operator()(absl::string_view lhs, absl::string_view rhs) const { return compare_internal::compare_result_as_ordering(rhs.compare(lhs)); } + StringBtreeDefaultGreater(std::greater<absl::Cord>) {} // NOLINT + absl::weak_ordering operator()(const absl::Cord &lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(rhs.Compare(lhs)); + } + absl::weak_ordering operator()(const absl::Cord &lhs, + absl::string_view rhs) const { + return compare_internal::compare_result_as_ordering(-lhs.Compare(rhs)); + } + absl::weak_ordering operator()(absl::string_view lhs, + const absl::Cord &rhs) const { + return compare_internal::compare_result_as_ordering(rhs.Compare(lhs)); + } }; // A helper class to convert a boolean comparison into a three-way "compare-to" -// comparison that returns a negative value to indicate less-than, zero to -// indicate equality and a positive value to indicate greater-than. This helper +// comparison that returns an `absl::weak_ordering`. This helper // class is specialized for less<std::string>, greater<std::string>, -// less<string_view>, and greater<string_view>. +// less<string_view>, greater<string_view>, less<absl::Cord>, and +// greater<absl::Cord>. // // key_compare_to_adapter is provided so that btree users // automatically get the more efficient compare-to code when using common -// google string types with common comparison functors. +// Abseil string types with common comparison functors. // These string-like specializations also turn on heterogeneous lookup by // default. template <typename Compare> @@ -145,10 +182,54 @@ struct key_compare_to_adapter<std::greater<absl::string_view>> { using type = StringBtreeDefaultGreater; }; +template <> +struct key_compare_to_adapter<std::less<absl::Cord>> { + using type = StringBtreeDefaultLess; +}; + +template <> +struct key_compare_to_adapter<std::greater<absl::Cord>> { + using type = StringBtreeDefaultGreater; +}; + +// Detects an 'absl_btree_prefer_linear_node_search' member. This is +// a protocol used as an opt-in or opt-out of linear search. +// +// For example, this would be useful for key types that wrap an integer +// and define their own cheap operator<(). For example: +// +// class K { +// public: +// using absl_btree_prefer_linear_node_search = std::true_type; +// ... +// private: +// friend bool operator<(K a, K b) { return a.k_ < b.k_; } +// int k_; +// }; +// +// btree_map<K, V> m; // Uses linear search +// +// If T has the preference tag, then it has a preference. +// Btree will use the tag's truth value. +template <typename T, typename = void> +struct has_linear_node_search_preference : std::false_type {}; +template <typename T, typename = void> +struct prefers_linear_node_search : std::false_type {}; +template <typename T> +struct has_linear_node_search_preference< + T, absl::void_t<typename T::absl_btree_prefer_linear_node_search>> + : std::true_type {}; +template <typename T> +struct prefers_linear_node_search< + T, absl::void_t<typename T::absl_btree_prefer_linear_node_search>> + : T::absl_btree_prefer_linear_node_search {}; + template <typename Key, typename Compare, typename Alloc, int TargetNodeSize, bool Multi, typename SlotPolicy> struct common_params { - // If Compare is a common comparator for a std::string-like type, then we adapt it + using original_key_compare = Compare; + + // If Compare is a common comparator for a string-like type, then we adapt it // to use heterogeneous lookup and to be a key-compare-to comparator. using key_compare = typename key_compare_to_adapter<Compare>::type; // A type which indicates if we have a key-compare-to functor or a plain old @@ -160,9 +241,6 @@ struct common_params { using size_type = std::make_signed<size_t>::type; using difference_type = ptrdiff_t; - // True if this is a multiset or multimap. - using is_multi_container = std::integral_constant<bool, Multi>; - using slot_policy = SlotPolicy; using slot_type = typename slot_policy::slot_type; using value_type = typename slot_policy::value_type; @@ -172,6 +250,23 @@ struct common_params { using reference = value_type &; using const_reference = const value_type &; + // For the given lookup key type, returns whether we can have multiple + // equivalent keys in the btree. If this is a multi-container, then we can. + // Otherwise, we can have multiple equivalent keys only if all of the + // following conditions are met: + // - The comparator is transparent. + // - The lookup key type is not the same as key_type. + // - The comparator is not a StringBtreeDefault{Less,Greater} comparator + // that we know has the same equivalence classes for all lookup types. + template <typename LookupKey> + constexpr static bool can_have_multiple_equivalent_keys() { + return Multi || + (IsTransparent<key_compare>::value && + !std::is_same<LookupKey, Key>::value && + !std::is_same<key_compare, StringBtreeDefaultLess>::value && + !std::is_same<key_compare, StringBtreeDefaultGreater>::value); + } + enum { kTargetNodeSize = TargetNodeSize, @@ -217,10 +312,6 @@ struct common_params { static void move(Alloc *alloc, slot_type *src, slot_type *dest) { slot_policy::move(alloc, src, dest); } - static void move(Alloc *alloc, slot_type *first, slot_type *last, - slot_type *result) { - slot_policy::move(alloc, first, last, result); - } }; // A parameters structure for holding the type parameters for a btree_map. @@ -238,23 +329,36 @@ struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi, using value_type = typename super_type::value_type; using init_type = typename super_type::init_type; - using key_compare = typename super_type::key_compare; - // Inherit from key_compare for empty base class optimization. - struct value_compare : private key_compare { - value_compare() = default; - explicit value_compare(const key_compare &cmp) : key_compare(cmp) {} + using original_key_compare = typename super_type::original_key_compare; + // Reference: https://en.cppreference.com/w/cpp/container/map/value_compare + class value_compare { + template <typename Params> + friend class btree; + + protected: + explicit value_compare(original_key_compare c) : comp(std::move(c)) {} - template <typename T, typename U> - auto operator()(const T &left, const U &right) const - -> decltype(std::declval<key_compare>()(left.first, right.first)) { - return key_compare::operator()(left.first, right.first); + original_key_compare comp; // NOLINT + + public: + auto operator()(const value_type &lhs, const value_type &rhs) const + -> decltype(comp(lhs.first, rhs.first)) { + return comp(lhs.first, rhs.first); } }; using is_map_container = std::true_type; - static const Key &key(const value_type &x) { return x.first; } - static const Key &key(const init_type &x) { return x.first; } - static const Key &key(const slot_type *x) { return slot_policy::key(x); } + template <typename V> + static auto key(const V &value) -> decltype(value.first) { + return value.first; + } + static const Key &key(const slot_type *s) { return slot_policy::key(s); } + static const Key &key(slot_type *s) { return slot_policy::key(s); } + // For use in node handle. + static auto mutable_key(slot_type *s) + -> decltype(slot_policy::mutable_key(s)) { + return slot_policy::mutable_key(s); + } static mapped_type &value(value_type *value) { return value->second; } }; @@ -295,13 +399,6 @@ struct set_slot_policy { static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) { *dest = std::move(*src); } - - template <typename Alloc> - static void move(Alloc *alloc, slot_type *first, slot_type *last, - slot_type *result) { - for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) - move(alloc, src, dest); - } }; // A parameters structure for holding the type parameters for a btree_set. @@ -312,11 +409,14 @@ struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi, set_slot_policy<Key>> { using value_type = Key; using slot_type = typename set_params::common_params::slot_type; - using value_compare = typename set_params::common_params::key_compare; + using value_compare = + typename set_params::common_params::original_key_compare; using is_map_container = std::false_type; - static const Key &key(const value_type &x) { return x; } - static const Key &key(const slot_type *x) { return *x; } + template <typename V> + static const V &key(const V &value) { return value; } + static const Key &key(const slot_type *slot) { return *slot; } + static const Key &key(slot_type *slot) { return *slot; } }; // An adapter class that converts a lower-bound compare into an upper-bound @@ -326,8 +426,8 @@ struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi, template <typename Compare> struct upper_bound_adapter { explicit upper_bound_adapter(const Compare &c) : comp(c) {} - template <typename K, typename LK> - bool operator()(const K &a, const LK &b) const { + template <typename K1, typename K2> + bool operator()(const K1 &a, const K2 &b) const { // Returns true when a is not greater than b. return !compare_internal::compare_result_as_less_than(comp(b, a)); } @@ -352,6 +452,10 @@ struct SearchResult { // useful information. template <typename V> struct SearchResult<V, false> { + SearchResult() {} + explicit SearchResult(V value) : value(value) {} + SearchResult(V value, MatchKind /*match*/) : value(value) {} + V value; static constexpr bool HasMatch() { return false; } @@ -364,7 +468,6 @@ struct SearchResult<V, false> { template <typename Params> class btree_node { using is_key_compare_to = typename Params::is_key_compare_to; - using is_multi_container = typename Params::is_multi_container; using field_type = typename Params::node_count_type; using allocator_type = typename Params::allocator_type; using slot_type = typename Params::slot_type; @@ -382,18 +485,25 @@ class btree_node { using difference_type = typename Params::difference_type; // Btree decides whether to use linear node search as follows: + // - If the comparator expresses a preference, use that. + // - If the key expresses a preference, use that. // - If the key is arithmetic and the comparator is std::less or // std::greater, choose linear. // - Otherwise, choose binary. // TODO(ezb): Might make sense to add condition(s) based on node-size. using use_linear_search = std::integral_constant< bool, - std::is_arithmetic<key_type>::value && - (std::is_same<std::less<key_type>, key_compare>::value || - std::is_same<std::greater<key_type>, key_compare>::value)>; - - // This class is organized by gtl::Layout as if it had the following - // structure: + has_linear_node_search_preference<key_compare>::value + ? prefers_linear_node_search<key_compare>::value + : has_linear_node_search_preference<key_type>::value + ? prefers_linear_node_search<key_type>::value + : std::is_arithmetic<key_type>::value && + (std::is_same<std::less<key_type>, key_compare>::value || + std::is_same<std::greater<key_type>, + key_compare>::value)>; + + // This class is organized by absl::container_internal::Layout as if it had + // the following structure: // // A pointer to the node's parent. // btree_node *parent; // @@ -407,23 +517,23 @@ class btree_node { // // is the same as the count of values. // field_type finish; // // The maximum number of values the node can hold. This is an integer in - // // [1, kNodeValues] for root leaf nodes, kNodeValues for non-root leaf + // // [1, kNodeSlots] for root leaf nodes, kNodeSlots for non-root leaf // // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal - // // nodes (even though there are still kNodeValues values in the node). + // // nodes (even though there are still kNodeSlots values in the node). // // TODO(ezb): make max_count use only 4 bits and record log2(capacity) // // to free extra bits for is_root, etc. // field_type max_count; // // // The array of values. The capacity is `max_count` for leaf nodes and - // // kNodeValues for internal nodes. Only the values in + // // kNodeSlots for internal nodes. Only the values in // // [start, finish) have been initialized and are valid. // slot_type values[max_count]; // // // The array of child pointers. The keys in children[i] are all less // // than key(i). The keys in children[i + 1] are all greater than key(i). - // // There are 0 children for leaf nodes and kNodeValues + 1 children for + // // There are 0 children for leaf nodes and kNodeSlots + 1 children for // // internal nodes. - // btree_node *children[kNodeValues + 1]; + // btree_node *children[kNodeSlots + 1]; // // This class is only constructed by EmptyNodeType. Normally, pointers to the // layout above are allocated, cast to btree_node*, and de-allocated within @@ -445,57 +555,62 @@ class btree_node { private: using layout_type = absl::container_internal::Layout<btree_node *, field_type, slot_type, btree_node *>; - constexpr static size_type SizeWithNValues(size_type n) { + constexpr static size_type SizeWithNSlots(size_type n) { return layout_type(/*parent*/ 1, /*position, start, finish, max_count*/ 4, - /*values*/ n, + /*slots*/ n, /*children*/ 0) .AllocSize(); } // A lower bound for the overhead of fields other than values in a leaf node. constexpr static size_type MinimumOverhead() { - return SizeWithNValues(1) - sizeof(value_type); + return SizeWithNSlots(1) - sizeof(value_type); } // Compute how many values we can fit onto a leaf node taking into account // padding. - constexpr static size_type NodeTargetValues(const int begin, const int end) { + constexpr static size_type NodeTargetSlots(const int begin, const int end) { return begin == end ? begin - : SizeWithNValues((begin + end) / 2 + 1) > + : SizeWithNSlots((begin + end) / 2 + 1) > params_type::kTargetNodeSize - ? NodeTargetValues(begin, (begin + end) / 2) - : NodeTargetValues((begin + end) / 2 + 1, end); + ? NodeTargetSlots(begin, (begin + end) / 2) + : NodeTargetSlots((begin + end) / 2 + 1, end); } enum { kTargetNodeSize = params_type::kTargetNodeSize, - kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize), + kNodeTargetSlots = NodeTargetSlots(0, params_type::kTargetNodeSize), - // We need a minimum of 3 values per internal node in order to perform + // We need a minimum of 3 slots per internal node in order to perform // splitting (1 value for the two nodes involved in the split and 1 value - // propagated to the parent as the delimiter for the split). - kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3, + // propagated to the parent as the delimiter for the split). For performance + // reasons, we don't allow 3 slots-per-node due to bad worst case occupancy + // of 1/3 (for a node, not a b-tree). + kMinNodeSlots = 4, + + kNodeSlots = + kNodeTargetSlots >= kMinNodeSlots ? kNodeTargetSlots : kMinNodeSlots, // The node is internal (i.e. is not a leaf node) if and only if `max_count` // has this value. kInternalNodeMaxCount = 0, }; - // Leaves can have less than kNodeValues values. - constexpr static layout_type LeafLayout(const int max_values = kNodeValues) { + // Leaves can have less than kNodeSlots values. + constexpr static layout_type LeafLayout(const int slot_count = kNodeSlots) { return layout_type(/*parent*/ 1, /*position, start, finish, max_count*/ 4, - /*values*/ max_values, + /*slots*/ slot_count, /*children*/ 0); } constexpr static layout_type InternalLayout() { return layout_type(/*parent*/ 1, /*position, start, finish, max_count*/ 4, - /*values*/ kNodeValues, - /*children*/ kNodeValues + 1); + /*slots*/ kNodeSlots, + /*children*/ kNodeSlots + 1); } - constexpr static size_type LeafSize(const int max_values = kNodeValues) { - return LeafLayout(max_values).AllocSize(); + constexpr static size_type LeafSize(const int slot_count = kNodeSlots) { + return LeafLayout(slot_count).AllocSize(); } constexpr static size_type InternalSize() { return InternalLayout().AllocSize(); @@ -552,10 +667,10 @@ class btree_node { } field_type max_count() const { // Internal nodes have max_count==kInternalNodeMaxCount. - // Leaf nodes have max_count in [1, kNodeValues]. + // Leaf nodes have max_count in [1, kNodeSlots]. const field_type max_count = GetField<1>()[3]; return max_count == field_type{kInternalNodeMaxCount} - ? field_type{kNodeValues} + ? field_type{kNodeSlots} : max_count; } @@ -633,7 +748,7 @@ class btree_node { } ++s; } - return {s}; + return SearchResult<int, false>{s}; } // Returns the position of the first value whose key is not less than k using @@ -668,7 +783,7 @@ class btree_node { e = mid; } } - return {s}; + return SearchResult<int, false>{s}; } // Returns the position of the first value whose key is not less than k using @@ -677,7 +792,7 @@ class btree_node { SearchResult<int, true> binary_search_impl( const K &k, int s, int e, const CompareTo &comp, std::true_type /* IsCompareTo */) const { - if (is_multi_container::value) { + if (params_type::template can_have_multiple_equivalent_keys<K>()) { MatchKind exact_match = MatchKind::kNe; while (s != e) { const int mid = (s + e) >> 1; @@ -688,14 +803,14 @@ class btree_node { e = mid; if (c == 0) { // Need to return the first value whose key is not less than k, - // which requires continuing the binary search if this is a - // multi-container. + // which requires continuing the binary search if there could be + // multiple equivalent keys. exact_match = MatchKind::kEq; } } } return {s, exact_match}; - } else { // Not a multi-container. + } else { // Can't have multiple equivalent keys. while (s != e) { const int mid = (s + e) >> 1; const absl::weak_ordering c = comp(key(mid), k); @@ -716,14 +831,10 @@ class btree_node { template <typename... Args> void emplace_value(size_type i, allocator_type *alloc, Args &&... args); - // Removes the value at position i, shifting all existing values and children - // at positions > i to the left by 1. - void remove_value(int i, allocator_type *alloc); - - // Removes the values at positions [i, i + to_erase), shifting all values - // after that range to the left by to_erase. Does not change children at all. - void remove_values_ignore_children(int i, int to_erase, - allocator_type *alloc); + // Removes the values at positions [i, i + to_erase), shifting all existing + // values and children after that range to the left by to_erase. Clears all + // children between [i, i + to_erase). + void remove_values(field_type i, field_type to_erase, allocator_type *alloc); // Rebalances a node with its right sibling. void rebalance_right_to_left(int to_move, btree_node *right, @@ -735,75 +846,87 @@ class btree_node { void split(int insert_position, btree_node *dest, allocator_type *alloc); // Merges a node with its right sibling, moving all of the values and the - // delimiting key in the parent node onto itself. - void merge(btree_node *sibling, allocator_type *alloc); - - // Swap the contents of "this" and "src". - void swap(btree_node *src, allocator_type *alloc); + // delimiting key in the parent node onto itself, and deleting the src node. + void merge(btree_node *src, allocator_type *alloc); // Node allocation/deletion routines. - static btree_node *init_leaf(btree_node *n, btree_node *parent, - int max_count) { - n->set_parent(parent); - n->set_position(0); - n->set_start(0); - n->set_finish(0); - n->set_max_count(max_count); + void init_leaf(btree_node *parent, int max_count) { + set_parent(parent); + set_position(0); + set_start(0); + set_finish(0); + set_max_count(max_count); absl::container_internal::SanitizerPoisonMemoryRegion( - n->start_slot(), max_count * sizeof(slot_type)); - return n; + start_slot(), max_count * sizeof(slot_type)); } - static btree_node *init_internal(btree_node *n, btree_node *parent) { - init_leaf(n, parent, kNodeValues); + void init_internal(btree_node *parent) { + init_leaf(parent, kNodeSlots); // Set `max_count` to a sentinel value to indicate that this node is // internal. - n->set_max_count(kInternalNodeMaxCount); + set_max_count(kInternalNodeMaxCount); absl::container_internal::SanitizerPoisonMemoryRegion( - &n->mutable_child(n->start()), - (kNodeValues + 1) * sizeof(btree_node *)); - return n; - } - void destroy(allocator_type *alloc) { - for (int i = start(); i < finish(); ++i) { - value_destroy(i, alloc); - } + &mutable_child(start()), (kNodeSlots + 1) * sizeof(btree_node *)); } - public: - // Exposed only for tests. - static bool testonly_uses_linear_node_search() { - return use_linear_search::value; + static void deallocate(const size_type size, btree_node *node, + allocator_type *alloc) { + absl::container_internal::Deallocate<Alignment()>(alloc, node, size); } + // Deletes a node and all of its children. + static void clear_and_delete(btree_node *node, allocator_type *alloc); + private: template <typename... Args> - void value_init(const size_type i, allocator_type *alloc, Args &&... args) { + void value_init(const field_type i, allocator_type *alloc, Args &&... args) { absl::container_internal::SanitizerUnpoisonObject(slot(i)); params_type::construct(alloc, slot(i), std::forward<Args>(args)...); } - void value_destroy(const size_type i, allocator_type *alloc) { + void value_destroy(const field_type i, allocator_type *alloc) { params_type::destroy(alloc, slot(i)); absl::container_internal::SanitizerPoisonObject(slot(i)); } + void value_destroy_n(const field_type i, const field_type n, + allocator_type *alloc) { + for (slot_type *s = slot(i), *end = slot(i + n); s != end; ++s) { + params_type::destroy(alloc, s); + absl::container_internal::SanitizerPoisonObject(s); + } + } + + static void transfer(slot_type *dest, slot_type *src, allocator_type *alloc) { + absl::container_internal::SanitizerUnpoisonObject(dest); + params_type::transfer(alloc, dest, src); + absl::container_internal::SanitizerPoisonObject(src); + } + + // Transfers value from slot `src_i` in `src_node` to slot `dest_i` in `this`. + void transfer(const size_type dest_i, const size_type src_i, + btree_node *src_node, allocator_type *alloc) { + transfer(slot(dest_i), src_node->slot(src_i), alloc); + } - // Move n values starting at value i in this node into the values starting at - // value j in node x. - void uninitialized_move_n(const size_type n, const size_type i, - const size_type j, btree_node *x, - allocator_type *alloc) { - absl::container_internal::SanitizerUnpoisonMemoryRegion( - x->slot(j), n * sizeof(slot_type)); - for (slot_type *src = slot(i), *end = src + n, *dest = x->slot(j); + // Transfers `n` values starting at value `src_i` in `src_node` into the + // values starting at value `dest_i` in `this`. + void transfer_n(const size_type n, const size_type dest_i, + const size_type src_i, btree_node *src_node, + allocator_type *alloc) { + for (slot_type *src = src_node->slot(src_i), *end = src + n, + *dest = slot(dest_i); src != end; ++src, ++dest) { - params_type::construct(alloc, dest, src); + transfer(dest, src, alloc); } } - // Destroys a range of n values, starting at index i. - void value_destroy_n(const size_type i, const size_type n, - allocator_type *alloc) { - for (int j = 0; j < n; ++j) { - value_destroy(i + j, alloc); + // Same as above, except that we start at the end and work our way to the + // beginning. + void transfer_n_backward(const size_type n, const size_type dest_i, + const size_type src_i, btree_node *src_node, + allocator_type *alloc) { + for (slot_type *src = src_node->slot(src_i + n - 1), *end = src - n, + *dest = slot(dest_i + n - 1); + src != end; --src, --dest) { + transfer(dest, src, alloc); } } @@ -820,6 +943,7 @@ struct btree_iterator { using key_type = typename Node::key_type; using size_type = typename Node::size_type; using params_type = typename Node::params_type; + using is_map_container = typename params_type::is_map_container; using node_type = Node; using normal_node = typename std::remove_const<Node>::type; @@ -831,7 +955,7 @@ struct btree_iterator { using slot_type = typename params_type::slot_type; using iterator = - btree_iterator<normal_node, normal_reference, normal_pointer>; + btree_iterator<normal_node, normal_reference, normal_pointer>; using const_iterator = btree_iterator<const_node, const_reference, const_pointer>; @@ -848,20 +972,19 @@ struct btree_iterator { btree_iterator(Node *n, int p) : node(n), position(p) {} // NOTE: this SFINAE allows for implicit conversions from iterator to - // const_iterator, but it specifically avoids defining copy constructors so - // that btree_iterator can be trivially copyable. This is for performance and - // binary size reasons. + // const_iterator, but it specifically avoids hiding the copy constructor so + // that the trivial one will be used when possible. template <typename N, typename R, typename P, absl::enable_if_t< std::is_same<btree_iterator<N, R, P>, iterator>::value && std::is_same<btree_iterator, const_iterator>::value, int> = 0> - btree_iterator(const btree_iterator<N, R, P> &x) // NOLINT - : node(x.node), position(x.position) {} + btree_iterator(const btree_iterator<N, R, P> other) // NOLINT + : node(other.node), position(other.position) {} private: // This SFINAE allows explicit conversions from const_iterator to - // iterator, but also avoids defining a copy constructor. + // iterator, but also avoids hiding the copy constructor. // NOTE: the const_cast is safe because this constructor is only called by // non-const methods and the container owns the nodes. template <typename N, typename R, typename P, @@ -869,8 +992,8 @@ struct btree_iterator { std::is_same<btree_iterator<N, R, P>, const_iterator>::value && std::is_same<btree_iterator, iterator>::value, int> = 0> - explicit btree_iterator(const btree_iterator<N, R, P> &x) - : node(const_cast<node_type *>(x.node)), position(x.position) {} + explicit btree_iterator(const btree_iterator<N, R, P> other) + : node(const_cast<node_type *>(other.node)), position(other.position) {} // Increment/decrement the iterator. void increment() { @@ -890,16 +1013,27 @@ struct btree_iterator { void decrement_slow(); public: - bool operator==(const const_iterator &x) const { - return node == x.node && position == x.position; + bool operator==(const iterator &other) const { + return node == other.node && position == other.position; } - bool operator!=(const const_iterator &x) const { - return node != x.node || position != x.position; + bool operator==(const const_iterator &other) const { + return node == other.node && position == other.position; + } + bool operator!=(const iterator &other) const { + return node != other.node || position != other.position; + } + bool operator!=(const const_iterator &other) const { + return node != other.node || position != other.position; } // Accessors for the key/value the iterator is pointing at. - reference operator*() const { return node->value(position); } - pointer operator->() const { return &node->value(position); } + reference operator*() const { + ABSL_HARDENING_ASSERT(node != nullptr); + ABSL_HARDENING_ASSERT(node->start() <= position); + ABSL_HARDENING_ASSERT(node->finish() > position); + return node->value(position); + } + pointer operator->() const { return &operator*(); } btree_iterator &operator++() { increment(); @@ -921,6 +1055,8 @@ struct btree_iterator { } private: + friend iterator; + friend const_iterator; template <typename Params> friend class btree; template <typename Tree> @@ -931,8 +1067,6 @@ struct btree_iterator { friend class btree_map_container; template <typename Tree> friend class btree_multiset_container; - template <typename N, typename R, typename P> - friend struct btree_iterator; template <typename TreeType, typename CheckerType> friend class base_checker; @@ -942,7 +1076,8 @@ struct btree_iterator { // The node in the tree the iterator is pointing at. Node *node; // The position within the node of the tree the iterator is pointing at. - // TODO(ezb): make this a field_type + // NOTE: this is an int rather than a field_type because iterators can point + // to invalid positions (such as -1) in certain circumstances. int position; }; @@ -950,6 +1085,8 @@ template <typename Params> class btree { using node_type = btree_node<Params>; using is_key_compare_to = typename Params::is_key_compare_to; + using init_type = typename Params::init_type; + using field_type = typename node_type::field_type; // We use a static empty node for the root/leftmost/rightmost of empty btrees // in order to avoid branching in begin()/end(). @@ -984,9 +1121,9 @@ class btree { #endif } - enum { - kNodeValues = node_type::kNodeValues, - kMinNodeValues = kNodeValues / 2, + enum : uint32_t { + kNodeSlots = node_type::kNodeSlots, + kMinNodeValues = kNodeSlots / 2, }; struct node_stats { @@ -994,9 +1131,9 @@ class btree { node_stats(size_type l, size_type i) : leaf_nodes(l), internal_nodes(i) {} - node_stats &operator+=(const node_stats &x) { - leaf_nodes += x.leaf_nodes; - internal_nodes += x.internal_nodes; + node_stats &operator+=(const node_stats &other) { + leaf_nodes += other.leaf_nodes; + internal_nodes += other.internal_nodes; return *this; } @@ -1010,13 +1147,15 @@ class btree { using size_type = typename Params::size_type; using difference_type = typename Params::difference_type; using key_compare = typename Params::key_compare; + using original_key_compare = typename Params::original_key_compare; using value_compare = typename Params::value_compare; using allocator_type = typename Params::allocator_type; using reference = typename Params::reference; using const_reference = typename Params::const_reference; using pointer = typename Params::pointer; using const_pointer = typename Params::const_pointer; - using iterator = btree_iterator<node_type, reference, pointer>; + using iterator = + typename btree_iterator<node_type, reference, pointer>::iterator; using const_iterator = typename iterator::const_iterator; using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; @@ -1028,28 +1167,46 @@ class btree { private: // For use in copy_or_move_values_in_order. - const value_type &maybe_move_from_iterator(const_iterator x) { return *x; } - value_type &&maybe_move_from_iterator(iterator x) { return std::move(*x); } + const value_type &maybe_move_from_iterator(const_iterator it) { return *it; } + value_type &&maybe_move_from_iterator(iterator it) { + // This is a destructive operation on the other container so it's safe for + // us to const_cast and move from the keys here even if it's a set. + return std::move(const_cast<value_type &>(*it)); + } // Copies or moves (depending on the template parameter) the values in - // x into this btree in their order in x. This btree must be empty before this - // method is called. This method is used in copy construction, copy - // assignment, and move assignment. + // other into this btree in their order in other. This btree must be empty + // before this method is called. This method is used in copy construction, + // copy assignment, and move assignment. template <typename Btree> - void copy_or_move_values_in_order(Btree *x); + void copy_or_move_values_in_order(Btree &other); // Validates that various assumptions/requirements are true at compile time. constexpr static bool static_assert_validation(); public: - btree(const key_compare &comp, const allocator_type &alloc); - - btree(const btree &x); - btree(btree &&x) noexcept - : root_(std::move(x.root_)), - rightmost_(absl::exchange(x.rightmost_, EmptyNode())), - size_(absl::exchange(x.size_, 0)) { - x.mutable_root() = EmptyNode(); + btree(const key_compare &comp, const allocator_type &alloc) + : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {} + + btree(const btree &other) : btree(other, other.allocator()) {} + btree(const btree &other, const allocator_type &alloc) + : btree(other.key_comp(), alloc) { + copy_or_move_values_in_order(other); + } + btree(btree &&other) noexcept + : root_(std::move(other.root_)), + rightmost_(absl::exchange(other.rightmost_, EmptyNode())), + size_(absl::exchange(other.size_, 0)) { + other.mutable_root() = EmptyNode(); + } + btree(btree &&other, const allocator_type &alloc) + : btree(other.key_comp(), alloc) { + if (alloc == other.allocator()) { + swap(other); + } else { + // Move values from `other` one at a time when allocators are different. + copy_or_move_values_in_order(other); + } } ~btree() { @@ -1059,9 +1216,9 @@ class btree { clear(); } - // Assign the contents of x to *this. - btree &operator=(const btree &x); - btree &operator=(btree &&x) noexcept; + // Assign the contents of other to *this. + btree &operator=(const btree &other); + btree &operator=(btree &&other) noexcept; iterator begin() { return iterator(leftmost()); } const_iterator begin() const { return const_iterator(leftmost()); } @@ -1078,17 +1235,22 @@ class btree { return const_reverse_iterator(begin()); } - // Finds the first element whose key is not less than key. + // Finds the first element whose key is not less than `key`. template <typename K> iterator lower_bound(const K &key) { - return internal_end(internal_lower_bound(key)); + return internal_end(internal_lower_bound(key).value); } template <typename K> const_iterator lower_bound(const K &key) const { - return internal_end(internal_lower_bound(key)); + return internal_end(internal_lower_bound(key).value); } - // Finds the first element whose key is greater than key. + // Finds the first element whose key is not less than `key` and also returns + // whether that element is equal to `key`. + template <typename K> + std::pair<iterator, bool> lower_bound_equal(const K &key) const; + + // Finds the first element whose key is greater than `key`. template <typename K> iterator upper_bound(const K &key) { return internal_end(internal_upper_bound(key)); @@ -1099,23 +1261,21 @@ class btree { } // Finds the range of values which compare equal to key. The first member of - // the returned pair is equal to lower_bound(key). The second member pair of - // the pair is equal to upper_bound(key). + // the returned pair is equal to lower_bound(key). The second member of the + // pair is equal to upper_bound(key). template <typename K> - std::pair<iterator, iterator> equal_range(const K &key) { - return {lower_bound(key), upper_bound(key)}; - } + std::pair<iterator, iterator> equal_range(const K &key); template <typename K> std::pair<const_iterator, const_iterator> equal_range(const K &key) const { - return {lower_bound(key), upper_bound(key)}; + return const_cast<btree *>(this)->equal_range(key); } // Inserts a value into the btree only if it does not already exist. The // boolean return value indicates whether insertion succeeded or failed. // Requirement: if `key` already exists in the btree, does not consume `args`. // Requirement: `key` is never referenced after consuming `args`. - template <typename... Args> - std::pair<iterator, bool> insert_unique(const key_type &key, Args &&... args); + template <typename K, typename... Args> + std::pair<iterator, bool> insert_unique(const K &key, Args &&... args); // Inserts with hint. Checks to see if the value should be placed immediately // before `position` in the tree. If so, then the insertion will take @@ -1123,14 +1283,23 @@ class btree { // logarithmic time as if a call to insert_unique() were made. // Requirement: if `key` already exists in the btree, does not consume `args`. // Requirement: `key` is never referenced after consuming `args`. - template <typename... Args> + template <typename K, typename... Args> std::pair<iterator, bool> insert_hint_unique(iterator position, - const key_type &key, + const K &key, Args &&... args); // Insert a range of values into the btree. + // Note: the first overload avoids constructing a value_type if the key + // already exists in the btree. + template <typename InputIterator, + typename = decltype(std::declval<const key_compare &>()( + params_type::key(*std::declval<InputIterator>()), + std::declval<const key_type &>()))> + void insert_iterator_unique(InputIterator b, InputIterator e, int); + // We need the second overload for cases in which we need to construct a + // value_type in order to compare it with the keys already in the btree. template <typename InputIterator> - void insert_iterator_unique(InputIterator b, InputIterator e); + void insert_iterator_unique(InputIterator b, InputIterator e, char); // Inserts a value into the btree. template <typename ValueType> @@ -1163,18 +1332,8 @@ class btree { // to the element after the last erased element. std::pair<size_type, iterator> erase_range(iterator begin, iterator end); - // Erases the specified key from the btree. Returns 1 if an element was - // erased and 0 otherwise. - template <typename K> - size_type erase_unique(const K &key); - - // Erases all of the entries matching the specified key from the - // btree. Returns the number of elements erased. - template <typename K> - size_type erase_multi(const K &key); - - // Finds the iterator corresponding to a key or returns end() if the key is - // not present. + // Finds an element with key equivalent to `key` or returns `end()` if `key` + // is not present. template <typename K> iterator find(const K &key) { return internal_end(internal_find(key)); @@ -1184,38 +1343,23 @@ class btree { return internal_end(internal_find(key)); } - // Returns a count of the number of times the key appears in the btree. - template <typename K> - size_type count_unique(const K &key) const { - const iterator begin = internal_find(key); - if (begin.node == nullptr) { - // The key doesn't exist in the tree. - return 0; - } - return 1; - } - // Returns a count of the number of times the key appears in the btree. - template <typename K> - size_type count_multi(const K &key) const { - const auto range = equal_range(key); - return std::distance(range.first, range.second); - } - // Clear the btree, deleting all of the values it contains. void clear(); - // Swap the contents of *this and x. - void swap(btree &x); + // Swaps the contents of `this` and `other`. + void swap(btree &other); const key_compare &key_comp() const noexcept { return root_.template get<0>(); } - template <typename K, typename LK> - bool compare_keys(const K &x, const LK &y) const { - return compare_internal::compare_result_as_less_than(key_comp()(x, y)); + template <typename K1, typename K2> + bool compare_keys(const K1 &a, const K2 &b) const { + return compare_internal::compare_result_as_less_than(key_comp()(a, b)); } - value_compare value_comp() const { return value_compare(key_comp()); } + value_compare value_comp() const { + return value_compare(original_key_compare(key_comp())); + } // Verifies the structure of the btree. void verify() const; @@ -1263,12 +1407,14 @@ class btree { } } - // The average number of bytes used per value stored in the btree. + // The average number of bytes used per value stored in the btree assuming + // random insertion order. static double average_bytes_per_value() { - // Returns the number of bytes per value on a leaf node that is 75% - // full. Experimentally, this matches up nicely with the computed number of - // bytes per value in trees that had their values inserted in random order. - return node_type::LeafSize() / (kNodeValues * 0.75); + // The expected number of values per node with random insertion order is the + // average of the maximum and minimum numbers of values per node. + const double expected_values_per_node = + (kNodeSlots + kMinNodeValues) / 2.0; + return node_type::LeafSize() / expected_values_per_node; } // The fullness of the btree. Computed as the number of elements in the btree @@ -1278,7 +1424,7 @@ class btree { // Returns 0 for empty trees. double fullness() const { if (empty()) return 0.0; - return static_cast<double>(size()) / (nodes() * kNodeValues); + return static_cast<double>(size()) / (nodes() * kNodeSlots); } // The overhead of the btree structure in bytes per node. Computed as the // total number of bytes used by the btree minus the number of bytes used for @@ -1322,38 +1468,24 @@ class btree { // Node creation/deletion routines. node_type *new_internal_node(node_type *parent) { - node_type *p = allocate(node_type::InternalSize()); - return node_type::init_internal(p, parent); + node_type *n = allocate(node_type::InternalSize()); + n->init_internal(parent); + return n; } node_type *new_leaf_node(node_type *parent) { - node_type *p = allocate(node_type::LeafSize()); - return node_type::init_leaf(p, parent, kNodeValues); + node_type *n = allocate(node_type::LeafSize()); + n->init_leaf(parent, kNodeSlots); + return n; } node_type *new_leaf_root_node(const int max_count) { - node_type *p = allocate(node_type::LeafSize(max_count)); - return node_type::init_leaf(p, p, max_count); + node_type *n = allocate(node_type::LeafSize(max_count)); + n->init_leaf(/*parent=*/n, max_count); + return n; } // Deletion helper routines. - void erase_same_node(iterator begin, iterator end); - iterator erase_from_leaf_node(iterator begin, size_type to_erase); iterator rebalance_after_delete(iterator iter); - // Deallocates a node of a certain size in bytes using the allocator. - void deallocate(const size_type size, node_type *node) { - absl::container_internal::Deallocate<node_type::Alignment()>( - mutable_allocator(), node, size); - } - - void delete_internal_node(node_type *node) { - node->destroy(mutable_allocator()); - deallocate(node_type::InternalSize(), node); - } - void delete_leaf_node(node_type *node) { - node->destroy(mutable_allocator()); - deallocate(node_type::LeafSize(node->max_count()), node); - } - // Rebalances or splits the node iter points to. void rebalance_or_split(iterator *iter); @@ -1391,28 +1523,19 @@ class btree { static IterType internal_last(IterType iter); // Returns an iterator pointing to the leaf position at which key would - // reside in the tree. We provide 2 versions of internal_locate. The first - // version uses a less-than comparator and is incapable of distinguishing when - // there is an exact match. The second version is for the key-compare-to - // specialization and distinguishes exact matches. The key-compare-to - // specialization allows the caller to avoid a subsequent comparison to - // determine if an exact match was made, which is important for keys with - // expensive comparison, such as strings. + // reside in the tree, unless there is an exact match - in which case, the + // result may not be on a leaf. When there's a three-way comparator, we can + // return whether there was an exact match. This allows the caller to avoid a + // subsequent comparison to determine if an exact match was made, which is + // important for keys with expensive comparison, such as strings. template <typename K> SearchResult<iterator, is_key_compare_to::value> internal_locate( const K &key) const; - template <typename K> - SearchResult<iterator, false> internal_locate_impl( - const K &key, std::false_type /* IsCompareTo */) const; - - template <typename K> - SearchResult<iterator, true> internal_locate_impl( - const K &key, std::true_type /* IsCompareTo */) const; - // Internal routine which implements lower_bound(). template <typename K> - iterator internal_lower_bound(const K &key) const; + SearchResult<iterator, is_key_compare_to::value> internal_lower_bound( + const K &key) const; // Internal routine which implements upper_bound(). template <typename K> @@ -1422,9 +1545,6 @@ class btree { template <typename K> iterator internal_find(const K &key) const; - // Deletes a node and all of its children. - void internal_clear(node_type *node); - // Verifies the tree structure of node. int internal_verify(const node_type *node, const key_type *lo, const key_type *hi) const; @@ -1444,13 +1564,6 @@ class btree { return res; } - public: - // Exposed only for tests. - static bool testonly_uses_linear_node_search() { - return node_type::testonly_uses_linear_node_search(); - } - - private: // We use compressed tuple in order to save space because key_compare and // allocator_type are usually empty. absl::container_internal::CompressedTuple<key_compare, allocator_type, @@ -1477,10 +1590,8 @@ inline void btree_node<P>::emplace_value(const size_type i, // Shift old values to create space for new value and then construct it in // place. if (i < finish()) { - value_init(finish(), alloc, slot(finish() - 1)); - for (size_type j = finish() - 1; j > i; --j) - params_type::move(alloc, slot(j - 1), slot(j)); - value_destroy(i, alloc); + transfer_n_backward(finish() - i, /*dest_i=*/i + 1, /*src_i=*/i, this, + alloc); } value_init(i, alloc, std::forward<Args>(args)...); set_finish(finish() + 1); @@ -1494,24 +1605,27 @@ inline void btree_node<P>::emplace_value(const size_type i, } template <typename P> -inline void btree_node<P>::remove_value(const int i, allocator_type *alloc) { - if (!leaf() && finish() > i + 1) { - assert(child(i + 1)->count() == 0); - for (size_type j = i + 1; j < finish(); ++j) { - set_child(j, child(j + 1)); +inline void btree_node<P>::remove_values(const field_type i, + const field_type to_erase, + allocator_type *alloc) { + // Transfer values after the removed range into their new places. + value_destroy_n(i, to_erase, alloc); + const field_type orig_finish = finish(); + const field_type src_i = i + to_erase; + transfer_n(orig_finish - src_i, i, src_i, this, alloc); + + if (!leaf()) { + // Delete all children between begin and end. + for (int j = 0; j < to_erase; ++j) { + clear_and_delete(child(i + j + 1), alloc); + } + // Rotate children after end into new positions. + for (int j = i + to_erase + 1; j <= orig_finish; ++j) { + set_child(j - to_erase, child(j)); + clear_child(j); } - clear_child(finish()); } - - remove_values_ignore_children(i, /*to_erase=*/1, alloc); -} - -template <typename P> -inline void btree_node<P>::remove_values_ignore_children( - const int i, const int to_erase, allocator_type *alloc) { - params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i)); - value_destroy_n(finish() - to_erase, to_erase, alloc); - set_finish(finish() - to_erase); + set_finish(orig_finish - to_erase); } template <typename P> @@ -1525,22 +1639,17 @@ void btree_node<P>::rebalance_right_to_left(const int to_move, assert(to_move <= right->count()); // 1) Move the delimiting value in the parent to the left node. - value_init(finish(), alloc, parent()->slot(position())); + transfer(finish(), position(), parent(), alloc); // 2) Move the (to_move - 1) values from the right node to the left node. - right->uninitialized_move_n(to_move - 1, right->start(), finish() + 1, this, - alloc); + transfer_n(to_move - 1, finish() + 1, right->start(), right, alloc); // 3) Move the new delimiting value to the parent from the right node. - params_type::move(alloc, right->slot(to_move - 1), - parent()->slot(position())); + parent()->transfer(position(), right->start() + to_move - 1, right, alloc); - // 4) Shift the values in the right node to their correct position. - params_type::move(alloc, right->slot(to_move), right->finish_slot(), - right->start_slot()); - - // 5) Destroy the now-empty to_move entries in the right node. - right->value_destroy_n(right->finish() - to_move, to_move, alloc); + // 4) Shift the values in the right node to their correct positions. + right->transfer_n(right->count() - to_move, right->start(), + right->start() + to_move, right, alloc); if (!leaf()) { // Move the child pointers from the right to the left node. @@ -1575,54 +1684,19 @@ void btree_node<P>::rebalance_left_to_right(const int to_move, // Lastly, a new delimiting value is moved from the left node into the // parent, and the remaining empty left node entries are destroyed. - if (right->count() >= to_move) { - // The original location of the right->count() values are sufficient to hold - // the new to_move entries from the parent and left node. - - // 1) Shift existing values in the right node to their correct positions. - right->uninitialized_move_n(to_move, right->finish() - to_move, - right->finish(), right, alloc); - for (slot_type *src = right->slot(right->finish() - to_move - 1), - *dest = right->slot(right->finish() - 1), - *end = right->start_slot(); - src >= end; --src, --dest) { - params_type::move(alloc, src, dest); - } - - // 2) Move the delimiting value in the parent to the right node. - params_type::move(alloc, parent()->slot(position()), - right->slot(to_move - 1)); - - // 3) Move the (to_move - 1) values from the left node to the right node. - params_type::move(alloc, slot(finish() - (to_move - 1)), finish_slot(), - right->start_slot()); - } else { - // The right node does not have enough initialized space to hold the new - // to_move entries, so part of them will move to uninitialized space. + // 1) Shift existing values in the right node to their correct positions. + right->transfer_n_backward(right->count(), right->start() + to_move, + right->start(), right, alloc); - // 1) Shift existing values in the right node to their correct positions. - right->uninitialized_move_n(right->count(), right->start(), - right->start() + to_move, right, alloc); + // 2) Move the delimiting value in the parent to the right node. + right->transfer(right->start() + to_move - 1, position(), parent(), alloc); - // 2) Move the delimiting value in the parent to the right node. - right->value_init(to_move - 1, alloc, parent()->slot(position())); - - // 3) Move the (to_move - 1) values from the left node to the right node. - const size_type uninitialized_remaining = to_move - right->count() - 1; - uninitialized_move_n(uninitialized_remaining, - finish() - uninitialized_remaining, right->finish(), - right, alloc); - params_type::move(alloc, slot(finish() - (to_move - 1)), - slot(finish() - uninitialized_remaining), - right->start_slot()); - } + // 3) Move the (to_move - 1) values from the left node to the right node. + right->transfer_n(to_move - 1, right->start(), finish() - (to_move - 1), this, + alloc); // 4) Move the new delimiting value to the parent from the left node. - params_type::move(alloc, slot(finish() - to_move), - parent()->slot(position())); - - // 5) Destroy the now-empty to_move entries in the left node. - value_destroy_n(finish() - to_move, to_move, alloc); + parent()->transfer(position(), finish() - to_move, this, alloc); if (!leaf()) { // Move the child pointers from the left to the right node. @@ -1645,7 +1719,7 @@ template <typename P> void btree_node<P>::split(const int insert_position, btree_node *dest, allocator_type *alloc) { assert(dest->count() == 0); - assert(max_count() == kNodeValues); + assert(max_count() == kNodeSlots); // We bias the split based on the position being inserted. If we're // inserting at the beginning of the left node then bias the split to put @@ -1653,7 +1727,7 @@ void btree_node<P>::split(const int insert_position, btree_node *dest, // right node then bias the split to put more values on the left node. if (insert_position == start()) { dest->set_finish(dest->start() + finish() - 1); - } else if (insert_position == kNodeValues) { + } else if (insert_position == kNodeSlots) { dest->set_finish(dest->start()); } else { dest->set_finish(dest->start() + count() / 2); @@ -1662,10 +1736,7 @@ void btree_node<P>::split(const int insert_position, btree_node *dest, assert(count() >= 1); // Move values from the left sibling to the right sibling. - uninitialized_move_n(dest->count(), finish(), dest->start(), dest, alloc); - - // Destroy the now-empty entries in the left node. - value_destroy_n(finish(), dest->count(), alloc); + dest->transfer_n(dest->count(), dest->start(), finish(), this, alloc); // The split key is the largest value in the left sibling. --mutable_finish(); @@ -1692,11 +1763,7 @@ void btree_node<P>::merge(btree_node *src, allocator_type *alloc) { value_init(finish(), alloc, parent()->slot(position())); // Move the values from the right to the left node. - src->uninitialized_move_n(src->count(), src->start(), finish() + 1, this, - alloc); - - // Destroy the now-empty entries in the right node. - src->value_destroy_n(src->start(), src->count(), alloc); + transfer_n(src->count(), finish() + 1, src->start(), src, alloc); if (!leaf()) { // Move the child pointers from the right to the left node. @@ -1710,56 +1777,59 @@ void btree_node<P>::merge(btree_node *src, allocator_type *alloc) { set_finish(start() + 1 + count() + src->count()); src->set_finish(src->start()); - // Remove the value on the parent node. - parent()->remove_value(position(), alloc); + // Remove the value on the parent node and delete the src node. + parent()->remove_values(position(), /*to_erase=*/1, alloc); } template <typename P> -void btree_node<P>::swap(btree_node *x, allocator_type *alloc) { - using std::swap; - assert(leaf() == x->leaf()); - - // Determine which is the smaller/larger node. - btree_node *smaller = this, *larger = x; - if (smaller->count() > larger->count()) { - swap(smaller, larger); +void btree_node<P>::clear_and_delete(btree_node *node, allocator_type *alloc) { + if (node->leaf()) { + node->value_destroy_n(node->start(), node->count(), alloc); + deallocate(LeafSize(node->max_count()), node, alloc); + return; } - - // Swap the values. - for (slot_type *a = smaller->start_slot(), *b = larger->start_slot(), - *end = smaller->finish_slot(); - a != end; ++a, ++b) { - params_type::swap(alloc, a, b); + if (node->count() == 0) { + deallocate(InternalSize(), node, alloc); + return; } - // Move values that can't be swapped. - const size_type to_move = larger->count() - smaller->count(); - larger->uninitialized_move_n(to_move, smaller->finish(), smaller->finish(), - smaller, alloc); - larger->value_destroy_n(smaller->finish(), to_move, alloc); + // The parent of the root of the subtree we are deleting. + btree_node *delete_root_parent = node->parent(); - if (!leaf()) { - // Swap the child pointers. - std::swap_ranges(&smaller->mutable_child(smaller->start()), - &smaller->mutable_child(smaller->finish() + 1), - &larger->mutable_child(larger->start())); - // Update swapped children's parent pointers. - int i = smaller->start(); - int j = larger->start(); - for (; i <= smaller->finish(); ++i, ++j) { - smaller->child(i)->set_parent(smaller); - larger->child(j)->set_parent(larger); - } - // Move the child pointers that couldn't be swapped. - for (; j <= larger->finish(); ++i, ++j) { - smaller->init_child(i, larger->child(j)); - larger->clear_child(j); - } + // Navigate to the leftmost leaf under node, and then delete upwards. + while (!node->leaf()) node = node->start_child(); + // Use `int` because `pos` needs to be able to hold `kNodeSlots+1`, which + // isn't guaranteed to be a valid `field_type`. + int pos = node->position(); + btree_node *parent = node->parent(); + for (;;) { + // In each iteration of the next loop, we delete one leaf node and go right. + assert(pos <= parent->finish()); + do { + node = parent->child(pos); + if (!node->leaf()) { + // Navigate to the leftmost leaf under node. + while (!node->leaf()) node = node->start_child(); + pos = node->position(); + parent = node->parent(); + } + node->value_destroy_n(node->start(), node->count(), alloc); + deallocate(LeafSize(node->max_count()), node, alloc); + ++pos; + } while (pos <= parent->finish()); + + // Once we've deleted all children of parent, delete parent and go up/right. + assert(pos > parent->finish()); + do { + node = parent; + pos = node->position(); + parent = node->parent(); + node->value_destroy_n(node->start(), node->count(), alloc); + deallocate(InternalSize(), node, alloc); + if (parent == delete_root_parent) return; + ++pos; + } while (pos > parent->finish()); } - - // Swap the `finish`s. - // TODO(ezb): with floating storage, will also need to swap starts. - swap(mutable_finish(), x->mutable_finish()); } //// @@ -1774,6 +1844,7 @@ void btree_iterator<N, R, P>::increment_slow() { position = node->position(); node = node->parent(); } + // TODO(ezb): assert we aren't incrementing end() instead of handling. if (position == node->finish()) { *this = save; } @@ -1797,6 +1868,7 @@ void btree_iterator<N, R, P>::decrement_slow() { position = node->position() - 1; node = node->parent(); } + // TODO(ezb): assert we aren't decrementing begin() instead of handling. if (position < node->start()) { *this = save; } @@ -1814,7 +1886,7 @@ void btree_iterator<N, R, P>::decrement_slow() { // btree methods template <typename P> template <typename Btree> -void btree<P>::copy_or_move_values_in_order(Btree *x) { +void btree<P>::copy_or_move_values_in_order(Btree &other) { static_assert(std::is_same<btree, Btree>::value || std::is_same<const btree, Btree>::value, "Btree type must be same or const."); @@ -1822,11 +1894,11 @@ void btree<P>::copy_or_move_values_in_order(Btree *x) { // We can avoid key comparisons because we know the order of the // values is the same order we'll store them in. - auto iter = x->begin(); - if (iter == x->end()) return; + auto iter = other.begin(); + if (iter == other.end()) return; insert_multi(maybe_move_from_iterator(iter)); ++iter; - for (; iter != x->end(); ++iter) { + for (; iter != other.end(); ++iter) { // If the btree is not empty, we can just insert the new value at the end // of the tree. internal_emplace(end(), maybe_move_from_iterator(iter)); @@ -1845,7 +1917,7 @@ constexpr bool btree<P>::static_assert_validation() { // Note: We assert that kTargetValues, which is computed from // Params::kTargetNodeSize, must fit the node_type::field_type. static_assert( - kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))), + kNodeSlots < (1 << (8 * sizeof(typename node_type::field_type))), "target node size too large"); // Verify that key_compare returns an absl::{weak,strong}_ordering or bool. @@ -1865,24 +1937,57 @@ constexpr bool btree<P>::static_assert_validation() { } template <typename P> -btree<P>::btree(const key_compare &comp, const allocator_type &alloc) - : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {} +template <typename K> +auto btree<P>::lower_bound_equal(const K &key) const + -> std::pair<iterator, bool> { + const SearchResult<iterator, is_key_compare_to::value> res = + internal_lower_bound(key); + const iterator lower = iterator(internal_end(res.value)); + const bool equal = res.HasMatch() + ? res.IsEq() + : lower != end() && !compare_keys(key, lower.key()); + return {lower, equal}; +} template <typename P> -btree<P>::btree(const btree &x) : btree(x.key_comp(), x.allocator()) { - copy_or_move_values_in_order(&x); +template <typename K> +auto btree<P>::equal_range(const K &key) -> std::pair<iterator, iterator> { + const std::pair<iterator, bool> lower_and_equal = lower_bound_equal(key); + const iterator lower = lower_and_equal.first; + if (!lower_and_equal.second) { + return {lower, lower}; + } + + const iterator next = std::next(lower); + if (!params_type::template can_have_multiple_equivalent_keys<K>()) { + // The next iterator after lower must point to a key greater than `key`. + // Note: if this assert fails, then it may indicate that the comparator does + // not meet the equivalence requirements for Compare + // (see https://en.cppreference.com/w/cpp/named_req/Compare). + assert(next == end() || compare_keys(key, next.key())); + return {lower, next}; + } + // Try once more to avoid the call to upper_bound() if there's only one + // equivalent key. This should prevent all calls to upper_bound() in cases of + // unique-containers with heterogeneous comparators in which all comparison + // operators have the same equivalence classes. + if (next == end() || compare_keys(key, next.key())) return {lower, next}; + + // In this case, we need to call upper_bound() to avoid worst case O(N) + // behavior if we were to iterate over equal keys. + return {lower, upper_bound(key)}; } template <typename P> -template <typename... Args> -auto btree<P>::insert_unique(const key_type &key, Args &&... args) +template <typename K, typename... Args> +auto btree<P>::insert_unique(const K &key, Args &&... args) -> std::pair<iterator, bool> { if (empty()) { mutable_root() = rightmost_ = new_leaf_root_node(1); } - auto res = internal_locate(key); - iterator &iter = res.value; + SearchResult<iterator, is_key_compare_to::value> res = internal_locate(key); + iterator iter = res.value; if (res.HasMatch()) { if (res.IsEq()) { @@ -1900,8 +2005,8 @@ auto btree<P>::insert_unique(const key_type &key, Args &&... args) } template <typename P> -template <typename... Args> -inline auto btree<P>::insert_hint_unique(iterator position, const key_type &key, +template <typename K, typename... Args> +inline auto btree<P>::insert_hint_unique(iterator position, const K &key, Args &&... args) -> std::pair<iterator, bool> { if (!empty()) { @@ -1925,14 +2030,23 @@ inline auto btree<P>::insert_hint_unique(iterator position, const key_type &key, } template <typename P> -template <typename InputIterator> -void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e) { +template <typename InputIterator, typename> +void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e, int) { for (; b != e; ++b) { insert_hint_unique(end(), params_type::key(*b), *b); } } template <typename P> +template <typename InputIterator> +void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e, char) { + for (; b != e; ++b) { + init_type value(*b); + insert_hint_unique(end(), params_type::key(value), std::move(value)); + } +} + +template <typename P> template <typename ValueType> auto btree<P>::insert_multi(const key_type &key, ValueType &&v) -> iterator { if (empty()) { @@ -1977,46 +2091,47 @@ void btree<P>::insert_iterator_multi(InputIterator b, InputIterator e) { } template <typename P> -auto btree<P>::operator=(const btree &x) -> btree & { - if (this != &x) { +auto btree<P>::operator=(const btree &other) -> btree & { + if (this != &other) { clear(); - *mutable_key_comp() = x.key_comp(); + *mutable_key_comp() = other.key_comp(); if (absl::allocator_traits< allocator_type>::propagate_on_container_copy_assignment::value) { - *mutable_allocator() = x.allocator(); + *mutable_allocator() = other.allocator(); } - copy_or_move_values_in_order(&x); + copy_or_move_values_in_order(other); } return *this; } template <typename P> -auto btree<P>::operator=(btree &&x) noexcept -> btree & { - if (this != &x) { +auto btree<P>::operator=(btree &&other) noexcept -> btree & { + if (this != &other) { clear(); using std::swap; if (absl::allocator_traits< allocator_type>::propagate_on_container_copy_assignment::value) { // Note: `root_` also contains the allocator and the key comparator. - swap(root_, x.root_); - swap(rightmost_, x.rightmost_); - swap(size_, x.size_); + swap(root_, other.root_); + swap(rightmost_, other.rightmost_); + swap(size_, other.size_); } else { - if (allocator() == x.allocator()) { - swap(mutable_root(), x.mutable_root()); - swap(*mutable_key_comp(), *x.mutable_key_comp()); - swap(rightmost_, x.rightmost_); - swap(size_, x.size_); + if (allocator() == other.allocator()) { + swap(mutable_root(), other.mutable_root()); + swap(*mutable_key_comp(), *other.mutable_key_comp()); + swap(rightmost_, other.rightmost_); + swap(size_, other.size_); } else { // We aren't allowed to propagate the allocator and the allocator is // different so we can't take over its memory. We must move each element - // individually. We need both `x` and `this` to have `x`s key comparator - // while moving the values so we can't swap the key comparators. - *mutable_key_comp() = x.key_comp(); - copy_or_move_values_in_order(&x); + // individually. We need both `other` and `this` to have `other`s key + // comparator while moving the values so we can't swap the key + // comparators. + *mutable_key_comp() = other.key_comp(); + copy_or_move_values_in_order(other); } } } @@ -2028,7 +2143,7 @@ auto btree<P>::erase(iterator iter) -> iterator { bool internal_delete = false; if (!iter.node->leaf()) { // Deletion of a value on an internal node. First, move the largest value - // from our left child here, then delete that position (in remove_value() + // from our left child here, then delete that position (in remove_values() // below). We can get to the largest value from our left child by // decrementing iter. iterator internal_iter(iter); @@ -2040,7 +2155,7 @@ auto btree<P>::erase(iterator iter) -> iterator { } // Delete the key from the leaf. - iter.node->remove_value(iter.position, mutable_allocator()); + iter.node->remove_values(iter.position, /*to_erase=*/1, mutable_allocator()); --size_; // We want to return the next value after the one we just erased. If we @@ -2115,7 +2230,9 @@ auto btree<P>::erase_range(iterator begin, iterator end) } if (begin.node == end.node) { - erase_same_node(begin, end); + assert(end.position > begin.position); + begin.node->remove_values(begin.position, end.position - begin.position, + mutable_allocator()); size_ -= count; return {count, rebalance_after_delete(begin)}; } @@ -2125,8 +2242,11 @@ auto btree<P>::erase_range(iterator begin, iterator end) if (begin.node->leaf()) { const size_type remaining_to_erase = size_ - target_size; const size_type remaining_in_node = begin.node->finish() - begin.position; - begin = erase_from_leaf_node( - begin, (std::min)(remaining_to_erase, remaining_in_node)); + const size_type to_erase = + (std::min)(remaining_to_erase, remaining_in_node); + begin.node->remove_values(begin.position, to_erase, mutable_allocator()); + size_ -= to_erase; + begin = rebalance_after_delete(begin); } else { begin = erase(begin); } @@ -2135,79 +2255,9 @@ auto btree<P>::erase_range(iterator begin, iterator end) } template <typename P> -void btree<P>::erase_same_node(iterator begin, iterator end) { - assert(begin.node == end.node); - assert(end.position > begin.position); - - node_type *node = begin.node; - size_type to_erase = end.position - begin.position; - if (!node->leaf()) { - // Delete all children between begin and end. - for (size_type i = 0; i < to_erase; ++i) { - internal_clear(node->child(begin.position + i + 1)); - } - // Rotate children after end into new positions. - for (size_type i = begin.position + to_erase + 1; i <= node->finish(); - ++i) { - node->set_child(i - to_erase, node->child(i)); - node->clear_child(i); - } - } - node->remove_values_ignore_children(begin.position, to_erase, - mutable_allocator()); - - // Do not need to update rightmost_, because - // * either end == this->end(), and therefore node == rightmost_, and still - // exists - // * or end != this->end(), and therefore rightmost_ hasn't been erased, since - // it wasn't covered in [begin, end) -} - -template <typename P> -auto btree<P>::erase_from_leaf_node(iterator begin, size_type to_erase) - -> iterator { - node_type *node = begin.node; - assert(node->leaf()); - assert(node->finish() > begin.position); - assert(begin.position + to_erase <= node->finish()); - - node->remove_values_ignore_children(begin.position, to_erase, - mutable_allocator()); - - size_ -= to_erase; - - return rebalance_after_delete(begin); -} - -template <typename P> -template <typename K> -auto btree<P>::erase_unique(const K &key) -> size_type { - const iterator iter = internal_find(key); - if (iter.node == nullptr) { - // The key doesn't exist in the tree, return nothing done. - return 0; - } - erase(iter); - return 1; -} - -template <typename P> -template <typename K> -auto btree<P>::erase_multi(const K &key) -> size_type { - const iterator begin = internal_lower_bound(key); - if (begin.node == nullptr) { - // The key doesn't exist in the tree, return nothing done. - return 0; - } - // Delete all of the keys between begin and upper_bound(key). - const iterator end = internal_end(internal_upper_bound(key)); - return erase_range(begin, end).first; -} - -template <typename P> void btree<P>::clear() { if (!empty()) { - internal_clear(root()); + node_type::clear_and_delete(root(), mutable_allocator()); } mutable_root() = EmptyNode(); rightmost_ = EmptyNode(); @@ -2215,20 +2265,20 @@ void btree<P>::clear() { } template <typename P> -void btree<P>::swap(btree &x) { +void btree<P>::swap(btree &other) { using std::swap; if (absl::allocator_traits< allocator_type>::propagate_on_container_swap::value) { // Note: `root_` also contains the allocator and the key comparator. - swap(root_, x.root_); + swap(root_, other.root_); } else { // It's undefined behavior if the allocators are unequal here. - assert(allocator() == x.allocator()); - swap(mutable_root(), x.mutable_root()); - swap(*mutable_key_comp(), *x.mutable_key_comp()); + assert(allocator() == other.allocator()); + swap(mutable_root(), other.mutable_root()); + swap(*mutable_key_comp(), *other.mutable_key_comp()); } - swap(rightmost_, x.rightmost_); - swap(size_, x.size_); + swap(rightmost_, other.rightmost_); + swap(size_, other.size_); } template <typename P> @@ -2248,7 +2298,7 @@ void btree<P>::rebalance_or_split(iterator *iter) { node_type *&node = iter->node; int &insert_position = iter->position; assert(node->count() == node->max_count()); - assert(kNodeValues == node->max_count()); + assert(kNodeSlots == node->max_count()); // First try to make room on the node by rebalancing. node_type *parent = node->parent(); @@ -2256,17 +2306,17 @@ void btree<P>::rebalance_or_split(iterator *iter) { if (node->position() > parent->start()) { // Try rebalancing with our left sibling. node_type *left = parent->child(node->position() - 1); - assert(left->max_count() == kNodeValues); - if (left->count() < kNodeValues) { + assert(left->max_count() == kNodeSlots); + if (left->count() < kNodeSlots) { // We bias rebalancing based on the position being inserted. If we're // inserting at the end of the right node then we bias rebalancing to // fill up the left node. - int to_move = (kNodeValues - left->count()) / - (1 + (insert_position < kNodeValues)); + int to_move = (kNodeSlots - left->count()) / + (1 + (insert_position < static_cast<int>(kNodeSlots))); to_move = (std::max)(1, to_move); if (insert_position - to_move >= node->start() || - left->count() + to_move < kNodeValues) { + left->count() + to_move < static_cast<int>(kNodeSlots)) { left->rebalance_right_to_left(to_move, node, mutable_allocator()); assert(node->max_count() - node->count() == to_move); @@ -2285,17 +2335,17 @@ void btree<P>::rebalance_or_split(iterator *iter) { if (node->position() < parent->finish()) { // Try rebalancing with our right sibling. node_type *right = parent->child(node->position() + 1); - assert(right->max_count() == kNodeValues); - if (right->count() < kNodeValues) { + assert(right->max_count() == kNodeSlots); + if (right->count() < kNodeSlots) { // We bias rebalancing based on the position being inserted. If we're // inserting at the beginning of the left node then we bias rebalancing // to fill up the right node. - int to_move = (kNodeValues - right->count()) / + int to_move = (static_cast<int>(kNodeSlots) - right->count()) / (1 + (insert_position > node->start())); to_move = (std::max)(1, to_move); if (insert_position <= node->finish() - to_move || - right->count() + to_move < kNodeValues) { + right->count() + to_move < static_cast<int>(kNodeSlots)) { node->rebalance_left_to_right(to_move, right, mutable_allocator()); if (insert_position > node->finish()) { @@ -2311,8 +2361,8 @@ void btree<P>::rebalance_or_split(iterator *iter) { // Rebalancing failed, make sure there is room on the parent node for a new // value. - assert(parent->max_count() == kNodeValues); - if (parent->count() == kNodeValues) { + assert(parent->max_count() == kNodeSlots); + if (parent->count() == kNodeSlots) { iterator parent_iter(node->parent(), node->position()); rebalance_or_split(&parent_iter); } @@ -2348,12 +2398,7 @@ void btree<P>::rebalance_or_split(iterator *iter) { template <typename P> void btree<P>::merge_nodes(node_type *left, node_type *right) { left->merge(right, mutable_allocator()); - if (right->leaf()) { - if (rightmost_ == right) rightmost_ = left; - delete_leaf_node(right); - } else { - delete_internal_node(right); - } + if (rightmost_ == right) rightmost_ = left; } template <typename P> @@ -2362,8 +2407,8 @@ bool btree<P>::try_merge_or_rebalance(iterator *iter) { if (iter->node->position() > parent->start()) { // Try merging with our left sibling. node_type *left = parent->child(iter->node->position() - 1); - assert(left->max_count() == kNodeValues); - if (1 + left->count() + iter->node->count() <= kNodeValues) { + assert(left->max_count() == kNodeSlots); + if (1U + left->count() + iter->node->count() <= kNodeSlots) { iter->position += 1 + left->count(); merge_nodes(left, iter->node); iter->node = left; @@ -2373,8 +2418,8 @@ bool btree<P>::try_merge_or_rebalance(iterator *iter) { if (iter->node->position() < parent->finish()) { // Try merging with our right sibling. node_type *right = parent->child(iter->node->position() + 1); - assert(right->max_count() == kNodeValues); - if (1 + iter->node->count() + right->count() <= kNodeValues) { + assert(right->max_count() == kNodeSlots); + if (1U + iter->node->count() + right->count() <= kNodeSlots) { merge_nodes(iter->node, right); return true; } @@ -2410,21 +2455,20 @@ bool btree<P>::try_merge_or_rebalance(iterator *iter) { template <typename P> void btree<P>::try_shrink() { - if (root()->count() > 0) { + node_type *orig_root = root(); + if (orig_root->count() > 0) { return; } // Deleted the last item on the root node, shrink the height of the tree. - if (root()->leaf()) { + if (orig_root->leaf()) { assert(size() == 0); - delete_leaf_node(root()); - mutable_root() = EmptyNode(); - rightmost_ = EmptyNode(); + mutable_root() = rightmost_ = EmptyNode(); } else { - node_type *child = root()->start_child(); + node_type *child = orig_root->start_child(); child->make_root(); - delete_internal_node(root()); mutable_root() = child; } + node_type::clear_and_delete(orig_root, mutable_allocator()); } template <typename P> @@ -2452,25 +2496,30 @@ inline auto btree<P>::internal_emplace(iterator iter, Args &&... args) --iter; ++iter.position; } - const int max_count = iter.node->max_count(); + const field_type max_count = iter.node->max_count(); + allocator_type *alloc = mutable_allocator(); if (iter.node->count() == max_count) { // Make room in the leaf for the new item. - if (max_count < kNodeValues) { + if (max_count < kNodeSlots) { // Insertion into the root where the root is smaller than the full node // size. Simply grow the size of the root node. assert(iter.node == root()); iter.node = - new_leaf_root_node((std::min<int>)(kNodeValues, 2 * max_count)); - iter.node->swap(root(), mutable_allocator()); - delete_leaf_node(root()); - mutable_root() = iter.node; - rightmost_ = iter.node; + new_leaf_root_node((std::min<int>)(kNodeSlots, 2 * max_count)); + // Transfer the values from the old root to the new root. + node_type *old_root = root(); + node_type *new_root = iter.node; + new_root->transfer_n(old_root->count(), new_root->start(), + old_root->start(), old_root, alloc); + new_root->set_finish(old_root->finish()); + old_root->set_finish(old_root->start()); + node_type::clear_and_delete(old_root, alloc); + mutable_root() = rightmost_ = new_root; } else { rebalance_or_split(&iter); } } - iter.node->emplace_value(iter.position, mutable_allocator(), - std::forward<Args>(args)...); + iter.node->emplace_value(iter.position, alloc, std::forward<Args>(args)...); ++size_; return iter; } @@ -2479,61 +2528,51 @@ template <typename P> template <typename K> inline auto btree<P>::internal_locate(const K &key) const -> SearchResult<iterator, is_key_compare_to::value> { - return internal_locate_impl(key, is_key_compare_to()); -} - -template <typename P> -template <typename K> -inline auto btree<P>::internal_locate_impl( - const K &key, std::false_type /* IsCompareTo */) const - -> SearchResult<iterator, false> { - iterator iter(const_cast<node_type *>(root())); - for (;;) { - iter.position = iter.node->lower_bound(key, key_comp()).value; - // NOTE: we don't need to walk all the way down the tree if the keys are - // equal, but determining equality would require doing an extra comparison - // on each node on the way down, and we will need to go all the way to the - // leaf node in the expected case. - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - return {iter}; -} - -template <typename P> -template <typename K> -inline auto btree<P>::internal_locate_impl( - const K &key, std::true_type /* IsCompareTo */) const - -> SearchResult<iterator, true> { iterator iter(const_cast<node_type *>(root())); for (;;) { - SearchResult<int, true> res = iter.node->lower_bound(key, key_comp()); + SearchResult<int, is_key_compare_to::value> res = + iter.node->lower_bound(key, key_comp()); iter.position = res.value; - if (res.match == MatchKind::kEq) { + if (res.IsEq()) { return {iter, MatchKind::kEq}; } + // Note: in the non-key-compare-to case, we don't need to walk all the way + // down the tree if the keys are equal, but determining equality would + // require doing an extra comparison on each node on the way down, and we + // will need to go all the way to the leaf node in the expected case. if (iter.node->leaf()) { break; } iter.node = iter.node->child(iter.position); } + // Note: in the non-key-compare-to case, the key may actually be equivalent + // here (and the MatchKind::kNe is ignored). return {iter, MatchKind::kNe}; } template <typename P> template <typename K> -auto btree<P>::internal_lower_bound(const K &key) const -> iterator { +auto btree<P>::internal_lower_bound(const K &key) const + -> SearchResult<iterator, is_key_compare_to::value> { + if (!params_type::template can_have_multiple_equivalent_keys<K>()) { + SearchResult<iterator, is_key_compare_to::value> ret = internal_locate(key); + ret.value = internal_last(ret.value); + return ret; + } iterator iter(const_cast<node_type *>(root())); + SearchResult<int, is_key_compare_to::value> res; + bool seen_eq = false; for (;;) { - iter.position = iter.node->lower_bound(key, key_comp()).value; + res = iter.node->lower_bound(key, key_comp()); + iter.position = res.value; if (iter.node->leaf()) { break; } + seen_eq = seen_eq || res.IsEq(); iter.node = iter.node->child(iter.position); } - return internal_last(iter); + if (res.IsEq()) return {iter, MatchKind::kEq}; + return {internal_last(iter), seen_eq ? MatchKind::kEq : MatchKind::kNe}; } template <typename P> @@ -2553,7 +2592,7 @@ auto btree<P>::internal_upper_bound(const K &key) const -> iterator { template <typename P> template <typename K> auto btree<P>::internal_find(const K &key) const -> iterator { - auto res = internal_locate(key); + SearchResult<iterator, is_key_compare_to::value> res = internal_locate(key); if (res.HasMatch()) { if (res.IsEq()) { return res.value; @@ -2568,18 +2607,6 @@ auto btree<P>::internal_find(const K &key) const -> iterator { } template <typename P> -void btree<P>::internal_clear(node_type *node) { - if (!node->leaf()) { - for (int i = node->start(); i <= node->finish(); ++i) { - internal_clear(node->child(i)); - } - delete_internal_node(node); - } else { - delete_leaf_node(node); - } -} - -template <typename P> int btree<P>::internal_verify(const node_type *node, const key_type *lo, const key_type *hi) const { assert(node->count() > 0); diff --git a/third_party/abseil-cpp/absl/container/internal/btree_container.h b/third_party/abseil-cpp/absl/container/internal/btree_container.h index f2e4c3a535..a99668c713 100644 --- a/third_party/abseil-cpp/absl/container/internal/btree_container.h +++ b/third_party/abseil-cpp/absl/container/internal/btree_container.h @@ -20,9 +20,11 @@ #include <iterator> #include <utility> +#include "absl/base/attributes.h" #include "absl/base/internal/throw_delegate.h" #include "absl/container/internal/btree.h" // IWYU pragma: export #include "absl/container/internal/common.h" +#include "absl/memory/memory.h" #include "absl/meta/type_traits.h" namespace absl { @@ -50,7 +52,7 @@ class btree_container { using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; using difference_type = typename Tree::difference_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using value_compare = typename Tree::value_compare; using allocator_type = typename Tree::allocator_type; using reference = typename Tree::reference; @@ -68,10 +70,23 @@ class btree_container { explicit btree_container(const key_compare &comp, const allocator_type &alloc = allocator_type()) : tree_(comp, alloc) {} - btree_container(const btree_container &x) = default; - btree_container(btree_container &&x) noexcept = default; - btree_container &operator=(const btree_container &x) = default; - btree_container &operator=(btree_container &&x) noexcept( + explicit btree_container(const allocator_type &alloc) + : tree_(key_compare(), alloc) {} + + btree_container(const btree_container &other) + : btree_container(other, absl::allocator_traits<allocator_type>:: + select_on_container_copy_construction( + other.get_allocator())) {} + btree_container(const btree_container &other, const allocator_type &alloc) + : tree_(other.tree_, alloc) {} + + btree_container(btree_container &&other) noexcept( + std::is_nothrow_move_constructible<Tree>::value) = default; + btree_container(btree_container &&other, const allocator_type &alloc) + : tree_(std::move(other.tree_), alloc) {} + + btree_container &operator=(const btree_container &other) = default; + btree_container &operator=(btree_container &&other) noexcept( std::is_nothrow_move_assignable<Tree>::value) = default; // Iterator routines. @@ -90,6 +105,11 @@ class btree_container { // Lookup routines. template <typename K = key_type> + size_type count(const key_arg<K> &key) const { + auto equal_range = this->equal_range(key); + return std::distance(equal_range.first, equal_range.second); + } + template <typename K = key_type> iterator find(const key_arg<K> &key) { return tree_.find(key); } @@ -138,6 +158,11 @@ class btree_container { iterator erase(const_iterator first, const_iterator last) { return tree_.erase_range(iterator(first), iterator(last)).second; } + template <typename K = key_type> + size_type erase(const key_arg<K> &key) { + auto equal_range = this->equal_range(key); + return tree_.erase_range(equal_range.first, equal_range.second).first; + } // Extract routines. node_type extract(iterator position) { @@ -151,10 +176,9 @@ class btree_container { return extract(iterator(position)); } - public: // Utility routines. - void clear() { tree_.clear(); } - void swap(btree_container &x) { tree_.swap(x.tree_); } + ABSL_ATTRIBUTE_REINITIALIZES void clear() { tree_.clear(); } + void swap(btree_container &other) { tree_.swap(other.tree_); } void verify() const { tree_.verify(); } // Size routines. @@ -191,7 +215,7 @@ class btree_container { allocator_type get_allocator() const { return tree_.get_allocator(); } // The key comparator used by the btree. - key_compare key_comp() const { return tree_.key_comp(); } + key_compare key_comp() const { return key_compare(tree_.key_comp()); } value_compare value_comp() const { return tree_.value_comp(); } // Support absl::Hash. @@ -224,7 +248,7 @@ class btree_set_container : public btree_container<Tree> { using key_type = typename Tree::key_type; using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; @@ -235,7 +259,7 @@ class btree_set_container : public btree_container<Tree> { using super_type::super_type; btree_set_container() {} - // Range constructor. + // Range constructors. template <class InputIterator> btree_set_container(InputIterator b, InputIterator e, const key_compare &comp = key_compare(), @@ -243,56 +267,55 @@ class btree_set_container : public btree_container<Tree> { : super_type(comp, alloc) { insert(b, e); } + template <class InputIterator> + btree_set_container(InputIterator b, InputIterator e, + const allocator_type &alloc) + : btree_set_container(b, e, key_compare(), alloc) {} - // Initializer list constructor. + // Initializer list constructors. btree_set_container(std::initializer_list<init_type> init, const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()) : btree_set_container(init.begin(), init.end(), comp, alloc) {} - - // Lookup routines. - template <typename K = key_type> - size_type count(const key_arg<K> &key) const { - return this->tree_.count_unique(key); - } + btree_set_container(std::initializer_list<init_type> init, + const allocator_type &alloc) + : btree_set_container(init.begin(), init.end(), alloc) {} // Insertion routines. - std::pair<iterator, bool> insert(const value_type &x) { - return this->tree_.insert_unique(params_type::key(x), x); + std::pair<iterator, bool> insert(const value_type &v) { + return this->tree_.insert_unique(params_type::key(v), v); } - std::pair<iterator, bool> insert(value_type &&x) { - return this->tree_.insert_unique(params_type::key(x), std::move(x)); + std::pair<iterator, bool> insert(value_type &&v) { + return this->tree_.insert_unique(params_type::key(v), std::move(v)); } template <typename... Args> std::pair<iterator, bool> emplace(Args &&... args) { init_type v(std::forward<Args>(args)...); return this->tree_.insert_unique(params_type::key(v), std::move(v)); } - iterator insert(const_iterator position, const value_type &x) { + iterator insert(const_iterator hint, const value_type &v) { return this->tree_ - .insert_hint_unique(iterator(position), params_type::key(x), x) + .insert_hint_unique(iterator(hint), params_type::key(v), v) .first; } - iterator insert(const_iterator position, value_type &&x) { + iterator insert(const_iterator hint, value_type &&v) { return this->tree_ - .insert_hint_unique(iterator(position), params_type::key(x), - std::move(x)) + .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v)) .first; } template <typename... Args> - iterator emplace_hint(const_iterator position, Args &&... args) { + iterator emplace_hint(const_iterator hint, Args &&... args) { init_type v(std::forward<Args>(args)...); return this->tree_ - .insert_hint_unique(iterator(position), params_type::key(v), - std::move(v)) + .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v)) .first; } template <typename InputIterator> void insert(InputIterator b, InputIterator e) { - this->tree_.insert_iterator_unique(b, e); + this->tree_.insert_iterator_unique(b, e, 0); } void insert(std::initializer_list<init_type> init) { - this->tree_.insert_iterator_unique(init.begin(), init.end()); + this->tree_.insert_iterator_unique(init.begin(), init.end(), 0); } insert_return_type insert(node_type &&node) { if (!node) return {this->end(), false, node_type()}; @@ -315,18 +338,13 @@ class btree_set_container : public btree_container<Tree> { return res.first; } - // Deletion routines. - template <typename K = key_type> - size_type erase(const key_arg<K> &key) { - return this->tree_.erase_unique(key); - } - using super_type::erase; - // Node extraction routines. template <typename K = key_type> node_type extract(const key_arg<K> &key) { - auto it = this->find(key); - return it == this->end() ? node_type() : extract(it); + const std::pair<iterator, bool> lower_and_equal = + this->tree_.lower_bound_equal(key); + return lower_and_equal.second ? extract(lower_and_equal.first) + : node_type(); } using super_type::extract; @@ -344,7 +362,7 @@ class btree_set_container : public btree_container<Tree> { int> = 0> void merge(btree_container<T> &src) { // NOLINT for (auto src_it = src.begin(); src_it != src.end();) { - if (insert(std::move(*src_it)).second) { + if (insert(std::move(params_type::element(src_it.slot()))).second) { src_it = src.erase(src_it); } else { ++src_it; @@ -371,6 +389,7 @@ template <typename Tree> class btree_map_container : public btree_set_container<Tree> { using super_type = btree_set_container<Tree>; using params_type = typename Tree::params_type; + friend class BtreeNodePeer; private: template <class K> @@ -380,7 +399,7 @@ class btree_map_container : public btree_set_container<Tree> { using key_type = typename Tree::key_type; using mapped_type = typename params_type::mapped_type; using value_type = typename Tree::value_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; @@ -392,111 +411,72 @@ class btree_map_container : public btree_set_container<Tree> { // Insertion routines. // Note: the nullptr template arguments and extra `const M&` overloads allow // for supporting bitfield arguments. - // Note: when we call `std::forward<M>(obj)` twice, it's safe because - // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when - // `ret.second` is false. - template <class M> - std::pair<iterator, bool> insert_or_assign(const key_type &k, const M &obj) { - const std::pair<iterator, bool> ret = this->tree_.insert_unique(k, k, obj); - if (!ret.second) ret.first->second = obj; - return ret; + template <typename K = key_type, class M> + std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, + const M &obj) { + return insert_or_assign_impl(k, obj); } - template <class M, key_type * = nullptr> - std::pair<iterator, bool> insert_or_assign(key_type &&k, const M &obj) { - const std::pair<iterator, bool> ret = - this->tree_.insert_unique(k, std::move(k), obj); - if (!ret.second) ret.first->second = obj; - return ret; + template <typename K = key_type, class M, K * = nullptr> + std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, const M &obj) { + return insert_or_assign_impl(std::forward<K>(k), obj); } - template <class M, M * = nullptr> - std::pair<iterator, bool> insert_or_assign(const key_type &k, M &&obj) { - const std::pair<iterator, bool> ret = - this->tree_.insert_unique(k, k, std::forward<M>(obj)); - if (!ret.second) ret.first->second = std::forward<M>(obj); - return ret; + template <typename K = key_type, class M, M * = nullptr> + std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, M &&obj) { + return insert_or_assign_impl(k, std::forward<M>(obj)); } - template <class M, key_type * = nullptr, M * = nullptr> - std::pair<iterator, bool> insert_or_assign(key_type &&k, M &&obj) { - const std::pair<iterator, bool> ret = - this->tree_.insert_unique(k, std::move(k), std::forward<M>(obj)); - if (!ret.second) ret.first->second = std::forward<M>(obj); - return ret; + template <typename K = key_type, class M, K * = nullptr, M * = nullptr> + std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, M &&obj) { + return insert_or_assign_impl(std::forward<K>(k), std::forward<M>(obj)); } - template <class M> - iterator insert_or_assign(const_iterator position, const key_type &k, + template <typename K = key_type, class M> + iterator insert_or_assign(const_iterator hint, const key_arg<K> &k, const M &obj) { - const std::pair<iterator, bool> ret = - this->tree_.insert_hint_unique(iterator(position), k, k, obj); - if (!ret.second) ret.first->second = obj; - return ret.first; + return insert_or_assign_hint_impl(hint, k, obj); } - template <class M, key_type * = nullptr> - iterator insert_or_assign(const_iterator position, key_type &&k, - const M &obj) { - const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( - iterator(position), k, std::move(k), obj); - if (!ret.second) ret.first->second = obj; - return ret.first; + template <typename K = key_type, class M, K * = nullptr> + iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, const M &obj) { + return insert_or_assign_hint_impl(hint, std::forward<K>(k), obj); } - template <class M, M * = nullptr> - iterator insert_or_assign(const_iterator position, const key_type &k, - M &&obj) { - const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( - iterator(position), k, k, std::forward<M>(obj)); - if (!ret.second) ret.first->second = std::forward<M>(obj); - return ret.first; + template <typename K = key_type, class M, M * = nullptr> + iterator insert_or_assign(const_iterator hint, const key_arg<K> &k, M &&obj) { + return insert_or_assign_hint_impl(hint, k, std::forward<M>(obj)); } - template <class M, key_type * = nullptr, M * = nullptr> - iterator insert_or_assign(const_iterator position, key_type &&k, M &&obj) { - const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( - iterator(position), k, std::move(k), std::forward<M>(obj)); - if (!ret.second) ret.first->second = std::forward<M>(obj); - return ret.first; + template <typename K = key_type, class M, K * = nullptr, M * = nullptr> + iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, M &&obj) { + return insert_or_assign_hint_impl(hint, std::forward<K>(k), + std::forward<M>(obj)); } - template <typename... Args> - std::pair<iterator, bool> try_emplace(const key_type &k, Args &&... args) { - return this->tree_.insert_unique( - k, std::piecewise_construct, std::forward_as_tuple(k), - std::forward_as_tuple(std::forward<Args>(args)...)); + + template <typename K = key_type, typename... Args, + typename absl::enable_if_t< + !std::is_convertible<K, const_iterator>::value, int> = 0> + std::pair<iterator, bool> try_emplace(const key_arg<K> &k, Args &&... args) { + return try_emplace_impl(k, std::forward<Args>(args)...); } - template <typename... Args> - std::pair<iterator, bool> try_emplace(key_type &&k, Args &&... args) { - // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` - // and then using `k` unsequenced. This is safe because the move is into a - // forwarding reference and insert_unique guarantees that `key` is never - // referenced after consuming `args`. - const key_type &key_ref = k; - return this->tree_.insert_unique( - key_ref, std::piecewise_construct, std::forward_as_tuple(std::move(k)), - std::forward_as_tuple(std::forward<Args>(args)...)); + template <typename K = key_type, typename... Args, + typename absl::enable_if_t< + !std::is_convertible<K, const_iterator>::value, int> = 0> + std::pair<iterator, bool> try_emplace(key_arg<K> &&k, Args &&... args) { + return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...); } - template <typename... Args> - iterator try_emplace(const_iterator hint, const key_type &k, + template <typename K = key_type, typename... Args> + iterator try_emplace(const_iterator hint, const key_arg<K> &k, Args &&... args) { - return this->tree_ - .insert_hint_unique(iterator(hint), k, std::piecewise_construct, - std::forward_as_tuple(k), - std::forward_as_tuple(std::forward<Args>(args)...)) - .first; + return try_emplace_hint_impl(hint, k, std::forward<Args>(args)...); } - template <typename... Args> - iterator try_emplace(const_iterator hint, key_type &&k, Args &&... args) { - // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k` - // and then using `k` unsequenced. This is safe because the move is into a - // forwarding reference and insert_hint_unique guarantees that `key` is - // never referenced after consuming `args`. - const key_type &key_ref = k; - return this->tree_ - .insert_hint_unique(iterator(hint), key_ref, std::piecewise_construct, - std::forward_as_tuple(std::move(k)), - std::forward_as_tuple(std::forward<Args>(args)...)) - .first; + template <typename K = key_type, typename... Args> + iterator try_emplace(const_iterator hint, key_arg<K> &&k, Args &&... args) { + return try_emplace_hint_impl(hint, std::forward<K>(k), + std::forward<Args>(args)...); } - mapped_type &operator[](const key_type &k) { + + template <typename K = key_type> + mapped_type &operator[](const key_arg<K> &k) { return try_emplace(k).first->second; } - mapped_type &operator[](key_type &&k) { - return try_emplace(std::move(k)).first->second; + template <typename K = key_type> + mapped_type &operator[](key_arg<K> &&k) { + return try_emplace(std::forward<K>(k)).first->second; } template <typename K = key_type> @@ -513,6 +493,40 @@ class btree_map_container : public btree_set_container<Tree> { base_internal::ThrowStdOutOfRange("absl::btree_map::at"); return it->second; } + + private: + // Note: when we call `std::forward<M>(obj)` twice, it's safe because + // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when + // `ret.second` is false. + template <class K, class M> + std::pair<iterator, bool> insert_or_assign_impl(K &&k, M &&obj) { + const std::pair<iterator, bool> ret = + this->tree_.insert_unique(k, std::forward<K>(k), std::forward<M>(obj)); + if (!ret.second) ret.first->second = std::forward<M>(obj); + return ret; + } + template <class K, class M> + iterator insert_or_assign_hint_impl(const_iterator hint, K &&k, M &&obj) { + const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( + iterator(hint), k, std::forward<K>(k), std::forward<M>(obj)); + if (!ret.second) ret.first->second = std::forward<M>(obj); + return ret.first; + } + + template <class K, class... Args> + std::pair<iterator, bool> try_emplace_impl(K &&k, Args &&... args) { + return this->tree_.insert_unique( + k, std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)), + std::forward_as_tuple(std::forward<Args>(args)...)); + } + template <class K, class... Args> + iterator try_emplace_hint_impl(const_iterator hint, K &&k, Args &&... args) { + return this->tree_ + .insert_hint_unique(iterator(hint), k, std::piecewise_construct, + std::forward_as_tuple(std::forward<K>(k)), + std::forward_as_tuple(std::forward<Args>(args)...)) + .first; + } }; // A common base class for btree_multiset and btree_multimap. @@ -530,7 +544,7 @@ class btree_multiset_container : public btree_container<Tree> { using key_type = typename Tree::key_type; using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; - using key_compare = typename Tree::key_compare; + using key_compare = typename Tree::original_key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; @@ -540,7 +554,7 @@ class btree_multiset_container : public btree_container<Tree> { using super_type::super_type; btree_multiset_container() {} - // Range constructor. + // Range constructors. template <class InputIterator> btree_multiset_container(InputIterator b, InputIterator e, const key_compare &comp = key_compare(), @@ -548,29 +562,30 @@ class btree_multiset_container : public btree_container<Tree> { : super_type(comp, alloc) { insert(b, e); } + template <class InputIterator> + btree_multiset_container(InputIterator b, InputIterator e, + const allocator_type &alloc) + : btree_multiset_container(b, e, key_compare(), alloc) {} - // Initializer list constructor. + // Initializer list constructors. btree_multiset_container(std::initializer_list<init_type> init, const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()) : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} - - // Lookup routines. - template <typename K = key_type> - size_type count(const key_arg<K> &key) const { - return this->tree_.count_multi(key); - } + btree_multiset_container(std::initializer_list<init_type> init, + const allocator_type &alloc) + : btree_multiset_container(init.begin(), init.end(), alloc) {} // Insertion routines. - iterator insert(const value_type &x) { return this->tree_.insert_multi(x); } - iterator insert(value_type &&x) { - return this->tree_.insert_multi(std::move(x)); + iterator insert(const value_type &v) { return this->tree_.insert_multi(v); } + iterator insert(value_type &&v) { + return this->tree_.insert_multi(std::move(v)); } - iterator insert(const_iterator position, const value_type &x) { - return this->tree_.insert_hint_multi(iterator(position), x); + iterator insert(const_iterator hint, const value_type &v) { + return this->tree_.insert_hint_multi(iterator(hint), v); } - iterator insert(const_iterator position, value_type &&x) { - return this->tree_.insert_hint_multi(iterator(position), std::move(x)); + iterator insert(const_iterator hint, value_type &&v) { + return this->tree_.insert_hint_multi(iterator(hint), std::move(v)); } template <typename InputIterator> void insert(InputIterator b, InputIterator e) { @@ -584,9 +599,9 @@ class btree_multiset_container : public btree_container<Tree> { return this->tree_.insert_multi(init_type(std::forward<Args>(args)...)); } template <typename... Args> - iterator emplace_hint(const_iterator position, Args &&... args) { + iterator emplace_hint(const_iterator hint, Args &&... args) { return this->tree_.insert_hint_multi( - iterator(position), init_type(std::forward<Args>(args)...)); + iterator(hint), init_type(std::forward<Args>(args)...)); } iterator insert(node_type &&node) { if (!node) return this->end(); @@ -605,18 +620,13 @@ class btree_multiset_container : public btree_container<Tree> { return res; } - // Deletion routines. - template <typename K = key_type> - size_type erase(const key_arg<K> &key) { - return this->tree_.erase_multi(key); - } - using super_type::erase; - // Node extraction routines. template <typename K = key_type> node_type extract(const key_arg<K> &key) { - auto it = this->find(key); - return it == this->end() ? node_type() : extract(it); + const std::pair<iterator, bool> lower_and_equal = + this->tree_.lower_bound_equal(key); + return lower_and_equal.second ? extract(lower_and_equal.first) + : node_type(); } using super_type::extract; @@ -632,8 +642,9 @@ class btree_multiset_container : public btree_container<Tree> { typename T::params_type::is_map_container>>::value, int> = 0> void merge(btree_container<T> &src) { // NOLINT - insert(std::make_move_iterator(src.begin()), - std::make_move_iterator(src.end())); + for (auto src_it = src.begin(), end = src.end(); src_it != end; ++src_it) { + insert(std::move(params_type::element(src_it.slot()))); + } src.clear(); } diff --git a/third_party/abseil-cpp/absl/container/internal/common.h b/third_party/abseil-cpp/absl/container/internal/common.h index 5037d80316..030e9d4ab0 100644 --- a/third_party/abseil-cpp/absl/container/internal/common.h +++ b/third_party/abseil-cpp/absl/container/internal/common.h @@ -138,6 +138,7 @@ class node_handle<Policy, PolicyTraits, Alloc, absl::void_t<typename Policy::mapped_type>> : public node_handle_base<PolicyTraits, Alloc> { using Base = node_handle_base<PolicyTraits, Alloc>; + using slot_type = typename PolicyTraits::slot_type; public: using key_type = typename Policy::key_type; @@ -145,8 +146,11 @@ class node_handle<Policy, PolicyTraits, Alloc, constexpr node_handle() {} - auto key() const -> decltype(PolicyTraits::key(this->slot())) { - return PolicyTraits::key(this->slot()); + // When C++17 is available, we can use std::launder to provide mutable + // access to the key. Otherwise, we provide const access. + auto key() const + -> decltype(PolicyTraits::mutable_key(std::declval<slot_type*>())) { + return PolicyTraits::mutable_key(this->slot()); } mapped_type& mapped() const { diff --git a/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h b/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h index 4bfe92fd99..5ebe164942 100644 --- a/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h +++ b/third_party/abseil-cpp/absl/container/internal/compressed_tuple.h @@ -169,9 +169,33 @@ constexpr bool ShouldAnyUseBase() { } template <typename T, typename V> -using TupleMoveConstructible = typename std::conditional< - std::is_reference<T>::value, std::is_convertible<V, T>, - std::is_constructible<T, V&&>>::type; +using TupleElementMoveConstructible = + typename std::conditional<std::is_reference<T>::value, + std::is_convertible<V, T>, + std::is_constructible<T, V&&>>::type; + +template <bool SizeMatches, class T, class... Vs> +struct TupleMoveConstructible : std::false_type {}; + +template <class... Ts, class... Vs> +struct TupleMoveConstructible<true, CompressedTuple<Ts...>, Vs...> + : std::integral_constant< + bool, absl::conjunction< + TupleElementMoveConstructible<Ts, Vs&&>...>::value> {}; + +template <typename T> +struct compressed_tuple_size; + +template <typename... Es> +struct compressed_tuple_size<CompressedTuple<Es...>> + : public std::integral_constant<std::size_t, sizeof...(Es)> {}; + +template <class T, class... Vs> +struct TupleItemsMoveConstructible + : std::integral_constant< + bool, TupleMoveConstructible<compressed_tuple_size<T>::value == + sizeof...(Vs), + T, Vs...>::value> {}; } // namespace internal_compressed_tuple @@ -217,22 +241,23 @@ class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple explicit constexpr CompressedTuple(const Ts&... base) : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {} - template <typename... Vs, + template <typename First, typename... Vs, absl::enable_if_t< absl::conjunction< // Ensure we are not hiding default copy/move constructors. absl::negation<std::is_same<void(CompressedTuple), - void(absl::decay_t<Vs>...)>>, - internal_compressed_tuple::TupleMoveConstructible< - Ts, Vs&&>...>::value, + void(absl::decay_t<First>)>>, + internal_compressed_tuple::TupleItemsMoveConstructible< + CompressedTuple<Ts...>, First, Vs...>>::value, bool> = true> - explicit constexpr CompressedTuple(Vs&&... base) + explicit constexpr CompressedTuple(First&& first, Vs&&... base) : CompressedTuple::CompressedTupleImpl(absl::in_place, + absl::forward<First>(first), absl::forward<Vs>(base)...) {} template <int I> ElemT<I>& get() & { - return internal_compressed_tuple::Storage<ElemT<I>, I>::get(); + return StorageT<I>::get(); } template <int I> diff --git a/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc b/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc index 1dae12db81..62a7483ee3 100644 --- a/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/compressed_tuple_test.cc @@ -277,11 +277,11 @@ TEST(CompressedTupleTest, Nested) { TEST(CompressedTupleTest, Reference) { int i = 7; - std::string s = "Very long std::string that goes in the heap"; + std::string s = "Very long string that goes in the heap"; CompressedTuple<int, int&, std::string, std::string&> x(i, i, s, s); // Sanity check. We should have not moved from `s` - EXPECT_EQ(s, "Very long std::string that goes in the heap"); + EXPECT_EQ(s, "Very long string that goes in the heap"); EXPECT_EQ(x.get<0>(), x.get<1>()); EXPECT_NE(&x.get<0>(), &x.get<1>()); diff --git a/third_party/abseil-cpp/absl/container/internal/container_memory.h b/third_party/abseil-cpp/absl/container/internal/container_memory.h index d24b0f8413..e67529ecb6 100644 --- a/third_party/abseil-cpp/absl/container/internal/container_memory.h +++ b/third_party/abseil-cpp/absl/container/internal/container_memory.h @@ -15,28 +15,34 @@ #ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ #define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ -#ifdef ADDRESS_SANITIZER -#include <sanitizer/asan_interface.h> -#endif - -#ifdef MEMORY_SANITIZER -#include <sanitizer/msan_interface.h> -#endif - #include <cassert> #include <cstddef> #include <memory> +#include <new> #include <tuple> #include <type_traits> #include <utility> +#include "absl/base/config.h" #include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" #include "absl/utility/utility.h" +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#include <sanitizer/asan_interface.h> +#endif + +#ifdef ABSL_HAVE_MEMORY_SANITIZER +#include <sanitizer/msan_interface.h> +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +template <size_t Alignment> +struct alignas(Alignment) AlignedType {}; + // Allocates at least n bytes aligned to the specified alignment. // Alignment must be a power of 2. It must be positive. // @@ -48,11 +54,14 @@ template <size_t Alignment, class Alloc> void* Allocate(Alloc* alloc, size_t n) { static_assert(Alignment > 0, ""); assert(n && "n must be positive"); - struct alignas(Alignment) M {}; + using M = AlignedType<Alignment>; using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; - A mem_alloc(*alloc); - void* p = AT::allocate(mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); + // On macOS, "mem_alloc" is a #define with one argument defined in + // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it + // with the "foo(bar)" syntax. + A my_mem_alloc(*alloc); + void* p = AT::allocate(my_mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); assert(reinterpret_cast<uintptr_t>(p) % Alignment == 0 && "allocator does not respect alignment"); return p; @@ -64,11 +73,14 @@ template <size_t Alignment, class Alloc> void Deallocate(Alloc* alloc, void* p, size_t n) { static_assert(Alignment > 0, ""); assert(n && "n must be positive"); - struct alignas(Alignment) M {}; + using M = AlignedType<Alignment>; using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; - A mem_alloc(*alloc); - AT::deallocate(mem_alloc, static_cast<M*>(p), + // On macOS, "mem_alloc" is a #define with one argument defined in + // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it + // with the "foo(bar)" syntax. + A my_mem_alloc(*alloc); + AT::deallocate(my_mem_alloc, static_cast<M*>(p), (n + sizeof(M) - 1) / sizeof(M)); } @@ -205,10 +217,10 @@ DecomposeValue(F&& f, Arg&& arg) { // Helper functions for asan and msan. inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER ASAN_POISON_MEMORY_REGION(m, s); #endif -#ifdef MEMORY_SANITIZER +#ifdef ABSL_HAVE_MEMORY_SANITIZER __msan_poison(m, s); #endif (void)m; @@ -216,10 +228,10 @@ inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { } inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER ASAN_UNPOISON_MEMORY_REGION(m, s); #endif -#ifdef MEMORY_SANITIZER +#ifdef ABSL_HAVE_MEMORY_SANITIZER __msan_unpoison(m, s); #endif (void)m; @@ -246,8 +258,8 @@ namespace memory_internal { // type, which is non-portable. template <class Pair, class = std::true_type> struct OffsetOf { - static constexpr size_t kFirst = -1; - static constexpr size_t kSecond = -1; + static constexpr size_t kFirst = static_cast<size_t>(-1); + static constexpr size_t kSecond = static_cast<size_t>(-1); }; template <class Pair> @@ -316,11 +328,12 @@ union map_slot_type { map_slot_type() {} ~map_slot_type() = delete; using value_type = std::pair<const K, V>; - using mutable_value_type = std::pair<K, V>; + using mutable_value_type = + std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>; value_type value; mutable_value_type mutable_value; - K key; + absl::remove_const_t<K> key; }; template <class K, class V> @@ -346,6 +359,20 @@ struct map_slot_policy { return slot->value; } + // When C++17 is available, we can use std::launder to provide mutable + // access to the key for use in node handle. +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + static K& mutable_key(slot_type* slot) { + // Still check for kMutableKeys so that we can avoid calling std::launder + // unless necessary because it can interfere with optimizations. + return kMutableKeys::value ? slot->key + : *std::launder(const_cast<K*>( + std::addressof(slot->value.first))); + } +#else // !(defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606) + static const K& mutable_key(slot_type* slot) { return key(slot); } +#endif + static const K& key(const slot_type* slot) { return kMutableKeys::value ? slot->key : slot->value.first; } @@ -424,13 +451,6 @@ struct map_slot_policy { std::move(src->value)); } } - - template <class Allocator> - static void move(Allocator* alloc, slot_type* first, slot_type* last, - slot_type* result) { - for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) - move(alloc, src, dest); - } }; } // namespace container_internal diff --git a/third_party/abseil-cpp/absl/container/internal/container_memory_test.cc b/third_party/abseil-cpp/absl/container/internal/container_memory_test.cc index 7942c7be48..fb9c4ddede 100644 --- a/third_party/abseil-cpp/absl/container/internal/container_memory_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/container_memory_test.cc @@ -16,10 +16,13 @@ #include <cstdint> #include <tuple> +#include <typeindex> +#include <typeinfo> #include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/internal/test_instance_tracker.h" #include "absl/strings/string_view.h" namespace absl { @@ -27,6 +30,11 @@ ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { +using ::absl::test_internal::CopyableMovableInstance; +using ::absl::test_internal::InstanceTracker; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Gt; using ::testing::Pair; TEST(Memory, AlignmentLargerThanBase) { @@ -45,6 +53,39 @@ TEST(Memory, AlignmentSmallerThanBase) { Deallocate<2>(&alloc, mem, 3); } +std::map<std::type_index, int>& AllocationMap() { + static auto* map = new std::map<std::type_index, int>; + return *map; +} + +template <typename T> +struct TypeCountingAllocator { + TypeCountingAllocator() = default; + template <typename U> + TypeCountingAllocator(const TypeCountingAllocator<U>&) {} // NOLINT + + using value_type = T; + + T* allocate(size_t n, const void* = nullptr) { + AllocationMap()[typeid(T)] += n; + return std::allocator<T>().allocate(n); + } + void deallocate(T* p, std::size_t n) { + AllocationMap()[typeid(T)] -= n; + return std::allocator<T>().deallocate(p, n); + } +}; + +TEST(Memory, AllocateDeallocateMatchType) { + TypeCountingAllocator<int> alloc; + void* mem = Allocate<1>(&alloc, 1); + // Verify that it was allocated + EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, Gt(0)))); + Deallocate<1>(&alloc, mem, 1); + // Verify that the deallocation matched. + EXPECT_THAT(AllocationMap(), ElementsAre(Pair(_, 0))); +} + class Fixture : public ::testing::Test { using Alloc = std::allocator<std::string>; @@ -125,7 +166,7 @@ TryDecomposeValue(F&& f, Arg&& arg) { } TEST(DecomposeValue, Decomposable) { - auto f = [](const int& x, int&& y) { + auto f = [](const int& x, int&& y) { // NOLINT EXPECT_EQ(&x, &y); EXPECT_EQ(42, x); return 'A'; @@ -159,7 +200,8 @@ TryDecomposePair(F&& f, Args&&... args) { } TEST(DecomposePair, Decomposable) { - auto f = [](const int& x, std::piecewise_construct_t, std::tuple<int&&> k, + auto f = [](const int& x, // NOLINT + std::piecewise_construct_t, std::tuple<int&&> k, std::tuple<double>&& v) { EXPECT_EQ(&x, &std::get<0>(k)); EXPECT_EQ(42, x); @@ -184,6 +226,31 @@ TEST(DecomposePair, NotDecomposable) { std::make_tuple(0.5))); } +TEST(MapSlotPolicy, ConstKeyAndValue) { + using slot_policy = map_slot_policy<const CopyableMovableInstance, + const CopyableMovableInstance>; + using slot_type = typename slot_policy::slot_type; + + union Slots { + Slots() {} + ~Slots() {} + slot_type slots[100]; + } slots; + + std::allocator< + std::pair<const CopyableMovableInstance, const CopyableMovableInstance>> + alloc; + InstanceTracker tracker; + slot_policy::construct(&alloc, &slots.slots[0], CopyableMovableInstance(1), + CopyableMovableInstance(1)); + for (int i = 0; i < 99; ++i) { + slot_policy::transfer(&alloc, &slots.slots[i + 1], &slots.slots[i]); + } + slot_policy::destroy(&alloc, &slots.slots[99]); + + EXPECT_EQ(tracker.copies(), 0); +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/internal/counting_allocator.h b/third_party/abseil-cpp/absl/container/internal/counting_allocator.h index 9efdc66213..927cf08255 100644 --- a/third_party/abseil-cpp/absl/container/internal/counting_allocator.h +++ b/third_party/abseil-cpp/absl/container/internal/counting_allocator.h @@ -15,7 +15,6 @@ #ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ #define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ -#include <cassert> #include <cstdint> #include <memory> @@ -31,33 +30,63 @@ namespace container_internal { // containers - that chain of allocators uses the same state and is // thus easier to query for aggregate allocation information. template <typename T> -class CountingAllocator : public std::allocator<T> { +class CountingAllocator { public: - using Alloc = std::allocator<T>; - using pointer = typename Alloc::pointer; - using size_type = typename Alloc::size_type; + using Allocator = std::allocator<T>; + using AllocatorTraits = std::allocator_traits<Allocator>; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; - CountingAllocator() : bytes_used_(nullptr) {} - explicit CountingAllocator(int64_t* b) : bytes_used_(b) {} + CountingAllocator() = default; + explicit CountingAllocator(int64_t* bytes_used) : bytes_used_(bytes_used) {} + CountingAllocator(int64_t* bytes_used, int64_t* instance_count) + : bytes_used_(bytes_used), instance_count_(instance_count) {} template <typename U> CountingAllocator(const CountingAllocator<U>& x) - : Alloc(x), bytes_used_(x.bytes_used_) {} + : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {} - pointer allocate(size_type n, - std::allocator<void>::const_pointer hint = nullptr) { - assert(bytes_used_ != nullptr); - *bytes_used_ += n * sizeof(T); - return Alloc::allocate(n, hint); + pointer allocate( + size_type n, + typename AllocatorTraits::const_void_pointer hint = nullptr) { + Allocator allocator; + pointer ptr = AllocatorTraits::allocate(allocator, n, hint); + if (bytes_used_ != nullptr) { + *bytes_used_ += n * sizeof(T); + } + return ptr; } void deallocate(pointer p, size_type n) { - Alloc::deallocate(p, n); - assert(bytes_used_ != nullptr); - *bytes_used_ -= n * sizeof(T); + Allocator allocator; + AllocatorTraits::deallocate(allocator, p, n); + if (bytes_used_ != nullptr) { + *bytes_used_ -= n * sizeof(T); + } } - template<typename U> + template <typename U, typename... Args> + void construct(U* p, Args&&... args) { + Allocator allocator; + AllocatorTraits::construct(allocator, p, std::forward<Args>(args)...); + if (instance_count_ != nullptr) { + *instance_count_ += 1; + } + } + + template <typename U> + void destroy(U* p) { + Allocator allocator; + AllocatorTraits::destroy(allocator, p); + if (instance_count_ != nullptr) { + *instance_count_ -= 1; + } + } + + template <typename U> class rebind { public: using other = CountingAllocator<U>; @@ -65,7 +94,8 @@ class CountingAllocator : public std::allocator<T> { friend bool operator==(const CountingAllocator& a, const CountingAllocator& b) { - return a.bytes_used_ == b.bytes_used_; + return a.bytes_used_ == b.bytes_used_ && + a.instance_count_ == b.instance_count_; } friend bool operator!=(const CountingAllocator& a, @@ -73,7 +103,8 @@ class CountingAllocator : public std::allocator<T> { return !(a == b); } - int64_t* bytes_used_; + int64_t* bytes_used_ = nullptr; + int64_t* instance_count_ = nullptr; }; } // namespace container_internal diff --git a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h index 401ddf4d83..250e662c9d 100644 --- a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h +++ b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h @@ -53,6 +53,7 @@ #include "absl/base/config.h" #include "absl/hash/hash.h" +#include "absl/strings/cord.h" #include "absl/strings/string_view.h" namespace absl { @@ -72,23 +73,39 @@ struct StringHash { size_t operator()(absl::string_view v) const { return absl::Hash<absl::string_view>{}(v); } + size_t operator()(const absl::Cord& v) const { + return absl::Hash<absl::Cord>{}(v); + } +}; + +struct StringEq { + using is_transparent = void; + bool operator()(absl::string_view lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } + bool operator()(const absl::Cord& lhs, absl::string_view rhs) const { + return lhs == rhs; + } + bool operator()(absl::string_view lhs, const absl::Cord& rhs) const { + return lhs == rhs; + } }; // Supports heterogeneous lookup for string-like elements. struct StringHashEq { using Hash = StringHash; - struct Eq { - using is_transparent = void; - bool operator()(absl::string_view lhs, absl::string_view rhs) const { - return lhs == rhs; - } - }; + using Eq = StringEq; }; template <> struct HashEq<std::string> : StringHashEq {}; template <> struct HashEq<absl::string_view> : StringHashEq {}; +template <> +struct HashEq<absl::Cord> : StringHashEq {}; // Supports heterogeneous lookup for pointers and smart pointers. template <class T> diff --git a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc index 2eefc7e0de..59576b8ede 100644 --- a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc @@ -19,6 +19,9 @@ #include <utility> #include "gtest/gtest.h" +#include "absl/random/random.h" +#include "absl/strings/cord.h" +#include "absl/strings/cord_test_helpers.h" #include "absl/strings/string_view.h" namespace absl { @@ -203,10 +206,91 @@ TYPED_TEST(HashPointer, Works) { EXPECT_NE(hash(&dummy), hash(cuptr)); } +TEST(EqCord, Works) { + hash_default_eq<absl::Cord> eq; + const absl::string_view a_string_view = "a"; + const absl::Cord a_cord(a_string_view); + const absl::string_view b_string_view = "b"; + const absl::Cord b_cord(b_string_view); + + EXPECT_TRUE(eq(a_cord, a_cord)); + EXPECT_TRUE(eq(a_cord, a_string_view)); + EXPECT_TRUE(eq(a_string_view, a_cord)); + EXPECT_FALSE(eq(a_cord, b_cord)); + EXPECT_FALSE(eq(a_cord, b_string_view)); + EXPECT_FALSE(eq(b_string_view, a_cord)); +} + +TEST(HashCord, Works) { + hash_default_hash<absl::Cord> hash; + const absl::string_view a_string_view = "a"; + const absl::Cord a_cord(a_string_view); + const absl::string_view b_string_view = "b"; + const absl::Cord b_cord(b_string_view); + + EXPECT_EQ(hash(a_cord), hash(a_cord)); + EXPECT_EQ(hash(b_cord), hash(b_cord)); + EXPECT_EQ(hash(a_string_view), hash(a_cord)); + EXPECT_EQ(hash(b_string_view), hash(b_cord)); + EXPECT_EQ(hash(absl::Cord("")), hash("")); + EXPECT_EQ(hash(absl::Cord()), hash(absl::string_view())); + + EXPECT_NE(hash(a_cord), hash(b_cord)); + EXPECT_NE(hash(a_cord), hash(b_string_view)); + EXPECT_NE(hash(a_string_view), hash(b_cord)); + EXPECT_NE(hash(a_string_view), hash(b_string_view)); +} + +void NoOpReleaser(absl::string_view data, void* arg) {} + +TEST(HashCord, FragmentedCordWorks) { + hash_default_hash<absl::Cord> hash; + absl::Cord c = absl::MakeFragmentedCord({"a", "b", "c"}); + EXPECT_FALSE(c.TryFlat().has_value()); + EXPECT_EQ(hash(c), hash("abc")); +} + +TEST(HashCord, FragmentedLongCordWorks) { + hash_default_hash<absl::Cord> hash; + // Crete some large strings which do not fit on the stack. + std::string a(65536, 'a'); + std::string b(65536, 'b'); + absl::Cord c = absl::MakeFragmentedCord({a, b}); + EXPECT_FALSE(c.TryFlat().has_value()); + EXPECT_EQ(hash(c), hash(a + b)); +} + +TEST(HashCord, RandomCord) { + hash_default_hash<absl::Cord> hash; + auto bitgen = absl::BitGen(); + for (int i = 0; i < 1000; ++i) { + const int number_of_segments = absl::Uniform(bitgen, 0, 10); + std::vector<std::string> pieces; + for (size_t s = 0; s < number_of_segments; ++s) { + std::string str; + str.resize(absl::Uniform(bitgen, 0, 4096)); + // MSVC needed the explicit return type in the lambda. + std::generate(str.begin(), str.end(), [&]() -> char { + return static_cast<char>(absl::Uniform<unsigned char>(bitgen)); + }); + pieces.push_back(str); + } + absl::Cord c = absl::MakeFragmentedCord(pieces); + EXPECT_EQ(hash(c), hash(std::string(c))); + } +} + // Cartesian product of (std::string, absl::string_view) -// with (std::string, absl::string_view, const char*). +// with (std::string, absl::string_view, const char*, absl::Cord). using StringTypesCartesianProduct = Types< // clang-format off + std::pair<absl::Cord, std::string>, + std::pair<absl::Cord, absl::string_view>, + std::pair<absl::Cord, absl::Cord>, + std::pair<absl::Cord, const char*>, + + std::pair<std::string, absl::Cord>, + std::pair<absl::string_view, absl::Cord>, std::pair<absl::string_view, std::string>, std::pair<absl::string_view, absl::string_view>, @@ -253,11 +337,11 @@ ABSL_NAMESPACE_END } // namespace absl enum Hash : size_t { - kStd = 0x2, // std::hash + kStd = 0x1, // std::hash #ifdef _MSC_VER kExtension = kStd, // In MSVC, std::hash == ::hash #else // _MSC_VER - kExtension = 0x4, // ::hash (GCC extension) + kExtension = 0x2, // ::hash (GCC extension) #endif // _MSC_VER }; diff --git a/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.cc b/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.cc index 75c4db6c36..59cc5aac7a 100644 --- a/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.cc +++ b/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.cc @@ -41,8 +41,10 @@ class RandomDeviceSeedSeq { } // namespace std::mt19937_64* GetSharedRng() { - RandomDeviceSeedSeq seed_seq; - static auto* rng = new std::mt19937_64(seed_seq); + static auto* rng = [] { + RandomDeviceSeedSeq seed_seq; + return new std::mt19937_64(seed_seq); + }(); return rng; } diff --git a/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.h b/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.h index 6869fe45e8..f1f555a5c1 100644 --- a/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.h +++ b/third_party/abseil-cpp/absl/container/internal/hash_generator_testing.h @@ -21,11 +21,13 @@ #include <stdint.h> #include <algorithm> +#include <cassert> #include <iosfwd> #include <random> #include <tuple> #include <type_traits> #include <utility> +#include <vector> #include "absl/container/internal/hash_policy_testing.h" #include "absl/memory/memory.h" @@ -153,6 +155,25 @@ using GeneratedType = decltype( typename Container::value_type, typename Container::key_type>::type>&>()()); +// Naive wrapper that performs a linear search of previous values. +// Beware this is O(SQR), which is reasonable for smaller kMaxValues. +template <class T, size_t kMaxValues = 64, class E = void> +struct UniqueGenerator { + Generator<T, E> gen; + std::vector<T> values; + + T operator()() { + assert(values.size() < kMaxValues); + for (;;) { + T value = gen(); + if (std::find(values.begin(), values.end(), value) == values.end()) { + values.push_back(value); + return value; + } + } + } +}; + } // namespace hash_internal } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h b/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h index 3e1209c6eb..46c97b18a2 100644 --- a/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h +++ b/third_party/abseil-cpp/absl/container/internal/hash_policy_traits.h @@ -17,6 +17,7 @@ #include <cstddef> #include <memory> +#include <new> #include <type_traits> #include <utility> @@ -29,15 +30,34 @@ namespace container_internal { // Defines how slots are initialized/destroyed/moved. template <class Policy, class = void> struct hash_policy_traits { + // The type of the keys stored in the hashtable. + using key_type = typename Policy::key_type; + private: struct ReturnKey { - // We return `Key` here. + // When C++17 is available, we can use std::launder to provide mutable + // access to the key for use in node handle. +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + template <class Key, + absl::enable_if_t<std::is_lvalue_reference<Key>::value, int> = 0> + static key_type& Impl(Key&& k, int) { + return *std::launder( + const_cast<key_type*>(std::addressof(std::forward<Key>(k)))); + } +#endif + + template <class Key> + static Key Impl(Key&& k, char) { + return std::forward<Key>(k); + } + // When Key=T&, we forward the lvalue reference. // When Key=T, we return by value to avoid a dangling reference. // eg, for string_hash_map. template <class Key, class... Args> - Key operator()(Key&& k, const Args&...) const { - return std::forward<Key>(k); + auto operator()(Key&& k, const Args&...) const + -> decltype(Impl(std::forward<Key>(k), 0)) { + return Impl(std::forward<Key>(k), 0); } }; @@ -52,9 +72,6 @@ struct hash_policy_traits { // The actual object stored in the hash table. using slot_type = typename Policy::slot_type; - // The type of the keys stored in the hashtable. - using key_type = typename Policy::key_type; - // The argument type for insertions into the hashtable. This is different // from value_type for increased performance. See initializer_list constructor // and insert() member functions for more details. @@ -156,7 +173,7 @@ struct hash_policy_traits { // Returns the "key" portion of the slot. // Used for node handle manipulation. template <class P = Policy> - static auto key(slot_type* slot) + static auto mutable_key(slot_type* slot) -> decltype(P::apply(ReturnKey(), element(slot))) { return P::apply(ReturnKey(), element(slot)); } diff --git a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.cc b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.cc index 5644725178..40cce0479e 100644 --- a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.cc +++ b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.cc @@ -21,10 +21,11 @@ #include <limits> #include "absl/base/attributes.h" -#include "absl/base/internal/exponential_biased.h" #include "absl/container/internal/have_sse.h" #include "absl/debugging/stacktrace.h" #include "absl/memory/memory.h" +#include "absl/profiling/internal/exponential_biased.h" +#include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/mutex.h" namespace absl { @@ -37,10 +38,9 @@ ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{ false }; ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10}; -ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20}; #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased +ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased g_exponential_biased_generator; #endif @@ -50,16 +50,14 @@ ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -HashtablezSampler& HashtablezSampler::Global() { +HashtablezSampler& GlobalHashtablezSampler() { static auto* sampler = new HashtablezSampler(); return *sampler; } -HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback( - DisposeCallback f) { - return dispose_.exchange(f, std::memory_order_relaxed); -} - +// TODO(bradleybear): The comments at this constructors declaration say that the +// fields are not initialized, but this definition does initialize the fields. +// Something needs to be cleaned up. HashtablezInfo::HashtablezInfo() { PrepareForSampling(); } HashtablezInfo::~HashtablezInfo() = default; @@ -67,10 +65,13 @@ void HashtablezInfo::PrepareForSampling() { capacity.store(0, std::memory_order_relaxed); size.store(0, std::memory_order_relaxed); num_erases.store(0, std::memory_order_relaxed); + num_rehashes.store(0, std::memory_order_relaxed); max_probe_length.store(0, std::memory_order_relaxed); total_probe_length.store(0, std::memory_order_relaxed); hashes_bitwise_or.store(0, std::memory_order_relaxed); hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed); + hashes_bitwise_xor.store(0, std::memory_order_relaxed); + max_reserve.store(0, std::memory_order_relaxed); create_time = absl::Now(); // The inliner makes hardcoded skip_count difficult (especially when combined @@ -78,93 +79,6 @@ void HashtablezInfo::PrepareForSampling() { // instead. depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, /* skip_count= */ 0); - dead = nullptr; -} - -HashtablezSampler::HashtablezSampler() - : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) { - absl::MutexLock l(&graveyard_.init_mu); - graveyard_.dead = &graveyard_; -} - -HashtablezSampler::~HashtablezSampler() { - HashtablezInfo* s = all_.load(std::memory_order_acquire); - while (s != nullptr) { - HashtablezInfo* next = s->next; - delete s; - s = next; - } -} - -void HashtablezSampler::PushNew(HashtablezInfo* sample) { - sample->next = all_.load(std::memory_order_relaxed); - while (!all_.compare_exchange_weak(sample->next, sample, - std::memory_order_release, - std::memory_order_relaxed)) { - } -} - -void HashtablezSampler::PushDead(HashtablezInfo* sample) { - if (auto* dispose = dispose_.load(std::memory_order_relaxed)) { - dispose(*sample); - } - - absl::MutexLock graveyard_lock(&graveyard_.init_mu); - absl::MutexLock sample_lock(&sample->init_mu); - sample->dead = graveyard_.dead; - graveyard_.dead = sample; -} - -HashtablezInfo* HashtablezSampler::PopDead() { - absl::MutexLock graveyard_lock(&graveyard_.init_mu); - - // The list is circular, so eventually it collapses down to - // graveyard_.dead == &graveyard_ - // when it is empty. - HashtablezInfo* sample = graveyard_.dead; - if (sample == &graveyard_) return nullptr; - - absl::MutexLock sample_lock(&sample->init_mu); - graveyard_.dead = sample->dead; - sample->PrepareForSampling(); - return sample; -} - -HashtablezInfo* HashtablezSampler::Register() { - int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); - if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) { - size_estimate_.fetch_sub(1, std::memory_order_relaxed); - dropped_samples_.fetch_add(1, std::memory_order_relaxed); - return nullptr; - } - - HashtablezInfo* sample = PopDead(); - if (sample == nullptr) { - // Resurrection failed. Hire a new warlock. - sample = new HashtablezInfo(); - PushNew(sample); - } - - return sample; -} - -void HashtablezSampler::Unregister(HashtablezInfo* sample) { - PushDead(sample); - size_estimate_.fetch_sub(1, std::memory_order_relaxed); -} - -int64_t HashtablezSampler::Iterate( - const std::function<void(const HashtablezInfo& stack)>& f) { - HashtablezInfo* s = all_.load(std::memory_order_acquire); - while (s != nullptr) { - absl::MutexLock l(&s->init_mu); - if (s->dead == nullptr) { - f(*s); - } - s = s->next; - } - - return dropped_samples_.load(std::memory_order_relaxed); } static bool ShouldForceSampling() { @@ -179,16 +93,20 @@ static bool ShouldForceSampling() { if (ABSL_PREDICT_TRUE(state == kDontForce)) return false; if (state == kUninitialized) { - state = AbslContainerInternalSampleEverything() ? kForce : kDontForce; + state = ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)() + ? kForce + : kDontForce; global_state.store(state, std::memory_order_relaxed); } return state == kForce; } -HashtablezInfo* SampleSlow(int64_t* next_sample) { +HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size) { if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; - return HashtablezSampler::Global().Register(); + HashtablezInfo* result = GlobalHashtablezSampler().Register(); + result->inline_element_size = inline_element_size; + return result; } #if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) @@ -210,15 +128,17 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { // that case. if (first) { if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr; - return SampleSlow(next_sample); + return SampleSlow(next_sample, inline_element_size); } - return HashtablezSampler::Global().Register(); + HashtablezInfo* result = GlobalHashtablezSampler().Register(); + result->inline_element_size = inline_element_size; + return result; #endif } void UnsampleSlow(HashtablezInfo* info) { - HashtablezSampler::Global().Unregister(info); + GlobalHashtablezSampler().Unregister(info); } void RecordInsertSlow(HashtablezInfo* info, size_t hash, @@ -226,7 +146,7 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash, // SwissTables probe in groups of 16, so scale this to count items probes and // not offset from desired. size_t probe_length = distance_from_desired; -#if SWISSTABLE_HAVE_SSE2 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 probe_length /= 16; #else probe_length /= 8; @@ -234,6 +154,7 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash, info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed); info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed); + info->hashes_bitwise_xor.fetch_xor(hash, std::memory_order_relaxed); info->max_probe_length.store( std::max(info->max_probe_length.load(std::memory_order_relaxed), probe_length), @@ -257,7 +178,7 @@ void SetHashtablezSampleParameter(int32_t rate) { void SetHashtablezMaxSamples(int32_t max) { if (max > 0) { - g_hashtablez_max_samples.store(max, std::memory_order_release); + GlobalHashtablezSampler().SetMaxSamples(max); } else { ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld", static_cast<long long>(max)); // NOLINT(runtime/int) diff --git a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h index 34d5e5723c..91fcdb34a3 100644 --- a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h +++ b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler.h @@ -47,6 +47,7 @@ #include "absl/base/internal/per_thread_tls.h" #include "absl/base/optimization.h" #include "absl/container/internal/have_sse.h" +#include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/mutex.h" #include "absl/utility/utility.h" @@ -57,7 +58,7 @@ namespace container_internal { // Stores information about a sampled hashtable. All mutations to this *must* // be made through `Record*` functions below. All reads from this *must* only // occur in the callback to `HashtablezSampler::Iterate`. -struct HashtablezInfo { +struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> { // Constructs the object but does not fill in any fields. HashtablezInfo(); ~HashtablezInfo(); @@ -73,18 +74,13 @@ struct HashtablezInfo { std::atomic<size_t> capacity; std::atomic<size_t> size; std::atomic<size_t> num_erases; + std::atomic<size_t> num_rehashes; std::atomic<size_t> max_probe_length; std::atomic<size_t> total_probe_length; std::atomic<size_t> hashes_bitwise_or; std::atomic<size_t> hashes_bitwise_and; - - // `HashtablezSampler` maintains intrusive linked lists for all samples. See - // comments on `HashtablezSampler::all_` for details on these. `init_mu` - // guards the ability to restore the sample to a pristine state. This - // prevents races with sampling and resurrecting an object. - absl::Mutex init_mu; - HashtablezInfo* next; - HashtablezInfo* dead ABSL_GUARDED_BY(init_mu); + std::atomic<size_t> hashes_bitwise_xor; + std::atomic<size_t> max_reserve; // All of the fields below are set by `PrepareForSampling`, they must not be // mutated in `Record*` functions. They are logically `const` in that sense. @@ -95,16 +91,34 @@ struct HashtablezInfo { absl::Time create_time; int32_t depth; void* stack[kMaxStackDepth]; + size_t inline_element_size; }; inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { -#if SWISSTABLE_HAVE_SSE2 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 total_probe_length /= 16; #else total_probe_length /= 8; #endif info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); info->num_erases.store(0, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_rehashes.store( + 1 + info->num_rehashes.load(std::memory_order_relaxed), + std::memory_order_relaxed); +} + +inline void RecordReservationSlow(HashtablezInfo* info, + size_t target_capacity) { + info->max_reserve.store( + (std::max)(info->max_reserve.load(std::memory_order_relaxed), + target_capacity), + std::memory_order_relaxed); +} + +inline void RecordClearedReservationSlow(HashtablezInfo* info) { + info->max_reserve.store(0, std::memory_order_relaxed); } inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, @@ -113,7 +127,8 @@ inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, info->capacity.store(capacity, std::memory_order_relaxed); if (size == 0) { // This is a clear, reset the total/num_erases too. - RecordRehashSlow(info, 0); + info->total_probe_length.store(0, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); } } @@ -122,12 +137,21 @@ void RecordInsertSlow(HashtablezInfo* info, size_t hash, inline void RecordEraseSlow(HashtablezInfo* info) { info->size.fetch_sub(1, std::memory_order_relaxed); - info->num_erases.fetch_add(1, std::memory_order_relaxed); + // There is only one concurrent writer, so `load` then `store` is sufficient + // instead of using `fetch_add`. + info->num_erases.store( + 1 + info->num_erases.load(std::memory_order_relaxed), + std::memory_order_relaxed); } -HashtablezInfo* SampleSlow(int64_t* next_sample); +HashtablezInfo* SampleSlow(int64_t* next_sample, size_t inline_element_size); void UnsampleSlow(HashtablezInfo* info); +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) class HashtablezInfoHandle { public: explicit HashtablezInfoHandle() : info_(nullptr) {} @@ -160,6 +184,16 @@ class HashtablezInfoHandle { RecordRehashSlow(info_, total_probe_length); } + inline void RecordReservation(size_t target_capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordReservationSlow(info_, target_capacity); + } + + inline void RecordClearedReservation() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordClearedReservationSlow(info_); + } + inline void RecordInsert(size_t hash, size_t distance_from_desired) { if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; RecordInsertSlow(info_, hash, distance_from_desired); @@ -179,100 +213,50 @@ class HashtablezInfoHandle { friend class HashtablezInfoHandlePeer; HashtablezInfo* info_; }; - -#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set +#else +// Ensure that when Hashtablez is turned off at compile time, HashtablezInfo can +// be removed by the linker, in order to reduce the binary size. +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() = default; + explicit HashtablezInfoHandle(std::nullptr_t) {} + + inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {} + inline void RecordRehash(size_t /*total_probe_length*/) {} + inline void RecordReservation(size_t /*target_capacity*/) {} + inline void RecordClearedReservation() {} + inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {} + inline void RecordErase() {} + + friend inline void swap(HashtablezInfoHandle& /*lhs*/, + HashtablezInfoHandle& /*rhs*/) {} +}; #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) -#if (ABSL_PER_THREAD_TLS == 1) && !defined(ABSL_BUILD_DLL) && \ - !defined(ABSL_CONSUME_DLL) -#define ABSL_INTERNAL_HASHTABLEZ_SAMPLE -#endif - #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample; -#endif // ABSL_PER_THREAD_TLS +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) // Returns an RAII sampling handle that manages registration and unregistation // with the global sampler. -inline HashtablezInfoHandle Sample() { +inline HashtablezInfoHandle Sample( + size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) { #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { return HashtablezInfoHandle(nullptr); } - return HashtablezInfoHandle(SampleSlow(&global_next_sample)); + return HashtablezInfoHandle( + SampleSlow(&global_next_sample, inline_element_size)); #else return HashtablezInfoHandle(nullptr); #endif // !ABSL_PER_THREAD_TLS } -// Holds samples and their associated stack traces with a soft limit of -// `SetHashtablezMaxSamples()`. -// -// Thread safe. -class HashtablezSampler { - public: - // Returns a global Sampler. - static HashtablezSampler& Global(); - - HashtablezSampler(); - ~HashtablezSampler(); - - // Registers for sampling. Returns an opaque registration info. - HashtablezInfo* Register(); - - // Unregisters the sample. - void Unregister(HashtablezInfo* sample); +using HashtablezSampler = + ::absl::profiling_internal::SampleRecorder<HashtablezInfo>; - // The dispose callback will be called on all samples the moment they are - // being unregistered. Only affects samples that are unregistered after the - // callback has been set. - // Returns the previous callback. - using DisposeCallback = void (*)(const HashtablezInfo&); - DisposeCallback SetDisposeCallback(DisposeCallback f); - - // Iterates over all the registered `StackInfo`s. Returning the number of - // samples that have been dropped. - int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f); - - private: - void PushNew(HashtablezInfo* sample); - void PushDead(HashtablezInfo* sample); - HashtablezInfo* PopDead(); - - std::atomic<size_t> dropped_samples_; - std::atomic<size_t> size_estimate_; - - // Intrusive lock free linked lists for tracking samples. - // - // `all_` records all samples (they are never removed from this list) and is - // terminated with a `nullptr`. - // - // `graveyard_.dead` is a circular linked list. When it is empty, - // `graveyard_.dead == &graveyard`. The list is circular so that - // every item on it (even the last) has a non-null dead pointer. This allows - // `Iterate` to determine if a given sample is live or dead using only - // information on the sample itself. - // - // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead - // looks like this (G is the Graveyard): - // - // +---+ +---+ +---+ +---+ +---+ - // all -->| A |--->| B |--->| C |--->| D |--->| E | - // | | | | | | | | | | - // +---+ | | +->| |-+ | | +->| |-+ | | - // | G | +---+ | +---+ | +---+ | +---+ | +---+ - // | | | | | | - // | | --------+ +--------+ | - // +---+ | - // ^ | - // +--------------------------------------+ - // - std::atomic<HashtablezInfo*> all_; - HashtablezInfo graveyard_; - - std::atomic<DisposeCallback> dispose_; -}; +// Returns a global Sampler. +HashtablezSampler& GlobalHashtablezSampler(); // Enables or disables sampling for Swiss tables. void SetHashtablezEnabled(bool enabled); @@ -288,7 +272,7 @@ void SetHashtablezMaxSamples(int32_t max); // initialization of static storage duration objects. // The definition of this constant is weak, which allows us to inject a // different value for it at link time. -extern "C" bool AbslContainerInternalSampleEverything(); +extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)(); } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc index 78b9d362ac..ed35a7eec3 100644 --- a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc +++ b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_force_weak_definition.cc @@ -21,7 +21,8 @@ ABSL_NAMESPACE_BEGIN namespace container_internal { // See hashtablez_sampler.h for details. -extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() { +extern "C" ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL( + AbslContainerInternalSampleEverything)() { return false; } diff --git a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc index 36f5ccdd02..449619a32c 100644 --- a/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc @@ -22,6 +22,7 @@ #include "gtest/gtest.h" #include "absl/base/attributes.h" #include "absl/container/internal/have_sse.h" +#include "absl/profiling/internal/sample_recorder.h" #include "absl/synchronization/blocking_counter.h" #include "absl/synchronization/internal/thread_pool.h" #include "absl/synchronization/mutex.h" @@ -29,7 +30,7 @@ #include "absl/time/clock.h" #include "absl/time/time.h" -#if SWISSTABLE_HAVE_SSE2 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 constexpr int kProbeLength = 16; #else constexpr int kProbeLength = 8; @@ -38,6 +39,7 @@ constexpr int kProbeLength = 8; namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) class HashtablezInfoHandlePeer { public: static bool IsSampled(const HashtablezInfoHandle& h) { @@ -46,6 +48,13 @@ class HashtablezInfoHandlePeer { static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; } }; +#else +class HashtablezInfoHandlePeer { + public: + static bool IsSampled(const HashtablezInfoHandle&) { return false; } + static HashtablezInfo* GetInfo(HashtablezInfoHandle*) { return nullptr; } +}; +#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) namespace { using ::absl::synchronization_internal::ThreadPool; @@ -69,18 +78,24 @@ HashtablezInfo* Register(HashtablezSampler* s, size_t size) { TEST(HashtablezInfoTest, PrepareForSampling) { absl::Time test_start = absl::Now(); + const size_t test_element_size = 17; HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); + info.inline_element_size = test_element_size; EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.num_rehashes.load(), 0); EXPECT_EQ(info.max_probe_length.load(), 0); EXPECT_EQ(info.total_probe_length.load(), 0); EXPECT_EQ(info.hashes_bitwise_or.load(), 0); EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); + EXPECT_EQ(info.max_reserve.load(), 0); EXPECT_GE(info.create_time, test_start); + EXPECT_EQ(info.inline_element_size, test_element_size); info.capacity.store(1, std::memory_order_relaxed); info.size.store(1, std::memory_order_relaxed); @@ -89,16 +104,22 @@ TEST(HashtablezInfoTest, PrepareForSampling) { info.total_probe_length.store(1, std::memory_order_relaxed); info.hashes_bitwise_or.store(1, std::memory_order_relaxed); info.hashes_bitwise_and.store(1, std::memory_order_relaxed); + info.hashes_bitwise_xor.store(1, std::memory_order_relaxed); + info.max_reserve.store(1, std::memory_order_relaxed); info.create_time = test_start - absl::Hours(20); info.PrepareForSampling(); EXPECT_EQ(info.capacity.load(), 0); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.num_rehashes.load(), 0); EXPECT_EQ(info.max_probe_length.load(), 0); EXPECT_EQ(info.total_probe_length.load(), 0); EXPECT_EQ(info.hashes_bitwise_or.load(), 0); EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0); + EXPECT_EQ(info.max_reserve.load(), 0); + EXPECT_EQ(info.inline_element_size, test_element_size); EXPECT_GE(info.create_time, test_start); } @@ -123,20 +144,25 @@ TEST(HashtablezInfoTest, RecordInsert) { EXPECT_EQ(info.max_probe_length.load(), 6); EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00); EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x0000FF00); RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength); EXPECT_EQ(info.max_probe_length.load(), 6); EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000); EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x000F0F00); RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength); EXPECT_EQ(info.max_probe_length.load(), 12); EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000); EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00); + EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x00F00F00); } TEST(HashtablezInfoTest, RecordErase) { + const size_t test_element_size = 29; HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); + info.inline_element_size = test_element_size; EXPECT_EQ(info.num_erases.load(), 0); EXPECT_EQ(info.size.load(), 0); RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); @@ -144,12 +170,15 @@ TEST(HashtablezInfoTest, RecordErase) { RecordEraseSlow(&info); EXPECT_EQ(info.size.load(), 0); EXPECT_EQ(info.num_erases.load(), 1); + EXPECT_EQ(info.inline_element_size, test_element_size); } TEST(HashtablezInfoTest, RecordRehash) { + const size_t test_element_size = 31; HashtablezInfo info; absl::MutexLock l(&info.init_mu); info.PrepareForSampling(); + info.inline_element_size = test_element_size; RecordInsertSlow(&info, 0x1, 0); RecordInsertSlow(&info, 0x2, kProbeLength); RecordInsertSlow(&info, 0x4, kProbeLength); @@ -167,16 +196,35 @@ TEST(HashtablezInfoTest, RecordRehash) { EXPECT_EQ(info.size.load(), 2); EXPECT_EQ(info.total_probe_length.load(), 3); EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.num_rehashes.load(), 1); + EXPECT_EQ(info.inline_element_size, test_element_size); +} + +TEST(HashtablezInfoTest, RecordReservation) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + RecordReservationSlow(&info, 3); + EXPECT_EQ(info.max_reserve.load(), 3); + + RecordReservationSlow(&info, 2); + // High watermark does not change + EXPECT_EQ(info.max_reserve.load(), 3); + + RecordReservationSlow(&info, 10); + // High watermark does change + EXPECT_EQ(info.max_reserve.load(), 10); } -#if defined(ABSL_HASHTABLEZ_SAMPLE) +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) TEST(HashtablezSamplerTest, SmallSampleParameter) { + const size_t test_element_size = 31; SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); for (int i = 0; i < 1000; ++i) { int64_t next_sample = 0; - HashtablezInfo* sample = SampleSlow(&next_sample); + HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size); EXPECT_GT(next_sample, 0); EXPECT_NE(sample, nullptr); UnsampleSlow(sample); @@ -184,12 +232,13 @@ TEST(HashtablezSamplerTest, SmallSampleParameter) { } TEST(HashtablezSamplerTest, LargeSampleParameter) { + const size_t test_element_size = 31; SetHashtablezEnabled(true); SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max()); for (int i = 0; i < 1000; ++i) { int64_t next_sample = 0; - HashtablezInfo* sample = SampleSlow(&next_sample); + HashtablezInfo* sample = SampleSlow(&next_sample, test_element_size); EXPECT_GT(next_sample, 0); EXPECT_NE(sample, nullptr); UnsampleSlow(sample); @@ -197,13 +246,14 @@ TEST(HashtablezSamplerTest, LargeSampleParameter) { } TEST(HashtablezSamplerTest, Sample) { + const size_t test_element_size = 31; SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); int64_t num_sampled = 0; int64_t total = 0; double sample_rate = 0.0; for (int i = 0; i < 1000000; ++i) { - HashtablezInfoHandle h = Sample(); + HashtablezInfoHandle h = Sample(test_element_size); ++total; if (HashtablezInfoHandlePeer::IsSampled(h)) { ++num_sampled; @@ -213,10 +263,9 @@ TEST(HashtablezSamplerTest, Sample) { } EXPECT_NEAR(sample_rate, 0.01, 0.005); } -#endif TEST(HashtablezSamplerTest, Handle) { - auto& sampler = HashtablezSampler::Global(); + auto& sampler = GlobalHashtablezSampler(); HashtablezInfoHandle h(sampler.Register()); auto* info = HashtablezInfoHandlePeer::GetInfo(&h); info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed); @@ -243,6 +292,8 @@ TEST(HashtablezSamplerTest, Handle) { }); EXPECT_FALSE(found); } +#endif + TEST(HashtablezSamplerTest, Registration) { HashtablezSampler sampler; diff --git a/third_party/abseil-cpp/absl/container/internal/have_sse.h b/third_party/abseil-cpp/absl/container/internal/have_sse.h index 43414418db..e75e1a16d3 100644 --- a/third_party/abseil-cpp/absl/container/internal/have_sse.h +++ b/third_party/abseil-cpp/absl/container/internal/have_sse.h @@ -16,33 +16,34 @@ #ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ #define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ -#ifndef SWISSTABLE_HAVE_SSE2 +#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 #if defined(__SSE2__) || \ (defined(_MSC_VER) && \ (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) -#define SWISSTABLE_HAVE_SSE2 1 +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 1 #else -#define SWISSTABLE_HAVE_SSE2 0 +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 0 #endif #endif -#ifndef SWISSTABLE_HAVE_SSSE3 +#ifndef ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 #ifdef __SSSE3__ -#define SWISSTABLE_HAVE_SSSE3 1 +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 1 #else -#define SWISSTABLE_HAVE_SSSE3 0 +#define ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 0 #endif #endif -#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 && \ + !ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 #error "Bad configuration!" #endif -#if SWISSTABLE_HAVE_SSE2 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 #include <emmintrin.h> #endif -#if SWISSTABLE_HAVE_SSSE3 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 #include <tmmintrin.h> #endif diff --git a/third_party/abseil-cpp/absl/container/internal/inlined_vector.h b/third_party/abseil-cpp/absl/container/internal/inlined_vector.h index 4d80b727bf..1d7d6cda72 100644 --- a/third_party/abseil-cpp/absl/container/internal/inlined_vector.h +++ b/third_party/abseil-cpp/absl/container/internal/inlined_vector.h @@ -21,8 +21,11 @@ #include <iterator> #include <limits> #include <memory> +#include <new> +#include <type_traits> #include <utility> +#include "absl/base/attributes.h" #include "absl/base/macros.h" #include "absl/container/internal/compressed_tuple.h" #include "absl/memory/memory.h" @@ -33,96 +36,135 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace inlined_vector_internal { +// GCC does not deal very well with the below code +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +template <typename A> +using AllocatorTraits = std::allocator_traits<A>; +template <typename A> +using ValueType = typename AllocatorTraits<A>::value_type; +template <typename A> +using SizeType = typename AllocatorTraits<A>::size_type; +template <typename A> +using Pointer = typename AllocatorTraits<A>::pointer; +template <typename A> +using ConstPointer = typename AllocatorTraits<A>::const_pointer; +template <typename A> +using SizeType = typename AllocatorTraits<A>::size_type; +template <typename A> +using DifferenceType = typename AllocatorTraits<A>::difference_type; +template <typename A> +using Reference = ValueType<A>&; +template <typename A> +using ConstReference = const ValueType<A>&; +template <typename A> +using Iterator = Pointer<A>; +template <typename A> +using ConstIterator = ConstPointer<A>; +template <typename A> +using ReverseIterator = typename std::reverse_iterator<Iterator<A>>; +template <typename A> +using ConstReverseIterator = typename std::reverse_iterator<ConstIterator<A>>; +template <typename A> +using MoveIterator = typename std::move_iterator<Iterator<A>>; + template <typename Iterator> using IsAtLeastForwardIterator = std::is_convertible< typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>; -template <typename AllocatorType, - typename ValueType = - typename absl::allocator_traits<AllocatorType>::value_type> +template <typename A> using IsMemcpyOk = - absl::conjunction<std::is_same<AllocatorType, std::allocator<ValueType>>, - absl::is_trivially_copy_constructible<ValueType>, - absl::is_trivially_copy_assignable<ValueType>, - absl::is_trivially_destructible<ValueType>>; + absl::conjunction<std::is_same<A, std::allocator<ValueType<A>>>, + absl::is_trivially_copy_constructible<ValueType<A>>, + absl::is_trivially_copy_assignable<ValueType<A>>, + absl::is_trivially_destructible<ValueType<A>>>; + +template <typename T> +struct TypeIdentity { + using type = T; +}; -template <typename AllocatorType, typename Pointer, typename SizeType> -void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first, - SizeType destroy_size) { - using AllocatorTraits = absl::allocator_traits<AllocatorType>; +// Used for function arguments in template functions to prevent ADL by forcing +// callers to explicitly specify the template parameter. +template <typename T> +using NoTypeDeduction = typename TypeIdentity<T>::type; +template <typename A> +void DestroyElements(NoTypeDeduction<A>& allocator, Pointer<A> destroy_first, + SizeType<A> destroy_size) { if (destroy_first != nullptr) { - for (auto i = destroy_size; i != 0;) { + for (SizeType<A> i = destroy_size; i != 0;) { --i; - AllocatorTraits::destroy(*alloc_ptr, destroy_first + i); - } - -#if !defined(NDEBUG) - { - using ValueType = typename AllocatorTraits::value_type; - - // Overwrite unused memory with `0xab` so we can catch uninitialized - // usage. - // - // Cast to `void*` to tell the compiler that we don't care that we might - // be scribbling on a vtable pointer. - void* memory_ptr = destroy_first; - auto memory_size = destroy_size * sizeof(ValueType); - std::memset(memory_ptr, 0xab, memory_size); + AllocatorTraits<A>::destroy(allocator, destroy_first + i); } -#endif // !defined(NDEBUG) } } -template <typename AllocatorType, typename Pointer, typename ValueAdapter, - typename SizeType> -void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first, - ValueAdapter* values_ptr, SizeType construct_size) { - for (SizeType i = 0; i < construct_size; ++i) { - ABSL_INTERNAL_TRY { - values_ptr->ConstructNext(alloc_ptr, construct_first + i); - } +template <typename A> +struct Allocation { + Pointer<A> data; + SizeType<A> capacity; +}; + +template <typename A, + bool IsOverAligned = + (alignof(ValueType<A>) > ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT)> +struct MallocAdapter { + static Allocation<A> Allocate(A& allocator, SizeType<A> requested_capacity) { + return {AllocatorTraits<A>::allocate(allocator, requested_capacity), + requested_capacity}; + } + + static void Deallocate(A& allocator, Pointer<A> pointer, + SizeType<A> capacity) { + AllocatorTraits<A>::deallocate(allocator, pointer, capacity); + } +}; + +template <typename A, typename ValueAdapter> +void ConstructElements(NoTypeDeduction<A>& allocator, + Pointer<A> construct_first, ValueAdapter& values, + SizeType<A> construct_size) { + for (SizeType<A> i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); } ABSL_INTERNAL_CATCH_ANY { - inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i); + DestroyElements<A>(allocator, construct_first, i); ABSL_INTERNAL_RETHROW; } } } -template <typename Pointer, typename ValueAdapter, typename SizeType> -void AssignElements(Pointer assign_first, ValueAdapter* values_ptr, - SizeType assign_size) { - for (SizeType i = 0; i < assign_size; ++i) { - values_ptr->AssignNext(assign_first + i); +template <typename A, typename ValueAdapter> +void AssignElements(Pointer<A> assign_first, ValueAdapter& values, + SizeType<A> assign_size) { + for (SizeType<A> i = 0; i < assign_size; ++i) { + values.AssignNext(assign_first + i); } } -template <typename AllocatorType> +template <typename A> struct StorageView { - using AllocatorTraits = absl::allocator_traits<AllocatorType>; - using Pointer = typename AllocatorTraits::pointer; - using SizeType = typename AllocatorTraits::size_type; - - Pointer data; - SizeType size; - SizeType capacity; + Pointer<A> data; + SizeType<A> size; + SizeType<A> capacity; }; -template <typename AllocatorType, typename Iterator> +template <typename A, typename Iterator> class IteratorValueAdapter { - using AllocatorTraits = absl::allocator_traits<AllocatorType>; - using Pointer = typename AllocatorTraits::pointer; - public: explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} - void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { - AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); + void ConstructNext(A& allocator, Pointer<A> construct_at) { + AllocatorTraits<A>::construct(allocator, construct_at, *it_); ++it_; } - void AssignNext(Pointer assign_at) { + void AssignNext(Pointer<A> assign_at) { *assign_at = *it_; ++it_; } @@ -131,166 +173,123 @@ class IteratorValueAdapter { Iterator it_; }; -template <typename AllocatorType> +template <typename A> class CopyValueAdapter { - using AllocatorTraits = absl::allocator_traits<AllocatorType>; - using ValueType = typename AllocatorTraits::value_type; - using Pointer = typename AllocatorTraits::pointer; - using ConstPointer = typename AllocatorTraits::const_pointer; - public: - explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {} + explicit CopyValueAdapter(ConstPointer<A> p) : ptr_(p) {} - void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { - AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); + void ConstructNext(A& allocator, Pointer<A> construct_at) { + AllocatorTraits<A>::construct(allocator, construct_at, *ptr_); } - void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } + void AssignNext(Pointer<A> assign_at) { *assign_at = *ptr_; } private: - ConstPointer ptr_; + ConstPointer<A> ptr_; }; -template <typename AllocatorType> +template <typename A> class DefaultValueAdapter { - using AllocatorTraits = absl::allocator_traits<AllocatorType>; - using ValueType = typename AllocatorTraits::value_type; - using Pointer = typename AllocatorTraits::pointer; - public: explicit DefaultValueAdapter() {} - void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { - AllocatorTraits::construct(*alloc_ptr, construct_at); + void ConstructNext(A& allocator, Pointer<A> construct_at) { + AllocatorTraits<A>::construct(allocator, construct_at); } - void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } + void AssignNext(Pointer<A> assign_at) { *assign_at = ValueType<A>(); } }; -template <typename AllocatorType> +template <typename A> class AllocationTransaction { - using AllocatorTraits = absl::allocator_traits<AllocatorType>; - using Pointer = typename AllocatorTraits::pointer; - using SizeType = typename AllocatorTraits::size_type; - public: - explicit AllocationTransaction(AllocatorType* alloc_ptr) - : alloc_data_(*alloc_ptr, nullptr) {} + explicit AllocationTransaction(A& allocator) + : allocator_data_(allocator, nullptr), capacity_(0) {} ~AllocationTransaction() { if (DidAllocate()) { - AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); + MallocAdapter<A>::Deallocate(GetAllocator(), GetData(), GetCapacity()); } } AllocationTransaction(const AllocationTransaction&) = delete; void operator=(const AllocationTransaction&) = delete; - AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - Pointer& GetData() { return alloc_data_.template get<1>(); } - SizeType& GetCapacity() { return capacity_; } + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer<A>& GetData() { return allocator_data_.template get<1>(); } + SizeType<A>& GetCapacity() { return capacity_; } bool DidAllocate() { return GetData() != nullptr; } - Pointer Allocate(SizeType capacity) { - GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); - GetCapacity() = capacity; - return GetData(); + + Pointer<A> Allocate(SizeType<A> requested_capacity) { + Allocation<A> result = + MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); + GetData() = result.data; + GetCapacity() = result.capacity; + return result.data; + } + + ABSL_MUST_USE_RESULT Allocation<A> Release() && { + Allocation<A> result = {GetData(), GetCapacity()}; + Reset(); + return result; } + private: void Reset() { GetData() = nullptr; GetCapacity() = 0; } - private: - container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_; - SizeType capacity_ = 0; + container_internal::CompressedTuple<A, Pointer<A>> allocator_data_; + SizeType<A> capacity_; }; -template <typename AllocatorType> +template <typename A> class ConstructionTransaction { - using AllocatorTraits = absl::allocator_traits<AllocatorType>; - using Pointer = typename AllocatorTraits::pointer; - using SizeType = typename AllocatorTraits::size_type; - public: - explicit ConstructionTransaction(AllocatorType* alloc_ptr) - : alloc_data_(*alloc_ptr, nullptr) {} + explicit ConstructionTransaction(A& allocator) + : allocator_data_(allocator, nullptr), size_(0) {} ~ConstructionTransaction() { if (DidConstruct()) { - inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()), - GetData(), GetSize()); + DestroyElements<A>(GetAllocator(), GetData(), GetSize()); } } ConstructionTransaction(const ConstructionTransaction&) = delete; void operator=(const ConstructionTransaction&) = delete; - AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - Pointer& GetData() { return alloc_data_.template get<1>(); } - SizeType& GetSize() { return size_; } + A& GetAllocator() { return allocator_data_.template get<0>(); } + Pointer<A>& GetData() { return allocator_data_.template get<1>(); } + SizeType<A>& GetSize() { return size_; } bool DidConstruct() { return GetData() != nullptr; } template <typename ValueAdapter> - void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) { - inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), - data, values_ptr, size); + void Construct(Pointer<A> data, ValueAdapter& values, SizeType<A> size) { + ConstructElements<A>(GetAllocator(), data, values, size); GetData() = data; GetSize() = size; } - void Commit() { + void Commit() && { GetData() = nullptr; GetSize() = 0; } private: - container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_; - SizeType size_ = 0; + container_internal::CompressedTuple<A, Pointer<A>> allocator_data_; + SizeType<A> size_; }; template <typename T, size_t N, typename A> class Storage { public: - using AllocatorTraits = absl::allocator_traits<A>; - using allocator_type = typename AllocatorTraits::allocator_type; - using value_type = typename AllocatorTraits::value_type; - using pointer = typename AllocatorTraits::pointer; - using const_pointer = typename AllocatorTraits::const_pointer; - using size_type = typename AllocatorTraits::size_type; - using difference_type = typename AllocatorTraits::difference_type; - - using reference = value_type&; - using const_reference = const value_type&; - using RValueReference = value_type&&; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator<iterator>; - using const_reverse_iterator = std::reverse_iterator<const_iterator>; - using MoveIterator = std::move_iterator<iterator>; - using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<allocator_type>; - - using StorageView = inlined_vector_internal::StorageView<allocator_type>; - - template <typename Iterator> - using IteratorValueAdapter = - inlined_vector_internal::IteratorValueAdapter<allocator_type, Iterator>; - using CopyValueAdapter = - inlined_vector_internal::CopyValueAdapter<allocator_type>; - using DefaultValueAdapter = - inlined_vector_internal::DefaultValueAdapter<allocator_type>; - - using AllocationTransaction = - inlined_vector_internal::AllocationTransaction<allocator_type>; - using ConstructionTransaction = - inlined_vector_internal::ConstructionTransaction<allocator_type>; - - static size_type NextCapacity(size_type current_capacity) { + static SizeType<A> NextCapacity(SizeType<A> current_capacity) { return current_capacity * 2; } - static size_type ComputeCapacity(size_type current_capacity, - size_type requested_capacity) { + static SizeType<A> ComputeCapacity(SizeType<A> current_capacity, + SizeType<A> requested_capacity) { return (std::max)(NextCapacity(current_capacity), requested_capacity); } @@ -298,140 +297,137 @@ class Storage { // Storage Constructors and Destructor // --------------------------------------------------------------------------- - Storage() : metadata_() {} + Storage() : metadata_(A(), /* size and is_allocated */ 0) {} - explicit Storage(const allocator_type& alloc) : metadata_(alloc, {}) {} + explicit Storage(const A& allocator) + : metadata_(allocator, /* size and is_allocated */ 0) {} ~Storage() { - pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); - inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize()); - DeallocateIfAllocated(); + if (GetSizeAndIsAllocated() == 0) { + // Empty and not allocated; nothing to do. + } else if (IsMemcpyOk<A>::value) { + // No destructors need to be run; just deallocate if necessary. + DeallocateIfAllocated(); + } else { + DestroyContents(); + } } // --------------------------------------------------------------------------- // Storage Member Accessors // --------------------------------------------------------------------------- - size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + SizeType<A>& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } - const size_type& GetSizeAndIsAllocated() const { + const SizeType<A>& GetSizeAndIsAllocated() const { return metadata_.template get<1>(); } - size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; } + SizeType<A> GetSize() const { return GetSizeAndIsAllocated() >> 1; } bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } - pointer GetAllocatedData() { return data_.allocated.allocated_data; } + Pointer<A> GetAllocatedData() { return data_.allocated.allocated_data; } - const_pointer GetAllocatedData() const { + ConstPointer<A> GetAllocatedData() const { return data_.allocated.allocated_data; } - pointer GetInlinedData() { - return reinterpret_cast<pointer>( + Pointer<A> GetInlinedData() { + return reinterpret_cast<Pointer<A>>( std::addressof(data_.inlined.inlined_data[0])); } - const_pointer GetInlinedData() const { - return reinterpret_cast<const_pointer>( + ConstPointer<A> GetInlinedData() const { + return reinterpret_cast<ConstPointer<A>>( std::addressof(data_.inlined.inlined_data[0])); } - size_type GetAllocatedCapacity() const { + SizeType<A> GetAllocatedCapacity() const { return data_.allocated.allocated_capacity; } - size_type GetInlinedCapacity() const { return static_cast<size_type>(N); } + SizeType<A> GetInlinedCapacity() const { return static_cast<SizeType<A>>(N); } - StorageView MakeStorageView() { - return GetIsAllocated() - ? StorageView{GetAllocatedData(), GetSize(), - GetAllocatedCapacity()} - : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()}; + StorageView<A> MakeStorageView() { + return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView<A>{GetInlinedData(), GetSize(), + GetInlinedCapacity()}; } - allocator_type* GetAllocPtr() { - return std::addressof(metadata_.template get<0>()); - } + A& GetAllocator() { return metadata_.template get<0>(); } - const allocator_type* GetAllocPtr() const { - return std::addressof(metadata_.template get<0>()); - } + const A& GetAllocator() const { return metadata_.template get<0>(); } // --------------------------------------------------------------------------- // Storage Member Mutators // --------------------------------------------------------------------------- + ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); + template <typename ValueAdapter> - void Initialize(ValueAdapter values, size_type new_size); + void Initialize(ValueAdapter values, SizeType<A> new_size); template <typename ValueAdapter> - void Assign(ValueAdapter values, size_type new_size); + void Assign(ValueAdapter values, SizeType<A> new_size); template <typename ValueAdapter> - void Resize(ValueAdapter values, size_type new_size); + void Resize(ValueAdapter values, SizeType<A> new_size); template <typename ValueAdapter> - iterator Insert(const_iterator pos, ValueAdapter values, - size_type insert_count); + Iterator<A> Insert(ConstIterator<A> pos, ValueAdapter values, + SizeType<A> insert_count); template <typename... Args> - reference EmplaceBack(Args&&... args); + Reference<A> EmplaceBack(Args&&... args); - iterator Erase(const_iterator from, const_iterator to); + Iterator<A> Erase(ConstIterator<A> from, ConstIterator<A> to); - void Reserve(size_type requested_capacity); + void Reserve(SizeType<A> requested_capacity); void ShrinkToFit(); void Swap(Storage* other_storage_ptr); void SetIsAllocated() { - GetSizeAndIsAllocated() |= static_cast<size_type>(1); + GetSizeAndIsAllocated() |= static_cast<SizeType<A>>(1); } void UnsetIsAllocated() { - GetSizeAndIsAllocated() &= ((std::numeric_limits<size_type>::max)() - 1); + GetSizeAndIsAllocated() &= ((std::numeric_limits<SizeType<A>>::max)() - 1); } - void SetSize(size_type size) { + void SetSize(SizeType<A> size) { GetSizeAndIsAllocated() = - (size << 1) | static_cast<size_type>(GetIsAllocated()); + (size << 1) | static_cast<SizeType<A>>(GetIsAllocated()); } - void SetAllocatedSize(size_type size) { - GetSizeAndIsAllocated() = (size << 1) | static_cast<size_type>(1); + void SetAllocatedSize(SizeType<A> size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast<SizeType<A>>(1); } - void SetInlinedSize(size_type size) { - GetSizeAndIsAllocated() = size << static_cast<size_type>(1); + void SetInlinedSize(SizeType<A> size) { + GetSizeAndIsAllocated() = size << static_cast<SizeType<A>>(1); } - void AddSize(size_type count) { - GetSizeAndIsAllocated() += count << static_cast<size_type>(1); + void AddSize(SizeType<A> count) { + GetSizeAndIsAllocated() += count << static_cast<SizeType<A>>(1); } - void SubtractSize(size_type count) { + void SubtractSize(SizeType<A> count) { assert(count <= GetSize()); - GetSizeAndIsAllocated() -= count << static_cast<size_type>(1); - } - - void SetAllocatedData(pointer data, size_type capacity) { - data_.allocated.allocated_data = data; - data_.allocated.allocated_capacity = capacity; + GetSizeAndIsAllocated() -= count << static_cast<SizeType<A>>(1); } - void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) { - SetAllocatedData(allocation_tx_ptr->GetData(), - allocation_tx_ptr->GetCapacity()); - - allocation_tx_ptr->Reset(); + void SetAllocation(Allocation<A> allocation) { + data_.allocated.allocated_data = allocation.data; + data_.allocated.allocated_capacity = allocation.capacity; } void MemcpyFrom(const Storage& other_storage) { - assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); + assert(IsMemcpyOk<A>::value || other_storage.GetIsAllocated()); GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); data_ = other_storage.data_; @@ -439,22 +435,23 @@ class Storage { void DeallocateIfAllocated() { if (GetIsAllocated()) { - AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(), - GetAllocatedCapacity()); + MallocAdapter<A>::Deallocate(GetAllocator(), GetAllocatedData(), + GetAllocatedCapacity()); } } private: - using Metadata = - container_internal::CompressedTuple<allocator_type, size_type>; + ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); + + using Metadata = container_internal::CompressedTuple<A, SizeType<A>>; struct Allocated { - pointer allocated_data; - size_type allocated_capacity; + Pointer<A> allocated_data; + SizeType<A> allocated_capacity; }; struct Inlined { - alignas(value_type) char inlined_data[sizeof(value_type[N])]; + alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])]; }; union Data { @@ -462,33 +459,75 @@ class Storage { Inlined inlined; }; + template <typename... Args> + ABSL_ATTRIBUTE_NOINLINE Reference<A> EmplaceBackSlow(Args&&... args); + Metadata metadata_; Data data_; }; template <typename T, size_t N, typename A> +void Storage<T, N, A>::DestroyContents() { + Pointer<A> data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); + DestroyElements<A>(GetAllocator(), data, GetSize()); + DeallocateIfAllocated(); +} + +template <typename T, size_t N, typename A> +void Storage<T, N, A>::InitFrom(const Storage& other) { + const SizeType<A> n = other.GetSize(); + assert(n > 0); // Empty sources handled handled in caller. + ConstPointer<A> src; + Pointer<A> dst; + if (!other.GetIsAllocated()) { + dst = GetInlinedData(); + src = other.GetInlinedData(); + } else { + // Because this is only called from the `InlinedVector` constructors, it's + // safe to take on the allocation with size `0`. If `ConstructElements(...)` + // throws, deallocation will be automatically handled by `~Storage()`. + SizeType<A> requested_capacity = ComputeCapacity(GetInlinedCapacity(), n); + Allocation<A> allocation = + MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); + SetAllocation(allocation); + dst = allocation.data; + src = other.GetAllocatedData(); + } + if (IsMemcpyOk<A>::value) { + std::memcpy(reinterpret_cast<char*>(dst), + reinterpret_cast<const char*>(src), n * sizeof(ValueType<A>)); + } else { + auto values = IteratorValueAdapter<A, ConstPointer<A>>(src); + ConstructElements<A>(GetAllocator(), dst, values, n); + } + GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); +} + +template <typename T, size_t N, typename A> template <typename ValueAdapter> -auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size) +auto Storage<T, N, A>::Initialize(ValueAdapter values, SizeType<A> new_size) -> void { // Only callable from constructors! assert(!GetIsAllocated()); assert(GetSize() == 0); - pointer construct_data; + Pointer<A> construct_data; if (new_size > GetInlinedCapacity()) { // Because this is only called from the `InlinedVector` constructors, it's // safe to take on the allocation with size `0`. If `ConstructElements(...)` // throws, deallocation will be automatically handled by `~Storage()`. - size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); - construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); - SetAllocatedData(construct_data, new_capacity); + SizeType<A> requested_capacity = + ComputeCapacity(GetInlinedCapacity(), new_size); + Allocation<A> allocation = + MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); + construct_data = allocation.data; + SetAllocation(allocation); SetIsAllocated(); } else { construct_data = GetInlinedData(); } - inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, - &values, new_size); + ConstructElements<A>(GetAllocator(), construct_data, values, new_size); // Since the initial size was guaranteed to be `0` and the allocated bit is // already correct for either case, *adding* `new_size` gives us the correct @@ -498,18 +537,20 @@ auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size) template <typename T, size_t N, typename A> template <typename ValueAdapter> -auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { - StorageView storage_view = MakeStorageView(); +auto Storage<T, N, A>::Assign(ValueAdapter values, SizeType<A> new_size) + -> void { + StorageView<A> storage_view = MakeStorageView(); - AllocationTransaction allocation_tx(GetAllocPtr()); + AllocationTransaction<A> allocation_tx(GetAllocator()); - absl::Span<value_type> assign_loop; - absl::Span<value_type> construct_loop; - absl::Span<value_type> destroy_loop; + absl::Span<ValueType<A>> assign_loop; + absl::Span<ValueType<A>> construct_loop; + absl::Span<ValueType<A>> destroy_loop; if (new_size > storage_view.capacity) { - size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); - construct_loop = {allocation_tx.Allocate(new_capacity), new_size}; + SizeType<A> requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + construct_loop = {allocation_tx.Allocate(requested_capacity), new_size}; destroy_loop = {storage_view.data, storage_view.size}; } else if (new_size > storage_view.size) { assign_loop = {storage_view.data, storage_view.size}; @@ -520,18 +561,16 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; } - inlined_vector_internal::AssignElements(assign_loop.data(), &values, - assign_loop.size()); + AssignElements<A>(assign_loop.data(), values, assign_loop.size()); - inlined_vector_internal::ConstructElements( - GetAllocPtr(), construct_loop.data(), &values, construct_loop.size()); + ConstructElements<A>(GetAllocator(), construct_loop.data(), values, + construct_loop.size()); - inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), - destroy_loop.size()); + DestroyElements<A>(GetAllocator(), destroy_loop.data(), destroy_loop.size()); if (allocation_tx.DidAllocate()) { DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); } @@ -540,125 +579,119 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { template <typename T, size_t N, typename A> template <typename ValueAdapter> -auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void { - StorageView storage_view = MakeStorageView(); - - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(storage_view.data)); - - AllocationTransaction allocation_tx(GetAllocPtr()); - ConstructionTransaction construction_tx(GetAllocPtr()); - - absl::Span<value_type> construct_loop; - absl::Span<value_type> move_construct_loop; - absl::Span<value_type> destroy_loop; - - if (new_size > storage_view.capacity) { - size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); - pointer new_data = allocation_tx.Allocate(new_capacity); - construct_loop = {new_data + storage_view.size, - new_size - storage_view.size}; - move_construct_loop = {new_data, storage_view.size}; - destroy_loop = {storage_view.data, storage_view.size}; - } else if (new_size > storage_view.size) { - construct_loop = {storage_view.data + storage_view.size, - new_size - storage_view.size}; +auto Storage<T, N, A>::Resize(ValueAdapter values, SizeType<A> new_size) + -> void { + StorageView<A> storage_view = MakeStorageView(); + Pointer<A> const base = storage_view.data; + const SizeType<A> size = storage_view.size; + A& alloc = GetAllocator(); + if (new_size <= size) { + // Destroy extra old elements. + DestroyElements<A>(alloc, base + new_size, size - new_size); + } else if (new_size <= storage_view.capacity) { + // Construct new elements in place. + ConstructElements<A>(alloc, base + size, values, new_size - size); } else { - destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; - } - - construction_tx.Construct(construct_loop.data(), &values, - construct_loop.size()); - - inlined_vector_internal::ConstructElements( - GetAllocPtr(), move_construct_loop.data(), &move_values, - move_construct_loop.size()); - - inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), - destroy_loop.size()); - - construction_tx.Commit(); - if (allocation_tx.DidAllocate()) { + // Steps: + // a. Allocate new backing store. + // b. Construct new elements in new backing store. + // c. Move existing elements from old backing store to now. + // d. Destroy all elements in old backing store. + // Use transactional wrappers for the first two steps so we can roll + // back if necessary due to exceptions. + AllocationTransaction<A> allocation_tx(alloc); + SizeType<A> requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer<A> new_data = allocation_tx.Allocate(requested_capacity); + + ConstructionTransaction<A> construction_tx(alloc); + construction_tx.Construct(new_data + size, values, new_size - size); + + IteratorValueAdapter<A, MoveIterator<A>> move_values( + (MoveIterator<A>(base))); + ConstructElements<A>(alloc, new_data, move_values, size); + + DestroyElements<A>(alloc, base, size); + std::move(construction_tx).Commit(); DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); } - SetSize(new_size); } template <typename T, size_t N, typename A> template <typename ValueAdapter> -auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values, - size_type insert_count) -> iterator { - StorageView storage_view = MakeStorageView(); +auto Storage<T, N, A>::Insert(ConstIterator<A> pos, ValueAdapter values, + SizeType<A> insert_count) -> Iterator<A> { + StorageView<A> storage_view = MakeStorageView(); - size_type insert_index = - std::distance(const_iterator(storage_view.data), pos); - size_type insert_end_index = insert_index + insert_count; - size_type new_size = storage_view.size + insert_count; + SizeType<A> insert_index = + std::distance(ConstIterator<A>(storage_view.data), pos); + SizeType<A> insert_end_index = insert_index + insert_count; + SizeType<A> new_size = storage_view.size + insert_count; if (new_size > storage_view.capacity) { - AllocationTransaction allocation_tx(GetAllocPtr()); - ConstructionTransaction construction_tx(GetAllocPtr()); - ConstructionTransaction move_construciton_tx(GetAllocPtr()); + AllocationTransaction<A> allocation_tx(GetAllocator()); + ConstructionTransaction<A> construction_tx(GetAllocator()); + ConstructionTransaction<A> move_construction_tx(GetAllocator()); - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(storage_view.data)); + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(storage_view.data)); - size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); - pointer new_data = allocation_tx.Allocate(new_capacity); + SizeType<A> requested_capacity = + ComputeCapacity(storage_view.capacity, new_size); + Pointer<A> new_data = allocation_tx.Allocate(requested_capacity); - construction_tx.Construct(new_data + insert_index, &values, insert_count); + construction_tx.Construct(new_data + insert_index, values, insert_count); - move_construciton_tx.Construct(new_data, &move_values, insert_index); + move_construction_tx.Construct(new_data, move_values, insert_index); - inlined_vector_internal::ConstructElements( - GetAllocPtr(), new_data + insert_end_index, &move_values, - storage_view.size - insert_index); + ConstructElements<A>(GetAllocator(), new_data + insert_end_index, + move_values, storage_view.size - insert_index); - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); + DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size); - construction_tx.Commit(); - move_construciton_tx.Commit(); + std::move(construction_tx).Commit(); + std::move(move_construction_tx).Commit(); DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetAllocatedSize(new_size); - return iterator(new_data + insert_index); + return Iterator<A>(new_data + insert_index); } else { - size_type move_construction_destination_index = + SizeType<A> move_construction_destination_index = (std::max)(insert_end_index, storage_view.size); - ConstructionTransaction move_construction_tx(GetAllocPtr()); + ConstructionTransaction<A> move_construction_tx(GetAllocator()); - IteratorValueAdapter<MoveIterator> move_construction_values( - MoveIterator(storage_view.data + - (move_construction_destination_index - insert_count))); - absl::Span<value_type> move_construction = { + IteratorValueAdapter<A, MoveIterator<A>> move_construction_values( + MoveIterator<A>(storage_view.data + + (move_construction_destination_index - insert_count))); + absl::Span<ValueType<A>> move_construction = { storage_view.data + move_construction_destination_index, new_size - move_construction_destination_index}; - pointer move_assignment_values = storage_view.data + insert_index; - absl::Span<value_type> move_assignment = { + Pointer<A> move_assignment_values = storage_view.data + insert_index; + absl::Span<ValueType<A>> move_assignment = { storage_view.data + insert_end_index, move_construction_destination_index - insert_end_index}; - absl::Span<value_type> insert_assignment = {move_assignment_values, - move_construction.size()}; + absl::Span<ValueType<A>> insert_assignment = {move_assignment_values, + move_construction.size()}; - absl::Span<value_type> insert_construction = { + absl::Span<ValueType<A>> insert_construction = { insert_assignment.data() + insert_assignment.size(), insert_count - insert_assignment.size()}; move_construction_tx.Construct(move_construction.data(), - &move_construction_values, + move_construction_values, move_construction.size()); - for (pointer destination = move_assignment.data() + move_assignment.size(), - last_destination = move_assignment.data(), - source = move_assignment_values + move_assignment.size(); + for (Pointer<A> + destination = move_assignment.data() + move_assignment.size(), + last_destination = move_assignment.data(), + source = move_assignment_values + move_assignment.size(); ;) { --destination; --source; @@ -666,114 +699,115 @@ auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values, *destination = std::move(*source); } - inlined_vector_internal::AssignElements(insert_assignment.data(), &values, - insert_assignment.size()); + AssignElements<A>(insert_assignment.data(), values, + insert_assignment.size()); - inlined_vector_internal::ConstructElements( - GetAllocPtr(), insert_construction.data(), &values, - insert_construction.size()); + ConstructElements<A>(GetAllocator(), insert_construction.data(), values, + insert_construction.size()); - move_construction_tx.Commit(); + std::move(move_construction_tx).Commit(); AddSize(insert_count); - return iterator(storage_view.data + insert_index); + return Iterator<A>(storage_view.data + insert_index); } } template <typename T, size_t N, typename A> template <typename... Args> -auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference { - StorageView storage_view = MakeStorageView(); - - AllocationTransaction allocation_tx(GetAllocPtr()); - - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(storage_view.data)); +auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> Reference<A> { + StorageView<A> storage_view = MakeStorageView(); + const SizeType<A> n = storage_view.size; + if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { + // Fast path; new element fits. + Pointer<A> last_ptr = storage_view.data + n; + AllocatorTraits<A>::construct(GetAllocator(), last_ptr, + std::forward<Args>(args)...); + AddSize(1); + return *last_ptr; + } + // TODO(b/173712035): Annotate with musttail attribute to prevent regression. + return EmplaceBackSlow(std::forward<Args>(args)...); +} - pointer construct_data; - if (storage_view.size == storage_view.capacity) { - size_type new_capacity = NextCapacity(storage_view.capacity); - construct_data = allocation_tx.Allocate(new_capacity); - } else { - construct_data = storage_view.data; +template <typename T, size_t N, typename A> +template <typename... Args> +auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> Reference<A> { + StorageView<A> storage_view = MakeStorageView(); + AllocationTransaction<A> allocation_tx(GetAllocator()); + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(storage_view.data)); + SizeType<A> requested_capacity = NextCapacity(storage_view.capacity); + Pointer<A> construct_data = allocation_tx.Allocate(requested_capacity); + Pointer<A> last_ptr = construct_data + storage_view.size; + + // Construct new element. + AllocatorTraits<A>::construct(GetAllocator(), last_ptr, + std::forward<Args>(args)...); + // Move elements from old backing store to new backing store. + ABSL_INTERNAL_TRY { + ConstructElements<A>(GetAllocator(), allocation_tx.GetData(), move_values, + storage_view.size); } - - pointer last_ptr = construct_data + storage_view.size; - - AllocatorTraits::construct(*GetAllocPtr(), last_ptr, - std::forward<Args>(args)...); - - if (allocation_tx.DidAllocate()) { - ABSL_INTERNAL_TRY { - inlined_vector_internal::ConstructElements( - GetAllocPtr(), allocation_tx.GetData(), &move_values, - storage_view.size); - } - ABSL_INTERNAL_CATCH_ANY { - AllocatorTraits::destroy(*GetAllocPtr(), last_ptr); - ABSL_INTERNAL_RETHROW; - } - - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); - - DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); - SetIsAllocated(); + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits<A>::destroy(GetAllocator(), last_ptr); + ABSL_INTERNAL_RETHROW; } + // Destroy elements in old backing store. + DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size); + DeallocateIfAllocated(); + SetAllocation(std::move(allocation_tx).Release()); + SetIsAllocated(); AddSize(1); return *last_ptr; } template <typename T, size_t N, typename A> -auto Storage<T, N, A>::Erase(const_iterator from, const_iterator to) - -> iterator { - StorageView storage_view = MakeStorageView(); +auto Storage<T, N, A>::Erase(ConstIterator<A> from, ConstIterator<A> to) + -> Iterator<A> { + StorageView<A> storage_view = MakeStorageView(); - size_type erase_size = std::distance(from, to); - size_type erase_index = - std::distance(const_iterator(storage_view.data), from); - size_type erase_end_index = erase_index + erase_size; + SizeType<A> erase_size = std::distance(from, to); + SizeType<A> erase_index = + std::distance(ConstIterator<A>(storage_view.data), from); + SizeType<A> erase_end_index = erase_index + erase_size; - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(storage_view.data + erase_end_index)); + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(storage_view.data + erase_end_index)); - inlined_vector_internal::AssignElements(storage_view.data + erase_index, - &move_values, - storage_view.size - erase_end_index); + AssignElements<A>(storage_view.data + erase_index, move_values, + storage_view.size - erase_end_index); - inlined_vector_internal::DestroyElements( - GetAllocPtr(), storage_view.data + (storage_view.size - erase_size), - erase_size); + DestroyElements<A>(GetAllocator(), + storage_view.data + (storage_view.size - erase_size), + erase_size); SubtractSize(erase_size); - return iterator(storage_view.data + erase_index); + return Iterator<A>(storage_view.data + erase_index); } template <typename T, size_t N, typename A> -auto Storage<T, N, A>::Reserve(size_type requested_capacity) -> void { - StorageView storage_view = MakeStorageView(); +auto Storage<T, N, A>::Reserve(SizeType<A> requested_capacity) -> void { + StorageView<A> storage_view = MakeStorageView(); if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; - AllocationTransaction allocation_tx(GetAllocPtr()); + AllocationTransaction<A> allocation_tx(GetAllocator()); - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(storage_view.data)); + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(storage_view.data)); - size_type new_capacity = + SizeType<A> new_requested_capacity = ComputeCapacity(storage_view.capacity, requested_capacity); - pointer new_data = allocation_tx.Allocate(new_capacity); + Pointer<A> new_data = allocation_tx.Allocate(new_requested_capacity); - inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data, - &move_values, storage_view.size); + ConstructElements<A>(GetAllocator(), new_data, move_values, + storage_view.size); - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); + DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size); DeallocateIfAllocated(); - AcquireAllocatedData(&allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); SetIsAllocated(); } @@ -782,41 +816,44 @@ auto Storage<T, N, A>::ShrinkToFit() -> void { // May only be called on allocated instances! assert(GetIsAllocated()); - StorageView storage_view{GetAllocatedData(), GetSize(), - GetAllocatedCapacity()}; + StorageView<A> storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; - AllocationTransaction allocation_tx(GetAllocPtr()); + AllocationTransaction<A> allocation_tx(GetAllocator()); - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(storage_view.data)); + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(storage_view.data)); - pointer construct_data; + Pointer<A> construct_data; if (storage_view.size > GetInlinedCapacity()) { - size_type new_capacity = storage_view.size; - construct_data = allocation_tx.Allocate(new_capacity); + SizeType<A> requested_capacity = storage_view.size; + construct_data = allocation_tx.Allocate(requested_capacity); + if (allocation_tx.GetCapacity() >= storage_view.capacity) { + // Already using the smallest available heap allocation. + return; + } } else { construct_data = GetInlinedData(); } ABSL_INTERNAL_TRY { - inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, - &move_values, storage_view.size); + ConstructElements<A>(GetAllocator(), construct_data, move_values, + storage_view.size); } ABSL_INTERNAL_CATCH_ANY { - SetAllocatedData(storage_view.data, storage_view.capacity); + SetAllocation({storage_view.data, storage_view.capacity}); ABSL_INTERNAL_RETHROW; } - inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, - storage_view.size); + DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size); - AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data, - storage_view.capacity); + MallocAdapter<A>::Deallocate(GetAllocator(), storage_view.data, + storage_view.capacity); if (allocation_tx.DidAllocate()) { - AcquireAllocatedData(&allocation_tx); + SetAllocation(std::move(allocation_tx).Release()); } else { UnsetIsAllocated(); } @@ -834,57 +871,60 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { Storage* large_ptr = other_storage_ptr; if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); - for (size_type i = 0; i < small_ptr->GetSize(); ++i) { + for (SizeType<A> i = 0; i < small_ptr->GetSize(); ++i) { swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); } - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(large_ptr->GetInlinedData() + small_ptr->GetSize())); - inlined_vector_internal::ConstructElements( - large_ptr->GetAllocPtr(), - small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values, - large_ptr->GetSize() - small_ptr->GetSize()); + ConstructElements<A>(large_ptr->GetAllocator(), + small_ptr->GetInlinedData() + small_ptr->GetSize(), + move_values, + large_ptr->GetSize() - small_ptr->GetSize()); - inlined_vector_internal::DestroyElements( - large_ptr->GetAllocPtr(), - large_ptr->GetInlinedData() + small_ptr->GetSize(), - large_ptr->GetSize() - small_ptr->GetSize()); + DestroyElements<A>(large_ptr->GetAllocator(), + large_ptr->GetInlinedData() + small_ptr->GetSize(), + large_ptr->GetSize() - small_ptr->GetSize()); } else { Storage* allocated_ptr = this; Storage* inlined_ptr = other_storage_ptr; if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); - StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(), - allocated_ptr->GetSize(), - allocated_ptr->GetAllocatedCapacity()}; + StorageView<A> allocated_storage_view{ + allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(), + allocated_ptr->GetAllocatedCapacity()}; - IteratorValueAdapter<MoveIterator> move_values( - MoveIterator(inlined_ptr->GetInlinedData())); + IteratorValueAdapter<A, MoveIterator<A>> move_values( + MoveIterator<A>(inlined_ptr->GetInlinedData())); ABSL_INTERNAL_TRY { - inlined_vector_internal::ConstructElements( - inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(), - &move_values, inlined_ptr->GetSize()); + ConstructElements<A>(inlined_ptr->GetAllocator(), + allocated_ptr->GetInlinedData(), move_values, + inlined_ptr->GetSize()); } ABSL_INTERNAL_CATCH_ANY { - allocated_ptr->SetAllocatedData(allocated_storage_view.data, - allocated_storage_view.capacity); + allocated_ptr->SetAllocation( + {allocated_storage_view.data, allocated_storage_view.capacity}); ABSL_INTERNAL_RETHROW; } - inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(), - inlined_ptr->GetInlinedData(), - inlined_ptr->GetSize()); + DestroyElements<A>(inlined_ptr->GetAllocator(), + inlined_ptr->GetInlinedData(), inlined_ptr->GetSize()); - inlined_ptr->SetAllocatedData(allocated_storage_view.data, - allocated_storage_view.capacity); + inlined_ptr->SetAllocation( + {allocated_storage_view.data, allocated_storage_view.capacity}); } swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); - swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); + swap(GetAllocator(), other_storage_ptr->GetAllocator()); } +// End ignore "array-bounds" and "maybe-uninitialized" +#if !defined(__clang__) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + } // namespace inlined_vector_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/container/internal/layout.h b/third_party/abseil-cpp/absl/container/internal/layout.h index 69cc85dd66..a59a243059 100644 --- a/third_party/abseil-cpp/absl/container/internal/layout.h +++ b/third_party/abseil-cpp/absl/container/internal/layout.h @@ -163,6 +163,7 @@ #include <assert.h> #include <stddef.h> #include <stdint.h> + #include <ostream> #include <string> #include <tuple> @@ -170,15 +171,16 @@ #include <typeinfo> #include <utility> -#ifdef ADDRESS_SANITIZER -#include <sanitizer/asan_interface.h> -#endif - +#include "absl/base/config.h" #include "absl/meta/type_traits.h" #include "absl/strings/str_cat.h" #include "absl/types/span.h" #include "absl/utility/utility.h" +#ifdef ABSL_HAVE_ADDRESS_SANITIZER +#include <sanitizer/asan_interface.h> +#endif + #if defined(__GXX_RTTI) #define ABSL_INTERNAL_HAS_CXA_DEMANGLE #endif @@ -402,7 +404,7 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, constexpr size_t Offset() const { static_assert(N < NumOffsets, "Index out of bounds"); return adl_barrier::Align( - Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1], + Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1], ElementAlignment<N>::value); } @@ -595,7 +597,7 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, constexpr size_t AllocSize() const { static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); return Offset<NumTypes - 1>() + - SizeOf<ElementType<NumTypes - 1>>() * size_[NumTypes - 1]; + SizeOf<ElementType<NumTypes - 1>>::value * size_[NumTypes - 1]; } // If built with --config=asan, poisons padding bytes (if any) in the @@ -614,12 +616,12 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, void PoisonPadding(const Char* p) const { static_assert(N < NumOffsets, "Index out of bounds"); (void)p; -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER PoisonPadding<Char, N - 1>(p); // The `if` is an optimization. It doesn't affect the observable behaviour. if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) { size_t start = - Offset<N - 1>() + SizeOf<ElementType<N - 1>>() * size_[N - 1]; + Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1]; ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start); } #endif @@ -643,7 +645,7 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, // produce "unsigned*" where another produces "unsigned int *". std::string DebugString() const { const auto offsets = Offsets(); - const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...}; + const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>::value...}; const std::string types[] = { adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); diff --git a/third_party/abseil-cpp/absl/container/internal/layout_benchmark.cc b/third_party/abseil-cpp/absl/container/internal/layout_benchmark.cc new file mode 100644 index 0000000000..d8636e8d5a --- /dev/null +++ b/third_party/abseil-cpp/absl/container/internal/layout_benchmark.cc @@ -0,0 +1,122 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// Every benchmark should have the same performance as the corresponding +// headroom benchmark. + +#include "absl/base/internal/raw_logging.h" +#include "absl/container/internal/layout.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace { + +using ::benchmark::DoNotOptimize; + +using Int128 = int64_t[2]; + +// This benchmark provides the upper bound on performance for BM_OffsetConstant. +template <size_t Offset, class... Ts> +void BM_OffsetConstantHeadroom(benchmark::State& state) { + for (auto _ : state) { + DoNotOptimize(Offset); + } +} + +template <size_t Offset, class... Ts> +void BM_OffsetConstant(benchmark::State& state) { + using L = Layout<Ts...>; + ABSL_RAW_CHECK(L::Partial(3, 5, 7).template Offset<3>() == Offset, + "Invalid offset"); + for (auto _ : state) { + DoNotOptimize(L::Partial(3, 5, 7).template Offset<3>()); + } +} + +template <class... Ts> +size_t VariableOffset(size_t n, size_t m, size_t k); + +template <> +size_t VariableOffset<int8_t, int16_t, int32_t, Int128>(size_t n, size_t m, + size_t k) { + auto Align = [](size_t n, size_t m) { return (n + m - 1) & ~(m - 1); }; + return Align(Align(Align(n * 1, 2) + m * 2, 4) + k * 4, 8); +} + +template <> +size_t VariableOffset<Int128, int32_t, int16_t, int8_t>(size_t n, size_t m, + size_t k) { + // No alignment is necessary. + return n * 16 + m * 4 + k * 2; +} + +// This benchmark provides the upper bound on performance for BM_OffsetVariable. +template <size_t Offset, class... Ts> +void BM_OffsetVariableHeadroom(benchmark::State& state) { + size_t n = 3; + size_t m = 5; + size_t k = 7; + ABSL_RAW_CHECK(VariableOffset<Ts...>(n, m, k) == Offset, "Invalid offset"); + for (auto _ : state) { + DoNotOptimize(n); + DoNotOptimize(m); + DoNotOptimize(k); + DoNotOptimize(VariableOffset<Ts...>(n, m, k)); + } +} + +template <size_t Offset, class... Ts> +void BM_OffsetVariable(benchmark::State& state) { + using L = Layout<Ts...>; + size_t n = 3; + size_t m = 5; + size_t k = 7; + ABSL_RAW_CHECK(L::Partial(n, m, k).template Offset<3>() == Offset, + "Inavlid offset"); + for (auto _ : state) { + DoNotOptimize(n); + DoNotOptimize(m); + DoNotOptimize(k); + DoNotOptimize(L::Partial(n, m, k).template Offset<3>()); + } +} + +// Run all benchmarks in two modes: +// +// Layout with padding: int8_t[3], int16_t[5], int32_t[7], Int128[?]. +// Layout without padding: Int128[3], int32_t[5], int16_t[7], int8_t[?]. + +#define OFFSET_BENCHMARK(NAME, OFFSET, T1, T2, T3, T4) \ + auto& NAME##_##OFFSET##_##T1##_##T2##_##T3##_##T4 = \ + NAME<OFFSET, T1, T2, T3, T4>; \ + BENCHMARK(NAME##_##OFFSET##_##T1##_##T2##_##T3##_##T4) + +OFFSET_BENCHMARK(BM_OffsetConstantHeadroom, 48, int8_t, int16_t, int32_t, + Int128); +OFFSET_BENCHMARK(BM_OffsetConstant, 48, int8_t, int16_t, int32_t, Int128); +OFFSET_BENCHMARK(BM_OffsetConstantHeadroom, 82, Int128, int32_t, int16_t, + int8_t); +OFFSET_BENCHMARK(BM_OffsetConstant, 82, Int128, int32_t, int16_t, int8_t); +OFFSET_BENCHMARK(BM_OffsetVariableHeadroom, 48, int8_t, int16_t, int32_t, + Int128); +OFFSET_BENCHMARK(BM_OffsetVariable, 48, int8_t, int16_t, int32_t, Int128); +OFFSET_BENCHMARK(BM_OffsetVariableHeadroom, 82, Int128, int32_t, int16_t, + int8_t); +OFFSET_BENCHMARK(BM_OffsetVariable, 82, Int128, int32_t, int16_t, int8_t); +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/container/internal/layout_test.cc b/third_party/abseil-cpp/absl/container/internal/layout_test.cc index 8f3628a1f1..54e5d5bbb8 100644 --- a/third_party/abseil-cpp/absl/container/internal/layout_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/layout_test.cc @@ -17,6 +17,7 @@ // We need ::max_align_t because some libstdc++ versions don't provide // std::max_align_t #include <stddef.h> + #include <cstdint> #include <memory> #include <sstream> @@ -24,6 +25,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/types/span.h" @@ -126,8 +128,10 @@ TEST(Layout, ElementTypes) { { using L = Layout<int32_t, int32_t>; SameType<std::tuple<int32_t, int32_t>, L::ElementTypes>(); - SameType<std::tuple<int32_t, int32_t>, decltype(L::Partial())::ElementTypes>(); - SameType<std::tuple<int32_t, int32_t>, decltype(L::Partial(0))::ElementTypes>(); + SameType<std::tuple<int32_t, int32_t>, + decltype(L::Partial())::ElementTypes>(); + SameType<std::tuple<int32_t, int32_t>, + decltype(L::Partial(0))::ElementTypes>(); } { using L = Layout<int8_t, int32_t, Int128>; @@ -366,18 +370,21 @@ TEST(Layout, PointerByIndex) { { using L = Layout<int32_t>; EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p)))); - EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p)))); + EXPECT_EQ(0, + Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<0>(p)))); } { using L = Layout<int32_t, int32_t>; EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p)))); - EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p)))); - EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<1>(p)))); EXPECT_EQ(0, - Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<0>(p)))); + Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p)))); EXPECT_EQ(12, - Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<1>(p)))); + Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<1>(p)))); + EXPECT_EQ( + 0, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<0>(p)))); + EXPECT_EQ( + 12, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<0>(p)))); EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<1>(p)))); } @@ -385,39 +392,44 @@ TEST(Layout, PointerByIndex) { using L = Layout<int8_t, int32_t, Int128>; EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<0>(p)))); - EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<1>(p)))); + EXPECT_EQ(0, + Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<0>(p)))); - EXPECT_EQ(4, Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<1>(p)))); + EXPECT_EQ(4, + Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<0>(p)))); - EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<1>(p)))); + EXPECT_EQ(8, + Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<0>(p)))); - EXPECT_EQ(0, - Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<1>(p)))); + EXPECT_EQ( + 0, Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<0>(p)))); - EXPECT_EQ(4, - Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<1>(p)))); + EXPECT_EQ( + 4, Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<1>(p)))); EXPECT_EQ(8, Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<0>(p)))); - EXPECT_EQ(8, - Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<1>(p)))); + EXPECT_EQ( + 8, Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<1>(p)))); EXPECT_EQ(24, Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<2>(p)))); EXPECT_EQ( 0, Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p)))); EXPECT_EQ( - 0, Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p)))); + 0, + Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p)))); EXPECT_EQ( 0, Distance(p, Type<const Int128*>(L::Partial(0, 0, 0).Pointer<2>(p)))); EXPECT_EQ( 0, Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p)))); EXPECT_EQ( - 4, Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p)))); + 4, + Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p)))); EXPECT_EQ( 8, Distance(p, Type<const Int128*>(L::Partial(1, 0, 0).Pointer<2>(p)))); EXPECT_EQ( @@ -426,7 +438,8 @@ TEST(Layout, PointerByIndex) { 24, Distance(p, Type<const Int128*>(L::Partial(5, 3, 1).Pointer<2>(p)))); EXPECT_EQ( - 8, Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p)))); + 8, + Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L(5, 3, 1).Pointer<0>(p)))); EXPECT_EQ(24, Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<2>(p)))); EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<1>(p)))); @@ -437,75 +450,78 @@ TEST(Layout, PointerByType) { alignas(max_align_t) const unsigned char p[100] = {}; { using L = Layout<int32_t>; - EXPECT_EQ(0, - Distance(p, Type<const int32_t*>(L::Partial().Pointer<int32_t>(p)))); - EXPECT_EQ(0, - Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<int32_t>(p)))); + EXPECT_EQ( + 0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<int32_t>(p)))); + EXPECT_EQ( + 0, + Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<int32_t>(p)))); EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<int32_t>(p)))); } { using L = Layout<int8_t, int32_t, Int128>; - EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<int8_t>(p)))); - EXPECT_EQ(0, - Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<int8_t>(p)))); - EXPECT_EQ(0, - Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<int32_t>(p)))); - EXPECT_EQ(0, - Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<int8_t>(p)))); - EXPECT_EQ(4, - Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<int32_t>(p)))); - EXPECT_EQ(0, - Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<int8_t>(p)))); - EXPECT_EQ(8, - Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<int32_t>(p)))); EXPECT_EQ( - 0, Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p)))); + 0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<int8_t>(p)))); EXPECT_EQ( - 0, Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p)))); + 0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<int8_t>(p)))); EXPECT_EQ( 0, - Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<Int128>(p)))); - EXPECT_EQ( - 0, Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p)))); + Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<int32_t>(p)))); EXPECT_EQ( - 4, Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p)))); + 0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<int8_t>(p)))); EXPECT_EQ( - 8, - Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<Int128>(p)))); + 4, + Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<int32_t>(p)))); EXPECT_EQ( - 0, Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p)))); + 0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<int8_t>(p)))); EXPECT_EQ( - 8, Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p)))); + 8, + Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<int32_t>(p)))); EXPECT_EQ( - 24, - Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<Int128>(p)))); + 0, + Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p)))); + EXPECT_EQ(0, Distance(p, Type<const int32_t*>( + L::Partial(0, 0).Pointer<int32_t>(p)))); EXPECT_EQ( 0, - Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p)))); + Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<Int128>(p)))); EXPECT_EQ( 0, - Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p)))); - EXPECT_EQ(0, Distance(p, Type<const Int128*>( - L::Partial(0, 0, 0).Pointer<Int128>(p)))); + Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p)))); + EXPECT_EQ(4, Distance(p, Type<const int32_t*>( + L::Partial(1, 0).Pointer<int32_t>(p)))); + EXPECT_EQ( + 8, + Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<Int128>(p)))); EXPECT_EQ( 0, - Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p)))); + Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p)))); + EXPECT_EQ(8, Distance(p, Type<const int32_t*>( + L::Partial(5, 3).Pointer<int32_t>(p)))); EXPECT_EQ( - 4, - Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p)))); + 24, + Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<Int128>(p)))); + EXPECT_EQ(0, Distance(p, Type<const int8_t*>( + L::Partial(0, 0, 0).Pointer<int8_t>(p)))); + EXPECT_EQ(0, Distance(p, Type<const int32_t*>( + L::Partial(0, 0, 0).Pointer<int32_t>(p)))); + EXPECT_EQ(0, Distance(p, Type<const Int128*>( + L::Partial(0, 0, 0).Pointer<Int128>(p)))); + EXPECT_EQ(0, Distance(p, Type<const int8_t*>( + L::Partial(1, 0, 0).Pointer<int8_t>(p)))); + EXPECT_EQ(4, Distance(p, Type<const int32_t*>( + L::Partial(1, 0, 0).Pointer<int32_t>(p)))); EXPECT_EQ(8, Distance(p, Type<const Int128*>( L::Partial(1, 0, 0).Pointer<Int128>(p)))); - EXPECT_EQ( - 0, - Distance(p, Type<const int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p)))); + EXPECT_EQ(0, Distance(p, Type<const int8_t*>( + L::Partial(5, 3, 1).Pointer<int8_t>(p)))); EXPECT_EQ(24, Distance(p, Type<const Int128*>( L::Partial(5, 3, 1).Pointer<Int128>(p)))); - EXPECT_EQ( - 8, - Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p)))); + EXPECT_EQ(8, Distance(p, Type<const int32_t*>( + L::Partial(5, 3, 1).Pointer<int32_t>(p)))); EXPECT_EQ(24, Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<Int128>(p)))); - EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<int32_t>(p)))); + EXPECT_EQ( + 8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<int32_t>(p)))); } } @@ -546,15 +562,18 @@ TEST(Layout, MutablePointerByIndex) { EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<1>(p)))); EXPECT_EQ(24, Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p)))); - EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p)))); + EXPECT_EQ(0, + Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p)))); - EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p)))); + EXPECT_EQ(4, + Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p)))); EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p)))); EXPECT_EQ(24, Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<2>(p)))); - EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p)))); + EXPECT_EQ(8, + Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<0>(p)))); EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<2>(p)))); EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<1>(p)))); @@ -566,48 +585,61 @@ TEST(Layout, MutablePointerByType) { { using L = Layout<int32_t>; EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<int32_t>(p)))); - EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<int32_t>(p)))); + EXPECT_EQ(0, + Distance(p, Type<int32_t*>(L::Partial(3).Pointer<int32_t>(p)))); EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<int32_t>(p)))); } { using L = Layout<int8_t, int32_t, Int128>; EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<int8_t>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<int8_t>(p)))); - EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0).Pointer<int32_t>(p)))); + EXPECT_EQ(0, + Distance(p, Type<int32_t*>(L::Partial(0).Pointer<int32_t>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<int8_t>(p)))); - EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1).Pointer<int32_t>(p)))); + EXPECT_EQ(4, + Distance(p, Type<int32_t*>(L::Partial(1).Pointer<int32_t>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<int8_t>(p)))); - EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5).Pointer<int32_t>(p)))); - EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p)))); - EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p)))); + EXPECT_EQ(8, + Distance(p, Type<int32_t*>(L::Partial(5).Pointer<int32_t>(p)))); + EXPECT_EQ(0, + Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p)))); + EXPECT_EQ( + 0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p)))); EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<Int128>(p)))); - EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p)))); - EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p)))); + EXPECT_EQ(0, + Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p)))); + EXPECT_EQ( + 4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p)))); EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<Int128>(p)))); - EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p)))); - EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p)))); + EXPECT_EQ(0, + Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p)))); + EXPECT_EQ( + 8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p)))); EXPECT_EQ(24, Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<Int128>(p)))); - EXPECT_EQ(0, - Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p)))); - EXPECT_EQ(0, - Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p)))); + EXPECT_EQ( + 0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p)))); + EXPECT_EQ( + 0, + Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p)))); EXPECT_EQ( 0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<Int128>(p)))); - EXPECT_EQ(0, - Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p)))); - EXPECT_EQ(4, - Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p)))); + EXPECT_EQ( + 0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p)))); + EXPECT_EQ( + 4, + Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p)))); EXPECT_EQ( 8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<Int128>(p)))); - EXPECT_EQ(0, - Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p)))); + EXPECT_EQ( + 0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p)))); EXPECT_EQ( 24, Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<Int128>(p)))); - EXPECT_EQ(8, - Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p)))); + EXPECT_EQ( + 8, + Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p)))); EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<int8_t>(p)))); EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<Int128>(p)))); EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<int32_t>(p)))); @@ -788,67 +820,72 @@ TEST(Layout, SliceByIndexData) { { using L = Layout<int32_t>; EXPECT_EQ( - 0, - Distance(p, Type<Span<const int32_t>>(L::Partial(0).Slice<0>(p)).data())); + 0, Distance( + p, Type<Span<const int32_t>>(L::Partial(0).Slice<0>(p)).data())); EXPECT_EQ( - 0, - Distance(p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data())); - EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(L(3).Slice<0>(p)).data())); + 0, Distance( + p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data())); + EXPECT_EQ(0, + Distance(p, Type<Span<const int32_t>>(L(3).Slice<0>(p)).data())); } { using L = Layout<int32_t, int32_t>; EXPECT_EQ( - 0, - Distance(p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data())); + 0, Distance( + p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data())); EXPECT_EQ( 0, - Distance(p, - Type<Span<const int32_t>>(L::Partial(3, 5).Slice<0>(p)).data())); + Distance( + p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<0>(p)).data())); EXPECT_EQ( 12, - Distance(p, - Type<Span<const int32_t>>(L::Partial(3, 5).Slice<1>(p)).data())); - EXPECT_EQ(0, - Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<0>(p)).data())); - EXPECT_EQ(12, - Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<1>(p)).data())); + Distance( + p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<0>(p)).data())); + EXPECT_EQ( + 12, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<1>(p)).data())); } { using L = Layout<int8_t, int32_t, Int128>; EXPECT_EQ( - 0, - Distance(p, Type<Span<const int8_t>>(L::Partial(0).Slice<0>(p)).data())); - EXPECT_EQ( - 0, - Distance(p, Type<Span<const int8_t>>(L::Partial(1).Slice<0>(p)).data())); + 0, Distance( + p, Type<Span<const int8_t>>(L::Partial(0).Slice<0>(p)).data())); EXPECT_EQ( - 0, - Distance(p, Type<Span<const int8_t>>(L::Partial(5).Slice<0>(p)).data())); + 0, Distance( + p, Type<Span<const int8_t>>(L::Partial(1).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<0>(p)).data())); + p, Type<Span<const int8_t>>(L::Partial(5).Slice<0>(p)).data())); EXPECT_EQ( 0, - Distance(p, - Type<Span<const int32_t>>(L::Partial(0, 0).Slice<1>(p)).data())); + Distance( + p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<0>(p)).data())); EXPECT_EQ( - 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<0>(p)).data())); + 0, + Distance( + p, Type<Span<const int32_t>>(L::Partial(0, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<0>(p)).data())); EXPECT_EQ( 4, - Distance(p, - Type<Span<const int32_t>>(L::Partial(1, 0).Slice<1>(p)).data())); + Distance( + p, Type<Span<const int32_t>>(L::Partial(1, 0).Slice<1>(p)).data())); EXPECT_EQ( - 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<0>(p)).data())); + 0, + Distance( + p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<0>(p)).data())); EXPECT_EQ( 8, - Distance(p, - Type<Span<const int32_t>>(L::Partial(5, 3).Slice<1>(p)).data())); + Distance( + p, Type<Span<const int32_t>>(L::Partial(5, 3).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data())); + p, + Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( @@ -862,7 +899,8 @@ TEST(Layout, SliceByIndexData) { EXPECT_EQ( 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data())); + p, + Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data())); EXPECT_EQ( 4, Distance( @@ -876,7 +914,8 @@ TEST(Layout, SliceByIndexData) { EXPECT_EQ( 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data())); + p, + Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ( 24, Distance( @@ -888,12 +927,14 @@ TEST(Layout, SliceByIndexData) { p, Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<0>(p)).data())); + 0, + Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ( 24, Distance(p, Type<Span<const Int128>>(L(5, 3, 1).Slice<2>(p)).data())); EXPECT_EQ( - 8, Distance(p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<1>(p)).data())); + 8, + Distance(p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<1>(p)).data())); } } @@ -904,98 +945,94 @@ TEST(Layout, SliceByTypeData) { EXPECT_EQ( 0, Distance( - p, Type<Span<const int32_t>>(L::Partial(0).Slice<int32_t>(p)).data())); + p, + Type<Span<const int32_t>>(L::Partial(0).Slice<int32_t>(p)).data())); EXPECT_EQ( 0, Distance( - p, Type<Span<const int32_t>>(L::Partial(3).Slice<int32_t>(p)).data())); + p, + Type<Span<const int32_t>>(L::Partial(3).Slice<int32_t>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<const int32_t>>(L(3).Slice<int32_t>(p)).data())); + 0, + Distance(p, Type<Span<const int32_t>>(L(3).Slice<int32_t>(p)).data())); } { using L = Layout<int8_t, int32_t, Int128>; EXPECT_EQ( - 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(0).Slice<int8_t>(p)).data())); - EXPECT_EQ( - 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(1).Slice<int8_t>(p)).data())); - EXPECT_EQ( - 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(5).Slice<int8_t>(p)).data())); - EXPECT_EQ( - 0, - Distance( - p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data())); - EXPECT_EQ( 0, Distance( p, - Type<Span<const int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data())); + Type<Span<const int8_t>>(L::Partial(0).Slice<int8_t>(p)).data())); EXPECT_EQ( 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data())); - EXPECT_EQ( - 4, - Distance( p, - Type<Span<const int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data())); + Type<Span<const int8_t>>(L::Partial(1).Slice<int8_t>(p)).data())); EXPECT_EQ( 0, Distance( - p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data())); - EXPECT_EQ( - 8, - Distance( p, - Type<Span<const int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data())); + Type<Span<const int8_t>>(L::Partial(5).Slice<int8_t>(p)).data())); EXPECT_EQ( 0, - Distance( - p, - Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data())); + Distance(p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)) + .data())); + EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>( + L::Partial(0, 0).Slice<int32_t>(p)) + .data())); EXPECT_EQ( 0, - Distance(p, Type<Span<const int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)) + Distance(p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)) .data())); - EXPECT_EQ(0, Distance(p, Type<Span<const Int128>>( - L::Partial(0, 0, 0).Slice<Int128>(p)) + EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>( + L::Partial(1, 0).Slice<int32_t>(p)) .data())); EXPECT_EQ( 0, - Distance( - p, - Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data())); - EXPECT_EQ( - 4, - Distance(p, Type<Span<const int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)) + Distance(p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)) .data())); + EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>( + L::Partial(5, 3).Slice<int32_t>(p)) + .data())); + EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>( + L::Partial(0, 0, 0).Slice<int8_t>(p)) + .data())); + EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>( + L::Partial(0, 0, 0).Slice<int32_t>(p)) + .data())); + EXPECT_EQ(0, Distance(p, Type<Span<const Int128>>( + L::Partial(0, 0, 0).Slice<Int128>(p)) + .data())); + EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>( + L::Partial(1, 0, 0).Slice<int8_t>(p)) + .data())); + EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>( + L::Partial(1, 0, 0).Slice<int32_t>(p)) + .data())); EXPECT_EQ(8, Distance(p, Type<Span<const Int128>>( L::Partial(1, 0, 0).Slice<Int128>(p)) .data())); - EXPECT_EQ( - 0, - Distance( - p, - Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data())); + EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>( + L::Partial(5, 3, 1).Slice<int8_t>(p)) + .data())); EXPECT_EQ(24, Distance(p, Type<Span<const Int128>>( L::Partial(5, 3, 1).Slice<Int128>(p)) .data())); - EXPECT_EQ( - 8, - Distance(p, Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)) - .data())); + EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>( + L::Partial(5, 3, 1).Slice<int32_t>(p)) + .data())); EXPECT_EQ( 0, - Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data())); + Distance(p, + Type<Span<const int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data())); EXPECT_EQ( 24, Distance(p, Type<Span<const Int128>>(L(5, 3, 1).Slice<Int128>(p)).data())); EXPECT_EQ( - 8, Distance( - p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data())); + 8, + Distance( + p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data())); } } @@ -1003,18 +1040,19 @@ TEST(Layout, MutableSliceByIndexData) { alignas(max_align_t) unsigned char p[100]; { using L = Layout<int32_t>; - EXPECT_EQ(0, - Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<0>(p)).data())); - EXPECT_EQ(0, - Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data())); EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<0>(p)).data())); } { using L = Layout<int32_t, int32_t>; - EXPECT_EQ(0, - Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<0>(p)).data())); + 0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<0>(p)).data())); EXPECT_EQ( 12, Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<1>(p)).data())); @@ -1023,55 +1061,63 @@ TEST(Layout, MutableSliceByIndexData) { } { using L = Layout<int8_t, int32_t, Int128>; - EXPECT_EQ(0, - Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<0>(p)).data())); - EXPECT_EQ(0, - Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<0>(p)).data())); - EXPECT_EQ(0, - Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<0>(p)).data())); - EXPECT_EQ( - 0, Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<0>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<1>(p)).data())); + 0, Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<0>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<0>(p)).data())); + 0, Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<0>(p)).data())); EXPECT_EQ( - 4, Distance(p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<1>(p)).data())); + 0, Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<0>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<0>(p)).data())); + 0, + Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<0>(p)).data())); EXPECT_EQ( - 8, Distance(p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<1>(p)).data())); + 0, + Distance(p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, - Distance(p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data())); + Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 4, + Distance(p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, - Distance(p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data())); + Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<0>(p)).data())); + EXPECT_EQ( + 8, + Distance(p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( p, Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data())); EXPECT_EQ( - 0, - Distance(p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data())); + 0, Distance( + p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data())); EXPECT_EQ( - 4, - Distance(p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data())); + 4, Distance( + p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data())); EXPECT_EQ( 8, Distance( p, Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data())); EXPECT_EQ( - 0, - Distance(p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data())); + 0, Distance( + p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ( 24, Distance( p, Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data())); EXPECT_EQ( - 8, - Distance(p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data())); - EXPECT_EQ(0, Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<0>(p)).data())); + 8, Distance( + p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data())); + EXPECT_EQ(0, + Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ(24, Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<2>(p)).data())); - EXPECT_EQ(8, Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<1>(p)).data())); + EXPECT_EQ(8, + Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<1>(p)).data())); } } @@ -1080,66 +1126,84 @@ TEST(Layout, MutableSliceByTypeData) { { using L = Layout<int32_t>; EXPECT_EQ( - 0, - Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<int32_t>(p)).data())); + 0, Distance( + p, Type<Span<int32_t>>(L::Partial(0).Slice<int32_t>(p)).data())); EXPECT_EQ( - 0, - Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<int32_t>(p)).data())); - EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<int32_t>(p)).data())); + 0, Distance( + p, Type<Span<int32_t>>(L::Partial(3).Slice<int32_t>(p)).data())); + EXPECT_EQ(0, + Distance(p, Type<Span<int32_t>>(L(3).Slice<int32_t>(p)).data())); } { using L = Layout<int8_t, int32_t, Int128>; EXPECT_EQ( - 0, Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<int8_t>(p)).data())); + 0, + Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<int8_t>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<int8_t>(p)).data())); + 0, + Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<int8_t>(p)).data())); EXPECT_EQ( - 0, Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<int8_t>(p)).data())); + 0, + Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<int8_t>(p)).data())); EXPECT_EQ( 0, - Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data())); + Distance(p, + Type<Span<int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data())); EXPECT_EQ( - 0, Distance( - p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data())); + 0, + Distance( + p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data())); EXPECT_EQ( 0, - Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data())); + Distance(p, + Type<Span<int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data())); EXPECT_EQ( - 4, Distance( - p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data())); + 4, + Distance( + p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data())); EXPECT_EQ( 0, - Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data())); + Distance(p, + Type<Span<int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data())); EXPECT_EQ( - 8, Distance( - p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data())); + 8, + Distance( + p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data())); EXPECT_EQ( - 0, Distance( - p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data())); + 0, + Distance( + p, + Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data())); EXPECT_EQ( 0, Distance( - p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)).data())); + p, + Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)).data())); EXPECT_EQ( 0, Distance( p, Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<Int128>(p)).data())); EXPECT_EQ( - 0, Distance( - p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data())); + 0, + Distance( + p, + Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data())); EXPECT_EQ( 4, Distance( - p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)).data())); + p, + Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)).data())); EXPECT_EQ( 8, Distance( p, Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<Int128>(p)).data())); EXPECT_EQ( - 0, Distance( - p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data())); + 0, + Distance( + p, + Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data())); EXPECT_EQ( 24, Distance( @@ -1148,14 +1212,16 @@ TEST(Layout, MutableSliceByTypeData) { EXPECT_EQ( 8, Distance( - p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)).data())); - EXPECT_EQ(0, - Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data())); + p, + Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data())); EXPECT_EQ( 24, Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<Int128>(p)).data())); EXPECT_EQ( - 8, Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data())); + 8, + Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data())); } } @@ -1254,17 +1320,17 @@ TEST(Layout, MutableSlices) { } { const auto x = L::Partial(1, 2, 3); - EXPECT_THAT( - (Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(x.Slices(p))), - Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), - IsSameSlice(x.Slice<2>(p)))); + EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>( + x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), + IsSameSlice(x.Slice<2>(p)))); } { const L x(1, 2, 3); - EXPECT_THAT( - (Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(x.Slices(p))), - Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), - IsSameSlice(x.Slice<2>(p)))); + EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>( + x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), + IsSameSlice(x.Slice<2>(p)))); } } @@ -1284,7 +1350,13 @@ TEST(Layout, CustomAlignment) { TEST(Layout, OverAligned) { constexpr size_t M = alignof(max_align_t); constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3); +#ifdef __GNUC__ + // Using __attribute__ ((aligned ())) instead of alignas to bypass a gcc bug: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357 + __attribute__((aligned(2 * M))) unsigned char p[x.AllocSize()]; +#else alignas(2 * M) unsigned char p[x.AllocSize()]; +#endif EXPECT_EQ(2 * M + 3, x.AllocSize()); EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M)); } @@ -1314,7 +1386,7 @@ struct Region { }; void ExpectRegionPoisoned(const unsigned char* p, size_t n, bool poisoned) { -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER for (size_t i = 0; i != n; ++i) { EXPECT_EQ(poisoned, __asan_address_is_poisoned(p + i)); } @@ -1396,7 +1468,8 @@ TEST(Layout, DebugString) { x.DebugString()); } { - constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3); + constexpr auto x = + Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3); EXPECT_EQ( "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; " "@16" + @@ -1404,7 +1477,8 @@ TEST(Layout, DebugString) { x.DebugString()); } { - constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3, 4); + constexpr auto x = + Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3, 4); EXPECT_EQ( "@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; " "@16" + diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h b/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h index 0a02757ddf..c7df2efc62 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_map.h @@ -51,8 +51,9 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> { using key_arg = typename KeyArgImpl::template type<K, key_type>; static_assert(!std::is_reference<key_type>::value, ""); - // TODO(alkis): remove this assertion and verify that reference mapped_type is - // supported. + + // TODO(b/187807849): Evaluate whether to support reference mapped_type and + // remove this assertion if/when it is supported. static_assert(!std::is_reference<mapped_type>::value, ""); using iterator = typename raw_hash_map::raw_hash_set::iterator; diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc index 919ac07405..687bcb8a4d 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc @@ -23,11 +23,17 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[16] = { + ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, + ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty}; + constexpr size_t Group::kWidth; // Returns "random" seed. inline size_t RandomSeed() { -#if ABSL_HAVE_THREAD_LOCAL +#ifdef ABSL_HAVE_THREAD_LOCAL static thread_local size_t counter = 0; size_t value = ++counter; #else // ABSL_HAVE_THREAD_LOCAL @@ -37,12 +43,25 @@ inline size_t RandomSeed() { return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter)); } -bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) { +bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) { // To avoid problems with weak hashes and single bit tests, we use % 13. // TODO(kfm,sbenza): revisit after we do unconditional mixing return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6; } +void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) { + assert(ctrl[capacity] == ctrl_t::kSentinel); + assert(IsValidCapacity(capacity)); + for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) { + Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); + } + // Copy the cloned ctrl bytes. + std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes()); + ctrl[capacity] = ctrl_t::kSentinel; +} +// Extern template instantiotion for inline function. +template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); + } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h index ca7be8d868..12682b3532 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h @@ -87,6 +87,17 @@ // // This probing function guarantees that after N probes, all the groups of the // table will be probed exactly once. +// +// The control state and slot array are stored contiguously in a shared heap +// allocation. The layout of this allocation is: `capacity()` control bytes, +// one sentinel control byte, `Group::kWidth - 1` cloned control bytes, +// <possible padding>, `capacity()` slots. The sentinel control byte is used in +// iteration so we know when we reach the end of the table. The cloned control +// bytes at the end of the table are cloned from the beginning of the table so +// groups that begin near the end of the table can see a full group. In cases in +// which there are more than `capacity()` cloned control bytes, the extra bytes +// are `kEmpty`, and these ensure that we always see at least one empty slot and +// can stop an unsuccessful search. #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ @@ -102,8 +113,8 @@ #include <type_traits> #include <utility> -#include "absl/base/internal/bits.h" #include "absl/base/internal/endian.h" +#include "absl/base/optimization.h" #include "absl/base/port.h" #include "absl/container/internal/common.h" #include "absl/container/internal/compressed_tuple.h" @@ -112,15 +123,25 @@ #include "absl/container/internal/hashtable_debug_hooks.h" #include "absl/container/internal/hashtablez_sampler.h" #include "absl/container/internal/have_sse.h" -#include "absl/container/internal/layout.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" #include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +template <typename AllocType> +void SwapAlloc(AllocType& lhs, AllocType& rhs, + std::true_type /* propagate_on_container_swap */) { + using std::swap; + swap(lhs, rhs); +} +template <typename AllocType> +void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/, + std::false_type /* propagate_on_container_swap */) {} + template <size_t Width> class probe_seq { public: @@ -168,24 +189,19 @@ struct IsDecomposable< // TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. template <class T> -constexpr bool IsNoThrowSwappable() { +constexpr bool IsNoThrowSwappable(std::true_type = {} /* is_swappable */) { using std::swap; return noexcept(swap(std::declval<T&>(), std::declval<T&>())); } - -template <typename T> -int TrailingZeros(T x) { - return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64( - static_cast<uint64_t>(x)) - : base_internal::CountTrailingZerosNonZero32( - static_cast<uint32_t>(x)); +template <class T> +constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) { + return false; } template <typename T> -int LeadingZeros(T x) { - return sizeof(T) == 8 - ? base_internal::CountLeadingZeros64(static_cast<uint64_t>(x)) - : base_internal::CountLeadingZeros32(static_cast<uint32_t>(x)); +uint32_t TrailingZeros(T x) { + ABSL_INTERNAL_ASSUME(x != 0); + return countr_zero(x); } // An abstraction over a bitmask. It provides an easy way to iterate through the @@ -215,26 +231,24 @@ class BitMask { } explicit operator bool() const { return mask_ != 0; } int operator*() const { return LowestBitSet(); } - int LowestBitSet() const { + uint32_t LowestBitSet() const { return container_internal::TrailingZeros(mask_) >> Shift; } - int HighestBitSet() const { - return (sizeof(T) * CHAR_BIT - container_internal::LeadingZeros(mask_) - - 1) >> - Shift; + uint32_t HighestBitSet() const { + return static_cast<uint32_t>((bit_width(mask_) - 1) >> Shift); } BitMask begin() const { return *this; } BitMask end() const { return BitMask(0); } - int TrailingZeros() const { + uint32_t TrailingZeros() const { return container_internal::TrailingZeros(mask_) >> Shift; } - int LeadingZeros() const { + uint32_t LeadingZeros() const { constexpr int total_significant_bits = SignificantBits << Shift; constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; - return container_internal::LeadingZeros(mask_ << extra_bits) >> Shift; + return countl_zero(mask_ << extra_bits) >> Shift; } private: @@ -248,48 +262,53 @@ class BitMask { T mask_; }; -using ctrl_t = signed char; using h2_t = uint8_t; // The values here are selected for maximum performance. See the static asserts -// below for details. -enum Ctrl : ctrl_t { +// below for details. We use an enum class so that when strict aliasing is +// enabled, the compiler knows ctrl_t doesn't alias other types. +enum class ctrl_t : int8_t { kEmpty = -128, // 0b10000000 kDeleted = -2, // 0b11111110 kSentinel = -1, // 0b11111111 }; static_assert( - kEmpty & kDeleted & kSentinel & 0x80, + (static_cast<int8_t>(ctrl_t::kEmpty) & + static_cast<int8_t>(ctrl_t::kDeleted) & + static_cast<int8_t>(ctrl_t::kSentinel) & 0x80) != 0, "Special markers need to have the MSB to make checking for them efficient"); -static_assert(kEmpty < kSentinel && kDeleted < kSentinel, - "kEmpty and kDeleted must be smaller than kSentinel to make the " - "SIMD test of IsEmptyOrDeleted() efficient"); -static_assert(kSentinel == -1, - "kSentinel must be -1 to elide loading it from memory into SIMD " - "registers (pcmpeqd xmm, xmm)"); -static_assert(kEmpty == -128, - "kEmpty must be -128 to make the SIMD check for its " +static_assert( + ctrl_t::kEmpty < ctrl_t::kSentinel && ctrl_t::kDeleted < ctrl_t::kSentinel, + "ctrl_t::kEmpty and ctrl_t::kDeleted must be smaller than " + "ctrl_t::kSentinel to make the SIMD test of IsEmptyOrDeleted() efficient"); +static_assert( + ctrl_t::kSentinel == static_cast<ctrl_t>(-1), + "ctrl_t::kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(ctrl_t::kEmpty == static_cast<ctrl_t>(-128), + "ctrl_t::kEmpty must be -128 to make the SIMD check for its " "existence efficient (psignb xmm, xmm)"); -static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F, - "kEmpty and kDeleted must share an unset bit that is not shared " - "by kSentinel to make the scalar test for MatchEmptyOrDeleted() " - "efficient"); -static_assert(kDeleted == -2, - "kDeleted must be -2 to make the implementation of " +static_assert( + (~static_cast<int8_t>(ctrl_t::kEmpty) & + ~static_cast<int8_t>(ctrl_t::kDeleted) & + static_cast<int8_t>(ctrl_t::kSentinel) & 0x7F) != 0, + "ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not " + "shared by ctrl_t::kSentinel to make the scalar test for " + "MatchEmptyOrDeleted() efficient"); +static_assert(ctrl_t::kDeleted == static_cast<ctrl_t>(-2), + "ctrl_t::kDeleted must be -2 to make the implementation of " "ConvertSpecialToEmptyAndFullToDeleted efficient"); // A single block of empty control bytes for tables without any slots allocated. // This enables removing a branch in the hot path of find(). +ABSL_DLL extern const ctrl_t kEmptyGroup[16]; inline ctrl_t* EmptyGroup() { - alignas(16) static constexpr ctrl_t empty_group[] = { - kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, - kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty}; - return const_cast<ctrl_t*>(empty_group); + return const_cast<ctrl_t*>(kEmptyGroup); } // Mixes a randomly generated per-process seed with `hash` and `ctrl` to // randomize insertion order within groups. -bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl); +bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl); // Returns a hash seed. // @@ -305,14 +324,14 @@ inline size_t HashSeed(const ctrl_t* ctrl) { inline size_t H1(size_t hash, const ctrl_t* ctrl) { return (hash >> 7) ^ HashSeed(ctrl); } -inline ctrl_t H2(size_t hash) { return hash & 0x7F; } +inline h2_t H2(size_t hash) { return hash & 0x7F; } -inline bool IsEmpty(ctrl_t c) { return c == kEmpty; } -inline bool IsFull(ctrl_t c) { return c >= 0; } -inline bool IsDeleted(ctrl_t c) { return c == kDeleted; } -inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; } +inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; } +inline bool IsFull(ctrl_t c) { return c >= static_cast<ctrl_t>(0); } +inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; } -#if SWISSTABLE_HAVE_SSE2 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 // https://github.com/abseil/abseil-cpp/issues/209 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853 @@ -346,33 +365,33 @@ struct GroupSse2Impl { // Returns a bitmask representing the positions of empty slots. BitMask<uint32_t, kWidth> MatchEmpty() const { -#if SWISSTABLE_HAVE_SSSE3 - // This only works because kEmpty is -128. +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 + // This only works because ctrl_t::kEmpty is -128. return BitMask<uint32_t, kWidth>( _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); #else - return Match(static_cast<h2_t>(kEmpty)); + return Match(static_cast<h2_t>(ctrl_t::kEmpty)); #endif } // Returns a bitmask representing the positions of empty or deleted slots. BitMask<uint32_t, kWidth> MatchEmptyOrDeleted() const { - auto special = _mm_set1_epi8(kSentinel); + auto special = _mm_set1_epi8(static_cast<int8_t>(ctrl_t::kSentinel)); return BitMask<uint32_t, kWidth>( _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))); } // Returns the number of trailing empty or deleted elements in the group. uint32_t CountLeadingEmptyOrDeleted() const { - auto special = _mm_set1_epi8(kSentinel); - return TrailingZeros( - _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1); + auto special = _mm_set1_epi8(static_cast<int8_t>(ctrl_t::kSentinel)); + return TrailingZeros(static_cast<uint32_t>( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1)); } void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { auto msbs = _mm_set1_epi8(static_cast<char>(-128)); auto x126 = _mm_set1_epi8(126); -#if SWISSTABLE_HAVE_SSSE3 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSSE3 auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs); #else auto zero = _mm_setzero_si128(); @@ -384,7 +403,7 @@ struct GroupSse2Impl { __m128i ctrl; }; -#endif // SWISSTABLE_HAVE_SSE2 +#endif // ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 struct GroupPortableImpl { static constexpr size_t kWidth = 8; @@ -399,7 +418,7 @@ struct GroupPortableImpl { // // Caveat: there are false positives but: // - they only occur if there is a real match - // - they never occur on kEmpty, kDeleted, kSentinel + // - they never occur on ctrl_t::kEmpty, ctrl_t::kDeleted, ctrl_t::kSentinel // - they will be handled gracefully by subsequent checks in code // // Example: @@ -438,12 +457,16 @@ struct GroupPortableImpl { uint64_t ctrl; }; -#if SWISSTABLE_HAVE_SSE2 +#if ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2 using Group = GroupSse2Impl; #else using Group = GroupPortableImpl; #endif +// The number of cloned control bytes that we copy from the beginning to the +// end of the control bytes array. +constexpr size_t NumClonedBytes() { return Group::kWidth - 1; } + template <class Policy, class Hash, class Eq, class Alloc> class raw_hash_set; @@ -451,31 +474,29 @@ inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } // PRECONDITION: // IsValidCapacity(capacity) -// ctrl[capacity] == kSentinel -// ctrl[i] != kSentinel for all i < capacity +// ctrl[capacity] == ctrl_t::kSentinel +// ctrl[i] != ctrl_t::kSentinel for all i < capacity // Applies mapping for every byte in ctrl: // DELETED -> EMPTY // EMPTY -> EMPTY // FULL -> DELETED -inline void ConvertDeletedToEmptyAndFullToDeleted( - ctrl_t* ctrl, size_t capacity) { - assert(ctrl[capacity] == kSentinel); - assert(IsValidCapacity(capacity)); - for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) { - Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); - } - // Copy the cloned ctrl bytes. - std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth); - ctrl[capacity] = kSentinel; -} +void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity); // Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1. inline size_t NormalizeCapacity(size_t n) { - return n ? ~size_t{} >> LeadingZeros(n) : 1; + return n ? ~size_t{} >> countl_zero(n) : 1; } -// We use 7/8th as maximum load factor. -// For 16-wide groups, that gives an average of two empty slots per group. +// General notes on capacity/growth methods below: +// - We use 7/8th as maximum load factor. For 16-wide groups, that gives an +// average of two empty slots per group. +// - For (capacity+1) >= Group::kWidth, growth is 7/8*capacity. +// - For (capacity+1) < Group::kWidth, growth == capacity. In this case, we +// never need to probe (the whole table fits in one group) so we don't need a +// load factor less than 1. + +// Given `capacity` of the table, returns the size (i.e. number of full slots) +// at which we should grow the capacity. inline size_t CapacityToGrowth(size_t capacity) { assert(IsValidCapacity(capacity)); // `capacity*7/8` @@ -486,7 +507,7 @@ inline size_t CapacityToGrowth(size_t capacity) { return capacity - capacity / 8; } // From desired "growth" to a lowerbound of the necessary capacity. -// Might not be a valid one and required NormalizeCapacity(). +// Might not be a valid one and requires NormalizeCapacity(). inline size_t GrowthToLowerboundCapacity(size_t growth) { // `growth*8/7` if (Group::kWidth == 8 && growth == 7) { @@ -496,6 +517,144 @@ inline size_t GrowthToLowerboundCapacity(size_t growth) { return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7); } +template <class InputIter> +size_t SelectBucketCountForIterRange(InputIter first, InputIter last, + size_t bucket_count) { + if (bucket_count != 0) { + return bucket_count; + } + using InputIterCategory = + typename std::iterator_traits<InputIter>::iterator_category; + if (std::is_base_of<std::random_access_iterator_tag, + InputIterCategory>::value) { + return GrowthToLowerboundCapacity( + static_cast<size_t>(std::distance(first, last))); + } + return 0; +} + +inline void AssertIsFull(ctrl_t* ctrl) { + ABSL_HARDENING_ASSERT((ctrl != nullptr && IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, or the table might have rehashed."); +} + +inline void AssertIsValid(ctrl_t* ctrl) { + ABSL_HARDENING_ASSERT((ctrl == nullptr || IsFull(*ctrl)) && + "Invalid operation on iterator. The element might have " + "been erased, or the table might have rehashed."); +} + +struct FindInfo { + size_t offset; + size_t probe_length; +}; + +// The representation of the object has two modes: +// - small: For capacities < kWidth-1 +// - large: For the rest. +// +// Differences: +// - In small mode we are able to use the whole capacity. The extra control +// bytes give us at least one "empty" control byte to stop the iteration. +// This is important to make 1 a valid capacity. +// +// - In small mode only the first `capacity()` control bytes after the +// sentinel are valid. The rest contain dummy ctrl_t::kEmpty values that do not +// represent a real slot. This is important to take into account on +// find_first_non_full(), where we never try ShouldInsertBackwards() for +// small tables. +inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; } + +inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, size_t hash, + size_t capacity) { + return probe_seq<Group::kWidth>(H1(hash, ctrl), capacity); +} + +// Probes the raw_hash_set with the probe sequence for hash and returns the +// pointer to the first empty or deleted slot. +// NOTE: this function must work with tables having both ctrl_t::kEmpty and +// ctrl_t::kDeleted in one group. Such tables appears during +// drop_deletes_without_resize. +// +// This function is very useful when insertions happen and: +// - the input is already a set +// - there are enough slots +// - the element with the hash is not in the table +template <typename = void> +inline FindInfo find_first_non_full(const ctrl_t* ctrl, size_t hash, + size_t capacity) { + auto seq = probe(ctrl, hash, capacity); + while (true) { + Group g{ctrl + seq.offset()}; + auto mask = g.MatchEmptyOrDeleted(); + if (mask) { +#if !defined(NDEBUG) + // We want to add entropy even when ASLR is not enabled. + // In debug build we will randomly insert in either the front or back of + // the group. + // TODO(kfm,sbenza): revisit after we do unconditional mixing + if (!is_small(capacity) && ShouldInsertBackwards(hash, ctrl)) { + return {seq.offset(mask.HighestBitSet()), seq.index()}; + } +#endif + return {seq.offset(mask.LowestBitSet()), seq.index()}; + } + seq.next(); + assert(seq.index() <= capacity && "full table!"); + } +} + +// Extern template for inline function keep possibility of inlining. +// When compiler decided to not inline, no symbols will be added to the +// corresponding translation unit. +extern template FindInfo find_first_non_full(const ctrl_t*, size_t, size_t); + +// Reset all ctrl bytes back to ctrl_t::kEmpty, except the sentinel. +inline void ResetCtrl(size_t capacity, ctrl_t* ctrl, const void* slot, + size_t slot_size) { + std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty), + capacity + 1 + NumClonedBytes()); + ctrl[capacity] = ctrl_t::kSentinel; + SanitizerPoisonMemoryRegion(slot, slot_size * capacity); +} + +// Sets the control byte, and if `i < NumClonedBytes()`, set the cloned byte +// at the end too. +inline void SetCtrl(size_t i, ctrl_t h, size_t capacity, ctrl_t* ctrl, + const void* slot, size_t slot_size) { + assert(i < capacity); + + auto* slot_i = static_cast<const char*>(slot) + i * slot_size; + if (IsFull(h)) { + SanitizerUnpoisonMemoryRegion(slot_i, slot_size); + } else { + SanitizerPoisonMemoryRegion(slot_i, slot_size); + } + + ctrl[i] = h; + ctrl[((i - NumClonedBytes()) & capacity) + (NumClonedBytes() & capacity)] = h; +} + +inline void SetCtrl(size_t i, h2_t h, size_t capacity, ctrl_t* ctrl, + const void* slot, size_t slot_size) { + SetCtrl(i, static_cast<ctrl_t>(h), capacity, ctrl, slot, slot_size); +} + +// The allocated block consists of `capacity + 1 + NumClonedBytes()` control +// bytes followed by `capacity` slots, which must be aligned to `slot_align`. +// SlotOffset returns the offset of the slots into the allocated block. +inline size_t SlotOffset(size_t capacity, size_t slot_align) { + assert(IsValidCapacity(capacity)); + const size_t num_control_bytes = capacity + 1 + NumClonedBytes(); + return (num_control_bytes + slot_align - 1) & (~slot_align + 1); +} + +// Returns the size of the allocated block. See also above comment. +inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) { + return SlotOffset(capacity, slot_align) + capacity * slot_size; +} + // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface // of policy). @@ -510,7 +669,8 @@ inline size_t GrowthToLowerboundCapacity(size_t growth) { // if they are equal, false if they are not. If two keys compare equal, then // their hash values as defined by Hash MUST be equal. // -// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which +// Allocator: an Allocator +// [https://en.cppreference.com/w/cpp/named_req/Allocator] with which // the storage of the hashtable will be allocated and the elements will be // constructed and destroyed. template <class Policy, class Hash, class Eq, class Alloc> @@ -551,13 +711,6 @@ class raw_hash_set { auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k)); auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k)); - using Layout = absl::container_internal::Layout<ctrl_t, slot_type>; - - static Layout MakeLayout(size_t capacity) { - assert(IsValidCapacity(capacity)); - return Layout(capacity + Group::kWidth + 1, capacity); - } - using AllocTraits = absl::allocator_traits<allocator_type>; using SlotAlloc = typename absl::allocator_traits< allocator_type>::template rebind_alloc<slot_type>; @@ -616,7 +769,7 @@ class raw_hash_set { // PRECONDITION: not an end() iterator. reference operator*() const { - assert_is_full(); + AssertIsFull(ctrl_); return PolicyTraits::element(slot_); } @@ -625,7 +778,7 @@ class raw_hash_set { // PRECONDITION: not an end() iterator. iterator& operator++() { - assert_is_full(); + AssertIsFull(ctrl_); ++ctrl_; ++slot_; skip_empty_or_deleted(); @@ -639,8 +792,8 @@ class raw_hash_set { } friend bool operator==(const iterator& a, const iterator& b) { - a.assert_is_valid(); - b.assert_is_valid(); + AssertIsValid(a.ctrl_); + AssertIsValid(b.ctrl_); return a.ctrl_ == b.ctrl_; } friend bool operator!=(const iterator& a, const iterator& b) { @@ -648,24 +801,19 @@ class raw_hash_set { } private: - iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end() - iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} - - void assert_is_full() const { assert(IsFull(*ctrl_)); } - void assert_is_valid() const { - assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); + iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) { + // This assumption helps the compiler know that any non-end iterator is + // not equal to any end iterator. + ABSL_INTERNAL_ASSUME(ctrl != nullptr); } void skip_empty_or_deleted() { while (IsEmptyOrDeleted(*ctrl_)) { - // ctrl is not necessarily aligned to Group::kWidth. It is also likely - // to read past the space for ctrl bytes and into slots. This is ok - // because ctrl has sizeof() == 1 and slot has sizeof() >= 1 so there - // is no way to read outside the combined slot array. uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); ctrl_ += shift; slot_ += shift; } + if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr; } ctrl_t* ctrl_ = nullptr; @@ -724,10 +872,10 @@ class raw_hash_set { explicit raw_hash_set(size_t bucket_count, const hasher& hash = hasher(), const key_equal& eq = key_equal(), const allocator_type& alloc = allocator_type()) - : ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) { + : ctrl_(EmptyGroup()), + settings_(0, HashtablezInfoHandle(), hash, eq, alloc) { if (bucket_count) { capacity_ = NormalizeCapacity(bucket_count); - reset_growth_left(); initialize_slots(); } } @@ -746,7 +894,8 @@ class raw_hash_set { raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0, const hasher& hash = hasher(), const key_equal& eq = key_equal(), const allocator_type& alloc = allocator_type()) - : raw_hash_set(bucket_count, hash, eq, alloc) { + : raw_hash_set(SelectBucketCountForIterRange(first, last, bucket_count), + hash, eq, alloc) { insert(first, last); } @@ -833,10 +982,11 @@ class raw_hash_set { // than a full `insert`. for (const auto& v : that) { const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v); - auto target = find_first_non_full(hash); - set_ctrl(target.offset, H2(hash)); + auto target = find_first_non_full(ctrl_, hash, capacity_); + SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, + sizeof(slot_type)); emplace_at(target.offset, v); - infoz_.RecordInsert(hash, target.probe_length); + infoz().RecordInsert(hash, target.probe_length); } size_ = that.size(); growth_left() -= that.size(); @@ -850,28 +1000,27 @@ class raw_hash_set { slots_(absl::exchange(that.slots_, nullptr)), size_(absl::exchange(that.size_, 0)), capacity_(absl::exchange(that.capacity_, 0)), - infoz_(absl::exchange(that.infoz_, HashtablezInfoHandle())), // Hash, equality and allocator are copied instead of moved because // `that` must be left valid. If Hash is std::function<Key>, moving it // would create a nullptr functor that cannot be called. - settings_(that.settings_) { - // growth_left was copied above, reset the one from `that`. - that.growth_left() = 0; - } + settings_(absl::exchange(that.growth_left(), 0), + absl::exchange(that.infoz(), HashtablezInfoHandle()), + that.hash_ref(), that.eq_ref(), that.alloc_ref()) {} raw_hash_set(raw_hash_set&& that, const allocator_type& a) : ctrl_(EmptyGroup()), slots_(nullptr), size_(0), capacity_(0), - settings_(0, that.hash_ref(), that.eq_ref(), a) { + settings_(0, HashtablezInfoHandle(), that.hash_ref(), that.eq_ref(), + a) { if (a == that.alloc_ref()) { std::swap(ctrl_, that.ctrl_); std::swap(slots_, that.slots_); std::swap(size_, that.size_); std::swap(capacity_, that.capacity_); std::swap(growth_left(), that.growth_left()); - std::swap(infoz_, that.infoz_); + std::swap(infoz(), that.infoz()); } else { reserve(that.size()); // Note: this will copy elements of dense_set and unordered_set instead of @@ -907,12 +1056,12 @@ class raw_hash_set { it.skip_empty_or_deleted(); return it; } - iterator end() { return {ctrl_ + capacity_}; } + iterator end() { return {}; } const_iterator begin() const { return const_cast<raw_hash_set*>(this)->begin(); } - const_iterator end() const { return const_cast<raw_hash_set*>(this)->end(); } + const_iterator end() const { return {}; } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } @@ -931,6 +1080,8 @@ class raw_hash_set { // past that we simply deallocate the array. if (capacity_ > 127) { destroy_slots(); + + infoz().RecordClearedReservation(); } else if (capacity_) { for (size_t i = 0; i != capacity_; ++i) { if (IsFull(ctrl_[i])) { @@ -938,11 +1089,11 @@ class raw_hash_set { } } size_ = 0; - reset_ctrl(); + ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); reset_growth_left(); } assert(empty()); - infoz_.RecordStorageChanged(0, capacity_); + infoz().RecordStorageChanged(0, capacity_); } // This overload kicks in when the argument is an rvalue of insertable and @@ -1015,7 +1166,7 @@ class raw_hash_set { template <class InputIt> void insert(InputIt first, InputIt last) { - for (; first != last; ++first) insert(*first); + for (; first != last; ++first) emplace(*first); } template <class T, RequiresNotInit<T> = 0, RequiresInsertable<const T&> = 0> @@ -1042,7 +1193,9 @@ class raw_hash_set { } iterator insert(const_iterator, node_type&& node) { - return insert(std::move(node)).first; + auto res = insert(std::move(node)); + node = std::move(res.node); + return res.position; } // This overload kicks in if we can deduce the key from args. This enables us @@ -1171,7 +1324,7 @@ class raw_hash_set { // This overload is necessary because otherwise erase<K>(const K&) would be // a better match if non-const iterator is passed as an argument. void erase(iterator it) { - it.assert_is_full(); + AssertIsFull(it.ctrl_); PolicyTraits::destroy(&alloc_ref(), it.slot_); erase_meta_only(it); } @@ -1205,7 +1358,7 @@ class raw_hash_set { } node_type extract(const_iterator position) { - position.inner_.assert_is_full(); + AssertIsFull(position.inner_.ctrl_); auto node = CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_); erase_meta_only(position); @@ -1222,8 +1375,8 @@ class raw_hash_set { void swap(raw_hash_set& that) noexcept( IsNoThrowSwappable<hasher>() && IsNoThrowSwappable<key_equal>() && - (!AllocTraits::propagate_on_container_swap::value || - IsNoThrowSwappable<allocator_type>())) { + IsNoThrowSwappable<allocator_type>( + typename AllocTraits::propagate_on_container_swap{})) { using std::swap; swap(ctrl_, that.ctrl_); swap(slots_, that.slots_); @@ -1232,32 +1385,43 @@ class raw_hash_set { swap(growth_left(), that.growth_left()); swap(hash_ref(), that.hash_ref()); swap(eq_ref(), that.eq_ref()); - swap(infoz_, that.infoz_); - if (AllocTraits::propagate_on_container_swap::value) { - swap(alloc_ref(), that.alloc_ref()); - } else { - // If the allocators do not compare equal it is officially undefined - // behavior. We choose to do nothing. - } + swap(infoz(), that.infoz()); + SwapAlloc(alloc_ref(), that.alloc_ref(), + typename AllocTraits::propagate_on_container_swap{}); } void rehash(size_t n) { if (n == 0 && capacity_ == 0) return; if (n == 0 && size_ == 0) { destroy_slots(); - infoz_.RecordStorageChanged(0, 0); + infoz().RecordStorageChanged(0, 0); + infoz().RecordClearedReservation(); return; } + // bitor is a faster way of doing `max` here. We will round up to the next // power-of-2-minus-1, so bitor is good enough. auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); // n == 0 unconditionally rehashes as per the standard. if (n == 0 || m > capacity_) { resize(m); + + // This is after resize, to ensure that we have completed the allocation + // and have potentially sampled the hashtable. + infoz().RecordReservation(n); } } - void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); } + void reserve(size_t n) { + if (n > size() + growth_left()) { + size_t m = GrowthToLowerboundCapacity(n); + resize(NormalizeCapacity(m)); + + // This is after resize, to ensure that we have completed the allocation + // and have potentially sampled the hashtable. + infoz().RecordReservation(n); + } + } // Extension API: support for heterogeneous keys. // @@ -1282,7 +1446,8 @@ class raw_hash_set { void prefetch(const key_arg<K>& key) const { (void)key; #if defined(__GNUC__) - auto seq = probe(hash_ref()(key)); + prefetch_heap_block(); + auto seq = probe(ctrl_, hash_ref()(key), capacity_); __builtin_prefetch(static_cast<const void*>(ctrl_ + seq.offset())); __builtin_prefetch(static_cast<const void*>(slots_ + seq.offset())); #endif // __GNUC__ @@ -1297,7 +1462,7 @@ class raw_hash_set { // called heterogeneous key support. template <class K = key_type> iterator find(const key_arg<K>& key, size_t hash) { - auto seq = probe(hash); + auto seq = probe(ctrl_, hash, capacity_); while (true) { Group g{ctrl_ + seq.offset()}; for (int i : g.Match(H2(hash))) { @@ -1308,10 +1473,12 @@ class raw_hash_set { } if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end(); seq.next(); + assert(seq.index() <= capacity_ && "full table!"); } } template <class K = key_type> iterator find(const key_arg<K>& key) { + prefetch_heap_block(); return find(key, hash_ref()(key)); } @@ -1321,6 +1488,7 @@ class raw_hash_set { } template <class K = key_type> const_iterator find(const key_arg<K>& key) const { + prefetch_heap_block(); return find(key, hash_ref()(key)); } @@ -1455,9 +1623,10 @@ class raw_hash_set { static_cast<size_t>(empty_after.TrailingZeros() + empty_before.LeadingZeros()) < Group::kWidth; - set_ctrl(index, was_never_full ? kEmpty : kDeleted); + SetCtrl(index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted, + capacity_, ctrl_, slots_, sizeof(slot_type)); growth_left() += was_never_full; - infoz_.RecordErase(); + infoz().RecordErase(); } void initialize_slots() { @@ -1474,17 +1643,18 @@ class raw_hash_set { // bound more carefully. if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value && slots_ == nullptr) { - infoz_ = Sample(); + infoz() = Sample(sizeof(slot_type)); } - auto layout = MakeLayout(capacity_); - char* mem = static_cast<char*>( - Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize())); - ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem)); - slots_ = layout.template Pointer<1>(mem); - reset_ctrl(); + char* mem = static_cast<char*>(Allocate<alignof(slot_type)>( + &alloc_ref(), + AllocSize(capacity_, sizeof(slot_type), alignof(slot_type)))); + ctrl_ = reinterpret_cast<ctrl_t*>(mem); + slots_ = reinterpret_cast<slot_type*>( + mem + SlotOffset(capacity_, alignof(slot_type))); + ResetCtrl(capacity_, ctrl_, slots_, sizeof(slot_type)); reset_growth_left(); - infoz_.RecordStorageChanged(size_, capacity_); + infoz().RecordStorageChanged(size_, capacity_); } void destroy_slots() { @@ -1494,10 +1664,12 @@ class raw_hash_set { PolicyTraits::destroy(&alloc_ref(), slots_ + i); } } - auto layout = MakeLayout(capacity_); + // Unpoison before returning the memory to the allocator. SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); - Deallocate<Layout::Alignment()>(&alloc_ref(), ctrl_, layout.AllocSize()); + Deallocate<alignof(slot_type)>( + &alloc_ref(), ctrl_, + AllocSize(capacity_, sizeof(slot_type), alignof(slot_type))); ctrl_ = EmptyGroup(); slots_ = nullptr; size_ = 0; @@ -1518,26 +1690,26 @@ class raw_hash_set { if (IsFull(old_ctrl[i])) { size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, PolicyTraits::element(old_slots + i)); - auto target = find_first_non_full(hash); + auto target = find_first_non_full(ctrl_, hash, capacity_); size_t new_i = target.offset; total_probe_length += target.probe_length; - set_ctrl(new_i, H2(hash)); + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); } } if (old_capacity) { SanitizerUnpoisonMemoryRegion(old_slots, sizeof(slot_type) * old_capacity); - auto layout = MakeLayout(old_capacity); - Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl, - layout.AllocSize()); + Deallocate<alignof(slot_type)>( + &alloc_ref(), old_ctrl, + AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type))); } - infoz_.RecordRehash(total_probe_length); + infoz().RecordRehash(total_probe_length); } void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE { assert(IsValidCapacity(capacity_)); - assert(!is_small()); + assert(!is_small(capacity_)); // Algorithm: // - mark all DELETED slots as EMPTY // - mark all FULL slots as DELETED @@ -1560,34 +1732,35 @@ class raw_hash_set { slot_type* slot = reinterpret_cast<slot_type*>(&raw); for (size_t i = 0; i != capacity_; ++i) { if (!IsDeleted(ctrl_[i])) continue; - size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, - PolicyTraits::element(slots_ + i)); - auto target = find_first_non_full(hash); - size_t new_i = target.offset; + const size_t hash = PolicyTraits::apply( + HashElement{hash_ref()}, PolicyTraits::element(slots_ + i)); + const FindInfo target = find_first_non_full(ctrl_, hash, capacity_); + const size_t new_i = target.offset; total_probe_length += target.probe_length; // Verify if the old and new i fall within the same group wrt the hash. // If they do, we don't need to move the object as it falls already in the // best probe we can. - const auto probe_index = [&](size_t pos) { - return ((pos - probe(hash).offset()) & capacity_) / Group::kWidth; + const size_t probe_offset = probe(ctrl_, hash, capacity_).offset(); + const auto probe_index = [probe_offset, this](size_t pos) { + return ((pos - probe_offset) & capacity_) / Group::kWidth; }; // Element doesn't move. if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { - set_ctrl(i, H2(hash)); + SetCtrl(i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); continue; } if (IsEmpty(ctrl_[new_i])) { // Transfer element to the empty spot. - // set_ctrl poisons/unpoisons the slots so we have to call it at the + // SetCtrl poisons/unpoisons the slots so we have to call it at the // right time. - set_ctrl(new_i, H2(hash)); + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); - set_ctrl(i, kEmpty); + SetCtrl(i, ctrl_t::kEmpty, capacity_, ctrl_, slots_, sizeof(slot_type)); } else { assert(IsDeleted(ctrl_[new_i])); - set_ctrl(new_i, H2(hash)); + SetCtrl(new_i, H2(hash), capacity_, ctrl_, slots_, sizeof(slot_type)); // Until we are done rehashing, DELETED marks previously FULL slots. // Swap i and new_i elements. PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i); @@ -1597,14 +1770,56 @@ class raw_hash_set { } } reset_growth_left(); - infoz_.RecordRehash(total_probe_length); + infoz().RecordRehash(total_probe_length); } void rehash_and_grow_if_necessary() { if (capacity_ == 0) { resize(1); - } else if (size() <= CapacityToGrowth(capacity()) / 2) { + } else if (capacity_ > Group::kWidth && + // Do these calcuations in 64-bit to avoid overflow. + size() * uint64_t{32} <= capacity_ * uint64_t{25}) { // Squash DELETED without growing if there is enough capacity. + // + // Rehash in place if the current size is <= 25/32 of capacity_. + // Rationale for such a high factor: 1) drop_deletes_without_resize() is + // faster than resize, and 2) it takes quite a bit of work to add + // tombstones. In the worst case, seems to take approximately 4 + // insert/erase pairs to create a single tombstone and so if we are + // rehashing because of tombstones, we can afford to rehash-in-place as + // long as we are reclaiming at least 1/8 the capacity without doing more + // than 2X the work. (Where "work" is defined to be size() for rehashing + // or rehashing in place, and 1 for an insert or erase.) But rehashing in + // place is faster per operation than inserting or even doubling the size + // of the table, so we actually afford to reclaim even less space from a + // resize-in-place. The decision is to rehash in place if we can reclaim + // at about 1/8th of the usable capacity (specifically 3/28 of the + // capacity) which means that the total cost of rehashing will be a small + // fraction of the total work. + // + // Here is output of an experiment using the BM_CacheInSteadyState + // benchmark running the old case (where we rehash-in-place only if we can + // reclaim at least 7/16*capacity_) vs. this code (which rehashes in place + // if we can recover 3/32*capacity_). + // + // Note that although in the worst-case number of rehashes jumped up from + // 15 to 190, but the number of operations per second is almost the same. + // + // Abridged output of running BM_CacheInSteadyState benchmark from + // raw_hash_set_benchmark. N is the number of insert/erase operations. + // + // | OLD (recover >= 7/16 | NEW (recover >= 3/32) + // size | N/s LoadFactor NRehashes | N/s LoadFactor NRehashes + // 448 | 145284 0.44 18 | 140118 0.44 19 + // 493 | 152546 0.24 11 | 151417 0.48 28 + // 538 | 151439 0.26 11 | 151152 0.53 38 + // 583 | 151765 0.28 11 | 150572 0.57 50 + // 628 | 150241 0.31 11 | 150853 0.61 66 + // 672 | 149602 0.33 12 | 150110 0.66 90 + // 717 | 149998 0.35 12 | 149531 0.70 129 + // 762 | 149836 0.37 13 | 148559 0.74 190 + // 807 | 149736 0.39 14 | 151107 0.39 14 + // 852 | 150204 0.42 15 | 151019 0.42 15 drop_deletes_without_resize(); } else { // Otherwise grow the container. @@ -1614,7 +1829,7 @@ class raw_hash_set { bool has_element(const value_type& elem) const { size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem); - auto seq = probe(hash); + auto seq = probe(ctrl_, hash, capacity_); while (true) { Group g{ctrl_ + seq.offset()}; for (int i : g.Match(H2(hash))) { @@ -1624,46 +1839,11 @@ class raw_hash_set { } if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false; seq.next(); - assert(seq.index() < capacity_ && "full table!"); + assert(seq.index() <= capacity_ && "full table!"); } return false; } - // Probes the raw_hash_set with the probe sequence for hash and returns the - // pointer to the first empty or deleted slot. - // NOTE: this function must work with tables having both kEmpty and kDelete - // in one group. Such tables appears during drop_deletes_without_resize. - // - // This function is very useful when insertions happen and: - // - the input is already a set - // - there are enough slots - // - the element with the hash is not in the table - struct FindInfo { - size_t offset; - size_t probe_length; - }; - FindInfo find_first_non_full(size_t hash) { - auto seq = probe(hash); - while (true) { - Group g{ctrl_ + seq.offset()}; - auto mask = g.MatchEmptyOrDeleted(); - if (mask) { -#if !defined(NDEBUG) - // We want to add entropy even when ASLR is not enabled. - // In debug build we will randomly insert in either the front or back of - // the group. - // TODO(kfm,sbenza): revisit after we do unconditional mixing - if (!is_small() && ShouldInsertBackwards(hash, ctrl_)) { - return {seq.offset(mask.HighestBitSet()), seq.index()}; - } -#endif - return {seq.offset(mask.LowestBitSet()), seq.index()}; - } - assert(seq.index() < capacity_ && "full table!"); - seq.next(); - } - } - // TODO(alkis): Optimize this assuming *this and that don't overlap. raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) { raw_hash_set tmp(std::move(that)); @@ -1679,8 +1859,9 @@ class raw_hash_set { protected: template <class K> std::pair<size_t, bool> find_or_prepare_insert(const K& key) { + prefetch_heap_block(); auto hash = hash_ref()(key); - auto seq = probe(hash); + auto seq = probe(ctrl_, hash, capacity_); while (true) { Group g{ctrl_ + seq.offset()}; for (int i : g.Match(H2(hash))) { @@ -1691,21 +1872,23 @@ class raw_hash_set { } if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break; seq.next(); + assert(seq.index() <= capacity_ && "full table!"); } return {prepare_insert(hash), true}; } size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE { - auto target = find_first_non_full(hash); + auto target = find_first_non_full(ctrl_, hash, capacity_); if (ABSL_PREDICT_FALSE(growth_left() == 0 && !IsDeleted(ctrl_[target.offset]))) { rehash_and_grow_if_necessary(); - target = find_first_non_full(hash); + target = find_first_non_full(ctrl_, hash, capacity_); } ++size_; growth_left() -= IsEmpty(ctrl_[target.offset]); - set_ctrl(target.offset, H2(hash)); - infoz_.RecordInsert(hash, target.probe_length); + SetCtrl(target.offset, H2(hash), capacity_, ctrl_, slots_, + sizeof(slot_type)); + infoz().RecordInsert(hash, target.probe_length); return target.offset; } @@ -1733,84 +1916,54 @@ class raw_hash_set { private: friend struct RawHashSetTestOnlyAccess; - probe_seq<Group::kWidth> probe(size_t hash) const { - return probe_seq<Group::kWidth>(H1(hash, ctrl_), capacity_); - } - - // Reset all ctrl bytes back to kEmpty, except the sentinel. - void reset_ctrl() { - std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth); - ctrl_[capacity_] = kSentinel; - SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); - } - void reset_growth_left() { growth_left() = CapacityToGrowth(capacity()) - size_; } - // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at - // the end too. - void set_ctrl(size_t i, ctrl_t h) { - assert(i < capacity_); - - if (IsFull(h)) { - SanitizerUnpoisonObject(slots_ + i); - } else { - SanitizerPoisonObject(slots_ + i); - } + size_t& growth_left() { return settings_.template get<0>(); } - ctrl_[i] = h; - ctrl_[((i - Group::kWidth) & capacity_) + 1 + - ((Group::kWidth - 1) & capacity_)] = h; + void prefetch_heap_block() const { + // Prefetch the heap-allocated memory region to resolve potential TLB + // misses. This is intended to overlap with execution of calculating the + // hash for a key. +#if defined(__GNUC__) + __builtin_prefetch(static_cast<const void*>(ctrl_), 0, 1); +#endif // __GNUC__ } - size_t& growth_left() { return settings_.template get<0>(); } + HashtablezInfoHandle& infoz() { return settings_.template get<1>(); } - // The representation of the object has two modes: - // - small: For capacities < kWidth-1 - // - large: For the rest. - // - // Differences: - // - In small mode we are able to use the whole capacity. The extra control - // bytes give us at least one "empty" control byte to stop the iteration. - // This is important to make 1 a valid capacity. - // - // - In small mode only the first `capacity()` control bytes after the - // sentinel are valid. The rest contain dummy kEmpty values that do not - // represent a real slot. This is important to take into account on - // find_first_non_full(), where we never try ShouldInsertBackwards() for - // small tables. - bool is_small() const { return capacity_ < Group::kWidth - 1; } - - hasher& hash_ref() { return settings_.template get<1>(); } - const hasher& hash_ref() const { return settings_.template get<1>(); } - key_equal& eq_ref() { return settings_.template get<2>(); } - const key_equal& eq_ref() const { return settings_.template get<2>(); } - allocator_type& alloc_ref() { return settings_.template get<3>(); } + hasher& hash_ref() { return settings_.template get<2>(); } + const hasher& hash_ref() const { return settings_.template get<2>(); } + key_equal& eq_ref() { return settings_.template get<3>(); } + const key_equal& eq_ref() const { return settings_.template get<3>(); } + allocator_type& alloc_ref() { return settings_.template get<4>(); } const allocator_type& alloc_ref() const { - return settings_.template get<3>(); + return settings_.template get<4>(); } // TODO(alkis): Investigate removing some of these fields: // - ctrl/slots can be derived from each other // - size can be moved into the slot array - ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t] - slot_type* slots_ = nullptr; // [capacity * slot_type] - size_t size_ = 0; // number of full slots - size_t capacity_ = 0; // total number of slots - HashtablezInfoHandle infoz_; - absl::container_internal::CompressedTuple<size_t /* growth_left */, hasher, + ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1 + NumClonedBytes()) * ctrl_t] + slot_type* slots_ = nullptr; // [capacity * slot_type] + size_t size_ = 0; // number of full slots + size_t capacity_ = 0; // total number of slots + absl::container_internal::CompressedTuple<size_t /* growth_left */, + HashtablezInfoHandle, hasher, key_equal, allocator_type> - settings_{0, hasher{}, key_equal{}, allocator_type{}}; + settings_{0, HashtablezInfoHandle{}, hasher{}, key_equal{}, + allocator_type{}}; }; // Erases all elements that satisfy the predicate `pred` from the container `c`. template <typename P, typename H, typename E, typename A, typename Predicate> -void EraseIf(Predicate pred, raw_hash_set<P, H, E, A>* c) { +void EraseIf(Predicate& pred, raw_hash_set<P, H, E, A>* c) { for (auto it = c->begin(), last = c->end(); it != last;) { - auto copy_it = it++; - if (pred(*copy_it)) { - c->erase(copy_it); + if (pred(*it)) { + c->erase(it++); + } else { + ++it; } } } @@ -1825,7 +1978,7 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> { const typename Set::key_type& key) { size_t num_probes = 0; size_t hash = set.hash_ref()(key); - auto seq = set.probe(hash); + auto seq = probe(set.ctrl_, hash, set.capacity_); while (true) { container_internal::Group g{set.ctrl_ + seq.offset()}; for (int i : g.Match(container_internal::H2(hash))) { @@ -1845,8 +1998,7 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> { static size_t AllocatedByteSize(const Set& c) { size_t capacity = c.capacity_; if (capacity == 0) return 0; - auto layout = Set::MakeLayout(capacity); - size_t m = layout.AllocSize(); + size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot)); size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr)); if (per_slot != ~size_t{}) { @@ -1864,8 +2016,8 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> { static size_t LowerBoundAllocatedByteSize(size_t size) { size_t capacity = GrowthToLowerboundCapacity(size); if (capacity == 0) return 0; - auto layout = Set::MakeLayout(NormalizeCapacity(capacity)); - size_t m = layout.AllocSize(); + size_t m = + AllocSize(NormalizeCapacity(capacity), sizeof(Slot), alignof(Slot)); size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr)); if (per_slot != ~size_t{}) { m += per_slot * size; diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc index 7ac4b9f7df..e73f53fd63 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc @@ -424,6 +424,81 @@ TEST_F(PropagateOnAll, Swap) { EXPECT_EQ(0, it->num_copies()); } +// This allocator is similar to std::pmr::polymorphic_allocator. +// Note the disabled assignment. +template <class T> +class PAlloc { + template <class> + friend class PAlloc; + + public: + // types + using value_type = T; + + // traits + using propagate_on_container_swap = std::false_type; + + PAlloc() noexcept = default; + explicit PAlloc(size_t id) noexcept : id_(id) {} + PAlloc(const PAlloc&) noexcept = default; + PAlloc& operator=(const PAlloc&) noexcept = delete; + + template <class U> + PAlloc(const PAlloc<U>& that) noexcept : id_(that.id_) {} // NOLINT + + template <class U> + struct rebind { + using other = PAlloc<U>; + }; + + constexpr PAlloc select_on_container_copy_construction() const { return {}; } + + // public member functions + T* allocate(size_t) { return new T; } + void deallocate(T* p, size_t) { delete p; } + + friend bool operator==(const PAlloc& a, const PAlloc& b) { + return a.id_ == b.id_; + } + friend bool operator!=(const PAlloc& a, const PAlloc& b) { return !(a == b); } + + private: + size_t id_ = std::numeric_limits<size_t>::max(); +}; + +// This doesn't compile with GCC 5.4 and 5.5 due to a bug in noexcept handing. +#if !defined(__GNUC__) || __GNUC__ != 5 || (__GNUC_MINOR__ != 4 && \ + __GNUC_MINOR__ != 5) +TEST(NoPropagateOn, Swap) { + using PA = PAlloc<char>; + using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>; + + Table t1(PA{1}), t2(PA{2}); + swap(t1, t2); + EXPECT_EQ(t1.get_allocator(), PA(1)); + EXPECT_EQ(t2.get_allocator(), PA(2)); +} +#endif + +TEST(NoPropagateOn, CopyConstruct) { + using PA = PAlloc<char>; + using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>; + + Table t1(PA{1}), t2(t1); + EXPECT_EQ(t1.get_allocator(), PA(1)); + EXPECT_EQ(t2.get_allocator(), PA()); +} + +TEST(NoPropagateOn, Assignment) { + using PA = PAlloc<char>; + using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>; + + Table t1(PA{1}), t2(PA{2}); + t1 = t2; + EXPECT_EQ(t1.get_allocator(), PA(1)); + EXPECT_EQ(t2.get_allocator(), PA(2)); +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc new file mode 100644 index 0000000000..c886d3ad43 --- /dev/null +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc @@ -0,0 +1,431 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/container/internal/raw_hash_set.h" + +#include <numeric> +#include <random> + +#include "absl/base/internal/raw_logging.h" +#include "absl/container/internal/hash_function_defaults.h" +#include "absl/strings/str_format.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { + +struct RawHashSetTestOnlyAccess { + template <typename C> + static auto GetSlots(const C& c) -> decltype(c.slots_) { + return c.slots_; + } +}; + +namespace { + +struct IntPolicy { + using slot_type = int64_t; + using key_type = int64_t; + using init_type = int64_t; + + static void construct(void*, int64_t* slot, int64_t v) { *slot = v; } + static void destroy(void*, int64_t*) {} + static void transfer(void*, int64_t* new_slot, int64_t* old_slot) { + *new_slot = *old_slot; + } + + static int64_t& element(slot_type* slot) { return *slot; } + + template <class F> + static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) { + return std::forward<F>(f)(x, x); + } +}; + +class StringPolicy { + template <class F, class K, class V, + class = typename std::enable_if< + std::is_convertible<const K&, absl::string_view>::value>::type> + decltype(std::declval<F>()( + std::declval<const absl::string_view&>(), std::piecewise_construct, + std::declval<std::tuple<K>>(), + std::declval<V>())) static apply_impl(F&& f, + std::pair<std::tuple<K>, V> p) { + const absl::string_view& key = std::get<0>(p.first); + return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first), + std::move(p.second)); + } + + public: + struct slot_type { + struct ctor {}; + + template <class... Ts> + slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {} + + std::pair<std::string, std::string> pair; + }; + + using key_type = std::string; + using init_type = std::pair<std::string, std::string>; + + template <class allocator_type, class... Args> + static void construct(allocator_type* alloc, slot_type* slot, Args... args) { + std::allocator_traits<allocator_type>::construct( + *alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...); + } + + template <class allocator_type> + static void destroy(allocator_type* alloc, slot_type* slot) { + std::allocator_traits<allocator_type>::destroy(*alloc, slot); + } + + template <class allocator_type> + static void transfer(allocator_type* alloc, slot_type* new_slot, + slot_type* old_slot) { + construct(alloc, new_slot, std::move(old_slot->pair)); + destroy(alloc, old_slot); + } + + static std::pair<std::string, std::string>& element(slot_type* slot) { + return slot->pair; + } + + template <class F, class... Args> + static auto apply(F&& f, Args&&... args) + -> decltype(apply_impl(std::forward<F>(f), + PairArgs(std::forward<Args>(args)...))) { + return apply_impl(std::forward<F>(f), + PairArgs(std::forward<Args>(args)...)); + } +}; + +struct StringHash : container_internal::hash_default_hash<absl::string_view> { + using is_transparent = void; +}; +struct StringEq : std::equal_to<absl::string_view> { + using is_transparent = void; +}; + +struct StringTable + : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> { + using Base = typename StringTable::raw_hash_set; + StringTable() {} + using Base::Base; +}; + +struct IntTable + : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>, + std::equal_to<int64_t>, std::allocator<int64_t>> { + using Base = typename IntTable::raw_hash_set; + IntTable() {} + using Base::Base; +}; + +struct string_generator { + template <class RNG> + std::string operator()(RNG& rng) const { + std::string res; + res.resize(12); + std::uniform_int_distribution<uint32_t> printable_ascii(0x20, 0x7E); + std::generate(res.begin(), res.end(), [&] { return printable_ascii(rng); }); + return res; + } + + size_t size; +}; + +// Model a cache in steady state. +// +// On a table of size N, keep deleting the LRU entry and add a random one. +void BM_CacheInSteadyState(benchmark::State& state) { + std::random_device rd; + std::mt19937 rng(rd()); + string_generator gen{12}; + StringTable t; + std::deque<std::string> keys; + while (t.size() < state.range(0)) { + auto x = t.emplace(gen(rng), gen(rng)); + if (x.second) keys.push_back(x.first->first); + } + ABSL_RAW_CHECK(state.range(0) >= 10, ""); + while (state.KeepRunning()) { + // Some cache hits. + std::deque<std::string>::const_iterator it; + for (int i = 0; i != 90; ++i) { + if (i % 10 == 0) it = keys.end(); + ::benchmark::DoNotOptimize(t.find(*--it)); + } + // Some cache misses. + for (int i = 0; i != 10; ++i) ::benchmark::DoNotOptimize(t.find(gen(rng))); + ABSL_RAW_CHECK(t.erase(keys.front()), keys.front().c_str()); + keys.pop_front(); + while (true) { + auto x = t.emplace(gen(rng), gen(rng)); + if (x.second) { + keys.push_back(x.first->first); + break; + } + } + } + state.SetItemsProcessed(state.iterations()); + state.SetLabel(absl::StrFormat("load_factor=%.2f", t.load_factor())); +} + +template <typename Benchmark> +void CacheInSteadyStateArgs(Benchmark* bm) { + // The default. + const float max_load_factor = 0.875; + // When the cache is at the steady state, the probe sequence will equal + // capacity if there is no reclamation of deleted slots. Pick a number large + // enough to make the benchmark slow for that case. + const size_t capacity = 1 << 10; + + // Check N data points to cover load factors in [0.4, 0.8). + const size_t kNumPoints = 10; + for (size_t i = 0; i != kNumPoints; ++i) + bm->Arg(std::ceil( + capacity * (max_load_factor + i * max_load_factor / kNumPoints) / 2)); +} +BENCHMARK(BM_CacheInSteadyState)->Apply(CacheInSteadyStateArgs); + +void BM_EndComparison(benchmark::State& state) { + std::random_device rd; + std::mt19937 rng(rd()); + string_generator gen{12}; + StringTable t; + while (t.size() < state.range(0)) { + t.emplace(gen(rng), gen(rng)); + } + + for (auto _ : state) { + for (auto it = t.begin(); it != t.end(); ++it) { + benchmark::DoNotOptimize(it); + benchmark::DoNotOptimize(t); + benchmark::DoNotOptimize(it != t.end()); + } + } +} +BENCHMARK(BM_EndComparison)->Arg(400); + +void BM_CopyCtor(benchmark::State& state) { + std::random_device rd; + std::mt19937 rng(rd()); + IntTable t; + std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{}); + + while (t.size() < state.range(0)) { + t.emplace(dist(rng)); + } + + for (auto _ : state) { + IntTable t2 = t; + benchmark::DoNotOptimize(t2); + } +} +BENCHMARK(BM_CopyCtor)->Range(128, 4096); + +void BM_CopyAssign(benchmark::State& state) { + std::random_device rd; + std::mt19937 rng(rd()); + IntTable t; + std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{}); + while (t.size() < state.range(0)) { + t.emplace(dist(rng)); + } + + IntTable t2; + for (auto _ : state) { + t2 = t; + benchmark::DoNotOptimize(t2); + } +} +BENCHMARK(BM_CopyAssign)->Range(128, 4096); + +void BM_RangeCtor(benchmark::State& state) { + std::random_device rd; + std::mt19937 rng(rd()); + std::uniform_int_distribution<uint64_t> dist(0, ~uint64_t{}); + std::vector<int> values; + const size_t desired_size = state.range(0); + while (values.size() < desired_size) { + values.emplace_back(dist(rng)); + } + + for (auto unused : state) { + IntTable t{values.begin(), values.end()}; + benchmark::DoNotOptimize(t); + } +} +BENCHMARK(BM_RangeCtor)->Range(128, 65536); + +void BM_NoOpReserveIntTable(benchmark::State& state) { + IntTable t; + t.reserve(100000); + for (auto _ : state) { + benchmark::DoNotOptimize(t); + t.reserve(100000); + } +} +BENCHMARK(BM_NoOpReserveIntTable); + +void BM_NoOpReserveStringTable(benchmark::State& state) { + StringTable t; + t.reserve(100000); + for (auto _ : state) { + benchmark::DoNotOptimize(t); + t.reserve(100000); + } +} +BENCHMARK(BM_NoOpReserveStringTable); + +void BM_ReserveIntTable(benchmark::State& state) { + int reserve_size = state.range(0); + for (auto _ : state) { + state.PauseTiming(); + IntTable t; + state.ResumeTiming(); + benchmark::DoNotOptimize(t); + t.reserve(reserve_size); + } +} +BENCHMARK(BM_ReserveIntTable)->Range(128, 4096); + +void BM_ReserveStringTable(benchmark::State& state) { + int reserve_size = state.range(0); + for (auto _ : state) { + state.PauseTiming(); + StringTable t; + state.ResumeTiming(); + benchmark::DoNotOptimize(t); + t.reserve(reserve_size); + } +} +BENCHMARK(BM_ReserveStringTable)->Range(128, 4096); + +// Like std::iota, except that ctrl_t doesn't support operator++. +template <typename CtrlIter> +void Iota(CtrlIter begin, CtrlIter end, int value) { + for (; begin != end; ++begin, ++value) { + *begin = static_cast<ctrl_t>(value); + } +} + +void BM_Group_Match(benchmark::State& state) { + std::array<ctrl_t, Group::kWidth> group; + Iota(group.begin(), group.end(), -4); + Group g{group.data()}; + h2_t h = 1; + for (auto _ : state) { + ::benchmark::DoNotOptimize(h); + ::benchmark::DoNotOptimize(g.Match(h)); + } +} +BENCHMARK(BM_Group_Match); + +void BM_Group_MatchEmpty(benchmark::State& state) { + std::array<ctrl_t, Group::kWidth> group; + Iota(group.begin(), group.end(), -4); + Group g{group.data()}; + for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmpty()); +} +BENCHMARK(BM_Group_MatchEmpty); + +void BM_Group_MatchEmptyOrDeleted(benchmark::State& state) { + std::array<ctrl_t, Group::kWidth> group; + Iota(group.begin(), group.end(), -4); + Group g{group.data()}; + for (auto _ : state) ::benchmark::DoNotOptimize(g.MatchEmptyOrDeleted()); +} +BENCHMARK(BM_Group_MatchEmptyOrDeleted); + +void BM_Group_CountLeadingEmptyOrDeleted(benchmark::State& state) { + std::array<ctrl_t, Group::kWidth> group; + Iota(group.begin(), group.end(), -2); + Group g{group.data()}; + for (auto _ : state) + ::benchmark::DoNotOptimize(g.CountLeadingEmptyOrDeleted()); +} +BENCHMARK(BM_Group_CountLeadingEmptyOrDeleted); + +void BM_Group_MatchFirstEmptyOrDeleted(benchmark::State& state) { + std::array<ctrl_t, Group::kWidth> group; + Iota(group.begin(), group.end(), -2); + Group g{group.data()}; + for (auto _ : state) ::benchmark::DoNotOptimize(*g.MatchEmptyOrDeleted()); +} +BENCHMARK(BM_Group_MatchFirstEmptyOrDeleted); + +void BM_DropDeletes(benchmark::State& state) { + constexpr size_t capacity = (1 << 20) - 1; + std::vector<ctrl_t> ctrl(capacity + 1 + Group::kWidth); + ctrl[capacity] = ctrl_t::kSentinel; + std::vector<ctrl_t> pattern = {ctrl_t::kEmpty, static_cast<ctrl_t>(2), + ctrl_t::kDeleted, static_cast<ctrl_t>(2), + ctrl_t::kEmpty, static_cast<ctrl_t>(1), + ctrl_t::kDeleted}; + for (size_t i = 0; i != capacity; ++i) { + ctrl[i] = pattern[i % pattern.size()]; + } + while (state.KeepRunning()) { + state.PauseTiming(); + std::vector<ctrl_t> ctrl_copy = ctrl; + state.ResumeTiming(); + ConvertDeletedToEmptyAndFullToDeleted(ctrl_copy.data(), capacity); + ::benchmark::DoNotOptimize(ctrl_copy[capacity]); + } +} +BENCHMARK(BM_DropDeletes); + +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl + +// These methods are here to make it easy to examine the assembly for targeted +// parts of the API. +auto CodegenAbslRawHashSetInt64Find(absl::container_internal::IntTable* table, + int64_t key) -> decltype(table->find(key)) { + return table->find(key); +} + +bool CodegenAbslRawHashSetInt64FindNeEnd( + absl::container_internal::IntTable* table, int64_t key) { + return table->find(key) != table->end(); +} + +auto CodegenAbslRawHashSetInt64Insert(absl::container_internal::IntTable* table, + int64_t key) + -> decltype(table->insert(key)) { + return table->insert(key); +} + +bool CodegenAbslRawHashSetInt64Contains( + absl::container_internal::IntTable* table, int64_t key) { + return table->contains(key); +} + +void CodegenAbslRawHashSetInt64Iterate( + absl::container_internal::IntTable* table) { + for (auto x : *table) benchmark::DoNotOptimize(x); +} + +int odr = + (::benchmark::DoNotOptimize(std::make_tuple( + &CodegenAbslRawHashSetInt64Find, &CodegenAbslRawHashSetInt64FindNeEnd, + &CodegenAbslRawHashSetInt64Insert, + &CodegenAbslRawHashSetInt64Contains, + &CodegenAbslRawHashSetInt64Iterate)), + 1); diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_probe_benchmark.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_probe_benchmark.cc new file mode 100644 index 0000000000..7169a2e206 --- /dev/null +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_probe_benchmark.cc @@ -0,0 +1,590 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// Generates probe length statistics for many combinations of key types and key +// distributions, all using the default hash function for swisstable. + +#include <memory> +#include <regex> // NOLINT +#include <vector> + +#include "absl/container/flat_hash_map.h" +#include "absl/container/internal/hash_function_defaults.h" +#include "absl/container/internal/hashtable_debug.h" +#include "absl/container/internal/raw_hash_set.h" +#include "absl/random/distributions.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" + +namespace { + +enum class OutputStyle { kRegular, kBenchmark }; + +// The --benchmark command line flag. +// This is populated from main(). +// When run in "benchmark" mode, we have different output. This allows +// A/B comparisons with tools like `benchy`. +absl::string_view benchmarks; + +OutputStyle output() { + return !benchmarks.empty() ? OutputStyle::kBenchmark : OutputStyle::kRegular; +} + +template <class T> +struct Policy { + using slot_type = T; + using key_type = T; + using init_type = T; + + template <class allocator_type, class Arg> + static void construct(allocator_type* alloc, slot_type* slot, + const Arg& arg) { + std::allocator_traits<allocator_type>::construct(*alloc, slot, arg); + } + + template <class allocator_type> + static void destroy(allocator_type* alloc, slot_type* slot) { + std::allocator_traits<allocator_type>::destroy(*alloc, slot); + } + + static slot_type& element(slot_type* slot) { return *slot; } + + template <class F, class... Args> + static auto apply(F&& f, const slot_type& arg) + -> decltype(std::forward<F>(f)(arg, arg)) { + return std::forward<F>(f)(arg, arg); + } +}; + +absl::BitGen& GlobalBitGen() { + static auto* value = new absl::BitGen; + return *value; +} + +// Keeps a pool of allocations and randomly gives one out. +// This introduces more randomization to the addresses given to swisstable and +// should help smooth out this factor from probe length calculation. +template <class T> +class RandomizedAllocator { + public: + using value_type = T; + + RandomizedAllocator() = default; + template <typename U> + RandomizedAllocator(RandomizedAllocator<U>) {} // NOLINT + + static T* allocate(size_t n) { + auto& pointers = GetPointers(n); + // Fill the pool + while (pointers.size() < kRandomPool) { + pointers.push_back(std::allocator<T>{}.allocate(n)); + } + + // Choose a random one. + size_t i = absl::Uniform<size_t>(GlobalBitGen(), 0, pointers.size()); + T* result = pointers[i]; + pointers[i] = pointers.back(); + pointers.pop_back(); + return result; + } + + static void deallocate(T* p, size_t n) { + // Just put it back on the pool. No need to release the memory. + GetPointers(n).push_back(p); + } + + private: + // We keep at least kRandomPool allocations for each size. + static constexpr size_t kRandomPool = 20; + + static std::vector<T*>& GetPointers(size_t n) { + static auto* m = new absl::flat_hash_map<size_t, std::vector<T*>>(); + return (*m)[n]; + } +}; + +template <class T> +struct DefaultHash { + using type = absl::container_internal::hash_default_hash<T>; +}; + +template <class T> +using DefaultHashT = typename DefaultHash<T>::type; + +template <class T> +struct Table : absl::container_internal::raw_hash_set< + Policy<T>, DefaultHashT<T>, + absl::container_internal::hash_default_eq<T>, + RandomizedAllocator<T>> {}; + +struct LoadSizes { + size_t min_load; + size_t max_load; +}; + +LoadSizes GetMinMaxLoadSizes() { + static const auto sizes = [] { + Table<int> t; + + // First, fill enough to have a good distribution. + constexpr size_t kMinSize = 10000; + while (t.size() < kMinSize) t.insert(t.size()); + + const auto reach_min_load_factor = [&] { + const double lf = t.load_factor(); + while (lf <= t.load_factor()) t.insert(t.size()); + }; + + // Then, insert until we reach min load factor. + reach_min_load_factor(); + const size_t min_load_size = t.size(); + + // Keep going until we hit min load factor again, then go back one. + t.insert(t.size()); + reach_min_load_factor(); + + return LoadSizes{min_load_size, t.size() - 1}; + }(); + return sizes; +} + +struct Ratios { + double min_load; + double avg_load; + double max_load; +}; + +// See absl/container/internal/hashtable_debug.h for details on +// probe length calculation. +template <class ElemFn> +Ratios CollectMeanProbeLengths() { + const auto min_max_sizes = GetMinMaxLoadSizes(); + + ElemFn elem; + using Key = decltype(elem()); + Table<Key> t; + + Ratios result; + while (t.size() < min_max_sizes.min_load) t.insert(elem()); + result.min_load = + absl::container_internal::GetHashtableDebugProbeSummary(t).mean; + + while (t.size() < (min_max_sizes.min_load + min_max_sizes.max_load) / 2) + t.insert(elem()); + result.avg_load = + absl::container_internal::GetHashtableDebugProbeSummary(t).mean; + + while (t.size() < min_max_sizes.max_load) t.insert(elem()); + result.max_load = + absl::container_internal::GetHashtableDebugProbeSummary(t).mean; + + return result; +} + +template <int Align> +uintptr_t PointerForAlignment() { + alignas(Align) static constexpr uintptr_t kInitPointer = 0; + return reinterpret_cast<uintptr_t>(&kInitPointer); +} + +// This incomplete type is used for testing hash of pointers of different +// alignments. +// NOTE: We are generating invalid pointer values on the fly with +// reinterpret_cast. There are not "safely derived" pointers so using them is +// technically UB. It is unlikely to be a problem, though. +template <int Align> +struct Ptr; + +template <int Align> +Ptr<Align>* MakePtr(uintptr_t v) { + if (sizeof(v) == 8) { + constexpr int kCopyBits = 16; + // Ensure high bits are all the same. + v = static_cast<uintptr_t>(static_cast<intptr_t>(v << kCopyBits) >> + kCopyBits); + } + return reinterpret_cast<Ptr<Align>*>(v); +} + +struct IntIdentity { + uint64_t i; + friend bool operator==(IntIdentity a, IntIdentity b) { return a.i == b.i; } + IntIdentity operator++(int) { return IntIdentity{i++}; } +}; + +template <int Align> +struct PtrIdentity { + explicit PtrIdentity(uintptr_t val = PointerForAlignment<Align>()) : i(val) {} + uintptr_t i; + friend bool operator==(PtrIdentity a, PtrIdentity b) { return a.i == b.i; } + PtrIdentity operator++(int) { + PtrIdentity p(i); + i += Align; + return p; + } +}; + +constexpr char kStringFormat[] = "/path/to/file/name-%07d-of-9999999.txt"; + +template <bool small> +struct String { + std::string value; + static std::string Make(uint32_t v) { + return {small ? absl::StrCat(v) : absl::StrFormat(kStringFormat, v)}; + } +}; + +template <> +struct DefaultHash<IntIdentity> { + struct type { + size_t operator()(IntIdentity t) const { return t.i; } + }; +}; + +template <int Align> +struct DefaultHash<PtrIdentity<Align>> { + struct type { + size_t operator()(PtrIdentity<Align> t) const { return t.i; } + }; +}; + +template <class T> +struct Sequential { + T operator()() const { return current++; } + mutable T current{}; +}; + +template <int Align> +struct Sequential<Ptr<Align>*> { + Ptr<Align>* operator()() const { + auto* result = MakePtr<Align>(current); + current += Align; + return result; + } + mutable uintptr_t current = PointerForAlignment<Align>(); +}; + + +template <bool small> +struct Sequential<String<small>> { + std::string operator()() const { return String<small>::Make(current++); } + mutable uint32_t current = 0; +}; + +template <class T, class U> +struct Sequential<std::pair<T, U>> { + mutable Sequential<T> tseq; + mutable Sequential<U> useq; + + using RealT = decltype(tseq()); + using RealU = decltype(useq()); + + mutable std::vector<RealT> ts; + mutable std::vector<RealU> us; + mutable size_t ti = 0, ui = 0; + + std::pair<RealT, RealU> operator()() const { + std::pair<RealT, RealU> value{get_t(), get_u()}; + if (ti == 0) { + ti = ui + 1; + ui = 0; + } else { + --ti; + ++ui; + } + return value; + } + + RealT get_t() const { + while (ti >= ts.size()) ts.push_back(tseq()); + return ts[ti]; + } + + RealU get_u() const { + while (ui >= us.size()) us.push_back(useq()); + return us[ui]; + } +}; + +template <class T, int percent_skip> +struct AlmostSequential { + mutable Sequential<T> current; + + auto operator()() const -> decltype(current()) { + while (absl::Uniform(GlobalBitGen(), 0.0, 1.0) <= percent_skip / 100.) + current(); + return current(); + } +}; + +struct Uniform { + template <typename T> + T operator()(T) const { + return absl::Uniform<T>(absl::IntervalClosed, GlobalBitGen(), T{0}, ~T{0}); + } +}; + +struct Gaussian { + template <typename T> + T operator()(T) const { + double d; + do { + d = absl::Gaussian<double>(GlobalBitGen(), 1e6, 1e4); + } while (d <= 0 || d > std::numeric_limits<T>::max() / 2); + return static_cast<T>(d); + } +}; + +struct Zipf { + template <typename T> + T operator()(T) const { + return absl::Zipf<T>(GlobalBitGen(), std::numeric_limits<T>::max(), 1.6); + } +}; + +template <class T, class Dist> +struct Random { + T operator()() const { return Dist{}(T{}); } +}; + +template <class Dist, int Align> +struct Random<Ptr<Align>*, Dist> { + Ptr<Align>* operator()() const { + return MakePtr<Align>(Random<uintptr_t, Dist>{}() * Align); + } +}; + +template <class Dist> +struct Random<IntIdentity, Dist> { + IntIdentity operator()() const { + return IntIdentity{Random<uint64_t, Dist>{}()}; + } +}; + +template <class Dist, int Align> +struct Random<PtrIdentity<Align>, Dist> { + PtrIdentity<Align> operator()() const { + return PtrIdentity<Align>{Random<uintptr_t, Dist>{}() * Align}; + } +}; + +template <class Dist, bool small> +struct Random<String<small>, Dist> { + std::string operator()() const { + return String<small>::Make(Random<uint32_t, Dist>{}()); + } +}; + +template <class T, class U, class Dist> +struct Random<std::pair<T, U>, Dist> { + auto operator()() const + -> decltype(std::make_pair(Random<T, Dist>{}(), Random<U, Dist>{}())) { + return std::make_pair(Random<T, Dist>{}(), Random<U, Dist>{}()); + } +}; + +template <typename> +std::string Name(); + +std::string Name(uint32_t*) { return "u32"; } +std::string Name(uint64_t*) { return "u64"; } +std::string Name(IntIdentity*) { return "IntIdentity"; } + +template <int Align> +std::string Name(Ptr<Align>**) { + return absl::StrCat("Ptr", Align); +} + +template <int Align> +std::string Name(PtrIdentity<Align>*) { + return absl::StrCat("PtrIdentity", Align); +} + +template <bool small> +std::string Name(String<small>*) { + return small ? "StrS" : "StrL"; +} + +template <class T, class U> +std::string Name(std::pair<T, U>*) { + if (output() == OutputStyle::kBenchmark) + return absl::StrCat("P_", Name<T>(), "_", Name<U>()); + return absl::StrCat("P<", Name<T>(), ",", Name<U>(), ">"); +} + +template <class T> +std::string Name(Sequential<T>*) { + return "Sequential"; +} + +template <class T, int P> +std::string Name(AlmostSequential<T, P>*) { + return absl::StrCat("AlmostSeq_", P); +} + +template <class T> +std::string Name(Random<T, Uniform>*) { + return "UnifRand"; +} + +template <class T> +std::string Name(Random<T, Gaussian>*) { + return "GausRand"; +} + +template <class T> +std::string Name(Random<T, Zipf>*) { + return "ZipfRand"; +} + +template <typename T> +std::string Name() { + return Name(static_cast<T*>(nullptr)); +} + +constexpr int kNameWidth = 15; +constexpr int kDistWidth = 16; + +bool CanRunBenchmark(absl::string_view name) { + static std::regex* const filter = []() -> std::regex* { + return benchmarks.empty() || benchmarks == "all" + ? nullptr + : new std::regex(std::string(benchmarks)); + }(); + return filter == nullptr || std::regex_search(std::string(name), *filter); +} + +struct Result { + std::string name; + std::string dist_name; + Ratios ratios; +}; + +template <typename T, typename Dist> +void RunForTypeAndDistribution(std::vector<Result>& results) { + std::string name = absl::StrCat(Name<T>(), "/", Name<Dist>()); + // We have to check against all three names (min/avg/max) before we run it. + // If any of them is enabled, we run it. + if (!CanRunBenchmark(absl::StrCat(name, "/min")) && + !CanRunBenchmark(absl::StrCat(name, "/avg")) && + !CanRunBenchmark(absl::StrCat(name, "/max"))) { + return; + } + results.push_back({Name<T>(), Name<Dist>(), CollectMeanProbeLengths<Dist>()}); +} + +template <class T> +void RunForType(std::vector<Result>& results) { + RunForTypeAndDistribution<T, Sequential<T>>(results); + RunForTypeAndDistribution<T, AlmostSequential<T, 20>>(results); + RunForTypeAndDistribution<T, AlmostSequential<T, 50>>(results); + RunForTypeAndDistribution<T, Random<T, Uniform>>(results); +#ifdef NDEBUG + // Disable these in non-opt mode because they take too long. + RunForTypeAndDistribution<T, Random<T, Gaussian>>(results); + RunForTypeAndDistribution<T, Random<T, Zipf>>(results); +#endif // NDEBUG +} + +} // namespace + +int main(int argc, char** argv) { + // Parse the benchmark flags. Ignore all of them except the regex pattern. + for (int i = 1; i < argc; ++i) { + absl::string_view arg = argv[i]; + const auto next = [&] { return argv[std::min(i + 1, argc - 1)]; }; + + if (absl::ConsumePrefix(&arg, "--benchmark_filter")) { + if (arg == "") { + // --benchmark_filter X + benchmarks = next(); + } else if (absl::ConsumePrefix(&arg, "=")) { + // --benchmark_filter=X + benchmarks = arg; + } + } + + // Any --benchmark flag turns on the mode. + if (absl::ConsumePrefix(&arg, "--benchmark")) { + if (benchmarks.empty()) benchmarks="all"; + } + } + + std::vector<Result> results; + RunForType<uint64_t>(results); + RunForType<IntIdentity>(results); + RunForType<Ptr<8>*>(results); + RunForType<Ptr<16>*>(results); + RunForType<Ptr<32>*>(results); + RunForType<Ptr<64>*>(results); + RunForType<PtrIdentity<8>>(results); + RunForType<PtrIdentity<16>>(results); + RunForType<PtrIdentity<32>>(results); + RunForType<PtrIdentity<64>>(results); + RunForType<std::pair<uint32_t, uint32_t>>(results); + RunForType<String<true>>(results); + RunForType<String<false>>(results); + RunForType<std::pair<uint64_t, String<true>>>(results); + RunForType<std::pair<String<true>, uint64_t>>(results); + RunForType<std::pair<uint64_t, String<false>>>(results); + RunForType<std::pair<String<false>, uint64_t>>(results); + + switch (output()) { + case OutputStyle::kRegular: + absl::PrintF("%-*s%-*s Min Avg Max\n%s\n", kNameWidth, + "Type", kDistWidth, "Distribution", + std::string(kNameWidth + kDistWidth + 10 * 3, '-')); + for (const auto& result : results) { + absl::PrintF("%-*s%-*s %8.4f %8.4f %8.4f\n", kNameWidth, result.name, + kDistWidth, result.dist_name, result.ratios.min_load, + result.ratios.avg_load, result.ratios.max_load); + } + break; + case OutputStyle::kBenchmark: { + absl::PrintF("{\n"); + absl::PrintF(" \"benchmarks\": [\n"); + absl::string_view comma; + for (const auto& result : results) { + auto print = [&](absl::string_view stat, double Ratios::*val) { + std::string name = + absl::StrCat(result.name, "/", result.dist_name, "/", stat); + // Check the regex again. We might had have enabled only one of the + // stats for the benchmark. + if (!CanRunBenchmark(name)) return; + absl::PrintF(" %s{\n", comma); + absl::PrintF(" \"cpu_time\": %f,\n", 1e9 * result.ratios.*val); + absl::PrintF(" \"real_time\": %f,\n", 1e9 * result.ratios.*val); + absl::PrintF(" \"iterations\": 1,\n"); + absl::PrintF(" \"name\": \"%s\",\n", name); + absl::PrintF(" \"time_unit\": \"ns\"\n"); + absl::PrintF(" }\n"); + comma = ","; + }; + print("min", &Ratios::min_load); + print("avg", &Ratios::avg_load); + print("max", &Ratios::max_load); + } + absl::PrintF(" ],\n"); + absl::PrintF(" \"context\": {\n"); + absl::PrintF(" }\n"); + absl::PrintF("}\n"); + break; + } + } + + return 0; +} diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc index a96ae68ac7..362b3caec3 100644 --- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc +++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set_test.cc @@ -14,6 +14,7 @@ #include "absl/container/internal/raw_hash_set.h" +#include <atomic> #include <cmath> #include <cstdint> #include <deque> @@ -22,10 +23,13 @@ #include <numeric> #include <random> #include <string> +#include <unordered_map> +#include <unordered_set> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/cycleclock.h" #include "absl/base/internal/raw_logging.h" #include "absl/container/internal/container_memory.h" @@ -47,14 +51,16 @@ struct RawHashSetTestOnlyAccess { namespace { -using ::testing::DoubleNear; using ::testing::ElementsAre; +using ::testing::Eq; using ::testing::Ge; using ::testing::Lt; -using ::testing::Optional; using ::testing::Pair; using ::testing::UnorderedElementsAre; +// Convenience function to static cast to ctrl_t. +ctrl_t CtrlT(int i) { return static_cast<ctrl_t>(i); } + TEST(Util, NormalizeCapacity) { EXPECT_EQ(1, NormalizeCapacity(0)); EXPECT_EQ(1, NormalizeCapacity(1)); @@ -74,8 +80,14 @@ TEST(Util, GrowthAndCapacity) { for (size_t growth = 0; growth < 10000; ++growth) { SCOPED_TRACE(growth); size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth)); - // The capacity is large enough for `growth` + // The capacity is large enough for `growth`. EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth)); + // For (capacity+1) < kWidth, growth should equal capacity. + if (capacity + 1 < Group::kWidth) { + EXPECT_THAT(CapacityToGrowth(capacity), Eq(capacity)); + } else { + EXPECT_THAT(CapacityToGrowth(capacity), Lt(capacity)); + } if (growth != 0 && capacity > 1) { // There is no smaller capacity that works. EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth)); @@ -161,15 +173,19 @@ TEST(Group, EmptyGroup) { TEST(Group, Match) { if (Group::kWidth == 16) { - ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, - 7, 5, 3, 1, 1, 1, 1, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), + ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), + CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), + CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; EXPECT_THAT(Group{group}.Match(0), ElementsAre()); EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15)); EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10)); EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9)); EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8)); } else if (Group::kWidth == 8) { - ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), + ctrl_t::kDeleted, CtrlT(2), CtrlT(1), + ctrl_t::kSentinel, CtrlT(1)}; EXPECT_THAT(Group{group}.Match(0), ElementsAre()); EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7)); EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4)); @@ -180,11 +196,15 @@ TEST(Group, Match) { TEST(Group, MatchEmpty) { if (Group::kWidth == 16) { - ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, - 7, 5, 3, 1, 1, 1, 1, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), + ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), + CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), + CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4)); } else if (Group::kWidth == 8) { - ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), + ctrl_t::kDeleted, CtrlT(2), CtrlT(1), + ctrl_t::kSentinel, CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0)); } else { FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; @@ -193,11 +213,15 @@ TEST(Group, MatchEmpty) { TEST(Group, MatchEmptyOrDeleted) { if (Group::kWidth == 16) { - ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, - 7, 5, 3, 1, 1, 1, 1, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3), + ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7), + CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1), + CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4)); } else if (Group::kWidth == 8) { - ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2), + ctrl_t::kDeleted, CtrlT(2), CtrlT(1), + ctrl_t::kSentinel, CtrlT(1)}; EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3)); } else { FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; @@ -208,28 +232,32 @@ TEST(Batch, DropDeletes) { constexpr size_t kCapacity = 63; constexpr size_t kGroupWidth = container_internal::Group::kWidth; std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth); - ctrl[kCapacity] = kSentinel; - std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted}; + ctrl[kCapacity] = ctrl_t::kSentinel; + std::vector<ctrl_t> pattern = { + ctrl_t::kEmpty, CtrlT(2), ctrl_t::kDeleted, CtrlT(2), + ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted}; for (size_t i = 0; i != kCapacity; ++i) { ctrl[i] = pattern[i % pattern.size()]; if (i < kGroupWidth - 1) ctrl[i + kCapacity + 1] = pattern[i % pattern.size()]; } ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity); - ASSERT_EQ(ctrl[kCapacity], kSentinel); - for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) { + ASSERT_EQ(ctrl[kCapacity], ctrl_t::kSentinel); + for (size_t i = 0; i < kCapacity + kGroupWidth; ++i) { ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()]; - if (i == kCapacity) expected = kSentinel; - if (expected == kDeleted) expected = kEmpty; - if (IsFull(expected)) expected = kDeleted; + if (i == kCapacity) expected = ctrl_t::kSentinel; + if (expected == ctrl_t::kDeleted) expected = ctrl_t::kEmpty; + if (IsFull(expected)) expected = ctrl_t::kDeleted; EXPECT_EQ(ctrl[i], expected) - << i << " " << int{pattern[i % pattern.size()]}; + << i << " " << static_cast<int>(pattern[i % pattern.size()]); } } TEST(Group, CountLeadingEmptyOrDeleted) { - const std::vector<ctrl_t> empty_examples = {kEmpty, kDeleted}; - const std::vector<ctrl_t> full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel}; + const std::vector<ctrl_t> empty_examples = {ctrl_t::kEmpty, ctrl_t::kDeleted}; + const std::vector<ctrl_t> full_examples = { + CtrlT(0), CtrlT(1), CtrlT(2), CtrlT(3), + CtrlT(5), CtrlT(9), CtrlT(127), ctrl_t::kSentinel}; for (ctrl_t empty : empty_examples) { std::vector<ctrl_t> e(Group::kWidth, empty); @@ -249,25 +277,44 @@ TEST(Group, CountLeadingEmptyOrDeleted) { } } -struct IntPolicy { - using slot_type = int64_t; - using key_type = int64_t; - using init_type = int64_t; +template <class T> +struct ValuePolicy { + using slot_type = T; + using key_type = T; + using init_type = T; - static void construct(void*, int64_t* slot, int64_t v) { *slot = v; } - static void destroy(void*, int64_t*) {} - static void transfer(void*, int64_t* new_slot, int64_t* old_slot) { - *new_slot = *old_slot; + template <class Allocator, class... Args> + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + absl::allocator_traits<Allocator>::construct(*alloc, slot, + std::forward<Args>(args)...); } - static int64_t& element(slot_type* slot) { return *slot; } + template <class Allocator> + static void destroy(Allocator* alloc, slot_type* slot) { + absl::allocator_traits<Allocator>::destroy(*alloc, slot); + } - template <class F> - static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) { - return std::forward<F>(f)(x, x); + template <class Allocator> + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + construct(alloc, new_slot, std::move(*old_slot)); + destroy(alloc, old_slot); + } + + static T& element(slot_type* slot) { return *slot; } + + template <class F, class... Args> + static decltype(absl::container_internal::DecomposeValue( + std::declval<F>(), std::declval<Args>()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposeValue( + std::forward<F>(f), std::forward<Args>(args)...); } }; +using IntPolicy = ValuePolicy<int64_t>; +using Uint8Policy = ValuePolicy<uint8_t>; + class StringPolicy { template <class F, class K, class V, class = typename std::enable_if< @@ -347,6 +394,13 @@ struct IntTable using Base::Base; }; +struct Uint8Table + : raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>, + std::equal_to<uint8_t>, std::allocator<uint8_t>> { + using Base = typename Uint8Table::raw_hash_set; + using Base::Base; +}; + template <typename T> struct CustomAlloc : std::allocator<T> { CustomAlloc() {} @@ -392,6 +446,13 @@ TEST(Table, EmptyFunctorOptimization) { size_t growth_left; void* infoz; }; + struct MockTableInfozDisabled { + void* ctrl; + void* slots; + size_t size; + size_t capacity; + size_t growth_left; + }; struct StatelessHash { size_t operator()(absl::string_view) const { return 0; } }; @@ -399,17 +460,27 @@ TEST(Table, EmptyFunctorOptimization) { size_t dummy; }; - EXPECT_EQ( - sizeof(MockTable), - sizeof( - raw_hash_set<StringPolicy, StatelessHash, - std::equal_to<absl::string_view>, std::allocator<int>>)); + if (std::is_empty<HashtablezInfoHandle>::value) { + EXPECT_EQ(sizeof(MockTableInfozDisabled), + sizeof(raw_hash_set<StringPolicy, StatelessHash, + std::equal_to<absl::string_view>, + std::allocator<int>>)); - EXPECT_EQ( - sizeof(MockTable) + sizeof(StatefulHash), - sizeof( - raw_hash_set<StringPolicy, StatefulHash, - std::equal_to<absl::string_view>, std::allocator<int>>)); + EXPECT_EQ(sizeof(MockTableInfozDisabled) + sizeof(StatefulHash), + sizeof(raw_hash_set<StringPolicy, StatefulHash, + std::equal_to<absl::string_view>, + std::allocator<int>>)); + } else { + EXPECT_EQ(sizeof(MockTable), + sizeof(raw_hash_set<StringPolicy, StatelessHash, + std::equal_to<absl::string_view>, + std::allocator<int>>)); + + EXPECT_EQ(sizeof(MockTable) + sizeof(StatefulHash), + sizeof(raw_hash_set<StringPolicy, StatefulHash, + std::equal_to<absl::string_view>, + std::allocator<int>>)); + } } TEST(Table, Empty) { @@ -497,6 +568,37 @@ TEST(Table, InsertCollisionAndFindAfterDelete) { EXPECT_TRUE(t.empty()); } +TEST(Table, InsertWithinCapacity) { + IntTable t; + t.reserve(10); + const size_t original_capacity = t.capacity(); + const auto addr = [&](int i) { + return reinterpret_cast<uintptr_t>(&*t.find(i)); + }; + // Inserting an element does not change capacity. + t.insert(0); + EXPECT_THAT(t.capacity(), original_capacity); + const uintptr_t original_addr_0 = addr(0); + // Inserting another element does not rehash. + t.insert(1); + EXPECT_THAT(t.capacity(), original_capacity); + EXPECT_THAT(addr(0), original_addr_0); + // Inserting lots of duplicate elements does not rehash. + for (int i = 0; i < 100; ++i) { + t.insert(i % 10); + } + EXPECT_THAT(t.capacity(), original_capacity); + EXPECT_THAT(addr(0), original_addr_0); + // Inserting a range of duplicate elements does not rehash. + std::vector<int> dup_range; + for (int i = 0; i < 100; ++i) { + dup_range.push_back(i % 10); + } + t.insert(dup_range.begin(), dup_range.end()); + EXPECT_THAT(t.capacity(), original_capacity); + EXPECT_THAT(addr(0), original_addr_0); +} + TEST(Table, LazyEmplace) { StringTable t; bool called = false; @@ -544,28 +646,53 @@ TEST(Table, Contains2) { } int decompose_constructed; +int decompose_copy_constructed; +int decompose_copy_assigned; +int decompose_move_constructed; +int decompose_move_assigned; struct DecomposeType { - DecomposeType(int i) : i(i) { // NOLINT + DecomposeType(int i = 0) : i(i) { // NOLINT ++decompose_constructed; } explicit DecomposeType(const char* d) : DecomposeType(*d) {} + DecomposeType(const DecomposeType& other) : i(other.i) { + ++decompose_copy_constructed; + } + DecomposeType& operator=(const DecomposeType& other) { + ++decompose_copy_assigned; + i = other.i; + return *this; + } + DecomposeType(DecomposeType&& other) : i(other.i) { + ++decompose_move_constructed; + } + DecomposeType& operator=(DecomposeType&& other) { + ++decompose_move_assigned; + i = other.i; + return *this; + } + int i; }; struct DecomposeHash { using is_transparent = void; - size_t operator()(DecomposeType a) const { return a.i; } + size_t operator()(const DecomposeType& a) const { return a.i; } size_t operator()(int a) const { return a; } size_t operator()(const char* a) const { return *a; } }; struct DecomposeEq { using is_transparent = void; - bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; } - bool operator()(DecomposeType a, int b) const { return a.i == b; } - bool operator()(DecomposeType a, const char* b) const { return a.i == *b; } + bool operator()(const DecomposeType& a, const DecomposeType& b) const { + return a.i == b.i; + } + bool operator()(const DecomposeType& a, int b) const { return a.i == b; } + bool operator()(const DecomposeType& a, const char* b) const { + return a.i == *b; + } }; struct DecomposePolicy { @@ -575,9 +702,9 @@ struct DecomposePolicy { template <typename T> static void construct(void*, DecomposeType* slot, T&& v) { - *slot = DecomposeType(std::forward<T>(v)); + ::new (slot) DecomposeType(std::forward<T>(v)); } - static void destroy(void*, DecomposeType*) {} + static void destroy(void*, DecomposeType* slot) { slot->~DecomposeType(); } static DecomposeType& element(slot_type* slot) { return *slot; } template <class F, class T> @@ -592,8 +719,13 @@ void TestDecompose(bool construct_three) { const int one = 1; const char* three_p = "3"; const auto& three = three_p; + const int elem_vector_count = 256; + std::vector<DecomposeType> elem_vector(elem_vector_count, DecomposeType{0}); + std::iota(elem_vector.begin(), elem_vector.end(), 0); - raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>> set1; + using DecomposeSet = + raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>>; + DecomposeSet set1; decompose_constructed = 0; int expected_constructed = 0; @@ -651,20 +783,72 @@ void TestDecompose(bool construct_three) { expected_constructed += construct_three; EXPECT_EQ(expected_constructed, decompose_constructed); } + + decompose_copy_constructed = 0; + decompose_copy_assigned = 0; + decompose_move_constructed = 0; + decompose_move_assigned = 0; + int expected_copy_constructed = 0; + int expected_move_constructed = 0; + { // raw_hash_set(first, last) with random-access iterators + DecomposeSet set2(elem_vector.begin(), elem_vector.end()); + // Expect exactly one copy-constructor call for each element if no + // rehashing is done. + expected_copy_constructed += elem_vector_count; + EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed); + EXPECT_EQ(expected_move_constructed, decompose_move_constructed); + EXPECT_EQ(0, decompose_move_assigned); + EXPECT_EQ(0, decompose_copy_assigned); + } + + { // raw_hash_set(first, last) with forward iterators + std::list<DecomposeType> elem_list(elem_vector.begin(), elem_vector.end()); + expected_copy_constructed = decompose_copy_constructed; + DecomposeSet set2(elem_list.begin(), elem_list.end()); + // Expect exactly N elements copied into set, expect at most 2*N elements + // moving internally for all resizing needed (for a growth factor of 2). + expected_copy_constructed += elem_vector_count; + EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed); + expected_move_constructed += elem_vector_count; + EXPECT_LT(expected_move_constructed, decompose_move_constructed); + expected_move_constructed += elem_vector_count; + EXPECT_GE(expected_move_constructed, decompose_move_constructed); + EXPECT_EQ(0, decompose_move_assigned); + EXPECT_EQ(0, decompose_copy_assigned); + expected_copy_constructed = decompose_copy_constructed; + expected_move_constructed = decompose_move_constructed; + } + + { // insert(first, last) + DecomposeSet set2; + set2.insert(elem_vector.begin(), elem_vector.end()); + // Expect exactly N elements copied into set, expect at most 2*N elements + // moving internally for all resizing needed (for a growth factor of 2). + const int expected_new_elements = elem_vector_count; + const int expected_max_element_moves = 2 * elem_vector_count; + expected_copy_constructed += expected_new_elements; + EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed); + expected_move_constructed += expected_max_element_moves; + EXPECT_GE(expected_move_constructed, decompose_move_constructed); + EXPECT_EQ(0, decompose_move_assigned); + EXPECT_EQ(0, decompose_copy_assigned); + expected_copy_constructed = decompose_copy_constructed; + expected_move_constructed = decompose_move_constructed; + } } TEST(Table, Decompose) { TestDecompose<DecomposeHash, DecomposeEq>(false); struct TransparentHashIntOverload { - size_t operator()(DecomposeType a) const { return a.i; } + size_t operator()(const DecomposeType& a) const { return a.i; } size_t operator()(int a) const { return a; } }; struct TransparentEqIntOverload { - bool operator()(DecomposeType a, DecomposeType b) const { + bool operator()(const DecomposeType& a, const DecomposeType& b) const { return a.i == b.i; } - bool operator()(DecomposeType a, int b) const { return a.i == b; } + bool operator()(const DecomposeType& a, int b) const { return a.i == b; } }; TestDecompose<TransparentHashIntOverload, DecomposeEq>(true); TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true); @@ -706,7 +890,7 @@ TEST(Table, RehashWithNoResize) { const size_t capacity = t.capacity(); // Remove elements from all groups except the first and the last one. - // All elements removed from full groups will be marked as kDeleted. + // All elements removed from full groups will be marked as ctrl_t::kDeleted. const size_t erase_begin = Group::kWidth / 2; const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth; for (size_t i = erase_begin; i < erase_end; ++i) { @@ -846,7 +1030,8 @@ TEST(Table, EraseMaintainsValidIterator) { std::vector<int64_t> CollectBadMergeKeys(size_t N) { static constexpr int kGroupSize = Group::kWidth - 1; - auto topk_range = [](size_t b, size_t e, IntTable* t) -> std::vector<int64_t> { + auto topk_range = [](size_t b, size_t e, + IntTable* t) -> std::vector<int64_t> { for (size_t i = b; i != e; ++i) { t->emplace(i); } @@ -1000,8 +1185,8 @@ using ProbeStatsPerSize = std::map<size_t, ProbeStats>; // 1. Create new table and reserve it to keys.size() * 2 // 2. Insert all keys xored with seed // 3. Collect ProbeStats from final table. -ProbeStats CollectProbeStatsOnKeysXoredWithSeed(const std::vector<int64_t>& keys, - size_t num_iters) { +ProbeStats CollectProbeStatsOnKeysXoredWithSeed( + const std::vector<int64_t>& keys, size_t num_iters) { const size_t reserve_size = keys.size() * 2; ProbeStats stats; @@ -1655,6 +1840,38 @@ TEST(Table, Merge) { EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0"))); } +TEST(Table, IteratorEmplaceConstructibleRequirement) { + struct Value { + explicit Value(absl::string_view view) : value(view) {} + std::string value; + + bool operator==(const Value& other) const { return value == other.value; } + }; + struct H { + size_t operator()(const Value& v) const { + return absl::Hash<std::string>{}(v.value); + } + }; + + struct Table : raw_hash_set<ValuePolicy<Value>, H, std::equal_to<Value>, + std::allocator<Value>> { + using Base = typename Table::raw_hash_set; + using Base::Base; + }; + + std::string input[3]{"A", "B", "C"}; + + Table t(std::begin(input), std::end(input)); + EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"})); + + input[0] = "D"; + input[1] = "E"; + input[2] = "F"; + t.insert(std::begin(input), std::end(input)); + EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"}, + Value{"D"}, Value{"E"}, Value{"F"})); +} + TEST(Nodes, EmptyNodeType) { using node_type = StringTable::node_type; node_type n; @@ -1666,9 +1883,9 @@ TEST(Nodes, EmptyNodeType) { } TEST(Nodes, ExtractInsert) { - constexpr char k0[] = "Very long std::string zero."; - constexpr char k1[] = "Very long std::string one."; - constexpr char k2[] = "Very long std::string two."; + constexpr char k0[] = "Very long string zero."; + constexpr char k1[] = "Very long string one."; + constexpr char k2[] = "Very long string two."; StringTable t = {{k0, ""}, {k1, ""}, {k2, ""}}; EXPECT_THAT(t, UnorderedElementsAre(Pair(k0, ""), Pair(k1, ""), Pair(k2, ""))); @@ -1709,6 +1926,26 @@ TEST(Nodes, ExtractInsert) { EXPECT_FALSE(node); } +TEST(Nodes, HintInsert) { + IntTable t = {1, 2, 3}; + auto node = t.extract(1); + EXPECT_THAT(t, UnorderedElementsAre(2, 3)); + auto it = t.insert(t.begin(), std::move(node)); + EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3)); + EXPECT_EQ(*it, 1); + EXPECT_FALSE(node); + + node = t.extract(2); + EXPECT_THAT(t, UnorderedElementsAre(1, 3)); + // reinsert 2 to make the next insert fail. + t.insert(2); + EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3)); + it = t.insert(t.begin(), std::move(node)); + EXPECT_EQ(*it, 2); + // The node was not emptied by the insert call. + EXPECT_TRUE(node); +} + IntTable MakeSimpleTable(size_t size) { IntTable t; while (t.size() < size) t.insert(t.size()); @@ -1791,39 +2028,81 @@ TEST(TableDeathTest, EraseOfEndAsserts) { IntTable t; // Extra simple "regexp" as regexp support is highly varied across platforms. - constexpr char kDeathMsg[] = "IsFull"; + constexpr char kDeathMsg[] = "Invalid operation on iterator"; EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); } -#if defined(ABSL_HASHTABLEZ_SAMPLE) +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) TEST(RawHashSamplerTest, Sample) { // Enable the feature even if the prod default is off. SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); - auto& sampler = HashtablezSampler::Global(); + auto& sampler = GlobalHashtablezSampler(); size_t start_size = 0; - start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; }); + std::unordered_set<const HashtablezInfo*> preexisting_info; + start_size += sampler.Iterate([&](const HashtablezInfo& info) { + preexisting_info.insert(&info); + ++start_size; + }); std::vector<IntTable> tables; for (int i = 0; i < 1000000; ++i) { tables.emplace_back(); + + const bool do_reserve = (i % 10 > 5); + const bool do_rehash = !do_reserve && (i % 10 > 0); + + if (do_reserve) { + // Don't reserve on all tables. + tables.back().reserve(10 * (i % 10)); + } + tables.back().insert(1); + tables.back().insert(i % 5); + + if (do_rehash) { + // Rehash some other tables. + tables.back().rehash(10 * (i % 10)); + } } size_t end_size = 0; - end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; }); + std::unordered_map<size_t, int> observed_checksums; + std::unordered_map<ssize_t, int> reservations; + end_size += sampler.Iterate([&](const HashtablezInfo& info) { + if (preexisting_info.count(&info) == 0) { + observed_checksums[info.hashes_bitwise_xor.load( + std::memory_order_relaxed)]++; + reservations[info.max_reserve.load(std::memory_order_relaxed)]++; + } + EXPECT_EQ(info.inline_element_size, sizeof(int64_t)); + ++end_size; + }); EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()), 0.01, 0.005); + EXPECT_EQ(observed_checksums.size(), 5); + for (const auto& [_, count] : observed_checksums) { + EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.2, 0.05); + } + + EXPECT_EQ(reservations.size(), 10); + for (const auto& [reservation, count] : reservations) { + EXPECT_GE(reservation, 0); + EXPECT_LT(reservation, 100); + + EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.1, 0.05) + << reservation; + } } -#endif // ABSL_HASHTABLEZ_SAMPLER +#endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) { // Enable the feature even if the prod default is off. SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); - auto& sampler = HashtablezSampler::Global(); + auto& sampler = GlobalHashtablezSampler(); size_t start_size = 0; start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; }); @@ -1839,7 +2118,7 @@ TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) { 0.00, 0.001); } -#ifdef ADDRESS_SANITIZER +#ifdef ABSL_HAVE_ADDRESS_SANITIZER TEST(Sanitizer, PoisoningUnused) { IntTable t; t.reserve(5); @@ -1863,7 +2142,37 @@ TEST(Sanitizer, PoisoningOnErase) { t.erase(0); EXPECT_TRUE(__asan_address_is_poisoned(&v)); } -#endif // ADDRESS_SANITIZER +#endif // ABSL_HAVE_ADDRESS_SANITIZER + +TEST(Table, AlignOne) { + // We previously had a bug in which we were copying a control byte over the + // first slot when alignof(value_type) is 1. We test repeated + // insertions/erases and verify that the behavior is correct. + Uint8Table t; + std::unordered_set<uint8_t> verifier; // NOLINT + + // Do repeated insertions/erases from the table. + for (int64_t i = 0; i < 100000; ++i) { + SCOPED_TRACE(i); + const uint8_t u = (i * -i) & 0xFF; + auto it = t.find(u); + auto verifier_it = verifier.find(u); + if (it == t.end()) { + ASSERT_EQ(verifier_it, verifier.end()); + t.insert(u); + verifier.insert(u); + } else { + ASSERT_NE(verifier_it, verifier.end()); + t.erase(it); + verifier.erase(verifier_it); + } + } + + EXPECT_EQ(t.size(), verifier.size()); + for (uint8_t u : t) { + EXPECT_EQ(verifier.count(u), 1); + } +} } // namespace } // namespace container_internal diff --git a/third_party/abseil-cpp/absl/container/internal/unordered_map_constructor_test.h b/third_party/abseil-cpp/absl/container/internal/unordered_map_constructor_test.h index 76ee95e6ab..c1d20f3c52 100644 --- a/third_party/abseil-cpp/absl/container/internal/unordered_map_constructor_test.h +++ b/third_party/abseil-cpp/absl/container/internal/unordered_map_constructor_test.h @@ -16,6 +16,7 @@ #define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ #include <algorithm> +#include <unordered_map> #include <vector> #include "gmock/gmock.h" @@ -178,7 +179,7 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { A alloc(0); std::vector<T> values; std::generate_n(std::back_inserter(values), 10, - hash_internal::Generator<T>()); + hash_internal::UniqueGenerator<T>()); TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc); EXPECT_EQ(m.hash_function(), hasher); EXPECT_EQ(m.key_eq(), equal); @@ -197,7 +198,7 @@ void InputIteratorBucketAllocTest(std::true_type) { A alloc(0); std::vector<T> values; std::generate_n(std::back_inserter(values), 10, - hash_internal::Generator<T>()); + hash_internal::UniqueGenerator<T>()); TypeParam m(values.begin(), values.end(), 123, alloc); EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); @@ -220,7 +221,7 @@ void InputIteratorBucketHashAllocTest(std::true_type) { A alloc(0); std::vector<T> values; std::generate_n(std::back_inserter(values), 10, - hash_internal::Generator<T>()); + hash_internal::UniqueGenerator<T>()); TypeParam m(values.begin(), values.end(), 123, hasher, alloc); EXPECT_EQ(m.hash_function(), hasher); EXPECT_EQ(m.get_allocator(), alloc); @@ -240,8 +241,9 @@ TYPED_TEST_P(ConstructorTest, CopyConstructor) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator<T> gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam n(m); EXPECT_EQ(m.hash_function(), n.hash_function()); EXPECT_EQ(m.key_eq(), n.key_eq()); @@ -261,8 +263,9 @@ void CopyConstructorAllocTest(std::true_type) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator<T> gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam n(m, A(11)); EXPECT_EQ(m.hash_function(), n.hash_function()); EXPECT_EQ(m.key_eq(), n.key_eq()); @@ -284,8 +287,9 @@ TYPED_TEST_P(ConstructorTest, MoveConstructor) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator<T> gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam t(m); TypeParam n(std::move(t)); EXPECT_EQ(m.hash_function(), n.hash_function()); @@ -306,8 +310,9 @@ void MoveConstructorAllocTest(std::true_type) { H hasher; E equal; A alloc(0); + hash_internal::UniqueGenerator<T> gen; TypeParam m(123, hasher, equal, alloc); - for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()()); + for (size_t i = 0; i != 10; ++i) m.insert(gen()); TypeParam t(m); TypeParam n(std::move(t), A(1)); EXPECT_EQ(m.hash_function(), n.hash_function()); @@ -324,7 +329,7 @@ TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { using T = hash_internal::GeneratedType<TypeParam>; - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -347,7 +352,7 @@ template <typename TypeParam> void InitializerListBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using A = typename TypeParam::allocator_type; - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; A alloc(0); TypeParam m(values, 123, alloc); @@ -370,7 +375,7 @@ void InitializerListBucketHashAllocTest(std::true_type) { using A = typename TypeParam::allocator_type; H hasher; A alloc(0); - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m(values, 123, hasher, alloc); EXPECT_EQ(m.hash_function(), hasher); @@ -391,7 +396,7 @@ TYPED_TEST_P(ConstructorTest, Assignment) { H hasher; E equal; A alloc(0); - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); TypeParam n; n = m; @@ -411,7 +416,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignment) { H hasher; E equal; A alloc(0); - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); TypeParam t(m); TypeParam n; @@ -423,7 +428,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignment) { TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) { using T = hash_internal::GeneratedType<TypeParam>; - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m; m = values; @@ -432,7 +437,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) { TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) { using T = hash_internal::GeneratedType<TypeParam>; - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; TypeParam m({gen(), gen(), gen()}); TypeParam n({gen()}); n = m; @@ -441,7 +446,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) { TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) { using T = hash_internal::GeneratedType<TypeParam>; - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; TypeParam m({gen(), gen(), gen()}); TypeParam t(m); TypeParam n({gen()}); @@ -451,7 +456,7 @@ TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) { TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) { using T = hash_internal::GeneratedType<TypeParam>; - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m; m = values; @@ -460,7 +465,7 @@ TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) { TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { using T = hash_internal::GeneratedType<TypeParam>; - hash_internal::Generator<T> gen; + hash_internal::UniqueGenerator<T> gen; std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; TypeParam m(values); m = *&m; // Avoid -Wself-assign diff --git a/third_party/abseil-cpp/absl/container/internal/unordered_map_modifiers_test.h b/third_party/abseil-cpp/absl/container/internal/unordered_map_modifiers_test.h index b8c513f157..d3543936f7 100644 --- a/third_party/abseil-cpp/absl/container/internal/unordered_map_modifiers_test.h +++ b/third_party/abseil-cpp/absl/container/internal/unordered_map_modifiers_test.h @@ -81,6 +81,38 @@ TYPED_TEST_P(ModifiersTest, InsertRange) { ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); } +TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) { + using T = hash_internal::GeneratedType<TypeParam>; + using V = typename TypeParam::mapped_type; + T val = hash_internal::Generator<T>()(); + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(val); + EXPECT_EQ(m.bucket_count(), original_capacity); + T val2 = {val.first, hash_internal::Generator<V>()()}; + m.insert(val2); + EXPECT_EQ(m.bucket_count(), original_capacity); +} + +TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) { +#if !defined(__GLIBCXX__) + using T = hash_internal::GeneratedType<TypeParam>; + std::vector<T> base_values; + std::generate_n(std::back_inserter(base_values), 10, + hash_internal::Generator<T>()); + std::vector<T> values; + while (values.size() != 100) { + std::copy_n(base_values.begin(), 10, std::back_inserter(values)); + } + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(values.begin(), values.end()); + EXPECT_EQ(m.bucket_count(), original_capacity); +#endif +} + TYPED_TEST_P(ModifiersTest, InsertOrAssign) { #ifdef UNORDERED_MAP_CXX17 using std::get; @@ -266,9 +298,10 @@ TYPED_TEST_P(ModifiersTest, Swap) { // TODO(alkis): Write tests for merge. REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, - InsertRange, InsertOrAssign, InsertOrAssignHint, - Emplace, EmplaceHint, TryEmplace, TryEmplaceHint, - Erase, EraseRange, EraseKey, Swap); + InsertRange, InsertWithinCapacity, + InsertRangeWithinCapacity, InsertOrAssign, + InsertOrAssignHint, Emplace, EmplaceHint, TryEmplace, + TryEmplaceHint, Erase, EraseRange, EraseKey, Swap); template <typename Type> struct is_unique_ptr : std::false_type {}; @@ -286,6 +319,8 @@ class UniquePtrModifiersTest : public ::testing::Test { } }; +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UniquePtrModifiersTest); + TYPED_TEST_SUITE_P(UniquePtrModifiersTest); // Test that we do not move from rvalue arguments if an insertion does not diff --git a/third_party/abseil-cpp/absl/container/internal/unordered_set_modifiers_test.h b/third_party/abseil-cpp/absl/container/internal/unordered_set_modifiers_test.h index 26be58d99f..6e473e45da 100644 --- a/third_party/abseil-cpp/absl/container/internal/unordered_set_modifiers_test.h +++ b/third_party/abseil-cpp/absl/container/internal/unordered_set_modifiers_test.h @@ -74,6 +74,36 @@ TYPED_TEST_P(ModifiersTest, InsertRange) { ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); } +TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) { + using T = hash_internal::GeneratedType<TypeParam>; + T val = hash_internal::Generator<T>()(); + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(val); + EXPECT_EQ(m.bucket_count(), original_capacity); + m.insert(val); + EXPECT_EQ(m.bucket_count(), original_capacity); +} + +TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) { +#if !defined(__GLIBCXX__) + using T = hash_internal::GeneratedType<TypeParam>; + std::vector<T> base_values; + std::generate_n(std::back_inserter(base_values), 10, + hash_internal::Generator<T>()); + std::vector<T> values; + while (values.size() != 100) { + values.insert(values.end(), base_values.begin(), base_values.end()); + } + TypeParam m; + m.reserve(10); + const size_t original_capacity = m.bucket_count(); + m.insert(values.begin(), values.end()); + EXPECT_EQ(m.bucket_count(), original_capacity); +#endif +} + TYPED_TEST_P(ModifiersTest, Emplace) { using T = hash_internal::GeneratedType<TypeParam>; T val = hash_internal::Generator<T>()(); @@ -180,8 +210,9 @@ TYPED_TEST_P(ModifiersTest, Swap) { // TODO(alkis): Write tests for merge. REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, - InsertRange, Emplace, EmplaceHint, Erase, EraseRange, - EraseKey, Swap); + InsertRange, InsertWithinCapacity, + InsertRangeWithinCapacity, Emplace, EmplaceHint, + Erase, EraseRange, EraseKey, Swap); } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/node_hash_map.h b/third_party/abseil-cpp/absl/container/node_hash_map.h index fccea1841c..7a39f6284c 100644 --- a/third_party/abseil-cpp/absl/container/node_hash_map.h +++ b/third_party/abseil-cpp/absl/container/node_hash_map.h @@ -225,7 +225,8 @@ class node_hash_map // // size_type erase(const key_type& key): // - // Erases the element with the matching key, if it exists. + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). using Base::erase; // node_hash_map::insert() @@ -374,6 +375,11 @@ class node_hash_map // key value and returns a node handle owning that extracted data. If the // `node_hash_map` does not contain an element with a matching key, this // function returns an empty node handle. + // + // NOTE: when compiled in an earlier version of C++ than C++17, + // `node_type::key()` returns a const reference to the key instead of a + // mutable reference. We cannot safely return a mutable reference without + // std::launder (which is not available before C++17). using Base::extract; // node_hash_map::merge() @@ -514,12 +520,6 @@ class node_hash_map // // Returns the function used for comparing keys equality. using Base::key_eq; - - ABSL_DEPRECATED("Call `hash_function()` instead.") - typename Base::hasher hash_funct() { return this->hash_function(); } - - ABSL_DEPRECATED("Call `rehash()` instead.") - void resize(typename Base::size_type hint) { this->rehash(hint); } }; // erase_if(node_hash_map<>, Pred) diff --git a/third_party/abseil-cpp/absl/container/node_hash_map_test.cc b/third_party/abseil-cpp/absl/container/node_hash_map_test.cc index 5d74b814b5..8f59a1e4a2 100644 --- a/third_party/abseil-cpp/absl/container/node_hash_map_test.cc +++ b/third_party/abseil-cpp/absl/container/node_hash_map_test.cc @@ -254,6 +254,21 @@ TEST(NodeHashMap, EraseIf) { } } +// This test requires std::launder for mutable key access in node handles. +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 +TEST(NodeHashMap, NodeHandleMutableKeyAccess) { + node_hash_map<std::string, std::string> map; + + map["key1"] = "mapped"; + + auto nh = map.extract(map.begin()); + nh.key().resize(3); + map.insert(std::move(nh)); + + EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped"))); +} +#endif + } // namespace } // namespace container_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/container/node_hash_set.h b/third_party/abseil-cpp/absl/container/node_hash_set.h index ad54b6dccb..93b15f4681 100644 --- a/third_party/abseil-cpp/absl/container/node_hash_set.h +++ b/third_party/abseil-cpp/absl/container/node_hash_set.h @@ -18,7 +18,7 @@ // // An `absl::node_hash_set<T>` is an unordered associative container designed to // be a more efficient replacement for `std::unordered_set`. Like -// `unordered_set`, search, insertion, and deletion of map elements can be done +// `unordered_set`, search, insertion, and deletion of set elements can be done // as an `O(1)` operation. However, `node_hash_set` (and other unordered // associative containers known as the collection of Abseil "Swiss tables") // contain other optimizations that result in both memory and computation @@ -60,7 +60,7 @@ struct NodeHashSetPolicy; // following notable differences: // // * Supports heterogeneous lookup, through `find()`, `operator[]()` and -// `insert()`, provided that the map is provided a compatible heterogeneous +// `insert()`, provided that the set is provided a compatible heterogeneous // hashing function and equality operator. // * Contains a `capacity()` member function indicating the number of element // slots (open, deleted, and empty) within the hash set. @@ -76,13 +76,13 @@ struct NodeHashSetPolicy; // Example: // // // Create a node hash set of three strings -// absl::node_hash_map<std::string, std::string> ducks = +// absl::node_hash_set<std::string> ducks = // {"huey", "dewey", "louie"}; // -// // Insert a new element into the node hash map -// ducks.insert("donald"}; +// // Insert a new element into the node hash set +// ducks.insert("donald"); // -// // Force a rehash of the node hash map +// // Force a rehash of the node hash set // ducks.rehash(0); // // // See if "dewey" is present @@ -100,7 +100,7 @@ class node_hash_set public: // Constructors and Assignment Operators // - // A node_hash_set supports the same overload set as `std::unordered_map` + // A node_hash_set supports the same overload set as `std::unordered_set` // for construction and assignment: // // * Default constructor @@ -167,7 +167,7 @@ class node_hash_set // available within the `node_hash_set`. // // NOTE: this member function is particular to `absl::node_hash_set` and is - // not provided in the `std::unordered_map` API. + // not provided in the `std::unordered_set` API. using Base::capacity; // node_hash_set::empty() @@ -208,7 +208,7 @@ class node_hash_set // `void`. // // NOTE: this return behavior is different than that of STL containers in - // general and `std::unordered_map` in particular. + // general and `std::unordered_set` in particular. // // iterator erase(const_iterator first, const_iterator last): // @@ -217,7 +217,8 @@ class node_hash_set // // size_type erase(const key_type& key): // - // Erases the element with the matching key, if it exists. + // Erases the element with the matching key, if it exists, returning the + // number of elements erased (0 or 1). using Base::erase; // node_hash_set::insert() @@ -313,7 +314,7 @@ class node_hash_set // node_hash_set::merge() // - // Extracts elements from a given `source` flat hash map into this + // Extracts elements from a given `source` node hash set into this // `node_hash_set`. If the destination `node_hash_set` already contains an // element with an equivalent key, that element is not extracted. using Base::merge; @@ -321,15 +322,15 @@ class node_hash_set // node_hash_set::swap(node_hash_set& other) // // Exchanges the contents of this `node_hash_set` with those of the `other` - // flat hash map, avoiding invocation of any move, copy, or swap operations on + // node hash set, avoiding invocation of any move, copy, or swap operations on // individual elements. // // All iterators and references on the `node_hash_set` remain valid, excepting // for the past-the-end iterator, which is invalidated. // - // `swap()` requires that the flat hash set's hashing and key equivalence + // `swap()` requires that the node hash set's hashing and key equivalence // functions be Swappable, and are exchaged using unqualified calls to - // non-member `swap()`. If the map's allocator has + // non-member `swap()`. If the set's allocator has // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value` // set to `true`, the allocators are also exchanged using an unqualified call // to non-member `swap()`; otherwise, the allocators are not swapped. @@ -384,14 +385,14 @@ class node_hash_set // node_hash_set::bucket_count() // // Returns the number of "buckets" within the `node_hash_set`. Note that - // because a flat hash map contains all elements within its internal storage, + // because a node hash set contains all elements within its internal storage, // this value simply equals the current capacity of the `node_hash_set`. using Base::bucket_count; // node_hash_set::load_factor() // // Returns the current load factor of the `node_hash_set` (the average number - // of slots occupied with a value within the hash map). + // of slots occupied with a value within the hash set). using Base::load_factor; // node_hash_set::max_load_factor() @@ -427,12 +428,6 @@ class node_hash_set // // Returns the function used for comparing keys equality. using Base::key_eq; - - ABSL_DEPRECATED("Call `hash_function()` instead.") - typename Base::hasher hash_funct() { return this->hash_function(); } - - ABSL_DEPRECATED("Call `rehash()` instead.") - void resize(typename Base::size_type hint) { this->rehash(hint); } }; // erase_if(node_hash_set<>, Pred) diff --git a/third_party/abseil-cpp/absl/container/sample_element_size_test.cc b/third_party/abseil-cpp/absl/container/sample_element_size_test.cc new file mode 100644 index 0000000000..b23626b409 --- /dev/null +++ b/third_party/abseil-cpp/absl/container/sample_element_size_test.cc @@ -0,0 +1,114 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/container/node_hash_map.h" +#include "absl/container/node_hash_set.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace { + +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) +// Create some tables of type `Table`, then look at all the new +// `HashtablezInfo`s to make sure that the `inline_element_size == +// expected_element_size`. The `inline_element_size` is the amount of memory +// allocated for each slot of a hash table, that is `sizeof(slot_type)`. Add +// the new `HashtablezInfo`s to `preexisting_info`. Store all the new tables +// into `tables`. +template <class Table> +void TestInlineElementSize( + HashtablezSampler& sampler, + // clang-tidy gives a false positive on this declaration. This unordered + // set cannot be flat_hash_set, however, since that would introduce a mutex + // deadlock. + std::unordered_set<const HashtablezInfo*>& preexisting_info, // NOLINT + std::vector<Table>& tables, const typename Table::value_type& elt, + size_t expected_element_size) { + for (int i = 0; i < 10; ++i) { + // We create a new table and must store it somewhere so that when we store + // a pointer to the resulting `HashtablezInfo` into `preexisting_info` + // that we aren't storing a dangling pointer. + tables.emplace_back(); + // We must insert an element to get a hashtablez to instantiate. + tables.back().insert(elt); + } + size_t new_count = 0; + sampler.Iterate([&](const HashtablezInfo& info) { + if (preexisting_info.insert(&info).second) { + EXPECT_EQ(info.inline_element_size, expected_element_size); + ++new_count; + } + }); + // Make sure we actually did get a new hashtablez. + EXPECT_GT(new_count, 0); +} + +struct bigstruct { + char a[1000]; + friend bool operator==(const bigstruct& x, const bigstruct& y) { + return memcmp(x.a, y.a, sizeof(x.a)) == 0; + } + template <typename H> + friend H AbslHashValue(H h, const bigstruct& c) { + return H::combine_contiguous(std::move(h), c.a, sizeof(c.a)); + } +}; +#endif + +TEST(FlatHashMap, SampleElementSize) { +#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) + // Enable sampling even if the prod default is off. + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(1); + + auto& sampler = GlobalHashtablezSampler(); + std::vector<flat_hash_map<int, bigstruct>> flat_map_tables; + std::vector<flat_hash_set<bigstruct>> flat_set_tables; + std::vector<node_hash_map<int, bigstruct>> node_map_tables; + std::vector<node_hash_set<bigstruct>> node_set_tables; + + // It takes thousands of new tables after changing the sampling parameters + // before you actually get some instrumentation. And if you must actually + // put something into those tables. + for (int i = 0; i < 10000; ++i) { + flat_map_tables.emplace_back(); + flat_map_tables.back()[i] = bigstruct{}; + } + + // clang-tidy gives a false positive on this declaration. This unordered set + // cannot be a flat_hash_set, however, since that would introduce a mutex + // deadlock. + std::unordered_set<const HashtablezInfo*> preexisting_info; // NOLINT + sampler.Iterate( + [&](const HashtablezInfo& info) { preexisting_info.insert(&info); }); + TestInlineElementSize(sampler, preexisting_info, flat_map_tables, + {0, bigstruct{}}, sizeof(int) + sizeof(bigstruct)); + TestInlineElementSize(sampler, preexisting_info, node_map_tables, + {0, bigstruct{}}, sizeof(void*)); + TestInlineElementSize(sampler, preexisting_info, flat_set_tables, // + bigstruct{}, sizeof(bigstruct)); + TestInlineElementSize(sampler, preexisting_info, node_set_tables, // + bigstruct{}, sizeof(void*)); +#endif +} + +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake b/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake index 77d4ace8be..942ce90a4d 100644 --- a/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake +++ b/third_party/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake @@ -12,16 +12,16 @@ else() set(ABSL_BUILD_DLL FALSE) endif() -if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64") if (MSVC) set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}") else() set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}") endif() -elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64") - if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64") + if (CMAKE_SIZEOF_VOID_P STREQUAL "8") set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}") - elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") + elseif(CMAKE_SIZEOF_VOID_P STREQUAL "4") set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}") else() message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.") @@ -32,20 +32,18 @@ else() endif() -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}") -elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - # MATCHES so we get both Clang and AppleClang +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # MATCHES so we get both Clang and AppleClang if(MSVC) # clang-cl is half MSVC, half LLVM set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_FLAGS}") - set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}") else() set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}") - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # AppleClang doesn't have lsan # https://developer.apple.com/documentation/code_diagnostics if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) @@ -54,7 +52,7 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") endif() endif() endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}") set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}") @@ -64,11 +62,4 @@ else() set(ABSL_TEST_COPTS "") endif() -if("${CMAKE_CXX_STANDARD}" EQUAL 98) - message(FATAL_ERROR "Abseil requires at least C++11") -elseif(NOT "${CMAKE_CXX_STANDARD}") - message(STATUS "No CMAKE_CXX_STANDARD set, assuming 11") - set(ABSL_CXX_STANDARD 11) -else() - set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}") -endif() +set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}") diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake index 7ef6339be2..a4ab1aa204 100644 --- a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake +++ b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake @@ -5,47 +5,6 @@ list(APPEND ABSL_CLANG_CL_FLAGS "/W3" - "-Wno-c++98-compat-pedantic" - "-Wno-conversion" - "-Wno-covered-switch-default" - "-Wno-deprecated" - "-Wno-disabled-macro-expansion" - "-Wno-double-promotion" - "-Wno-comma" - "-Wno-extra-semi" - "-Wno-extra-semi-stmt" - "-Wno-packed" - "-Wno-padded" - "-Wno-sign-compare" - "-Wno-float-conversion" - "-Wno-float-equal" - "-Wno-format-nonliteral" - "-Wno-gcc-compat" - "-Wno-global-constructors" - "-Wno-exit-time-destructors" - "-Wno-non-modular-include-in-module" - "-Wno-old-style-cast" - "-Wno-range-loop-analysis" - "-Wno-reserved-id-macro" - "-Wno-shorten-64-to-32" - "-Wno-switch-enum" - "-Wno-thread-safety-negative" - "-Wno-unknown-warning-option" - "-Wno-unreachable-code" - "-Wno-unused-macros" - "-Wno-weak-vtables" - "-Wno-zero-as-null-pointer-constant" - "-Wbitfield-enum-conversion" - "-Wbool-conversion" - "-Wconstant-conversion" - "-Wenum-conversion" - "-Wint-conversion" - "-Wliteral-conversion" - "-Wnon-literal-null-conversion" - "-Wnull-conversion" - "-Wobjc-literal-conversion" - "-Wno-sign-conversion" - "-Wstring-conversion" "/DNOMINMAX" "/DWIN32_LEAN_AND_MEAN" "/D_CRT_SECURE_NO_WARNINGS" @@ -78,16 +37,17 @@ list(APPEND ABSL_GCC_FLAGS "-Wextra" "-Wcast-qual" "-Wconversion-null" + "-Wformat-security" "-Wmissing-declarations" "-Woverlength-strings" "-Wpointer-arith" + "-Wundef" "-Wunused-local-typedefs" "-Wunused-result" "-Wvarargs" "-Wvla" "-Wwrite-strings" - "-Wno-missing-field-initializers" - "-Wno-sign-compare" + "-DNOMINMAX" ) list(APPEND ABSL_GCC_TEST_FLAGS @@ -103,48 +63,39 @@ list(APPEND ABSL_GCC_TEST_FLAGS list(APPEND ABSL_LLVM_FLAGS "-Wall" "-Wextra" - "-Weverything" - "-Wno-c++98-compat-pedantic" - "-Wno-conversion" - "-Wno-covered-switch-default" - "-Wno-deprecated" - "-Wno-disabled-macro-expansion" - "-Wno-double-promotion" - "-Wno-comma" - "-Wno-extra-semi" - "-Wno-extra-semi-stmt" - "-Wno-packed" - "-Wno-padded" - "-Wno-sign-compare" + "-Wcast-qual" + "-Wconversion" + "-Wfloat-overflow-conversion" + "-Wfloat-zero-conversion" + "-Wfor-loop-analysis" + "-Wformat-security" + "-Wgnu-redeclared-enum" + "-Winfinite-recursion" + "-Winvalid-constexpr" + "-Wliteral-conversion" + "-Wmissing-declarations" + "-Woverlength-strings" + "-Wpointer-arith" + "-Wself-assign" + "-Wshadow-all" + "-Wstring-conversion" + "-Wtautological-overlap-compare" + "-Wundef" + "-Wuninitialized" + "-Wunreachable-code" + "-Wunused-comparison" + "-Wunused-local-typedefs" + "-Wunused-result" + "-Wvla" + "-Wwrite-strings" "-Wno-float-conversion" - "-Wno-float-equal" - "-Wno-format-nonliteral" - "-Wno-gcc-compat" - "-Wno-global-constructors" - "-Wno-exit-time-destructors" - "-Wno-non-modular-include-in-module" - "-Wno-old-style-cast" - "-Wno-range-loop-analysis" - "-Wno-reserved-id-macro" + "-Wno-implicit-float-conversion" + "-Wno-implicit-int-float-conversion" + "-Wno-implicit-int-conversion" "-Wno-shorten-64-to-32" - "-Wno-switch-enum" - "-Wno-thread-safety-negative" - "-Wno-unknown-warning-option" - "-Wno-unreachable-code" - "-Wno-unused-macros" - "-Wno-weak-vtables" - "-Wno-zero-as-null-pointer-constant" - "-Wbitfield-enum-conversion" - "-Wbool-conversion" - "-Wconstant-conversion" - "-Wenum-conversion" - "-Wint-conversion" - "-Wliteral-conversion" - "-Wnon-literal-null-conversion" - "-Wnull-conversion" - "-Wobjc-literal-conversion" "-Wno-sign-conversion" - "-Wstring-conversion" + "-Wno-unknown-warning-option" + "-DNOMINMAX" ) list(APPEND ABSL_LLVM_TEST_FLAGS diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl index 3cc487845c..a6efc98e11 100644 --- a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl +++ b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl @@ -6,47 +6,6 @@ ABSL_CLANG_CL_FLAGS = [ "/W3", - "-Wno-c++98-compat-pedantic", - "-Wno-conversion", - "-Wno-covered-switch-default", - "-Wno-deprecated", - "-Wno-disabled-macro-expansion", - "-Wno-double-promotion", - "-Wno-comma", - "-Wno-extra-semi", - "-Wno-extra-semi-stmt", - "-Wno-packed", - "-Wno-padded", - "-Wno-sign-compare", - "-Wno-float-conversion", - "-Wno-float-equal", - "-Wno-format-nonliteral", - "-Wno-gcc-compat", - "-Wno-global-constructors", - "-Wno-exit-time-destructors", - "-Wno-non-modular-include-in-module", - "-Wno-old-style-cast", - "-Wno-range-loop-analysis", - "-Wno-reserved-id-macro", - "-Wno-shorten-64-to-32", - "-Wno-switch-enum", - "-Wno-thread-safety-negative", - "-Wno-unknown-warning-option", - "-Wno-unreachable-code", - "-Wno-unused-macros", - "-Wno-weak-vtables", - "-Wno-zero-as-null-pointer-constant", - "-Wbitfield-enum-conversion", - "-Wbool-conversion", - "-Wconstant-conversion", - "-Wenum-conversion", - "-Wint-conversion", - "-Wliteral-conversion", - "-Wnon-literal-null-conversion", - "-Wnull-conversion", - "-Wobjc-literal-conversion", - "-Wno-sign-conversion", - "-Wstring-conversion", "/DNOMINMAX", "/DWIN32_LEAN_AND_MEAN", "/D_CRT_SECURE_NO_WARNINGS", @@ -79,16 +38,17 @@ ABSL_GCC_FLAGS = [ "-Wextra", "-Wcast-qual", "-Wconversion-null", + "-Wformat-security", "-Wmissing-declarations", "-Woverlength-strings", "-Wpointer-arith", + "-Wundef", "-Wunused-local-typedefs", "-Wunused-result", "-Wvarargs", "-Wvla", "-Wwrite-strings", - "-Wno-missing-field-initializers", - "-Wno-sign-compare", + "-DNOMINMAX", ] ABSL_GCC_TEST_FLAGS = [ @@ -104,48 +64,39 @@ ABSL_GCC_TEST_FLAGS = [ ABSL_LLVM_FLAGS = [ "-Wall", "-Wextra", - "-Weverything", - "-Wno-c++98-compat-pedantic", - "-Wno-conversion", - "-Wno-covered-switch-default", - "-Wno-deprecated", - "-Wno-disabled-macro-expansion", - "-Wno-double-promotion", - "-Wno-comma", - "-Wno-extra-semi", - "-Wno-extra-semi-stmt", - "-Wno-packed", - "-Wno-padded", - "-Wno-sign-compare", + "-Wcast-qual", + "-Wconversion", + "-Wfloat-overflow-conversion", + "-Wfloat-zero-conversion", + "-Wfor-loop-analysis", + "-Wformat-security", + "-Wgnu-redeclared-enum", + "-Winfinite-recursion", + "-Winvalid-constexpr", + "-Wliteral-conversion", + "-Wmissing-declarations", + "-Woverlength-strings", + "-Wpointer-arith", + "-Wself-assign", + "-Wshadow-all", + "-Wstring-conversion", + "-Wtautological-overlap-compare", + "-Wundef", + "-Wuninitialized", + "-Wunreachable-code", + "-Wunused-comparison", + "-Wunused-local-typedefs", + "-Wunused-result", + "-Wvla", + "-Wwrite-strings", "-Wno-float-conversion", - "-Wno-float-equal", - "-Wno-format-nonliteral", - "-Wno-gcc-compat", - "-Wno-global-constructors", - "-Wno-exit-time-destructors", - "-Wno-non-modular-include-in-module", - "-Wno-old-style-cast", - "-Wno-range-loop-analysis", - "-Wno-reserved-id-macro", + "-Wno-implicit-float-conversion", + "-Wno-implicit-int-float-conversion", + "-Wno-implicit-int-conversion", "-Wno-shorten-64-to-32", - "-Wno-switch-enum", - "-Wno-thread-safety-negative", - "-Wno-unknown-warning-option", - "-Wno-unreachable-code", - "-Wno-unused-macros", - "-Wno-weak-vtables", - "-Wno-zero-as-null-pointer-constant", - "-Wbitfield-enum-conversion", - "-Wbool-conversion", - "-Wconstant-conversion", - "-Wenum-conversion", - "-Wint-conversion", - "-Wliteral-conversion", - "-Wnon-literal-null-conversion", - "-Wnull-conversion", - "-Wobjc-literal-conversion", "-Wno-sign-conversion", - "-Wstring-conversion", + "-Wno-unknown-warning-option", + "-DNOMINMAX", ] ABSL_LLVM_TEST_FLAGS = [ diff --git a/third_party/abseil-cpp/absl/copts/configure_copts.bzl b/third_party/abseil-cpp/absl/copts/configure_copts.bzl index 9dd6bd0ae4..40d5849a3a 100644 --- a/third_party/abseil-cpp/absl/copts/configure_copts.bzl +++ b/third_party/abseil-cpp/absl/copts/configure_copts.bzl @@ -22,21 +22,21 @@ load( ) ABSL_DEFAULT_COPTS = select({ - "//absl:windows": ABSL_MSVC_FLAGS, - "//absl:llvm_compiler": ABSL_LLVM_FLAGS, + "//absl:msvc_compiler": ABSL_MSVC_FLAGS, + "//absl:clang-cl_compiler": ABSL_CLANG_CL_FLAGS, + "//absl:clang_compiler": ABSL_LLVM_FLAGS, "//conditions:default": ABSL_GCC_FLAGS, }) -# in absence of modules (--compiler=gcc or -c opt), cc_tests leak their copts -# to their (included header) dependencies and fail to build outside absl ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({ - "//absl:windows": ABSL_MSVC_TEST_FLAGS, - "//absl:llvm_compiler": ABSL_LLVM_TEST_FLAGS, + "//absl:msvc_compiler": ABSL_MSVC_TEST_FLAGS, + "//absl:clang-cl_compiler": ABSL_CLANG_CL_TEST_FLAGS, + "//absl:clang_compiler": ABSL_LLVM_TEST_FLAGS, "//conditions:default": ABSL_GCC_TEST_FLAGS, }) ABSL_DEFAULT_LINKOPTS = select({ - "//absl:windows": ABSL_MSVC_LINKOPTS, + "//absl:msvc_compiler": ABSL_MSVC_LINKOPTS, "//conditions:default": [], }) @@ -48,8 +48,9 @@ ABSL_RANDOM_RANDEN_COPTS = select({ ":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS, ":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, - ":cpu_haswell": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_k8": ABSL_RANDOM_HWAES_X64_FLAGS, ":cpu_ppc": ["-mcrypto"], + ":cpu_aarch64": ABSL_RANDOM_HWAES_ARM64_FLAGS, # Supported by default or unsupported. "//conditions:default": [], @@ -65,11 +66,12 @@ def absl_random_randen_copts_init(): # These configs have consistent flags to enable HWAES intsructions. cpu_configs = [ "ppc", - "haswell", + "k8", "darwin_x86_64", "darwin", "x64_windows_msvc", "x64_windows", + "aarch64", ] for cpu in cpu_configs: native.config_setting( diff --git a/third_party/abseil-cpp/absl/copts/copts.py b/third_party/abseil-cpp/absl/copts/copts.py index 704ef23450..0d6c1ec3a6 100644 --- a/third_party/abseil-cpp/absl/copts/copts.py +++ b/third_party/abseil-cpp/absl/copts/copts.py @@ -16,77 +16,6 @@ MSVC_BIG_WARNING_FLAGS = [ "/W3", ] -LLVM_BIG_WARNING_FLAGS = [ - "-Wall", - "-Wextra", - "-Weverything", -] - -# Docs on single flags is preceded by a comment. -# Docs on groups of flags is preceded by ###. -LLVM_DISABLE_WARNINGS_FLAGS = [ - # Abseil does not support C++98 - "-Wno-c++98-compat-pedantic", - # Turns off all implicit conversion warnings. Most are re-enabled below. - "-Wno-conversion", - "-Wno-covered-switch-default", - "-Wno-deprecated", - "-Wno-disabled-macro-expansion", - "-Wno-double-promotion", - ### - # Turned off as they include valid C++ code. - "-Wno-comma", - "-Wno-extra-semi", - "-Wno-extra-semi-stmt", - "-Wno-packed", - "-Wno-padded", - ### - # Google style does not use unsigned integers, though STL containers - # have unsigned types. - "-Wno-sign-compare", - ### - "-Wno-float-conversion", - "-Wno-float-equal", - "-Wno-format-nonliteral", - # Too aggressive: warns on Clang extensions enclosed in Clang-only - # compilation paths. - "-Wno-gcc-compat", - ### - # Some internal globals are necessary. Don't do this at home. - "-Wno-global-constructors", - "-Wno-exit-time-destructors", - ### - "-Wno-non-modular-include-in-module", - "-Wno-old-style-cast", - # Warns on preferred usage of non-POD types such as string_view - "-Wno-range-loop-analysis", - "-Wno-reserved-id-macro", - "-Wno-shorten-64-to-32", - "-Wno-switch-enum", - "-Wno-thread-safety-negative", - "-Wno-unknown-warning-option", - "-Wno-unreachable-code", - # Causes warnings on include guards - "-Wno-unused-macros", - "-Wno-weak-vtables", - # Causes warnings on usage of types/compare.h comparison operators. - "-Wno-zero-as-null-pointer-constant", - ### - # Implicit conversion warnings turned off by -Wno-conversion - # which are re-enabled below. - "-Wbitfield-enum-conversion", - "-Wbool-conversion", - "-Wconstant-conversion", - "-Wenum-conversion", - "-Wint-conversion", - "-Wliteral-conversion", - "-Wnon-literal-null-conversion", - "-Wnull-conversion", - "-Wobjc-literal-conversion", - "-Wno-sign-conversion", - "-Wstring-conversion", -] - LLVM_TEST_DISABLE_WARNINGS_FLAGS = [ "-Wno-c99-extensions", "-Wno-deprecated-declarations", @@ -125,21 +54,18 @@ COPT_VARS = { "-Wextra", "-Wcast-qual", "-Wconversion-null", + "-Wformat-security", "-Wmissing-declarations", "-Woverlength-strings", "-Wpointer-arith", + "-Wundef", "-Wunused-local-typedefs", "-Wunused-result", "-Wvarargs", "-Wvla", # variable-length array "-Wwrite-strings", - # gcc-4.x has spurious missing field initializer warnings. - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750 - # Remove when gcc-4.x is no longer supported. - "-Wno-missing-field-initializers", - # Google style does not use unsigned integers, though STL containers - # have unsigned types. - "-Wno-sign-compare", + # Don't define min and max macros (Build on Windows using gcc) + "-DNOMINMAX", ], "ABSL_GCC_TEST_FLAGS": [ "-Wno-conversion-null", @@ -150,12 +76,52 @@ COPT_VARS = { "-Wno-unused-parameter", "-Wno-unused-private-field", ], - "ABSL_LLVM_FLAGS": - LLVM_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS, + "ABSL_LLVM_FLAGS": [ + "-Wall", + "-Wextra", + "-Wcast-qual", + "-Wconversion", + "-Wfloat-overflow-conversion", + "-Wfloat-zero-conversion", + "-Wfor-loop-analysis", + "-Wformat-security", + "-Wgnu-redeclared-enum", + "-Winfinite-recursion", + "-Winvalid-constexpr", + "-Wliteral-conversion", + "-Wmissing-declarations", + "-Woverlength-strings", + "-Wpointer-arith", + "-Wself-assign", + "-Wshadow-all", + "-Wstring-conversion", + "-Wtautological-overlap-compare", + "-Wundef", + "-Wuninitialized", + "-Wunreachable-code", + "-Wunused-comparison", + "-Wunused-local-typedefs", + "-Wunused-result", + "-Wvla", + "-Wwrite-strings", + # Warnings that are enabled by group warning flags like -Wall that we + # explicitly disable. + "-Wno-float-conversion", + "-Wno-implicit-float-conversion", + "-Wno-implicit-int-float-conversion", + "-Wno-implicit-int-conversion", + "-Wno-shorten-64-to-32", + "-Wno-sign-conversion", + # Disable warnings on unknown warning flags (when warning flags are + # unknown on older compiler versions) + "-Wno-unknown-warning-option", + # Don't define min and max macros (Build on Windows using clang) + "-DNOMINMAX", + ], "ABSL_LLVM_TEST_FLAGS": LLVM_TEST_DISABLE_WARNINGS_FLAGS, "ABSL_CLANG_CL_FLAGS": - (MSVC_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS + MSVC_DEFINES), + (MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES), "ABSL_CLANG_CL_TEST_FLAGS": LLVM_TEST_DISABLE_WARNINGS_FLAGS, "ABSL_MSVC_FLAGS": diff --git a/third_party/abseil-cpp/absl/copts/generate_copts.py b/third_party/abseil-cpp/absl/copts/generate_copts.py index 0e5dc9fad2..34be2fc23b 100644..100755 --- a/third_party/abseil-cpp/absl/copts/generate_copts.py +++ b/third_party/abseil-cpp/absl/copts/generate_copts.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 """Generate Abseil compile compile option configs. Usage: <path_to_absl>/copts/generate_copts.py diff --git a/third_party/abseil-cpp/absl/debugging/BUILD.bazel b/third_party/abseil-cpp/absl/debugging/BUILD.bazel index 8f521bec46..3c4ea8dcdf 100644 --- a/third_party/abseil-cpp/absl/debugging/BUILD.bazel +++ b/third_party/abseil-cpp/absl/debugging/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -26,7 +25,7 @@ package( default_visibility = ["//visibility:public"], ) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "stacktrace", @@ -34,8 +33,10 @@ cc_library( "internal/stacktrace_aarch64-inl.inc", "internal/stacktrace_arm-inl.inc", "internal/stacktrace_config.h", + "internal/stacktrace_emscripten-inl.inc", "internal/stacktrace_generic-inl.inc", "internal/stacktrace_powerpc-inl.inc", + "internal/stacktrace_riscv-inl.inc", "internal/stacktrace_unimplemented-inl.inc", "internal/stacktrace_win32-inl.inc", "internal/stacktrace_x86-inl.inc", @@ -55,7 +56,9 @@ cc_library( name = "symbolize", srcs = [ "symbolize.cc", + "symbolize_darwin.inc", "symbolize_elf.inc", + "symbolize_emscripten.inc", "symbolize_unimplemented.inc", "symbolize_win32.inc", ], @@ -65,7 +68,8 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS + select({ - "//absl:windows": ["-DEFAULTLIB:dbghelp.lib"], + "//absl:msvc_compiler": ["-DEFAULTLIB:dbghelp.lib"], + "//absl:clang-cl_compiler": ["-DEFAULTLIB:dbghelp.lib"], "//conditions:default": [], }), deps = [ @@ -77,6 +81,7 @@ cc_library( "//absl/base:dynamic_annotations", "//absl/base:malloc_internal", "//absl/base:raw_logging_internal", + "//absl/strings", ], ) @@ -84,20 +89,24 @@ cc_test( name = "symbolize_test", srcs = ["symbolize_test.cc"], copts = ABSL_TEST_COPTS + select({ - "//absl:windows": ["/Z7"], + "//absl:msvc_compiler": ["/Z7"], + "//absl:clang-cl_compiler": ["/Z7"], "//conditions:default": [], }), linkopts = ABSL_DEFAULT_LINKOPTS + select({ - "//absl:windows": ["/DEBUG"], + "//absl:msvc_compiler": ["/DEBUG"], + "//absl:clang-cl_compiler": ["/DEBUG"], "//conditions:default": [], }), deps = [ ":stack_consumption", ":symbolize", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/memory", + "//absl/strings", "@com_google_googletest//:gtest", ], ) @@ -144,7 +153,9 @@ cc_test( srcs = ["failure_signal_handler_test.cc"], copts = ABSL_TEST_COPTS, linkopts = select({ - "//absl:windows": [], + "//absl:msvc_compiler": [], + "//absl:clang-cl_compiler": [], + "//absl:wasm": [], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, deps = [ @@ -171,6 +182,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -185,6 +197,7 @@ cc_library( srcs = ["internal/demangle.cc"], hdrs = ["internal/demangle.h"], copts = ABSL_DEFAULT_COPTS, + visibility = ["//visibility:private"], deps = [ "//absl/base", "//absl/base:config", @@ -200,6 +213,7 @@ cc_test( deps = [ ":demangle_internal", ":stack_consumption", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/memory", @@ -233,7 +247,7 @@ cc_library( # These targets exists for use in tests only, explicitly configuring the # LEAK_SANITIZER macro. It must be linked with -fsanitize=leak for lsan. ABSL_LSAN_LINKOPTS = select({ - "//absl:llvm_compiler": ["-fsanitize=leak"], + "//absl:clang_compiler": ["-fsanitize=leak"], "//conditions:default": [], }) @@ -243,13 +257,14 @@ cc_library( srcs = ["leak_check.cc"], hdrs = ["leak_check.h"], copts = select({ - "//absl:llvm_compiler": ["-DLEAK_SANITIZER"], + "//absl:clang_compiler": ["-DLEAK_SANITIZER"], "//conditions:default": [], }), linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ "//absl/base:config", + "//absl/base:core_headers", ], ) @@ -263,6 +278,7 @@ cc_library( visibility = ["//visibility:private"], deps = [ "//absl/base:config", + "//absl/base:core_headers", ], ) @@ -270,7 +286,7 @@ cc_test( name = "leak_check_test", srcs = ["leak_check_test.cc"], copts = select({ - "//absl:llvm_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"], + "//absl:clang_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"], "//conditions:default": [], }), linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS, @@ -332,6 +348,7 @@ cc_test( srcs = ["internal/stack_consumption_test.cc"], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["notsan"], deps = [ ":stack_consumption", "//absl/base:core_headers", diff --git a/third_party/abseil-cpp/absl/debugging/CMakeLists.txt b/third_party/abseil-cpp/absl/debugging/CMakeLists.txt index 7733615939..b16fa007e3 100644 --- a/third_party/abseil-cpp/absl/debugging/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/debugging/CMakeLists.txt @@ -22,8 +22,10 @@ absl_cc_library( "internal/stacktrace_aarch64-inl.inc" "internal/stacktrace_arm-inl.inc" "internal/stacktrace_config.h" + "internal/stacktrace_emscripten-inl.inc" "internal/stacktrace_generic-inl.inc" "internal/stacktrace_powerpc-inl.inc" + "internal/stacktrace_riscv-inl.inc" "internal/stacktrace_unimplemented-inl.inc" "internal/stacktrace_win32-inl.inc" "internal/stacktrace_x86-inl.inc" @@ -46,7 +48,9 @@ absl_cc_library( "internal/symbolize.h" SRCS "symbolize.cc" + "symbolize_darwin.inc" "symbolize_elf.inc" + "symbolize_emscripten.inc" "symbolize_unimplemented.inc" "symbolize_win32.inc" COPTS @@ -63,6 +67,7 @@ absl_cc_library( absl::dynamic_annotations absl::malloc_internal absl::raw_logging_internal + absl::strings PUBLIC ) @@ -80,10 +85,12 @@ absl_cc_test( absl::stack_consumption absl::symbolize absl::base + absl::config absl::core_headers absl::memory absl::raw_logging_internal - gmock + absl::strings + GTest::gmock ) absl_cc_library( @@ -137,7 +144,7 @@ absl_cc_test( absl::strings absl::raw_logging_internal Threads::Threads - gmock + GTest::gmock ) absl_cc_library( @@ -186,10 +193,11 @@ absl_cc_test( DEPS absl::demangle_internal absl::stack_consumption + absl::config absl::core_headers absl::memory absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -256,7 +264,7 @@ absl_cc_test( DEPS absl::leak_check_api_enabled_for_testing absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -270,7 +278,7 @@ absl_cc_test( DEPS absl::leak_check_api_disabled_for_testing absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -287,7 +295,7 @@ absl_cc_test( absl::leak_check_disable absl::base absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -317,7 +325,7 @@ absl_cc_test( absl::stack_consumption absl::core_headers absl::raw_logging_internal - gmock_main + GTest::gmock_main ) # component target diff --git a/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc b/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc index 1f69bfa84d..689e5979e7 100644 --- a/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc +++ b/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc @@ -21,6 +21,7 @@ #ifdef _WIN32 #include <windows.h> #else +#include <sched.h> #include <unistd.h> #endif @@ -135,9 +136,10 @@ static bool SetupAlternateStackOnce() { #else const size_t page_mask = sysconf(_SC_PAGESIZE) - 1; #endif - size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask; -#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ - defined(THREAD_SANITIZER) + size_t stack_size = + (std::max<size_t>(SIGSTKSZ, 65536) + page_mask) & ~page_mask; +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER) // Account for sanitizer instrumentation requiring additional stack space. stack_size *= 5; #endif @@ -219,17 +221,24 @@ static void WriteToStderr(const char* data) { absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data)); } -static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) { - char buf[64]; +static void WriteSignalMessage(int signo, int cpu, + void (*writerfn)(const char*)) { + char buf[96]; + char on_cpu[32] = {0}; + if (cpu != -1) { + snprintf(on_cpu, sizeof(on_cpu), " on cpu %d", cpu); + } const char* const signal_string = debugging_internal::FailureSignalToString(signo); if (signal_string != nullptr && signal_string[0] != '\0') { - snprintf(buf, sizeof(buf), "*** %s received at time=%ld ***\n", + snprintf(buf, sizeof(buf), "*** %s received at time=%ld%s ***\n", signal_string, - static_cast<long>(time(nullptr))); // NOLINT(runtime/int) + static_cast<long>(time(nullptr)), // NOLINT(runtime/int) + on_cpu); } else { - snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld ***\n", - signo, static_cast<long>(time(nullptr))); // NOLINT(runtime/int) + snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld%s ***\n", + signo, static_cast<long>(time(nullptr)), // NOLINT(runtime/int) + on_cpu); } writerfn(buf); } @@ -269,10 +278,10 @@ ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace( // Called by AbslFailureSignalHandler() to write the failure info. It is // called once with writerfn set to WriteToStderr() and then possibly // with writerfn set to the user provided function. -static void WriteFailureInfo(int signo, void* ucontext, +static void WriteFailureInfo(int signo, void* ucontext, int cpu, void (*writerfn)(const char*)) { WriterFnStruct writerfn_struct{writerfn}; - WriteSignalMessage(signo, writerfn); + WriteSignalMessage(signo, cpu, writerfn); WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper, &writerfn_struct); } @@ -334,6 +343,14 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { } } + // Increase the chance that the CPU we report was the same CPU on which the + // signal was received by doing this as early as possible, i.e. after + // verifying that this is not a recursive signal handler invocation. + int my_cpu = -1; +#ifdef ABSL_HAVE_SCHED_GETCPU + my_cpu = sched_getcpu(); +#endif + #ifdef ABSL_HAVE_ALARM // Set an alarm to abort the program in case this code hangs or deadlocks. if (fsh_options.alarm_on_failure_secs > 0) { @@ -344,12 +361,13 @@ static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { #endif // First write to stderr. - WriteFailureInfo(signo, ucontext, WriteToStderr); + WriteFailureInfo(signo, ucontext, my_cpu, WriteToStderr); // Riskier code (because it is less likely to be async-signal-safe) // goes after this point. if (fsh_options.writerfn != nullptr) { - WriteFailureInfo(signo, ucontext, fsh_options.writerfn); + WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn); + fsh_options.writerfn(nullptr); } if (fsh_options.call_previous_handler) { diff --git a/third_party/abseil-cpp/absl/debugging/failure_signal_handler.h b/third_party/abseil-cpp/absl/debugging/failure_signal_handler.h index f5a83962f1..500115c0ab 100644 --- a/third_party/abseil-cpp/absl/debugging/failure_signal_handler.h +++ b/third_party/abseil-cpp/absl/debugging/failure_signal_handler.h @@ -88,9 +88,9 @@ struct FailureSignalHandlerOptions { bool call_previous_handler = false; // If non-null, indicates a pointer to a callback function that will be called - // upon failure, with a std::string argument containing failure data. This function + // upon failure, with a string argument containing failure data. This function // may be used as a hook to write failure data to a secondary location, such - // as a log file. This function may also be called with null data, as a hint + // as a log file. This function will also be called with null data, as a hint // to flush any buffered data before the program may be terminated. Consider // flushing any buffered data in all calls to this function. // diff --git a/third_party/abseil-cpp/absl/debugging/failure_signal_handler_test.cc b/third_party/abseil-cpp/absl/debugging/failure_signal_handler_test.cc index 863fb5149e..6a62428b33 100644 --- a/third_party/abseil-cpp/absl/debugging/failure_signal_handler_test.cc +++ b/third_party/abseil-cpp/absl/debugging/failure_signal_handler_test.cc @@ -55,7 +55,7 @@ TEST_P(FailureSignalHandlerDeathTest, AbslFailureSignal) { exit_regex); #else // Windows doesn't have testing::KilledBySignal(). - EXPECT_DEATH(InstallHandlerAndRaise(signo), exit_regex); + EXPECT_DEATH_IF_SUPPORTED(InstallHandlerAndRaise(signo), exit_regex); #endif } @@ -107,8 +107,8 @@ TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) { testing::KilledBySignal(signo), exit_regex); #else // Windows doesn't have testing::KilledBySignal(). - EXPECT_DEATH(InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo), - exit_regex); + EXPECT_DEATH_IF_SUPPORTED( + InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo), exit_regex); #endif // Open the file in this process and check its contents. @@ -122,6 +122,12 @@ TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) { "*** ", absl::debugging_internal::FailureSignalToString(signo), " received at "))); + // On platforms where it is possible to get the current CPU, the + // CPU number is also logged. Check that it is present in output. +#if defined(__linux__) + EXPECT_THAT(error_line, testing::HasSubstr(" on cpu ")); +#endif + if (absl::debugging_internal::StackTraceWorksForTest()) { std::getline(error_output, error_line); EXPECT_THAT(error_line, StartsWith("PC: ")); diff --git a/third_party/abseil-cpp/absl/debugging/internal/address_is_readable.cc b/third_party/abseil-cpp/absl/debugging/internal/address_is_readable.cc index 6537606366..329c285f3b 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/address_is_readable.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/address_is_readable.cc @@ -68,6 +68,7 @@ static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) { // unimplemented. // This is a namespace-scoped variable for correct zero-initialization. static std::atomic<uint64_t> pid_and_fds; // initially 0, an invalid pid. + bool AddressIsReadable(const void *addr) { absl::base_internal::ErrnoSaver errno_saver; // We test whether a byte is readable by using write(). Normally, this would @@ -86,7 +87,7 @@ bool AddressIsReadable(const void *addr) { int pid; int read_fd; int write_fd; - uint64_t local_pid_and_fds = pid_and_fds.load(std::memory_order_relaxed); + uint64_t local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); while (current_pid != pid) { int p[2]; @@ -98,13 +99,13 @@ bool AddressIsReadable(const void *addr) { fcntl(p[1], F_SETFD, FD_CLOEXEC); uint64_t new_pid_and_fds = Pack(current_pid, p[0], p[1]); if (pid_and_fds.compare_exchange_strong( - local_pid_and_fds, new_pid_and_fds, std::memory_order_relaxed, + local_pid_and_fds, new_pid_and_fds, std::memory_order_release, std::memory_order_relaxed)) { local_pid_and_fds = new_pid_and_fds; // fds exposed to other threads } else { // fds not exposed to other threads; we can close them. close(p[0]); close(p[1]); - local_pid_and_fds = pid_and_fds.load(std::memory_order_relaxed); + local_pid_and_fds = pid_and_fds.load(std::memory_order_acquire); } Unpack(local_pid_and_fds, &pid, &read_fd, &write_fd); } @@ -124,7 +125,7 @@ bool AddressIsReadable(const void *addr) { // If pid_and_fds contains the problematic file descriptors we just used, // this call will forget them, and the loop will try again. pid_and_fds.compare_exchange_strong(local_pid_and_fds, 0, - std::memory_order_relaxed, + std::memory_order_release, std::memory_order_relaxed); } } while (errno == EBADF); diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle.cc index fc615c3f58..93ae32796c 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/demangle.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/demangle.cc @@ -126,6 +126,7 @@ static const AbbrevPair kBuiltinTypeList[] = { {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) {"Di", "char32_t", 0}, + {"Du", "char8_t", 0}, {"Ds", "char16_t", 0}, {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) {nullptr, nullptr, 0}, @@ -151,7 +152,7 @@ static const AbbrevPair kSubstitutionList[] = { // frame, so every byte counts. typedef struct { int mangled_idx; // Cursor of mangled name. - int out_cur_idx; // Cursor of output std::string. + int out_cur_idx; // Cursor of output string. int prev_name_idx; // For constructors/destructors. signed int prev_name_length : 16; // For constructors/destructors. signed int nest_level : 15; // For nested names. @@ -172,8 +173,8 @@ static_assert(sizeof(ParseState) == 4 * sizeof(int), // Only one copy of this exists for each call to Demangle, so the size of this // struct is nearly inconsequential. typedef struct { - const char *mangled_begin; // Beginning of input std::string. - char *out; // Beginning of output std::string. + const char *mangled_begin; // Beginning of input string. + char *out; // Beginning of output string. int out_end_idx; // One past last allowed output character. int recursion_depth; // For stack exhaustion prevention. int steps; // Cap how much work we'll do, regardless of depth. @@ -385,30 +386,35 @@ static bool IsDigit(char c) { return c >= '0' && c <= '9'; } // by GCC 4.5.x and later versions (and our locally-modified version of GCC // 4.4.x) to indicate functions which have been cloned during optimization. // We treat any sequence (.<alpha>+.<digit>+)+ as a function clone suffix. +// Additionally, '_' is allowed along with the alphanumeric sequence. static bool IsFunctionCloneSuffix(const char *str) { size_t i = 0; while (str[i] != '\0') { - // Consume a single .<alpha>+.<digit>+ sequence. - if (str[i] != '.' || !IsAlpha(str[i + 1])) { - return false; + bool parsed = false; + // Consume a single [.<alpha> | _]*[.<digit>]* sequence. + if (str[i] == '.' && (IsAlpha(str[i + 1]) || str[i + 1] == '_')) { + parsed = true; + i += 2; + while (IsAlpha(str[i]) || str[i] == '_') { + ++i; + } } - i += 2; - while (IsAlpha(str[i])) { - ++i; + if (str[i] == '.' && IsDigit(str[i + 1])) { + parsed = true; + i += 2; + while (IsDigit(str[i])) { + ++i; + } } - if (str[i] != '.' || !IsDigit(str[i + 1])) { + if (!parsed) return false; - } - i += 2; - while (IsDigit(str[i])) { - ++i; - } } return true; // Consumed everything in "str". } static bool EndsWith(State *state, const char chr) { return state->parse_state.out_cur_idx > 0 && + state->parse_state.out_cur_idx < state->out_end_idx && chr == state->out[state->parse_state.out_cur_idx - 1]; } @@ -421,8 +427,10 @@ static void MaybeAppendWithLength(State *state, const char *const str, if (str[0] == '<' && EndsWith(state, '<')) { Append(state, " ", 1); } - // Remember the last identifier name for ctors/dtors. - if (IsAlpha(str[0]) || str[0] == '_') { + // Remember the last identifier name for ctors/dtors, + // but only if we haven't yet overflown the buffer. + if (state->parse_state.out_cur_idx < state->out_end_idx && + (IsAlpha(str[0]) || str[0] == '_')) { state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; state->parse_state.prev_name_length = length; } @@ -962,6 +970,7 @@ static bool ParseOperatorName(State *state, int *arity) { // ::= TT <type> // ::= TI <type> // ::= TS <type> +// ::= TH <type> # thread-local // ::= Tc <call-offset> <call-offset> <(base) encoding> // ::= GV <(object) name> // ::= T <call-offset> <(base) encoding> @@ -980,7 +989,7 @@ static bool ParseSpecialName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; - if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTIS") && + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") && ParseType(state)) { return true; } @@ -1077,20 +1086,28 @@ static bool ParseVOffset(State *state) { return false; } -// <ctor-dtor-name> ::= C1 | C2 | C3 +// <ctor-dtor-name> ::= C1 | C2 | C3 | CI1 <base-class-type> | CI2 +// <base-class-type> // ::= D0 | D1 | D2 // # GCC extensions: "unified" constructor/destructor. See -// # https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847 +// # +// https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847 // ::= C4 | D4 static bool ParseCtorDtorName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; - if (ParseOneCharToken(state, 'C') && ParseCharClass(state, "1234")) { - const char *const prev_name = state->out + state->parse_state.prev_name_idx; - MaybeAppendWithLength(state, prev_name, - state->parse_state.prev_name_length); - return true; + if (ParseOneCharToken(state, 'C')) { + if (ParseCharClass(state, "1234")) { + const char *const prev_name = + state->out + state->parse_state.prev_name_idx; + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); + return true; + } else if (ParseOneCharToken(state, 'I') && ParseCharClass(state, "12") && + ParseClassEnumType(state)) { + return true; + } } state->parse_state = copy; @@ -1139,6 +1156,7 @@ static bool ParseDecltype(State *state) { // ::= <decltype> // ::= <substitution> // ::= Dp <type> # pack expansion of (C++0x) +// ::= Dv <num-elems> _ # GNU vector extension // static bool ParseType(State *state) { ComplexityGuard guard(state); @@ -1205,6 +1223,12 @@ static bool ParseType(State *state) { return true; } + if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + return false; } @@ -1253,13 +1277,42 @@ static bool ParseBuiltinType(State *state) { return false; } -// <function-type> ::= F [Y] <bare-function-type> E +// <exception-spec> ::= Do # non-throwing +// exception-specification (e.g., +// noexcept, throw()) +// ::= DO <expression> E # computed (instantiation-dependent) +// noexcept +// ::= Dw <type>+ E # dynamic exception specification +// with instantiation-dependent types +static bool ParseExceptionSpec(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (ParseTwoCharToken(state, "Do")) return true; + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "DO") && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + if (ParseTwoCharToken(state, "Dw") && OneOrMore(ParseType, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <function-type> ::= [exception-spec] F [Y] <bare-function-type> [O] E static bool ParseFunctionType(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; - if (ParseOneCharToken(state, 'F') && + if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') && Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && + Optional(ParseOneCharToken(state, 'O')) && ParseOneCharToken(state, 'E')) { return true; } @@ -1564,6 +1617,7 @@ static bool ParseUnresolvedName(State *state) { // ::= <2-ary operator-name> <expression> <expression> // ::= <3-ary operator-name> <expression> <expression> <expression> // ::= cl <expression>+ E +// ::= cp <simple-id> <expression>* E # Clang-specific. // ::= cv <type> <expression> # type (expression) // ::= cv <type> _ <expression>* E # type (expr-list) // ::= st <type> @@ -1586,14 +1640,23 @@ static bool ParseExpression(State *state) { return true; } - // Object/function call expression. ParseState copy = state->parse_state; + + // Object/function call expression. if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { return true; } state->parse_state = copy; + // Clang-specific "cp <simple-id> <expression>* E" + // https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338 + if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) && + ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + // Function-param expression (level 0). if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { @@ -1887,7 +1950,8 @@ static bool Overflowed(const State *state) { bool Demangle(const char *mangled, char *out, int out_size) { State state; InitState(&state, mangled, out, out_size); - return ParseTopLevelMangledName(&state) && !Overflowed(&state); + return ParseTopLevelMangledName(&state) && !Overflowed(&state) && + state.parse_state.out_cur_idx > 0; } } // namespace debugging_internal diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc index c6f1ce184c..6b142902ca 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc @@ -18,6 +18,7 @@ #include <string> #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/debugging/internal/stack_consumption.h" #include "absl/memory/memory.h" @@ -69,12 +70,34 @@ TEST(Demangle, Clones) { EXPECT_STREQ("Foo()", tmp); EXPECT_TRUE(Demangle("_ZL3Foov.isra.2.constprop.18", tmp, sizeof(tmp))); EXPECT_STREQ("Foo()", tmp); - // Invalid (truncated), should not demangle. - EXPECT_FALSE(Demangle("_ZL3Foov.clo", tmp, sizeof(tmp))); + // Demangle suffixes produced by -funique-internal-linkage-names. + EXPECT_TRUE(Demangle("_ZL3Foov.__uniq.12345", tmp, sizeof(tmp))); + EXPECT_STREQ("Foo()", tmp); + EXPECT_TRUE(Demangle("_ZL3Foov.__uniq.12345.isra.2.constprop.18", tmp, + sizeof(tmp))); + EXPECT_STREQ("Foo()", tmp); + // Suffixes without the number should also demangle. + EXPECT_TRUE(Demangle("_ZL3Foov.clo", tmp, sizeof(tmp))); + EXPECT_STREQ("Foo()", tmp); + // Suffixes with just the number should also demangle. + EXPECT_TRUE(Demangle("_ZL3Foov.123", tmp, sizeof(tmp))); + EXPECT_STREQ("Foo()", tmp); + // (.clone. followed by non-number), should also demangle. + EXPECT_TRUE(Demangle("_ZL3Foov.clone.foo", tmp, sizeof(tmp))); + EXPECT_STREQ("Foo()", tmp); + // (.clone. followed by multiple numbers), should also demangle. + EXPECT_TRUE(Demangle("_ZL3Foov.clone.123.456", tmp, sizeof(tmp))); + EXPECT_STREQ("Foo()", tmp); + // (a long valid suffix), should demangle. + EXPECT_TRUE(Demangle("_ZL3Foov.part.9.165493.constprop.775.31805", tmp, + sizeof(tmp))); + EXPECT_STREQ("Foo()", tmp); + // Invalid (. without anything else), should not demangle. + EXPECT_FALSE(Demangle("_ZL3Foov.", tmp, sizeof(tmp))); + // Invalid (. with mix of alpha and digits), should not demangle. + EXPECT_FALSE(Demangle("_ZL3Foov.abc123", tmp, sizeof(tmp))); // Invalid (.clone. not followed by number), should not demangle. EXPECT_FALSE(Demangle("_ZL3Foov.clone.", tmp, sizeof(tmp))); - // Invalid (.clone. followed by non-number), should not demangle. - EXPECT_FALSE(Demangle("_ZL3Foov.clone.foo", tmp, sizeof(tmp))); // Invalid (.constprop. not followed by number), should not demangle. EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp))); } @@ -82,9 +105,10 @@ TEST(Demangle, Clones) { // Tests that verify that Demangle footprint is within some limit. // They are not to be run under sanitizers as the sanitizers increase // stack consumption by about 4x. -#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \ - !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ - !defined(THREAD_SANITIZER) +#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \ + !defined(ABSL_HAVE_ADDRESS_SANITIZER) && \ + !defined(ABSL_HAVE_MEMORY_SANITIZER) && \ + !defined(ABSL_HAVE_THREAD_SANITIZER) static const char *g_mangled; static char g_demangle_buffer[4096]; diff --git a/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.cc b/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.cc index 24cc01302d..29a281812b 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.cc @@ -22,6 +22,7 @@ #include <string.h> #include <cassert> #include <cstddef> +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" // From binutils/include/elf/common.h (this doesn't appear to be documented @@ -43,11 +44,11 @@ namespace debugging_internal { namespace { -#if __WORDSIZE == 32 +#if __SIZEOF_POINTER__ == 4 const int kElfClass = ELFCLASS32; int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); } int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); } -#elif __WORDSIZE == 64 +#elif __SIZEOF_POINTER__ == 8 const int kElfClass = ELFCLASS64; int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); } int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); } @@ -175,17 +176,17 @@ void ElfMemImage::Init(const void *base) { } switch (base_as_char[EI_DATA]) { case ELFDATA2LSB: { - if (__LITTLE_ENDIAN != __BYTE_ORDER) { - assert(false); - return; - } +#ifndef ABSL_IS_LITTLE_ENDIAN + assert(false); + return; +#endif break; } case ELFDATA2MSB: { - if (__BIG_ENDIAN != __BYTE_ORDER) { - assert(false); - return; - } +#ifndef ABSL_IS_BIG_ENDIAN + assert(false); + return; +#endif break; } default: { @@ -221,7 +222,7 @@ void ElfMemImage::Init(const void *base) { reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr + relocation); for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) { - const ElfW(Xword) value = dynamic_entry->d_un.d_val + relocation; + const auto value = dynamic_entry->d_un.d_val + relocation; switch (dynamic_entry->d_tag) { case DT_HASH: hash_ = reinterpret_cast<ElfW(Word) *>(value); diff --git a/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.h b/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.h index 46bfade350..a894bd423e 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.h +++ b/third_party/abseil-cpp/absl/debugging/internal/elf_mem_image.h @@ -31,8 +31,8 @@ #error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set #endif -#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \ - !defined(__asmjs__) && !defined(__wasm__) +#if defined(__ELF__) && !defined(__native_client__) && !defined(__asmjs__) && \ + !defined(__wasm__) #define ABSL_HAVE_ELF_MEM_IMAGE 1 #endif @@ -40,6 +40,10 @@ #include <link.h> // for ElfW +#if defined(__FreeBSD__) && !defined(ElfW) +#define ElfW(x) __ElfN(x) +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { diff --git a/third_party/abseil-cpp/absl/debugging/internal/examine_stack.cc b/third_party/abseil-cpp/absl/debugging/internal/examine_stack.cc index a3dd893a9d..589a3ef367 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/examine_stack.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/examine_stack.cc @@ -20,6 +20,10 @@ #include <unistd.h> #endif +#ifdef __APPLE__ +#include <sys/ucontext.h> +#endif + #include <csignal> #include <cstdio> @@ -42,30 +46,72 @@ void* GetProgramCounter(void* vuc) { ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc); #if defined(__aarch64__) return reinterpret_cast<void*>(context->uc_mcontext.pc); +#elif defined(__alpha__) + return reinterpret_cast<void*>(context->uc_mcontext.sc_pc); #elif defined(__arm__) return reinterpret_cast<void*>(context->uc_mcontext.arm_pc); +#elif defined(__hppa__) + return reinterpret_cast<void*>(context->uc_mcontext.sc_iaoq[0]); #elif defined(__i386__) if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs)) return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]); +#elif defined(__ia64__) + return reinterpret_cast<void*>(context->uc_mcontext.sc_ip); +#elif defined(__m68k__) + return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]); #elif defined(__mips__) return reinterpret_cast<void*>(context->uc_mcontext.pc); #elif defined(__powerpc64__) return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]); #elif defined(__powerpc__) - return reinterpret_cast<void*>(context->uc_mcontext.regs->nip); + return reinterpret_cast<void*>(context->uc_mcontext.uc_regs->gregs[32]); #elif defined(__riscv) return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]); #elif defined(__s390__) && !defined(__s390x__) return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff); #elif defined(__s390__) && defined(__s390x__) return reinterpret_cast<void*>(context->uc_mcontext.psw.addr); +#elif defined(__sh__) + return reinterpret_cast<void*>(context->uc_mcontext.pc); +#elif defined(__sparc__) && !defined(__arch64__) + return reinterpret_cast<void*>(context->uc_mcontext.gregs[19]); +#elif defined(__sparc__) && defined(__arch64__) + return reinterpret_cast<void*>(context->uc_mcontext.mc_gregs[19]); #elif defined(__x86_64__) if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs)) return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]); +#elif defined(__e2k__) + return reinterpret_cast<void*>(context->uc_mcontext.cr0_hi); #else #error "Undefined Architecture." #endif } +#elif defined(__APPLE__) + if (vuc != nullptr) { + ucontext_t* signal_ucontext = reinterpret_cast<ucontext_t*>(vuc); +#if defined(__aarch64__) + return reinterpret_cast<void*>( + __darwin_arm_thread_state64_get_pc(signal_ucontext->uc_mcontext->__ss)); +#elif defined(__arm__) +#if __DARWIN_UNIX03 + return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__pc); +#else + return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.pc); +#endif +#elif defined(__i386__) +#if __DARWIN_UNIX03 + return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__eip); +#else + return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.eip); +#endif +#elif defined(__x86_64__) +#if __DARWIN_UNIX03 + return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__rip); +#else + return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.rip); +#endif +#endif + } #elif defined(__akaros__) auto* ctx = reinterpret_cast<struct user_context*>(vuc); return reinterpret_cast<void*>(get_user_ctx_pc(ctx)); diff --git a/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.cc b/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.cc index 875ca6d91f..513486498a 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.cc @@ -42,7 +42,8 @@ namespace { // one of them is null, the results of p<q, p>q, p<=q, and p>=q are // unspecified. Therefore, instead we hardcode the direction of the // stack on platforms we know about. -#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) +#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \ + defined(__aarch64__) || defined(__riscv) constexpr bool kStackGrowsDown = true; #else #error Need to define kStackGrowsDown diff --git a/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.h b/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.h index 5e60ec4229..f41b64c39d 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.h +++ b/third_party/abseil-cpp/absl/debugging/internal/stack_consumption.h @@ -24,8 +24,9 @@ // Use this feature test macro to detect its availability. #ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION #error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly -#elif !defined(__APPLE__) && !defined(_WIN32) && \ - (defined(__i386__) || defined(__x86_64__) || defined(__ppc__)) +#elif !defined(__APPLE__) && !defined(_WIN32) && \ + (defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \ + defined(__aarch64__) || defined(__riscv)) #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 namespace absl { diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc index 411ea308e9..f4859d7c21 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -37,8 +37,11 @@ static const unsigned char* GetKernelRtSigreturnAddress() { absl::debugging_internal::VDSOSupport vdso; if (vdso.IsPresent()) { absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; - if (!vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", STT_FUNC, - &symbol_info) || + auto lookup = [&](int type) { + return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", type, + &symbol_info); + }; + if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || symbol_info.address == nullptr) { // Unexpected: VDSO is present, yet the expected symbol is missing // or null. @@ -74,6 +77,8 @@ static inline uintptr_t ComputeStackFrameSize(const T* low, // checks (the strictness of which is controlled by the boolean parameter // "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. template<bool STRICT_UNWINDING, bool WITH_CONTEXT> +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static void **NextStackFrame(void **old_frame_pointer, const void *uc) { void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer); bool check_frame_size = true; @@ -123,6 +128,8 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc) { } template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { #ifdef __GNUC__ diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc index fffda968dd..2a1bf2e886 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc @@ -1,9 +1,18 @@ -// Copyright 2011 and onwards Google Inc. -// All rights reserved. +// Copyright 2017 The Abseil Authors. // -// Author: Doug Kwan -// This is inspired by Craig Silverstein's PowerPC stacktrace code. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 // +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// This is inspired by Craig Silverstein's PowerPC stacktrace code. #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_config.h b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_config.h index d4e8480a8e..ff21b719a0 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_config.h +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_config.h @@ -21,6 +21,8 @@ #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ +#include "absl/base/config.h" + #if defined(ABSL_STACKTRACE_INL_HEADER) #error ABSL_STACKTRACE_INL_HEADER cannot be directly set @@ -28,43 +30,58 @@ #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_win32-inl.inc" -#elif defined(__linux__) && !defined(__ANDROID__) - -#if !defined(NO_FRAME_POINTER) -# if defined(__i386__) || defined(__x86_64__) +#elif defined(__APPLE__) +#ifdef ABSL_HAVE_THREAD_LOCAL +// Thread local support required for UnwindImpl. #define ABSL_STACKTRACE_INL_HEADER \ - "absl/debugging/internal/stacktrace_x86-inl.inc" -# elif defined(__ppc__) || defined(__PPC__) + "absl/debugging/internal/stacktrace_generic-inl.inc" +#endif // defined(ABSL_HAVE_THREAD_LOCAL) + +#elif defined(__EMSCRIPTEN__) #define ABSL_STACKTRACE_INL_HEADER \ - "absl/debugging/internal/stacktrace_powerpc-inl.inc" -# elif defined(__aarch64__) + "absl/debugging/internal/stacktrace_emscripten-inl.inc" + +#elif defined(__linux__) && !defined(__ANDROID__) + +#if defined(NO_FRAME_POINTER) && \ + (defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)) +// Note: The libunwind-based implementation is not available to open-source +// users. #define ABSL_STACKTRACE_INL_HEADER \ - "absl/debugging/internal/stacktrace_aarch64-inl.inc" -# elif defined(__arm__) + "absl/debugging/internal/stacktrace_libunwind-inl.inc" +#define STACKTRACE_USES_LIBUNWIND 1 +#elif defined(NO_FRAME_POINTER) && defined(__has_include) +#if __has_include(<execinfo.h>) // Note: When using glibc this may require -funwind-tables to function properly. #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_generic-inl.inc" -# else +#endif // __has_include(<execinfo.h>) +#elif defined(__i386__) || defined(__x86_64__) +#define ABSL_STACKTRACE_INL_HEADER \ + "absl/debugging/internal/stacktrace_x86-inl.inc" +#elif defined(__ppc__) || defined(__PPC__) #define ABSL_STACKTRACE_INL_HEADER \ - "absl/debugging/internal/stacktrace_unimplemented-inl.inc" -# endif -#else // defined(NO_FRAME_POINTER) -# if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) + "absl/debugging/internal/stacktrace_powerpc-inl.inc" +#elif defined(__aarch64__) #define ABSL_STACKTRACE_INL_HEADER \ - "absl/debugging/internal/stacktrace_generic-inl.inc" -# elif defined(__ppc__) || defined(__PPC__) + "absl/debugging/internal/stacktrace_aarch64-inl.inc" +#elif defined(__riscv) #define ABSL_STACKTRACE_INL_HEADER \ - "absl/debugging/internal/stacktrace_generic-inl.inc" -# else + "absl/debugging/internal/stacktrace_riscv-inl.inc" +#elif defined(__has_include) +#if __has_include(<execinfo.h>) +// Note: When using glibc this may require -funwind-tables to function properly. #define ABSL_STACKTRACE_INL_HEADER \ - "absl/debugging/internal/stacktrace_unimplemented-inl.inc" -# endif -#endif // NO_FRAME_POINTER + "absl/debugging/internal/stacktrace_generic-inl.inc" +#endif // __has_include(<execinfo.h>) +#endif // defined(__has_include) + +#endif // defined(__linux__) && !defined(__ANDROID__) -#else +// Fallback to the empty implementation. +#if !defined(ABSL_STACKTRACE_INL_HEADER) #define ABSL_STACKTRACE_INL_HEADER \ "absl/debugging/internal/stacktrace_unimplemented-inl.inc" - #endif #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_CONFIG_H_ diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_emscripten-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_emscripten-inl.inc new file mode 100644 index 0000000000..0f44451438 --- /dev/null +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_emscripten-inl.inc @@ -0,0 +1,110 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// Portable implementation - just use glibc +// +// Note: The glibc implementation may cause a call to malloc. +// This can cause a deadlock in HeapProfiler. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ + +#include <emscripten.h> + +#include <atomic> +#include <cstring> + +#include "absl/base/attributes.h" +#include "absl/debugging/stacktrace.h" + +extern "C" { +uintptr_t emscripten_stack_snapshot(); +uint32_t emscripten_stack_unwind_buffer(uintptr_t pc, void *buffer, + uint32_t depth); +} + +// Sometimes, we can try to get a stack trace from within a stack +// trace, which can cause a self-deadlock. +// Protect against such reentrant call by failing to get a stack trace. +// +// We use __thread here because the code here is extremely low level -- it is +// called while collecting stack traces from within malloc and mmap, and thus +// can not call anything which might call malloc or mmap itself. +static __thread int recursive = 0; + +// The stack trace function might be invoked very early in the program's +// execution (e.g. from the very first malloc). +// As such, we suppress usage of backtrace during this early stage of execution. +static std::atomic<bool> disable_stacktraces(true); // Disabled until healthy. +// Waiting until static initializers run seems to be late enough. +// This file is included into stacktrace.cc so this will only run once. +ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() { + // Check if we can even create stacktraces. If not, bail early and leave + // disable_stacktraces set as-is. + // clang-format off + if (!EM_ASM_INT({ return (typeof wasmOffsetConverter !== 'undefined'); })) { + return 0; + } + // clang-format on + disable_stacktraces.store(false, std::memory_order_relaxed); + return 0; +}(); + +template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { + if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { + return 0; + } + ++recursive; + + static_cast<void>(ucp); // Unused. + constexpr int kStackLength = 64; + void *stack[kStackLength]; + + int size; + uintptr_t pc = emscripten_stack_snapshot(); + size = emscripten_stack_unwind_buffer(pc, stack, kStackLength); + + int result_count = size - skip_count; + if (result_count < 0) result_count = 0; + if (result_count > max_depth) result_count = max_depth; + for (int i = 0; i < result_count; i++) result[i] = stack[i + skip_count]; + + if (IS_STACK_FRAMES) { + // No implementation for finding out the stack frame sizes yet. + memset(sizes, 0, sizeof(*sizes) * result_count); + } + if (min_dropped_frames != nullptr) { + if (size - skip_count - max_depth > 0) { + *min_dropped_frames = size - skip_count - max_depth; + } else { + *min_dropped_frames = 0; + } + } + + --recursive; + + return result_count; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { return true; } +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_EMSCRIPTEN_INL_H_ diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc index ac034c9f5b..b2792a1f3a 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc @@ -1,7 +1,16 @@ -// Copyright 2000 - 2007 Google Inc. -// All rights reserved. +// Copyright 2017 The Abseil Authors. // -// Author: Sanjay Ghemawat +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. // // Portable implementation - just use glibc // diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc index 2e7c2f404f..cf8c05160c 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -131,7 +131,12 @@ static void **NextStackFrame(void **old_sp, const void *uc) { const ucontext_t* signal_context = reinterpret_cast<const ucontext_t*>(uc); void **const sp_before_signal = - reinterpret_cast<void**>(signal_context->uc_mcontext.gp_regs[PT_R1]); +#if defined(__PPC64__) + reinterpret_cast<void **>(signal_context->uc_mcontext.gp_regs[PT_R1]); +#else + reinterpret_cast<void **>( + signal_context->uc_mcontext.uc_regs->gregs[PT_R1]); +#endif // Check that alleged sp before signal is nonnull and is reasonably // aligned. if (sp_before_signal != nullptr && diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_riscv-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_riscv-inl.inc new file mode 100644 index 0000000000..8cbc78548c --- /dev/null +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_riscv-inl.inc @@ -0,0 +1,234 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ +#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ + +// Generate stack trace for riscv + +#include <sys/ucontext.h> + +#include "absl/base/config.h" +#if defined(__linux__) +#include <sys/mman.h> +#include <ucontext.h> +#include <unistd.h> +#endif + +#include <atomic> +#include <cassert> +#include <cstdint> +#include <iostream> + +#include "absl/base/attributes.h" +#include "absl/debugging/internal/address_is_readable.h" +#include "absl/debugging/internal/vdso_support.h" +#include "absl/debugging/stacktrace.h" + +static const uintptr_t kUnknownFrameSize = 0; + +#if defined(__linux__) +// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. +static const unsigned char *GetKernelRtSigreturnAddress() { + constexpr uintptr_t kImpossibleAddress = 0; + ABSL_CONST_INIT static std::atomic<uintptr_t> memoized(kImpossibleAddress); + uintptr_t address = memoized.load(std::memory_order_relaxed); + if (address != kImpossibleAddress) { + return reinterpret_cast<const unsigned char *>(address); + } + + address = reinterpret_cast<uintptr_t>(nullptr); + +#if ABSL_HAVE_VDSO_SUPPORT + absl::debugging_internal::VDSOSupport vdso; + if (vdso.IsPresent()) { + absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; + // Symbol versioning pulled from arch/riscv/kernel/vdso/vdso.lds at v5.10. + auto lookup = [&](int type) { + return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_4.15", type, + &symbol_info); + }; + if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || + symbol_info.address == nullptr) { + // Unexpected: VDSO is present, yet the expected symbol is missing or + // null. + assert(false && "VDSO is present, but doesn't have expected symbol"); + } else { + if (reinterpret_cast<uintptr_t>(symbol_info.address) != + kImpossibleAddress) { + address = reinterpret_cast<uintptr_t>(symbol_info.address); + } else { + assert(false && "VDSO returned invalid address"); + } + } + } +#endif + + memoized.store(address, std::memory_order_relaxed); + return reinterpret_cast<const unsigned char *>(address); +} +#endif // __linux__ + +// Compute the size of a stack frame in [low..high). We assume that low < high. +// Return size of kUnknownFrameSize. +template <typename T> +static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { + const char *low_char_ptr = reinterpret_cast<const char *>(low); + const char *high_char_ptr = reinterpret_cast<const char *>(high); + return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; +} + +// Given a pointer to a stack frame, locate and return the calling stackframe, +// or return null if no stackframe can be found. Perform sanity checks (the +// strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template <bool STRICT_UNWINDING, bool WITH_CONTEXT> +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { + // . + // . + // . + // +-> +----------------+ + // | | return address | + // | | previous fp | + // | | ... | + // | +----------------+ <-+ + // | | return address | | + // +---|- previous fp | | + // | ... | | + // $fp ->|----------------+ | + // | return address | | + // | previous fp -|---+ + // $sp ->| ... | + // +----------------+ + void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); + bool check_frame_size = true; + +#if defined(__linux__) + if (WITH_CONTEXT && uc != nullptr) { + // Check to see if next frame's return address is __kernel_rt_sigreturn. + if (old_frame_pointer[-1] == GetKernelRtSigreturnAddress()) { + const ucontext_t *ucv = static_cast<const ucontext_t *>(uc); + // old_frame_pointer is not suitable for unwinding, look at ucontext to + // discover frame pointer before signal. + // + // RISCV ELF psABI has the frame pointer at x8/fp/s0. + // -- RISCV psABI Table 18.2 + void **const pre_signal_frame_pointer = + reinterpret_cast<void **>(ucv->uc_mcontext.__gregs[8]); + + // Check the alleged frame pointer is actually readable. This is to + // prevent "double fault" in case we hit the first fault due to stack + // corruption. + if (!absl::debugging_internal::AddressIsReadable( + pre_signal_frame_pointer)) + return nullptr; + + // Alleged frame pointer is readable, use it for further unwinding. + new_frame_pointer = pre_signal_frame_pointer; + + // Skip frame size check if we return from a signal. We may be using an + // alterate stack for signals. + check_frame_size = false; + } + } +#endif + + // The RISCV ELF psABI mandates that the stack pointer is always 16-byte + // aligned. + // FIXME(abdulras) this doesn't hold for ILP32E which only mandates a 4-byte + // alignment. + if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0) + return nullptr; + + // Check frame size. In strict mode, we assume frames to be under 100,000 + // bytes. In non-strict mode, we relax the limit to 1MB. + if (check_frame_size) { + const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; + const uintptr_t frame_size = + ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); + if (frame_size == kUnknownFrameSize || frame_size > max_size) + return nullptr; + } + + return new_frame_pointer; +} + +template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> +ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. +ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. +static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, + const void *ucp, int *min_dropped_frames) { +#if defined(__GNUC__) + void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0)); +#else +#error reading stack pointer not yet supported on this platform +#endif + + skip_count++; // Skip the frame for this function. + int n = 0; + + // The `frame_pointer` that is computed here points to the top of the frame. + // The two words preceding the address are the return address and the previous + // frame pointer. To find a PC value associated with the current frame, we + // need to go down a level in the call chain. So we remember the return + // address of the last frame seen. This does not work for the first stack + // frame, which belongs to `UnwindImp()` but we skip the frame for + // `UnwindImp()` anyway. + void *prev_return_address = nullptr; + + while (frame_pointer && n < max_depth) { + // The absl::GetStackFrames routine si called when we are in some + // informational context (the failure signal handler for example). Use the + // non-strict unwinding rules to produce a stack trace that is as complete + // as possible (even if it contains a few bogus entries in some rare cases). + void **next_frame_pointer = + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + + if (skip_count > 0) { + skip_count--; + } else { + result[n] = prev_return_address; + if (IS_STACK_FRAMES) { + sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); + } + n++; + } + prev_return_address = frame_pointer[-1]; + frame_pointer = next_frame_pointer; + } + if (min_dropped_frames != nullptr) { + // Implementation detail: we clamp the max of frames we are willing to + // count, so as not to spend too much time in the loop below. + const int kMaxUnwind = 200; + int j = 0; + for (; frame_pointer != nullptr && j < kMaxUnwind; j++) { + frame_pointer = + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + } + *min_dropped_frames = j; + } + return n; +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace debugging_internal { +bool StackTraceWorksForTest() { return true; } +} // namespace debugging_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc index 9c2c558044..1c666c8b56 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc @@ -46,11 +46,19 @@ typedef USHORT NTAPI RtlCaptureStackBackTrace_Function( OUT PVOID *backtrace, OUT PULONG backtrace_hash); +// It is not possible to load RtlCaptureStackBackTrace at static init time in +// UWP. CaptureStackBackTrace is the public version of RtlCaptureStackBackTrace +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = + &::CaptureStackBackTrace; +#else // Load the function we need at static init time, where we don't have // to worry about someone else holding the loader's lock. static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = - (RtlCaptureStackBackTrace_Function*) - GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); + (RtlCaptureStackBackTrace_Function*)GetProcAddress( + GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); +#endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, diff --git a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc index bc320ff75b..847a547359 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/third_party/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc @@ -27,6 +27,7 @@ #include <cassert> #include <cstdint> +#include <limits> #include "absl/base/macros.h" #include "absl/base/port.h" @@ -132,9 +133,8 @@ static uintptr_t GetFP(const void *vuc) { const uintptr_t bp = 0; const uintptr_t sp = 0; #endif - // Sanity-check that the base pointer is valid. It should be as long as - // SHRINK_WRAP_FRAME_POINTER is not set, but it's possible that some code in - // the process is compiled with --copt=-fomit-frame-pointer or + // Sanity-check that the base pointer is valid. It's possible that some + // code in the process is compiled with --copt=-fomit-frame-pointer or // --copt=-momit-leaf-frame-pointer. // // TODO(bcmills): -momit-leaf-frame-pointer is currently the default @@ -159,7 +159,8 @@ static uintptr_t GetFP(const void *vuc) { template <bool STRICT_UNWINDING, bool WITH_CONTEXT> ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. -static void **NextStackFrame(void **old_fp, const void *uc) { +static void **NextStackFrame(void **old_fp, const void *uc, + size_t stack_low, size_t stack_high) { void **new_fp = (void **)*old_fp; #if defined(__linux__) && defined(__i386__) @@ -247,7 +248,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) { // using an alternate signal stack. // // TODO(bcmills): The GetFP call should be completely unnecessary when - // SHRINK_WRAP_FRAME_POINTER is set (because we should be back in the thread's + // ENABLE_COMBINED_UNWINDER is set (because we should be back in the thread's // stack by this point), but it is empirically still needed (e.g. when the // stack includes a call to abort). unw_get_reg returns UNW_EBADREG for some // frames. Figure out why GetValidFrameAddr and/or libunwind isn't doing what @@ -258,6 +259,18 @@ static void **NextStackFrame(void **old_fp, const void *uc) { // at a greater address that the current one. if (new_fp_u <= old_fp_u) return nullptr; if (new_fp_u - old_fp_u > kMaxFrameBytes) return nullptr; + + if (stack_low < old_fp_u && old_fp_u <= stack_high) { + // Old BP was in the expected stack region... + if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) { + // ... but new BP is outside of expected stack region. + // It is most likely bogus. + return nullptr; + } + } else { + // We may be here if we are executing in a co-routine with a + // separate stack. We can't do safety checks in this case. + } } else { if (new_fp == nullptr) return nullptr; // skip AddressIsReadable() below // In the non-strict mode, allow discontiguous stack frames. @@ -297,13 +310,17 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, int n = 0; void **fp = reinterpret_cast<void **>(__builtin_frame_address(0)); + size_t stack_low = getpagesize(); // Assume that the first page is not stack. + size_t stack_high = std::numeric_limits<size_t>::max() - sizeof(void *); + while (fp && n < max_depth) { if (*(fp + 1) == reinterpret_cast<void *>(0)) { // In 64-bit code, we often see a frame that // points to itself and has a return address of 0. break; } - void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp); + void **next_fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>( + fp, ucp, stack_low, stack_high); if (skip_count > 0) { skip_count--; } else { @@ -326,7 +343,8 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, const int kMaxUnwind = 1000; int j = 0; for (; fp != nullptr && j < kMaxUnwind; j++) { - fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp); + fp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(fp, ucp, stack_low, + stack_high); } *min_dropped_frames = j; } diff --git a/third_party/abseil-cpp/absl/debugging/internal/symbolize.h b/third_party/abseil-cpp/absl/debugging/internal/symbolize.h index 5d0858b5c7..27d5e6521e 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/symbolize.h +++ b/third_party/abseil-cpp/absl/debugging/internal/symbolize.h @@ -18,15 +18,18 @@ #ifndef ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ #define ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ +#ifdef __cplusplus + #include <cstddef> #include <cstdint> #include "absl/base/config.h" +#include "absl/strings/string_view.h" #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set -#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \ - !defined(__asmjs__) && !defined(__wasm__) +#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) \ + && !defined(__asmjs__) && !defined(__wasm__) #define ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE 1 #include <elf.h> @@ -45,7 +48,7 @@ namespace debugging_internal { // // This is not async-signal-safe. bool ForEachSection(int fd, - const std::function<bool(const std::string& name, + const std::function<bool(absl::string_view name, const ElfW(Shdr) &)>& callback); // Gets the section header for the given name, if it exists. Returns true on @@ -59,6 +62,18 @@ ABSL_NAMESPACE_END #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE +#ifdef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE +#error ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE cannot be directly set +#elif defined(__APPLE__) +#define ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE 1 +#endif + +#ifdef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE +#error ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE cannot be directly set +#elif defined(__EMSCRIPTEN__) +#define ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE 1 +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { @@ -109,20 +124,30 @@ bool RemoveAllSymbolDecorators(void); // filename != nullptr // // Returns true if the file was successfully registered. -bool RegisterFileMappingHint( - const void* start, const void* end, uint64_t offset, const char* filename); +bool RegisterFileMappingHint(const void* start, const void* end, + uint64_t offset, const char* filename); // Looks up the file mapping registered by RegisterFileMappingHint for an // address range. If there is one, the file name is stored in *filename and // *start and *end are modified to reflect the registered mapping. Returns // whether any hint was found. -bool GetFileMappingHint(const void** start, - const void** end, - uint64_t * offset, +bool GetFileMappingHint(const void** start, const void** end, uint64_t* offset, const char** filename); } // namespace debugging_internal ABSL_NAMESPACE_END } // namespace absl +#endif // __cplusplus + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" +#endif // __cplusplus + + bool + AbslInternalGetFileMappingHint(const void** start, const void** end, + uint64_t* offset, const char** filename); + #endif // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ diff --git a/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc b/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc index 1e8a78ac9c..977a9f6b3c 100644 --- a/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc +++ b/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc @@ -20,12 +20,25 @@ #ifdef ABSL_HAVE_VDSO_SUPPORT // defined in vdso_support.h +#if !defined(__has_include) +#define __has_include(header) 0 +#endif + #include <errno.h> #include <fcntl.h> +#if __has_include(<syscall.h>) +#include <syscall.h> +#elif __has_include(<sys/syscall.h>) #include <sys/syscall.h> +#endif #include <unistd.h> -#if __GLIBC_PREREQ(2, 16) // GLIBC-2.16 implements getauxval. +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)) +#define ABSL_HAVE_GETAUXVAL +#endif + +#ifdef ABSL_HAVE_GETAUXVAL #include <sys/auxv.h> #endif @@ -37,6 +50,11 @@ #define AT_SYSINFO_EHDR 33 // for crosstoolv10 #endif +#if defined(__FreeBSD__) +using Elf64_auxv_t = Elf64_Auxinfo; +using Elf32_auxv_t = Elf32_Auxinfo; +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { @@ -65,7 +83,7 @@ VDSOSupport::VDSOSupport() // the operation should be idempotent. const void *VDSOSupport::Init() { const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase; -#if __GLIBC_PREREQ(2, 16) +#ifdef ABSL_HAVE_GETAUXVAL if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) { errno = 0; const void *const sysinfo_ehdr = @@ -74,17 +92,8 @@ const void *VDSOSupport::Init() { vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed); } } -#endif // __GLIBC_PREREQ(2, 16) +#endif // ABSL_HAVE_GETAUXVAL if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) { - // Valgrind zaps AT_SYSINFO_EHDR and friends from the auxv[] - // on stack, and so glibc works as if VDSO was not present. - // But going directly to kernel via /proc/self/auxv below bypasses - // Valgrind zapping. So we check for Valgrind separately. - if (RunningOnValgrind()) { - vdso_base_.store(nullptr, std::memory_order_relaxed); - getcpu_fn_.store(&GetCPUViaSyscall, std::memory_order_relaxed); - return nullptr; - } int fd = open("/proc/self/auxv", O_RDONLY); if (fd == -1) { // Kernel too old to have a VDSO. @@ -175,18 +184,6 @@ int GetCPU() { return ret_code == 0 ? cpu : ret_code; } -// We need to make sure VDSOSupport::Init() is called before -// InitGoogle() does any setuid or chroot calls. If VDSOSupport -// is used in any global constructor, this will happen, since -// VDSOSupport's constructor calls Init. But if not, we need to -// ensure it here, with a global constructor of our own. This -// is an allowed exception to the normal rule against non-trivial -// global constructors. -static class VDSOInitHelper { - public: - VDSOInitHelper() { VDSOSupport::Init(); } -} vdso_init_helper; - } // namespace debugging_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/debugging/leak_check.cc b/third_party/abseil-cpp/absl/debugging/leak_check.cc index ff9049559d..764ca0ad00 100644 --- a/third_party/abseil-cpp/absl/debugging/leak_check.cc +++ b/third_party/abseil-cpp/absl/debugging/leak_check.cc @@ -16,6 +16,7 @@ // When lsan is not linked in, these functions are not available, // therefore Abseil code which depends on these functions is conditioned on the // definition of LEAK_SANITIZER. +#include "absl/base/attributes.h" #include "absl/debugging/leak_check.h" #ifndef LEAK_SANITIZER @@ -23,6 +24,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN bool HaveLeakSanitizer() { return false; } +bool LeakCheckerIsActive() { return false; } void DoIgnoreLeak(const void*) { } void RegisterLivePointers(const void*, size_t) { } void UnRegisterLivePointers(const void*, size_t) { } @@ -35,9 +37,23 @@ ABSL_NAMESPACE_END #include <sanitizer/lsan_interface.h> +#if ABSL_HAVE_ATTRIBUTE_WEAK +extern "C" ABSL_ATTRIBUTE_WEAK int __lsan_is_turned_off(); +#endif + namespace absl { ABSL_NAMESPACE_BEGIN bool HaveLeakSanitizer() { return true; } + +#if ABSL_HAVE_ATTRIBUTE_WEAK +bool LeakCheckerIsActive() { + return !(&__lsan_is_turned_off && __lsan_is_turned_off()); +} +#else +bool LeakCheckerIsActive() { return true; } +#endif + +bool FindAndReportLeaks() { return __lsan_do_recoverable_leak_check(); } void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); } void RegisterLivePointers(const void* ptr, size_t size) { __lsan_register_root_region(ptr, size); diff --git a/third_party/abseil-cpp/absl/debugging/leak_check.h b/third_party/abseil-cpp/absl/debugging/leak_check.h index 7a5a22dd1c..5fc2b052e4 100644 --- a/third_party/abseil-cpp/absl/debugging/leak_check.h +++ b/third_party/abseil-cpp/absl/debugging/leak_check.h @@ -43,6 +43,12 @@ ABSL_NAMESPACE_BEGIN // currently built into this target. bool HaveLeakSanitizer(); +// LeakCheckerIsActive() +// +// Returns true if a leak-checking sanitizer (either ASan or standalone LSan) is +// currently built into this target and is turned on. +bool LeakCheckerIsActive(); + // DoIgnoreLeak() // // Implements `IgnoreLeak()` below. This function should usually @@ -62,7 +68,8 @@ void DoIgnoreLeak(const void* ptr); // // If the passed `ptr` does not point to an actively allocated object at the // time `IgnoreLeak()` is called, the call is a no-op; if it is actively -// allocated, the object must not get deallocated later. +// allocated, leak sanitizer will assume this object is referenced even if +// there is no actual reference in user memory. // template <typename T> T* IgnoreLeak(T* ptr) { @@ -70,6 +77,19 @@ T* IgnoreLeak(T* ptr) { return ptr; } +// FindAndReportLeaks() +// +// If any leaks are detected, prints a leak report and returns true. This +// function may be called repeatedly, and does not affect end-of-process leak +// checking. +// +// Example: +// if (FindAndReportLeaks()) { +// ... diagnostic already printed. Exit with failure code. +// exit(1) +// } +bool FindAndReportLeaks(); + // LeakCheckDisabler // // This helper class indicates that any heap allocations done in the code block diff --git a/third_party/abseil-cpp/absl/debugging/leak_check_fail_test.cc b/third_party/abseil-cpp/absl/debugging/leak_check_fail_test.cc index 2887ceab14..c49b81a9d9 100644 --- a/third_party/abseil-cpp/absl/debugging/leak_check_fail_test.cc +++ b/third_party/abseil-cpp/absl/debugging/leak_check_fail_test.cc @@ -25,7 +25,7 @@ TEST(LeakCheckTest, LeakMemory) { // failed exit code. char* foo = strdup("lsan should complain about this leaked string"); - ABSL_RAW_LOG(INFO, "Should detect leaked std::string %s", foo); + ABSL_RAW_LOG(INFO, "Should detect leaked string %s", foo); } TEST(LeakCheckTest, LeakMemoryAfterDisablerScope) { @@ -34,7 +34,7 @@ TEST(LeakCheckTest, LeakMemoryAfterDisablerScope) { // failed exit code. { absl::LeakCheckDisabler disabler; } char* foo = strdup("lsan should also complain about this leaked string"); - ABSL_RAW_LOG(INFO, "Re-enabled leak detection.Should detect leaked std::string %s", + ABSL_RAW_LOG(INFO, "Re-enabled leak detection.Should detect leaked string %s", foo); } diff --git a/third_party/abseil-cpp/absl/debugging/leak_check_test.cc b/third_party/abseil-cpp/absl/debugging/leak_check_test.cc index 93a7edd2d0..9fcfc8e50b 100644 --- a/third_party/abseil-cpp/absl/debugging/leak_check_test.cc +++ b/third_party/abseil-cpp/absl/debugging/leak_check_test.cc @@ -23,20 +23,22 @@ namespace { TEST(LeakCheckTest, DetectLeakSanitizer) { #ifdef ABSL_EXPECT_LEAK_SANITIZER EXPECT_TRUE(absl::HaveLeakSanitizer()); + EXPECT_TRUE(absl::LeakCheckerIsActive()); #else EXPECT_FALSE(absl::HaveLeakSanitizer()); + EXPECT_FALSE(absl::LeakCheckerIsActive()); #endif } TEST(LeakCheckTest, IgnoreLeakSuppressesLeakedMemoryErrors) { auto foo = absl::IgnoreLeak(new std::string("some ignored leaked string")); - ABSL_RAW_LOG(INFO, "Ignoring leaked std::string %s", foo->c_str()); + ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str()); } TEST(LeakCheckTest, LeakCheckDisablerIgnoresLeak) { absl::LeakCheckDisabler disabler; - auto foo = new std::string("some std::string leaked while checks are disabled"); - ABSL_RAW_LOG(INFO, "Ignoring leaked std::string %s", foo->c_str()); + auto foo = new std::string("some string leaked while checks are disabled"); + ABSL_RAW_LOG(INFO, "Ignoring leaked string %s", foo->c_str()); } } // namespace diff --git a/third_party/abseil-cpp/absl/debugging/stacktrace.cc b/third_party/abseil-cpp/absl/debugging/stacktrace.cc index 1f7c7d82b2..ff8069f843 100644 --- a/third_party/abseil-cpp/absl/debugging/stacktrace.cc +++ b/third_party/abseil-cpp/absl/debugging/stacktrace.cc @@ -49,8 +49,10 @@ # include "absl/debugging/internal/stacktrace_aarch64-inl.inc" # include "absl/debugging/internal/stacktrace_arm-inl.inc" +# include "absl/debugging/internal/stacktrace_emscripten-inl.inc" # include "absl/debugging/internal/stacktrace_generic-inl.inc" # include "absl/debugging/internal/stacktrace_powerpc-inl.inc" +# include "absl/debugging/internal/stacktrace_riscv-inl.inc" # include "absl/debugging/internal/stacktrace_unimplemented-inl.inc" # include "absl/debugging/internal/stacktrace_win32-inl.inc" # include "absl/debugging/internal/stacktrace_x86-inl.inc" diff --git a/third_party/abseil-cpp/absl/debugging/symbolize.cc b/third_party/abseil-cpp/absl/debugging/symbolize.cc index 54ed97002a..f1abdfda59 100644 --- a/third_party/abseil-cpp/absl/debugging/symbolize.cc +++ b/third_party/abseil-cpp/absl/debugging/symbolize.cc @@ -14,12 +14,25 @@ #include "absl/debugging/symbolize.h" +#ifdef _WIN32 +#include <winapifamily.h> +#if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)) || \ + WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +// UWP doesn't have access to win32 APIs. +#define ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32 +#endif +#endif + #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) #include "absl/debugging/symbolize_elf.inc" -#elif defined(_WIN32) +#elif defined(ABSL_INTERNAL_HAVE_SYMBOLIZE_WIN32) // The Windows Symbolizer only works if PDB files containing the debug info // are available to the program at runtime. #include "absl/debugging/symbolize_win32.inc" +#elif defined(__APPLE__) +#include "absl/debugging/symbolize_darwin.inc" +#elif defined(__EMSCRIPTEN__) +#include "absl/debugging/symbolize_emscripten.inc" #else #include "absl/debugging/symbolize_unimplemented.inc" #endif diff --git a/third_party/abseil-cpp/absl/debugging/symbolize_darwin.inc b/third_party/abseil-cpp/absl/debugging/symbolize_darwin.inc new file mode 100644 index 0000000000..443ce9efc4 --- /dev/null +++ b/third_party/abseil-cpp/absl/debugging/symbolize_darwin.inc @@ -0,0 +1,101 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <cxxabi.h> +#include <execinfo.h> + +#include <algorithm> +#include <cstring> + +#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +void InitializeSymbolizer(const char*) {} + +namespace debugging_internal { +namespace { + +static std::string GetSymbolString(absl::string_view backtrace_line) { + // Example Backtrace lines: + // 0 libimaging_shared.dylib 0x018c152a + // _ZNSt11_Deque_baseIN3nik7mediadb4PageESaIS2_EE17_M_initialize_mapEm + 3478 + // + // or + // 0 libimaging_shared.dylib 0x0000000001895c39 + // _ZN3nik4util19register_shared_ptrINS_3gpu7TextureEEEvPKvS5_ + 39 + // + // or + // 0 mysterious_app 0x0124000120120009 main + 17 + auto address_pos = backtrace_line.find(" 0x"); + if (address_pos == absl::string_view::npos) return std::string(); + absl::string_view symbol_view = backtrace_line.substr(address_pos + 1); + + auto space_pos = symbol_view.find(" "); + if (space_pos == absl::string_view::npos) return std::string(); + symbol_view = symbol_view.substr(space_pos + 1); // to mangled symbol + + auto plus_pos = symbol_view.find(" + "); + if (plus_pos == absl::string_view::npos) return std::string(); + symbol_view = symbol_view.substr(0, plus_pos); // strip remainng + + return std::string(symbol_view); +} + +} // namespace +} // namespace debugging_internal + +bool Symbolize(const void* pc, char* out, int out_size) { + if (out_size <= 0 || pc == nullptr) { + out = nullptr; + return false; + } + + // This allocates a char* array. + char** frame_strings = backtrace_symbols(const_cast<void**>(&pc), 1); + + if (frame_strings == nullptr) return false; + + std::string symbol = debugging_internal::GetSymbolString(frame_strings[0]); + free(frame_strings); + + char tmp_buf[1024]; + if (debugging_internal::Demangle(symbol.c_str(), tmp_buf, sizeof(tmp_buf))) { + size_t len = strlen(tmp_buf); + if (len + 1 <= static_cast<size_t>(out_size)) { // +1 for '\0' + assert(len < sizeof(tmp_buf)); + memmove(out, tmp_buf, len + 1); + } + } else { + strncpy(out, symbol.c_str(), out_size); + } + + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = std::min<int>(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + + return true; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/debugging/symbolize_elf.inc b/third_party/abseil-cpp/absl/debugging/symbolize_elf.inc index c371635ffa..3ff343d64f 100644 --- a/third_party/abseil-cpp/absl/debugging/symbolize_elf.inc +++ b/third_party/abseil-cpp/absl/debugging/symbolize_elf.inc @@ -57,6 +57,7 @@ #include <unistd.h> #include <algorithm> +#include <array> #include <atomic> #include <cerrno> #include <cinttypes> @@ -74,6 +75,11 @@ #include "absl/base/port.h" #include "absl/debugging/internal/demangle.h" #include "absl/debugging/internal/vdso_support.h" +#include "absl/strings/string_view.h" + +#if defined(__FreeBSD__) && !defined(ElfW) +#define ElfW(x) __ElfN(x) +#endif namespace absl { ABSL_NAMESPACE_BEGIN @@ -82,6 +88,12 @@ ABSL_NAMESPACE_BEGIN static char *argv0_value = nullptr; void InitializeSymbolizer(const char *argv0) { +#ifdef ABSL_HAVE_VDSO_SUPPORT + // We need to make sure VDSOSupport::Init() is called before any setuid or + // chroot calls, so InitializeSymbolizer() should be called very early in the + // life of a program. + absl::debugging_internal::VDSOSupport::Init(); +#endif if (argv0_value != nullptr) { free(argv0_value); argv0_value = nullptr; @@ -149,13 +161,15 @@ struct FileMappingHint { // Moreover, we are using only TryLock(), if the decorator list // is being modified (is busy), we skip all decorators, and possibly // loose some info. Sorry, that's the best we could do. -base_internal::SpinLock g_decorators_mu(base_internal::kLinkerInitialized); +ABSL_CONST_INIT absl::base_internal::SpinLock g_decorators_mu( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); const int kMaxFileMappingHints = 8; int g_num_file_mapping_hints; FileMappingHint g_file_mapping_hints[kMaxFileMappingHints]; // Protects g_file_mapping_hints. -base_internal::SpinLock g_file_mapping_mu(base_internal::kLinkerInitialized); +ABSL_CONST_INIT absl::base_internal::SpinLock g_file_mapping_mu( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); // Async-signal-safe function to zero a buffer. // memset() is not guaranteed to be async-signal-safe. @@ -175,6 +189,7 @@ struct ObjFile { fd(-1), elf_type(-1) { SafeMemZero(&elf_header, sizeof(elf_header)); + SafeMemZero(&phdr[0], sizeof(phdr)); } char *filename; @@ -187,6 +202,10 @@ struct ObjFile { int fd; int elf_type; ElfW(Ehdr) elf_header; + + // PT_LOAD program header describing executable code. + // Normally we expect just one, but SWIFT binaries have two. + std::array<ElfW(Phdr), 2> phdr; }; // Build 4-way associative cache for symbols. Within each cache line, symbols @@ -496,7 +515,7 @@ static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType( const int kMaxSectionNameLen = 64; bool ForEachSection(int fd, - const std::function<bool(const std::string &name, + const std::function<bool(absl::string_view name, const ElfW(Shdr) &)> &callback) { ElfW(Ehdr) elf_header; if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { @@ -518,7 +537,7 @@ bool ForEachSection(int fd, return false; } off_t name_offset = shstrtab.sh_offset + out.sh_name; - char header_name[kMaxSectionNameLen + 1]; + char header_name[kMaxSectionNameLen]; ssize_t n_read = ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset); if (n_read == -1) { @@ -527,9 +546,8 @@ bool ForEachSection(int fd, // Long read? return false; } - header_name[n_read] = '\0'; - std::string name(header_name); + absl::string_view name(header_name, strnlen(header_name, n_read)); if (!callback(name, out)) { break; } @@ -687,6 +705,16 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( const char *start_address = ComputeOffset(original_start_address, relocation); +#ifdef __arm__ + // ARM functions are always aligned to multiples of two bytes; the + // lowest-order bit in start_address is ignored by the CPU and indicates + // whether the function contains ARM (0) or Thumb (1) code. We don't care + // about what encoding is being used; we just want the real start address + // of the function. + start_address = reinterpret_cast<const char *>( + reinterpret_cast<uintptr_t>(start_address) & ~1); +#endif + if (deref_function_descriptor_pointer && InSection(original_start_address, opd)) { // The opd section is mapped into memory. Just dereference @@ -1264,6 +1292,36 @@ static bool MaybeInitializeObjFile(ObjFile *obj) { ABSL_RAW_LOG(WARNING, "%s: failed to read elf header", obj->filename); return false; } + const int phnum = obj->elf_header.e_phnum; + const int phentsize = obj->elf_header.e_phentsize; + size_t phoff = obj->elf_header.e_phoff; + size_t num_executable_load_segments = 0; + for (int j = 0; j < phnum; j++) { + ElfW(Phdr) phdr; + if (!ReadFromOffsetExact(obj->fd, &phdr, sizeof(phdr), phoff)) { + ABSL_RAW_LOG(WARNING, "%s: failed to read program header %d", + obj->filename, j); + return false; + } + phoff += phentsize; + constexpr int rx = PF_X | PF_R; + if (phdr.p_type != PT_LOAD || (phdr.p_flags & rx) != rx) { + // Not a LOAD segment, or not executable code. + continue; + } + if (num_executable_load_segments < obj->phdr.size()) { + memcpy(&obj->phdr[num_executable_load_segments++], &phdr, sizeof(phdr)); + } else { + ABSL_RAW_LOG(WARNING, "%s: too many executable LOAD segments", + obj->filename); + break; + } + } + if (num_executable_load_segments == 0) { + // This object has no "r-x" LOAD segments. That's unexpected. + ABSL_RAW_LOG(WARNING, "%s: no executable LOAD segments", obj->filename); + return false; + } } return true; } @@ -1287,23 +1345,52 @@ const char *Symbolizer::GetSymbol(const void *const pc) { int fd = -1; if (obj != nullptr) { if (MaybeInitializeObjFile(obj)) { - if (obj->elf_type == ET_DYN && - reinterpret_cast<uint64_t>(obj->start_addr) >= obj->offset) { + const size_t start_addr = reinterpret_cast<size_t>(obj->start_addr); + if (obj->elf_type == ET_DYN && start_addr >= obj->offset) { // This object was relocated. // // For obj->offset > 0, adjust the relocation since a mapping at offset // X in the file will have a start address of [true relocation]+X. - relocation = reinterpret_cast<ptrdiff_t>(obj->start_addr) - obj->offset; + relocation = start_addr - obj->offset; + + // Note: some binaries have multiple "rx" LOAD segments. We must + // find the right one. + ElfW(Phdr) *phdr = nullptr; + for (size_t j = 0; j < obj->phdr.size(); j++) { + ElfW(Phdr) &p = obj->phdr[j]; + if (p.p_type != PT_LOAD) { + // We only expect PT_LOADs. This must be PT_NULL that we didn't + // write over (i.e. we exhausted all interesting PT_LOADs). + ABSL_RAW_CHECK(p.p_type == PT_NULL, "unexpected p_type"); + break; + } + if (pc < reinterpret_cast<void *>(start_addr + p.p_memsz)) { + phdr = &p; + break; + } + } + if (phdr == nullptr) { + // That's unexpected. Hope for the best. + ABSL_RAW_LOG( + WARNING, + "%s: unable to find LOAD segment for pc: %p, start_addr: %zx", + obj->filename, pc, start_addr); + } else { + // Adjust relocation in case phdr.p_vaddr != 0. + // This happens for binaries linked with `lld --rosegment`, and for + // binaries linked with BFD `ld -z separate-code`. + relocation -= phdr->p_vaddr - phdr->p_offset; + } } fd = obj->fd; - } - if (GetSymbolFromObjectFile(*obj, pc, relocation, symbol_buf_, - sizeof(symbol_buf_), tmp_buf_, - sizeof(tmp_buf_)) == SYMBOL_FOUND) { - // Only try to demangle the symbol name if it fit into symbol_buf_. - DemangleInplace(symbol_buf_, sizeof(symbol_buf_), tmp_buf_, - sizeof(tmp_buf_)); + if (GetSymbolFromObjectFile(*obj, pc, relocation, symbol_buf_, + sizeof(symbol_buf_), tmp_buf_, + sizeof(tmp_buf_)) == SYMBOL_FOUND) { + // Only try to demangle the symbol name if it fit into symbol_buf_. + DemangleInplace(symbol_buf_, sizeof(symbol_buf_), tmp_buf_, + sizeof(tmp_buf_)); + } } } else { #if ABSL_HAVE_VDSO_SUPPORT @@ -1374,7 +1461,7 @@ int InstallSymbolDecorator(SymbolDecorator decorator, void *arg) { if (!g_decorators_mu.TryLock()) { // Someone else is using decorators. Get out. - return false; + return -2; } int ret = ticket; if (g_num_decorators >= kMaxDecorators) { @@ -1402,7 +1489,7 @@ bool RegisterFileMappingHint(const void *start, const void *end, uint64_t offset if (g_num_file_mapping_hints >= kMaxFileMappingHints) { ret = false; } else { - // TODO(ckennelly): Move this into a std::string copy routine. + // TODO(ckennelly): Move this into a string copy routine. int len = strlen(filename); char *dst = static_cast<char *>( base_internal::LowLevelAlloc::AllocWithArena(len + 1, SigSafeArena())); @@ -1453,7 +1540,7 @@ bool GetFileMappingHint(const void **start, const void **end, uint64_t *offset, bool Symbolize(const void *pc, char *out, int out_size) { // Symbolization is very slow under tsan. - ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN(); + ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN(); SAFE_ASSERT(out_size >= 0); debugging_internal::Symbolizer *s = debugging_internal::AllocateSymbolizer(); const char *name = s->GetSymbol(pc); @@ -1472,9 +1559,16 @@ bool Symbolize(const void *pc, char *out, int out_size) { } } debugging_internal::FreeSymbolizer(s); - ANNOTATE_IGNORE_READS_AND_WRITES_END(); + ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END(); return ok; } ABSL_NAMESPACE_END } // namespace absl + +extern "C" bool AbslInternalGetFileMappingHint(const void **start, + const void **end, uint64_t *offset, + const char **filename) { + return absl::debugging_internal::GetFileMappingHint(start, end, offset, + filename); +} diff --git a/third_party/abseil-cpp/absl/debugging/symbolize_emscripten.inc b/third_party/abseil-cpp/absl/debugging/symbolize_emscripten.inc new file mode 100644 index 0000000000..c226c45666 --- /dev/null +++ b/third_party/abseil-cpp/absl/debugging/symbolize_emscripten.inc @@ -0,0 +1,72 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <cxxabi.h> +#include <emscripten.h> + +#include <algorithm> +#include <cstring> + +#include "absl/base/internal/raw_logging.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +extern "C" { +const char* emscripten_pc_get_function(const void* pc); +} + +// clang-format off +EM_JS(bool, HaveOffsetConverter, (), + { return typeof wasmOffsetConverter !== 'undefined'; }); +// clang-format on + +namespace absl { +ABSL_NAMESPACE_BEGIN + +void InitializeSymbolizer(const char*) { + if (!HaveOffsetConverter()) { + ABSL_RAW_LOG(INFO, + "Symbolization unavailable. Rebuild with -sWASM=1 " + "and -sUSE_OFFSET_CONVERTER=1."); + } +} + +bool Symbolize(const void* pc, char* out, int out_size) { + // Check if we have the offset converter necessary for pc_get_function. + // Without it, the program will abort(). + if (!HaveOffsetConverter()) { + return false; + } + const char* func_name = emscripten_pc_get_function(pc); + if (func_name == nullptr) { + return false; + } + + strncpy(out, func_name, out_size); + + if (out[out_size - 1] != '\0') { + // strncpy() does not '\0' terminate when it truncates. + static constexpr char kEllipsis[] = "..."; + int ellipsis_size = std::min<int>(sizeof(kEllipsis) - 1, out_size - 1); + memcpy(out + out_size - ellipsis_size - 1, kEllipsis, ellipsis_size); + out[out_size - 1] = '\0'; + } + + return true; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/debugging/symbolize_test.cc b/third_party/abseil-cpp/absl/debugging/symbolize_test.cc index a1d03aab53..c710a3da81 100644 --- a/third_party/abseil-cpp/absl/debugging/symbolize_test.cc +++ b/third_party/abseil-cpp/absl/debugging/symbolize_test.cc @@ -27,11 +27,13 @@ #include "gtest/gtest.h" #include "absl/base/attributes.h" #include "absl/base/casts.h" +#include "absl/base/config.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/optimization.h" #include "absl/debugging/internal/stack_consumption.h" #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" using testing::Contains; @@ -144,7 +146,22 @@ static const char *TrySymbolize(void *pc) { return TrySymbolizeWithLimit(pc, sizeof(try_symbolize_buffer)); } -#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE +#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \ + defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE) || \ + defined(ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE) + +// Test with a return address. +void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { +#if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) + void *return_address = __builtin_return_address(0); + const char *symbol = TrySymbolize(return_address); + ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed"); + ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed"); + std::cout << "TestWithReturnAddress passed" << std::endl; +#endif +} + +#ifndef ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE TEST(Symbolize, Cached) { // Compilers should give us pointers to them. @@ -218,8 +235,8 @@ static const char *SymbolizeStackConsumption(void *pc, int *stack_consumed) { static int GetStackConsumptionUpperLimit() { // Symbolize stack consumption should be within 2kB. int stack_consumption_upper_limit = 2048; -#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ - defined(THREAD_SANITIZER) +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER) // Account for sanitizer instrumentation requiring additional stack space. stack_consumption_upper_limit *= 5; #endif @@ -258,6 +275,7 @@ TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) { #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION +#ifndef ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE // Use a 64K page size for PPC. const size_t kPageSize = 64 << 10; // We place a read-only symbols into the .text section and verify that we can @@ -399,8 +417,8 @@ TEST(Symbolize, ForEachSection) { std::vector<std::string> sections; ASSERT_TRUE(absl::debugging_internal::ForEachSection( - fd, [§ions](const std::string &name, const ElfW(Shdr) &) { - sections.push_back(name); + fd, [§ions](const absl::string_view name, const ElfW(Shdr) &) { + sections.emplace_back(name); return true; })); @@ -413,6 +431,8 @@ TEST(Symbolize, ForEachSection) { close(fd); } +#endif // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE +#endif // !ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE // x86 specific tests. Uses some inline assembler. extern "C" { @@ -461,17 +481,46 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() { } } -// Test with a return address. -void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { +#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) +// Test that we correctly identify bounds of Thumb functions on ARM. +// +// Thumb functions have the lowest-order bit set in their addresses in the ELF +// symbol table. This requires some extra logic to properly compute function +// bounds. To test this logic, nudge a Thumb function right up against an ARM +// function and try to symbolize the ARM function. +// +// A naive implementation will simply use the Thumb function's entry point as +// written in the symbol table and will therefore treat the Thumb function as +// extending one byte further in the instruction stream than it actually does. +// When asked to symbolize the start of the ARM function, it will identify an +// overlap between the Thumb and ARM functions, and it will return the name of +// the Thumb function. +// +// A correct implementation, on the other hand, will null out the lowest-order +// bit in the Thumb function's entry point. It will correctly compute the end of +// the Thumb function, it will find no overlap between the Thumb and ARM +// functions, and it will return the name of the ARM function. + +__attribute__((target("thumb"))) int ArmThumbOverlapThumb(int x) { + return x * x * x; +} + +__attribute__((target("arm"))) int ArmThumbOverlapArm(int x) { + return x * x * x; +} + +void ABSL_ATTRIBUTE_NOINLINE TestArmThumbOverlap() { #if defined(ABSL_HAVE_ATTRIBUTE_NOINLINE) - void *return_address = __builtin_return_address(0); - const char *symbol = TrySymbolize(return_address); - ABSL_RAW_CHECK(symbol != nullptr, "TestWithReturnAddress failed"); - ABSL_RAW_CHECK(strcmp(symbol, "main") == 0, "TestWithReturnAddress failed"); - std::cout << "TestWithReturnAddress passed" << std::endl; + const char *symbol = TrySymbolize((void *)&ArmThumbOverlapArm); + ABSL_RAW_CHECK(symbol != nullptr, "TestArmThumbOverlap failed"); + ABSL_RAW_CHECK(strcmp("ArmThumbOverlapArm()", symbol) == 0, + "TestArmThumbOverlap failed"); + std::cout << "TestArmThumbOverlap passed" << std::endl; #endif } +#endif // defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) + #elif defined(_WIN32) #if !defined(ABSL_CONSUME_DLL) @@ -514,7 +563,6 @@ TEST(Symbolize, SymbolizeWithDemangling) { #endif // !defined(ABSL_CONSUME_DLL) #else // Symbolizer unimplemented - TEST(Symbolize, Unimplemented) { char buf[64]; EXPECT_FALSE(absl::Symbolize((void *)(&nonstatic_func), buf, sizeof(buf))); @@ -541,10 +589,14 @@ int main(int argc, char **argv) { absl::InitializeSymbolizer(argv[0]); testing::InitGoogleTest(&argc, argv); -#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE +#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) || \ + defined(ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE) TestWithPCInsideInlineFunction(); TestWithPCInsideNonInlineFunction(); TestWithReturnAddress(); +#if defined(__arm__) && ABSL_HAVE_ATTRIBUTE(target) + TestArmThumbOverlap(); +#endif #endif return RUN_ALL_TESTS(); diff --git a/third_party/abseil-cpp/absl/flags/BUILD.bazel b/third_party/abseil-cpp/absl/flags/BUILD.bazel index cdb4e7e8fe..d20deab464 100644 --- a/third_party/abseil-cpp/absl/flags/BUILD.bazel +++ b/third_party/abseil-cpp/absl/flags/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,29 +23,21 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( - name = "flag_internal", - srcs = [ - "internal/flag.cc", - ], + name = "path_util", hdrs = [ - "internal/flag.h", + "internal/path_util.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//absl/base:__subpackages__"], + visibility = [ + "//absl/flags:__pkg__", + ], deps = [ - ":config", - ":handle", - ":registry", - "//absl/base", "//absl/base:config", - "//absl/base:core_headers", - "//absl/memory", "//absl/strings", - "//absl/synchronization", ], ) @@ -73,22 +64,6 @@ cc_library( ) cc_library( - name = "path_util", - hdrs = [ - "internal/path_util.h", - ], - copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/flags:__pkg__", - ], - deps = [ - "//absl/base:config", - "//absl/strings", - ], -) - -cc_library( name = "config", srcs = [ "usage_config.cc", @@ -129,34 +104,47 @@ cc_library( ) cc_library( - name = "handle", + name = "commandlineflag_internal", + srcs = [ + "internal/commandlineflag.cc", + ], hdrs = [ "internal/commandlineflag.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/flags:__pkg__", + deps = [ + "//absl/base:config", + "//absl/base:fast_type_id", + ], +) + +cc_library( + name = "commandlineflag", + srcs = [ + "commandlineflag.cc", ], + hdrs = [ + "commandlineflag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":config", - ":marshalling", + ":commandlineflag_internal", "//absl/base:config", - "//absl/base:core_headers", + "//absl/base:fast_type_id", "//absl/strings", "//absl/types:optional", ], ) cc_library( - name = "registry", + name = "private_handle_accessor", srcs = [ - "internal/registry.cc", - "internal/type_erased.cc", + "internal/private_handle_accessor.cc", ], hdrs = [ - "internal/registry.h", - "internal/type_erased.h", + "internal/private_handle_accessor.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, @@ -164,20 +152,71 @@ cc_library( "//absl/flags:__pkg__", ], deps = [ + ":commandlineflag", + ":commandlineflag_internal", + "//absl/base:config", + "//absl/strings", + ], +) + +cc_library( + name = "reflection", + srcs = [ + "reflection.cc", + ], + hdrs = [ + "internal/registry.h", + "reflection.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":commandlineflag", + ":commandlineflag_internal", ":config", - ":handle", + ":private_handle_accessor", "//absl/base:config", "//absl/base:core_headers", - "//absl/base:raw_logging_internal", + "//absl/container:flat_hash_map", "//absl/strings", "//absl/synchronization", ], ) cc_library( + name = "flag_internal", + srcs = [ + "internal/flag.cc", + ], + hdrs = [ + "internal/flag.h", + "internal/sequence_lock.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl/base:__subpackages__"], + deps = [ + ":commandlineflag", + ":commandlineflag_internal", + ":config", + ":marshalling", + ":reflection", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/synchronization", + "//absl/utility", + ], +) + +cc_library( name = "flag", srcs = [ "flag.cc", + "internal/flag_msvc.inc", ], hdrs = [ "declare.h", @@ -188,9 +227,7 @@ cc_library( deps = [ ":config", ":flag_internal", - ":handle", - ":marshalling", - ":registry", + ":reflection", "//absl/base", "//absl/base:config", "//absl/base:core_headers", @@ -212,15 +249,17 @@ cc_library( "//absl/flags:__pkg__", ], deps = [ + ":commandlineflag", ":config", ":flag", ":flag_internal", - ":handle", ":path_util", + ":private_handle_accessor", ":program_name", - ":registry", + ":reflection", "//absl/base:config", "//absl/base:core_headers", + "//absl/container:flat_hash_map", "//absl/strings", ], ) @@ -254,12 +293,14 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":commandlineflag", + ":commandlineflag_internal", ":config", ":flag", ":flag_internal", - ":handle", + ":private_handle_accessor", ":program_name", - ":registry", + ":reflection", ":usage", ":usage_internal", "//absl/base:config", @@ -276,15 +317,17 @@ cc_test( name = "commandlineflag_test", size = "small", srcs = [ - "internal/commandlineflag_test.cc", + "commandlineflag_test.cc", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":commandlineflag", + ":commandlineflag_internal", ":config", ":flag", - ":handle", - ":registry", + ":private_handle_accessor", + ":reflection", "//absl/memory", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -318,10 +361,12 @@ cc_test( ":config", ":flag", ":flag_internal", - ":handle", - ":registry", + ":marshalling", + ":reflection", "//absl/base:core_headers", + "//absl/base:malloc_internal", "//absl/strings", + "//absl/time", "@com_google_googletest//:gtest_main", ], ) @@ -333,10 +378,18 @@ cc_binary( "flag_benchmark.cc", ], copts = ABSL_TEST_COPTS, + linkopts = select({ + "//conditions:default": [], + }) + ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ + "flag_benchmark.lds", ":flag", + ":marshalling", + ":parse", + ":reflection", + "//absl/strings", "//absl/time", "//absl/types:optional", "@com_github_google_benchmark//:benchmark_main", @@ -358,35 +411,36 @@ cc_test( ) cc_test( - name = "path_util_test", + name = "parse_test", size = "small", srcs = [ - "internal/path_util_test.cc", + "parse_test.cc", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":path_util", + ":flag", + ":parse", + ":reflection", + ":usage_internal", + "//absl/base:raw_logging_internal", + "//absl/base:scoped_set_env", + "//absl/strings", + "//absl/types:span", "@com_google_googletest//:gtest_main", ], ) cc_test( - name = "parse_test", + name = "path_util_test", size = "small", srcs = [ - "parse_test.cc", + "internal/path_util_test.cc", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":flag", - ":parse", - ":registry", - "//absl/base:raw_logging_internal", - "//absl/base:scoped_set_env", - "//absl/strings", - "//absl/types:span", + ":path_util", "@com_google_googletest//:gtest_main", ], ) @@ -407,19 +461,40 @@ cc_test( ) cc_test( - name = "type_erased_test", + name = "reflection_test", size = "small", srcs = [ - "internal/type_erased_test.cc", + "reflection_test.cc", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":commandlineflag_internal", ":flag", - ":handle", ":marshalling", - ":registry", + ":reflection", + ":usage_internal", "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "sequence_lock_test", + size = "small", + timeout = "moderate", + srcs = [ + "internal/sequence_lock_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + shard_count = 31, + deps = [ + ":flag_internal", + "//absl/base", + "//absl/container:fixed_array", + "//absl/time", "@com_google_googletest//:gtest_main", ], ) @@ -455,10 +530,9 @@ cc_test( ":parse", ":path_util", ":program_name", - ":registry", + ":reflection", ":usage", ":usage_internal", - "//absl/memory", "//absl/strings", "@com_google_googletest//:gtest", ], diff --git a/third_party/abseil-cpp/absl/flags/CMakeLists.txt b/third_party/abseil-cpp/absl/flags/CMakeLists.txt index 1d25f0ded8..7f3298e9dd 100644 --- a/third_party/abseil-cpp/absl/flags/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/flags/CMakeLists.txt @@ -17,22 +17,16 @@ # Internal-only target, do not depend on directly. absl_cc_library( NAME - flags_internal - SRCS - "internal/flag.cc" + flags_path_util HDRS - "internal/flag.h" + "internal/path_util.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::base absl::config - absl::flags_config - absl::flags_handle - absl::flags_registry - absl::synchronization + absl::strings PUBLIC ) @@ -57,22 +51,6 @@ absl_cc_library( PUBLIC ) -# Internal-only target, do not depend on directly. -absl_cc_library( - NAME - flags_path_util - HDRS - "internal/path_util.h" - COPTS - ${ABSL_DEFAULT_COPTS} - LINKOPTS - ${ABSL_DEFAULT_LINKOPTS} - DEPS - absl::config - absl::strings - PUBLIC -) - absl_cc_library( NAME flags_config @@ -116,7 +94,9 @@ absl_cc_library( # Internal-only target, do not depend on directly. absl_cc_library( NAME - flags_handle + flags_commandlineflag_internal + SRCS + "internal/commandlineflag.cc" HDRS "internal/commandlineflag.h" COPTS @@ -125,37 +105,93 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config - absl::flags_config - absl::flags_marshalling - absl::core_headers + absl::fast_type_id +) + +absl_cc_library( + NAME + flags_commandlineflag + SRCS + "commandlineflag.cc" + HDRS + "commandlineflag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::fast_type_id + absl::flags_commandlineflag_internal absl::optional - absl::raw_logging_internal absl::strings - absl::synchronization ) # Internal-only target, do not depend on directly. absl_cc_library( NAME - flags_registry + flags_private_handle_accessor + SRCS + "internal/private_handle_accessor.cc" + HDRS + "internal/private_handle_accessor.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::flags_commandlineflag + absl::flags_commandlineflag_internal + absl::strings +) + +absl_cc_library( + NAME + flags_reflection SRCS - "internal/registry.cc" - "internal/type_erased.cc" + "reflection.cc" HDRS + "reflection.h" "internal/registry.h" - "internal/type_erased.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::flags_commandlineflag + absl::flags_private_handle_accessor absl::flags_config - absl::flags_handle - absl::core_headers - absl::raw_logging_internal absl::strings absl::synchronization + absl::flat_hash_map +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_internal + SRCS + "internal/flag.cc" + HDRS + "internal/flag.h" + "internal/sequence_lock.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::config + absl::flags_commandlineflag + absl::flags_commandlineflag_internal + absl::flags_config + absl::flags_marshalling + absl::synchronization + absl::meta + absl::utility + PUBLIC ) absl_cc_library( @@ -166,17 +202,17 @@ absl_cc_library( HDRS "declare.h" "flag.h" + "internal/flag_msvc.inc" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::flags_commandlineflag absl::flags_config - absl::flags_handle absl::flags_internal - absl::flags_marshalling - absl::flags_registry + absl::flags_reflection absl::base absl::core_headers absl::strings @@ -198,11 +234,13 @@ absl_cc_library( absl::config absl::flags_config absl::flags - absl::flags_handle + absl::flags_commandlineflag absl::flags_internal absl::flags_path_util + absl::flags_private_handle_accessor absl::flags_program_name - absl::flags_registry + absl::flags_reflection + absl::flat_hash_map absl::strings absl::synchronization ) @@ -243,10 +281,12 @@ absl_cc_library( absl::core_headers absl::flags_config absl::flags - absl::flags_handle + absl::flags_commandlineflag + absl::flags_commandlineflag_internal absl::flags_internal + absl::flags_private_handle_accessor absl::flags_program_name - absl::flags_registry + absl::flags_reflection absl::flags_usage absl::strings absl::synchronization @@ -259,17 +299,19 @@ absl_cc_test( NAME flags_commandlineflag_test SRCS - "internal/commandlineflag_test.cc" + "commandlineflag_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS absl::flags + absl::flags_commandlineflag + absl::flags_commandlineflag_internal absl::flags_config - absl::flags_handle - absl::flags_registry + absl::flags_private_handle_accessor + absl::flags_reflection absl::memory absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -281,7 +323,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags_config - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -296,11 +338,12 @@ absl_cc_test( absl::core_headers absl::flags absl::flags_config - absl::flags_handle absl::flags_internal - absl::flags_registry + absl::flags_marshalling + absl::flags_reflection absl::strings - gtest_main + absl::time + GTest::gtest_main ) absl_cc_test( @@ -312,7 +355,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags_marshalling - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -325,12 +368,13 @@ absl_cc_test( DEPS absl::flags absl::flags_parse - absl::flags_registry + absl::flags_reflection + absl::flags_usage_internal absl::raw_logging_internal absl::scoped_set_env absl::span absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -342,7 +386,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::flags_path_util - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -355,24 +399,38 @@ absl_cc_test( DEPS absl::flags_program_name absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( NAME - flags_type_erased_test + flags_reflection_test SRCS - "internal/type_erased_test.cc" + "reflection_test.cc" COPTS ${ABSL_TEST_COPTS} DEPS + absl::flags_commandlineflag_internal absl::flags - absl::flags_handle - absl::flags_marshalling - absl::flags_registry + absl::flags_reflection + absl::flags_usage absl::memory absl::strings - gtest_main + GTest::gmock_main +) + +absl_cc_test( + NAME + flags_sequence_lock_test + SRCS + "internal/sequence_lock_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::flags_internal + absl::time + GTest::gmock_main ) absl_cc_test( @@ -387,7 +445,7 @@ absl_cc_test( absl::flags_path_util absl::flags_program_name absl::strings - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -403,9 +461,8 @@ absl_cc_test( absl::flags_path_util absl::flags_program_name absl::flags_parse - absl::flags_registry + absl::flags_reflection absl::flags_usage - absl::memory absl::strings - gtest + GTest::gtest ) diff --git a/third_party/abseil-cpp/absl/flags/commandlineflag.cc b/third_party/abseil-cpp/absl/flags/commandlineflag.cc new file mode 100644 index 0000000000..9f3b4a5a28 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/commandlineflag.cc @@ -0,0 +1,34 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/flags/commandlineflag.h" + +#include <string> + +#include "absl/base/config.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +bool CommandLineFlag::IsRetired() const { return false; } +bool CommandLineFlag::ParseFrom(absl::string_view value, std::string* error) { + return ParseFrom(value, flags_internal::SET_FLAGS_VALUE, + flags_internal::kProgrammaticChange, *error); +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/commandlineflag.h b/third_party/abseil-cpp/absl/flags/commandlineflag.h new file mode 100644 index 0000000000..f2fa08977f --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/commandlineflag.h @@ -0,0 +1,200 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// File: commandlineflag.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `CommandLineFlag`, which acts as a type-erased +// handle for accessing metadata about the Abseil Flag in question. +// +// Because an actual Abseil flag is of an unspecified type, you should not +// manipulate or interact directly with objects of that type. Instead, use the +// CommandLineFlag type as an intermediary. +#ifndef ABSL_FLAGS_COMMANDLINEFLAG_H_ +#define ABSL_FLAGS_COMMANDLINEFLAG_H_ + +#include <memory> +#include <string> + +#include "absl/base/config.h" +#include "absl/base/internal/fast_type_id.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { +class PrivateHandleAccessor; +} // namespace flags_internal + +// CommandLineFlag +// +// This type acts as a type-erased handle for an instance of an Abseil Flag and +// holds reflection information pertaining to that flag. Use CommandLineFlag to +// access a flag's name, location, help string etc. +// +// To obtain an absl::CommandLineFlag, invoke `absl::FindCommandLineFlag()` +// passing it the flag name string. +// +// Example: +// +// // Obtain reflection handle for a flag named "flagname". +// const absl::CommandLineFlag* my_flag_data = +// absl::FindCommandLineFlag("flagname"); +// +// // Now you can get flag info from that reflection handle. +// std::string flag_location = my_flag_data->Filename(); +// ... +class CommandLineFlag { + public: + constexpr CommandLineFlag() = default; + + // Not copyable/assignable. + CommandLineFlag(const CommandLineFlag&) = delete; + CommandLineFlag& operator=(const CommandLineFlag&) = delete; + + // absl::CommandLineFlag::IsOfType() + // + // Return true iff flag has type T. + template <typename T> + inline bool IsOfType() const { + return TypeId() == base_internal::FastTypeId<T>(); + } + + // absl::CommandLineFlag::TryGet() + // + // Attempts to retrieve the flag value. Returns value on success, + // absl::nullopt otherwise. + template <typename T> + absl::optional<T> TryGet() const { + if (IsRetired() || !IsOfType<T>()) { + return absl::nullopt; + } + + // Implementation notes: + // + // We are wrapping a union around the value of `T` to serve three purposes: + // + // 1. `U.value` has correct size and alignment for a value of type `T` + // 2. The `U.value` constructor is not invoked since U's constructor does + // not do it explicitly. + // 3. The `U.value` destructor is invoked since U's destructor does it + // explicitly. This makes `U` a kind of RAII wrapper around non default + // constructible value of T, which is destructed when we leave the + // scope. We do need to destroy U.value, which is constructed by + // CommandLineFlag::Read even though we left it in a moved-from state + // after std::move. + // + // All of this serves to avoid requiring `T` being default constructible. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + Read(&u.value); + // allow retired flags to be "read", so we can report invalid access. + if (IsRetired()) { + return absl::nullopt; + } + return std::move(u.value); + } + + // absl::CommandLineFlag::Name() + // + // Returns name of this flag. + virtual absl::string_view Name() const = 0; + + // absl::CommandLineFlag::Filename() + // + // Returns name of the file where this flag is defined. + virtual std::string Filename() const = 0; + + // absl::CommandLineFlag::Help() + // + // Returns help message associated with this flag. + virtual std::string Help() const = 0; + + // absl::CommandLineFlag::IsRetired() + // + // Returns true iff this object corresponds to retired flag. + virtual bool IsRetired() const; + + // absl::CommandLineFlag::DefaultValue() + // + // Returns the default value for this flag. + virtual std::string DefaultValue() const = 0; + + // absl::CommandLineFlag::CurrentValue() + // + // Returns the current value for this flag. + virtual std::string CurrentValue() const = 0; + + // absl::CommandLineFlag::ParseFrom() + // + // Sets the value of the flag based on specified string `value`. If the flag + // was successfully set to new value, it returns true. Otherwise, sets `error` + // to indicate the error, leaves the flag unchanged, and returns false. + bool ParseFrom(absl::string_view value, std::string* error); + + protected: + ~CommandLineFlag() = default; + + private: + friend class flags_internal::PrivateHandleAccessor; + + // Sets the value of the flag based on specified string `value`. If the flag + // was successfully set to new value, it returns true. Otherwise, sets `error` + // to indicate the error, leaves the flag unchanged, and returns false. There + // are three ways to set the flag's value: + // * Update the current flag value + // * Update the flag's default value + // * Update the current flag value if it was never set before + // The mode is selected based on `set_mode` parameter. + virtual bool ParseFrom(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string& error) = 0; + + // Returns id of the flag's value type. + virtual flags_internal::FlagFastTypeId TypeId() const = 0; + + // Interface to save flag to some persistent state. Returns current flag state + // or nullptr if flag does not support saving and restoring a state. + virtual std::unique_ptr<flags_internal::FlagStateInterface> SaveState() = 0; + + // Copy-construct a new value of the flag's type in a memory referenced by + // the dst based on the current flag's value. + virtual void Read(void* dst) const = 0; + + // To be deleted. Used to return true if flag's current value originated from + // command line. + virtual bool IsSpecifiedOnCommandLine() const = 0; + + // Validates supplied value usign validator or parseflag routine + virtual bool ValidateInputValue(absl::string_view value) const = 0; + + // Checks that flags default value can be converted to string and back to the + // flag's value type. + virtual void CheckDefaultValueParsingRoundtrip() const = 0; +}; + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_COMMANDLINEFLAG_H_ diff --git a/third_party/abseil-cpp/absl/flags/commandlineflag_test.cc b/third_party/abseil-cpp/absl/flags/commandlineflag_test.cc new file mode 100644 index 0000000000..585db4ba78 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/commandlineflag_test.cc @@ -0,0 +1,231 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/flags/commandlineflag.h" + +#include <memory> +#include <string> + +#include "gtest/gtest.h" +#include "absl/flags/flag.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/private_handle_accessor.h" +#include "absl/flags/reflection.h" +#include "absl/flags/usage_config.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +ABSL_FLAG(int, int_flag, 201, "int_flag help"); +ABSL_FLAG(std::string, string_flag, "dflt", + absl::StrCat("string_flag", " help")); +ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); + +// These are only used to test default values. +ABSL_FLAG(int, int_flag2, 201, ""); +ABSL_FLAG(std::string, string_flag2, "dflt", ""); + +namespace { + +namespace flags = absl::flags_internal; + +class CommandLineFlagTest : public testing::Test { + protected: + static void SetUpTestSuite() { + // Install a function to normalize filenames before this test is run. + absl::FlagsUsageConfig default_config; + default_config.normalize_filename = &CommandLineFlagTest::NormalizeFileName; + absl::SetFlagsUsageConfig(default_config); + } + + void SetUp() override { flag_saver_ = absl::make_unique<absl::FlagSaver>(); } + void TearDown() override { flag_saver_.reset(); } + + private: + static std::string NormalizeFileName(absl::string_view fname) { +#ifdef _WIN32 + std::string normalized(fname); + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + fname = normalized; +#endif + return std::string(fname); + } + + std::unique_ptr<absl::FlagSaver> flag_saver_; +}; + +TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { + auto* flag_01 = absl::FindCommandLineFlag("int_flag"); + + ASSERT_TRUE(flag_01); + EXPECT_EQ(flag_01->Name(), "int_flag"); + EXPECT_EQ(flag_01->Help(), "int_flag help"); + EXPECT_TRUE(!flag_01->IsRetired()); + EXPECT_TRUE(flag_01->IsOfType<int>()); + EXPECT_TRUE(!flag_01->IsOfType<bool>()); + EXPECT_TRUE(!flag_01->IsOfType<std::string>()); + EXPECT_TRUE(absl::EndsWith(flag_01->Filename(), + "absl/flags/commandlineflag_test.cc")) + << flag_01->Filename(); + + auto* flag_02 = absl::FindCommandLineFlag("string_flag"); + + ASSERT_TRUE(flag_02); + EXPECT_EQ(flag_02->Name(), "string_flag"); + EXPECT_EQ(flag_02->Help(), "string_flag help"); + EXPECT_TRUE(!flag_02->IsRetired()); + EXPECT_TRUE(flag_02->IsOfType<std::string>()); + EXPECT_TRUE(!flag_02->IsOfType<bool>()); + EXPECT_TRUE(!flag_02->IsOfType<int>()); + EXPECT_TRUE(absl::EndsWith(flag_02->Filename(), + "absl/flags/commandlineflag_test.cc")) + << flag_02->Filename(); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestValueAccessMethods) { + absl::SetFlag(&FLAGS_int_flag2, 301); + auto* flag_01 = absl::FindCommandLineFlag("int_flag2"); + + ASSERT_TRUE(flag_01); + EXPECT_EQ(flag_01->CurrentValue(), "301"); + EXPECT_EQ(flag_01->DefaultValue(), "201"); + + absl::SetFlag(&FLAGS_string_flag2, "new_str_value"); + auto* flag_02 = absl::FindCommandLineFlag("string_flag2"); + + ASSERT_TRUE(flag_02); + EXPECT_EQ(flag_02->CurrentValue(), "new_str_value"); + EXPECT_EQ(flag_02->DefaultValue(), "dflt"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestParseFromCurrentValue) { + std::string err; + + auto* flag_01 = absl::FindCommandLineFlag("int_flag"); + EXPECT_FALSE( + flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "11", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); + EXPECT_FALSE( + flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "-123", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_FALSE( + flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); + + EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'"); + EXPECT_FALSE( + flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); + + EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "A1", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'"); + EXPECT_FALSE( + flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "0x10", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16); + EXPECT_FALSE( + flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "011", flags::SET_FLAGS_VALUE, flags::kCommandLine, err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); + EXPECT_TRUE(flags::PrivateHandleAccessor::IsSpecifiedOnCommandLine(*flag_01)); + + EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err)); + EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); + + auto* flag_02 = absl::FindCommandLineFlag("string_flag"); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + err)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz"); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_02, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, err)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), ""); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestParseFromDefaultValue) { + std::string err; + + auto* flag_01 = absl::FindCommandLineFlag("int_flag"); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, + err)); + EXPECT_EQ(flag_01->DefaultValue(), "111"); + + auto* flag_02 = absl::FindCommandLineFlag("string_flag"); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, + err)); + EXPECT_EQ(flag_02->DefaultValue(), "abc"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestParseFromIfDefault) { + std::string err; + + auto* flag_01 = absl::FindCommandLineFlag("int_flag"); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, + err)) + << err; + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, + err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); + // EXPECT_EQ(err, "ERROR: int_flag is already set to 22"); + + // Reset back to default value + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "201", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + err)); + + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + *flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, + err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201); + // EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/flags/config.h b/third_party/abseil-cpp/absl/flags/config.h index 001f8feaf6..5ab1f311dc 100644 --- a/third_party/abseil-cpp/absl/flags/config.h +++ b/third_party/abseil-cpp/absl/flags/config.h @@ -45,17 +45,6 @@ #define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES #endif -// ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD macro is used for using atomics with -// double words, e.g. absl::Duration. -// For reasons in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878, modern -// versions of GCC do not support cmpxchg16b instruction in standard atomics. -#ifdef ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD -#error "ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD should not be defined." -#elif defined(__clang__) && defined(__x86_64__) && \ - defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) -#define ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD 1 -#endif - // ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI // for flag type identification. #ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI @@ -64,4 +53,24 @@ #define ABSL_FLAGS_INTERNAL_HAS_RTTI 1 #endif // !defined(__GNUC__) || defined(__GXX_RTTI) +// These macros represent the "source of truth" for the list of supported +// built-in types. +#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \ + A(bool, bool) \ + A(short, short) \ + A(unsigned short, unsigned_short) \ + A(int, int) \ + A(unsigned int, unsigned_int) \ + A(long, long) \ + A(unsigned long, unsigned_long) \ + A(long long, long_long) \ + A(unsigned long long, unsigned_long_long) \ + A(double, double) \ + A(float, float) + +#define ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(A) \ + ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \ + A(std::string, std_string) \ + A(std::vector<std::string>, std_vector_of_string) + #endif // ABSL_FLAGS_CONFIG_H_ diff --git a/third_party/abseil-cpp/absl/flags/declare.h b/third_party/abseil-cpp/absl/flags/declare.h index 0f8cc6a599..b9794d8b85 100644 --- a/third_party/abseil-cpp/absl/flags/declare.h +++ b/third_party/abseil-cpp/absl/flags/declare.h @@ -26,7 +26,6 @@ #define ABSL_FLAGS_DECLARE_H_ #include "absl/base/config.h" -#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN diff --git a/third_party/abseil-cpp/absl/flags/flag.cc b/third_party/abseil-cpp/absl/flags/flag.cc index f7a457bf0c..531df1287a 100644 --- a/third_party/abseil-cpp/absl/flags/flag.cc +++ b/third_party/abseil-cpp/absl/flags/flag.cc @@ -16,8 +16,6 @@ #include "absl/flags/flag.h" #include "absl/base/config.h" -#include "absl/flags/internal/commandlineflag.h" -#include "absl/flags/internal/flag.h" namespace absl { ABSL_NAMESPACE_BEGIN diff --git a/third_party/abseil-cpp/absl/flags/flag.h b/third_party/abseil-cpp/absl/flags/flag.h index cff02c1fcb..a724ccc97d 100644 --- a/third_party/abseil-cpp/absl/flags/flag.h +++ b/third_party/abseil-cpp/absl/flags/flag.h @@ -33,14 +33,12 @@ #include <type_traits> #include "absl/base/attributes.h" -#include "absl/base/casts.h" #include "absl/base/config.h" +#include "absl/base/optimization.h" #include "absl/flags/config.h" -#include "absl/flags/declare.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/registry.h" -#include "absl/flags/marshalling.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -73,98 +71,7 @@ ABSL_NAMESPACE_BEGIN template <typename T> using Flag = flags_internal::Flag<T>; #else -// MSVC debug builds do not implement initialization with constexpr constructors -// correctly. To work around this we add a level of indirection, so that the -// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias -// to that class) and dynamically allocates an instance when necessary. We also -// forward all calls to internal::Flag methods via trampoline methods. In this -// setup the `absl::Flag` class does not have constructor and virtual methods, -// all the data members are public and thus MSVC is able to initialize it at -// link time. To deal with multiple threads accessing the flag for the first -// time concurrently we use an atomic boolean indicating if flag object is -// initialized. We also employ the double-checked locking pattern where the -// second level of protection is a global Mutex, so if two threads attempt to -// construct the flag concurrently only one wins. -// This solution is based on a recomendation here: -// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 - -namespace flags_internal { -absl::Mutex* GetGlobalConstructionGuard(); -} // namespace flags_internal - -template <typename T> -class Flag { - public: - // No constructor and destructor to ensure this is an aggregate type. - // Visual Studio 2015 still requires the constructor for class to be - // constexpr initializable. -#if _MSC_VER <= 1900 - constexpr Flag(const char* name, const char* filename, - const flags_internal::HelpGenFunc help_gen, - const flags_internal::FlagDfltGenFunc default_value_gen) - : name_(name), - filename_(filename), - help_gen_(help_gen), - default_value_gen_(default_value_gen), - inited_(false), - impl_(nullptr) {} -#endif - - flags_internal::Flag<T>* GetImpl() const { - if (!inited_.load(std::memory_order_acquire)) { - absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); - - if (inited_.load(std::memory_order_acquire)) { - return impl_; - } - - impl_ = - new flags_internal::Flag<T>(name_, filename_, - {flags_internal::FlagHelpMsg(help_gen_), - flags_internal::FlagHelpKind::kGenFunc}, - default_value_gen_); - inited_.store(true, std::memory_order_release); - } - - return impl_; - } - - // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. - // See https://abseil.io/docs/cpp/guides/flags - bool IsRetired() const { return GetImpl()->IsRetired(); } - bool IsAbseilFlag() const { return GetImpl()->IsAbseilFlag(); } - absl::string_view Name() const { return GetImpl()->Name(); } - std::string Help() const { return GetImpl()->Help(); } - bool IsModified() const { return GetImpl()->IsModified(); } - bool IsSpecifiedOnCommandLine() const { - return GetImpl()->IsSpecifiedOnCommandLine(); - } - absl::string_view Typename() const { return GetImpl()->Typename(); } - std::string Filename() const { return GetImpl()->Filename(); } - std::string DefaultValue() const { return GetImpl()->DefaultValue(); } - std::string CurrentValue() const { return GetImpl()->CurrentValue(); } - template <typename U> - inline bool IsOfType() const { - return GetImpl()->template IsOfType<U>(); - } - T Get() const { return GetImpl()->Get(); } - bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); } - void Set(const T& v) { GetImpl()->Set(v); } - void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) { - GetImpl()->SetCallback(mutation_callback); - } - void InvokeCallback() { GetImpl()->InvokeCallback(); } - - // The data members are logically private, but they need to be public for - // this to be an aggregate type. - const char* name_; - const char* filename_; - const flags_internal::HelpGenFunc help_gen_; - const flags_internal::FlagDfltGenFunc default_value_gen_; - - mutable std::atomic<bool> inited_; - mutable flags_internal::Flag<T>* impl_; -}; +#include "absl/flags/internal/flag_msvc.inc" #endif // GetFlag() @@ -185,7 +92,7 @@ class Flag { // std::string first_name = absl::GetFlag(FLAGS_firstname); template <typename T> ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) { - return flag.Get(); + return flags_internal::FlagImplPeer::InvokeGet<T>(flag); } // SetFlag() @@ -197,7 +104,7 @@ ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) { // but especially within performance-critical code. template <typename T> void SetFlag(absl::Flag<T>* flag, const T& v) { - flag->Set(v); + flags_internal::FlagImplPeer::InvokeSet(*flag, v); } // Overload of `SetFlag()` to allow callers to pass in a value that is @@ -206,7 +113,22 @@ void SetFlag(absl::Flag<T>* flag, const T& v) { template <typename T, typename V> void SetFlag(absl::Flag<T>* flag, const V& v) { T value(v); - flag->Set(value); + flags_internal::FlagImplPeer::InvokeSet(*flag, value); +} + +// GetFlagReflectionHandle() +// +// Returns the reflection handle corresponding to specified Abseil Flag +// instance. Use this handle to access flag's reflection information, like name, +// location, default value etc. +// +// Example: +// +// std::string = absl::GetFlagReflectionHandle(FLAGS_count).DefaultValue(); + +template <typename T> +const CommandLineFlag& GetFlagReflectionHandle(const absl::Flag<T>& f) { + return flags_internal::FlagImplPeer::InvokeReflect(f); } ABSL_NAMESPACE_END @@ -249,6 +171,8 @@ ABSL_NAMESPACE_END // // ABSL_FLAG(T, name, default_value, help).OnUpdate(callback); // +// `callback` should be convertible to `void (*)()`. +// // After any setting of the flag value, the callback will be called at least // once. A rapid sequence of changes may be merged together into the same // callback. No concurrent calls to the callback will be made for the same @@ -263,33 +187,36 @@ ABSL_NAMESPACE_END // Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this // comment serves as its API documentation. - // ----------------------------------------------------------------------------- // Implementation details below this section // ----------------------------------------------------------------------------- // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES +#if !defined(_MSC_VER) || defined(__clang__) +#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag +#define ABSL_FLAG_IMPL_HELP_ARG(name) \ + absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>( \ + FLAGS_help_storage_##name) +#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) \ + absl::flags_internal::DefaultArg<Type, AbslFlagDefaultGenFor##name>(0) +#else +#define ABSL_FLAG_IMPL_FLAG_PTR(flag) flag.GetImpl() +#define ABSL_FLAG_IMPL_HELP_ARG(name) &AbslFlagHelpGenFor##name::NonConst +#define ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name) &AbslFlagDefaultGenFor##name::Gen +#endif #if ABSL_FLAGS_STRIP_NAMES #define ABSL_FLAG_IMPL_FLAGNAME(txt) "" #define ABSL_FLAG_IMPL_FILENAME() "" -#if !defined(_MSC_VER) || defined(__clang__) -#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar<T, false>(&flag) -#else -#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar<T, false>(flag.GetImpl()) -#endif +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(flag), \ + nullptr) #else #define ABSL_FLAG_IMPL_FLAGNAME(txt) txt #define ABSL_FLAG_IMPL_FILENAME() __FILE__ -#if !defined(_MSC_VER) || defined(__clang__) -#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar<T, true>(&flag) -#else -#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ - absl::flags_internal::FlagRegistrar<T, true>(flag.GetImpl()) -#endif +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar<T, true>(ABSL_FLAG_IMPL_FLAG_PTR(flag), \ + __FILE__) #endif // ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP @@ -305,50 +232,48 @@ ABSL_NAMESPACE_END // between the two via the call to HelpArg in absl::Flag instantiation below. // If help message expression is constexpr evaluable compiler will optimize // away this whole struct. -#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ - struct AbslFlagHelpGenFor##name { \ - template <typename T = void> \ - static constexpr const char* Const() { \ - return absl::flags_internal::HelpConstexprWrap( \ - ABSL_FLAG_IMPL_FLAGHELP(txt)); \ - } \ - static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ - } - -#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - static void* AbslFlagsInitFlag##name() { \ - return absl::flags_internal::MakeFromDefaultValue<Type>(default_value); \ - } +// TODO(rogeeff): place these generated structs into local namespace and apply +// ABSL_INTERNAL_UNIQUE_SHORT_NAME. +// TODO(rogeeff): Apply __attribute__((nodebug)) to FLAGS_help_storage_##name +#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ + struct AbslFlagHelpGenFor##name { \ + /* The expression is run in the caller as part of the */ \ + /* default value argument. That keeps temporaries alive */ \ + /* long enough for NonConst to work correctly. */ \ + static constexpr absl::string_view Value( \ + absl::string_view absl_flag_help = ABSL_FLAG_IMPL_FLAGHELP(txt)) { \ + return absl_flag_help; \ + } \ + static std::string NonConst() { return std::string(Value()); } \ + }; \ + constexpr auto FLAGS_help_storage_##name ABSL_INTERNAL_UNIQUE_SMALL_NAME() \ + ABSL_ATTRIBUTE_SECTION_VARIABLE(flags_help_cold) = \ + absl::flags_internal::HelpStringAsArray<AbslFlagHelpGenFor##name>( \ + 0); + +#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + struct AbslFlagDefaultGenFor##name { \ + Type value = absl::flags_internal::InitDefaultValue<Type>(default_value); \ + static void Gen(void* absl_flag_default_loc) { \ + new (absl_flag_default_loc) Type(AbslFlagDefaultGenFor##name{}.value); \ + } \ + }; // ABSL_FLAG_IMPL // // Note: Name of registrar object is not arbitrary. It is used to "grab" // global name for FLAGS_no<flag_name> symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. -#if !defined(_MSC_VER) || defined(__clang__) -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - namespace absl /* block flags in namespaces */ {} \ - ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ - ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ - absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \ - &AbslFlagsInitFlag##name}; \ - extern bool FLAGS_no##name; \ - bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) -#else -// MSVC version uses aggregate initialization. We also do not try to -// optimize away help wrapper. -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - namespace absl /* block flags in namespaces */ {} \ - ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ - ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ - &AbslFlagHelpGenFor##name::NonConst, &AbslFlagsInitFlag##name}; \ - extern bool FLAGS_no##name; \ - bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) -#endif +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ + ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ + ABSL_FLAG_IMPL_HELP_ARG(name), ABSL_FLAG_IMPL_DEFAULT_ARG(Type, name)}; \ + extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ + absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ + ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) // ABSL_RETIRED_FLAG // @@ -369,11 +294,12 @@ ABSL_NAMESPACE_END // // `default_value` is only used as a double check on the type. `explanation` is // unused. -// TODO(rogeeff): Return an anonymous struct instead of bool, and place it into -// the unnamed namespace. -#define ABSL_RETIRED_FLAG(type, flagname, default_value, explanation) \ - ABSL_ATTRIBUTE_UNUSED static const bool ignored_##flagname = \ - ([] { return type(default_value); }, \ - absl::flags_internal::RetiredFlag<type>(#flagname)) +// TODO(rogeeff): replace RETIRED_FLAGS with FLAGS once forward declarations of +// retired flags are cleaned up. +#define ABSL_RETIRED_FLAG(type, name, default_value, explanation) \ + static absl::flags_internal::RetiredFlag<type> RETIRED_FLAGS_##name; \ + ABSL_ATTRIBUTE_UNUSED static const auto RETIRED_FLAGS_REG_##name = \ + (RETIRED_FLAGS_##name.Retire(#name), \ + ::absl::flags_internal::FlagRegistrarEmpty{}) #endif // ABSL_FLAGS_FLAG_H_ diff --git a/third_party/abseil-cpp/absl/flags/flag_benchmark.cc b/third_party/abseil-cpp/absl/flags/flag_benchmark.cc index 87f731704c..fc572d9ce3 100644 --- a/third_party/abseil-cpp/absl/flags/flag_benchmark.cc +++ b/third_party/abseil-cpp/absl/flags/flag_benchmark.cc @@ -13,7 +13,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <stdint.h> + +#include <string> +#include <vector> + #include "absl/flags/flag.h" +#include "absl/flags/marshalling.h" +#include "absl/flags/parse.h" +#include "absl/flags/reflection.h" +#include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/optional.h" #include "benchmark/benchmark.h" @@ -92,20 +101,150 @@ std::string AbslUnparseFlag(const UDT&) { return ""; } A(AbslDuration) \ A(UDT) -#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, ""); +#define REPLICATE_0(A, T, name, index) A(T, name, index) +#define REPLICATE_1(A, T, name, index) \ + REPLICATE_0(A, T, name, index##0) REPLICATE_0(A, T, name, index##1) +#define REPLICATE_2(A, T, name, index) \ + REPLICATE_1(A, T, name, index##0) REPLICATE_1(A, T, name, index##1) +#define REPLICATE_3(A, T, name, index) \ + REPLICATE_2(A, T, name, index##0) REPLICATE_2(A, T, name, index##1) +#define REPLICATE_4(A, T, name, index) \ + REPLICATE_3(A, T, name, index##0) REPLICATE_3(A, T, name, index##1) +#define REPLICATE_5(A, T, name, index) \ + REPLICATE_4(A, T, name, index##0) REPLICATE_4(A, T, name, index##1) +#define REPLICATE_6(A, T, name, index) \ + REPLICATE_5(A, T, name, index##0) REPLICATE_5(A, T, name, index##1) +#define REPLICATE_7(A, T, name, index) \ + REPLICATE_6(A, T, name, index##0) REPLICATE_6(A, T, name, index##1) +#define REPLICATE_8(A, T, name, index) \ + REPLICATE_7(A, T, name, index##0) REPLICATE_7(A, T, name, index##1) +#define REPLICATE_9(A, T, name, index) \ + REPLICATE_8(A, T, name, index##0) REPLICATE_8(A, T, name, index##1) +#if defined(_MSC_VER) +#define REPLICATE(A, T, name) \ + REPLICATE_7(A, T, name, 0) REPLICATE_7(A, T, name, 1) +#define SINGLE_FLAG(T) FLAGS_##T##_flag_00000000 +#else +#define REPLICATE(A, T, name) \ + REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1) +#define SINGLE_FLAG(T) FLAGS_##T##_flag_0000000000 +#endif +#define REPLICATE_ALL(A, T, name) \ + REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1) + +#define COUNT(T, name, index) +1 +constexpr size_t kNumFlags = 0 REPLICATE(COUNT, _, _); +#if defined(__clang__) && defined(__linux__) +// Force the flags used for benchmarks into a separate ELF section. +// This ensures that, even when other parts of the code might change size, +// the layout of the flags across cachelines is kept constant. This makes +// benchmark results more reproducible across unrelated code changes. +#pragma clang section data = ".benchmark_flags" +#endif +#define DEFINE_FLAG(T, name, index) ABSL_FLAG(T, name##_##index, {}, ""); +#define FLAG_DEF(T) REPLICATE(DEFINE_FLAG, T, T##_flag); BENCHMARKED_TYPES(FLAG_DEF) +#if defined(__clang__) && defined(__linux__) +#pragma clang section data = "" +#endif +// Register thousands of flags to bloat up the size of the registry. +// This mimics real life production binaries. +#define BLOAT_FLAG(_unused1, _unused2, index) \ + ABSL_FLAG(int, bloat_flag_##index, 0, ""); +REPLICATE_ALL(BLOAT_FLAG, _, _) namespace { -#define BM_GetFlag(T) \ - void BM_GetFlag_##T(benchmark::State& state) { \ - for (auto _ : state) { \ - benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \ - } \ - } \ - BENCHMARK(BM_GetFlag_##T); +#define FLAG_PTR(T, name, index) &FLAGS_##name##_##index, +#define FLAG_PTR_ARR(T) \ + static constexpr absl::Flag<T>* FlagPtrs_##T[] = { \ + REPLICATE(FLAG_PTR, T, T##_flag)}; +BENCHMARKED_TYPES(FLAG_PTR_ARR) + +#define BM_SingleGetFlag(T) \ + void BM_SingleGetFlag_##T(benchmark::State& state) { \ + for (auto _ : state) { \ + benchmark::DoNotOptimize(absl::GetFlag(SINGLE_FLAG(T))); \ + } \ + } \ + BENCHMARK(BM_SingleGetFlag_##T)->ThreadRange(1, 16); + +BENCHMARKED_TYPES(BM_SingleGetFlag) + +template <typename T> +struct Accumulator { + using type = T; +}; +template <> +struct Accumulator<String> { + using type = size_t; +}; +template <> +struct Accumulator<VectorOfStrings> { + using type = size_t; +}; +template <> +struct Accumulator<OptionalInt> { + using type = bool; +}; +template <> +struct Accumulator<OptionalString> { + using type = bool; +}; +template <> +struct Accumulator<UDT> { + using type = bool; +}; + +template <typename T> +void Accumulate(typename Accumulator<T>::type& a, const T& f) { + a += f; +} +void Accumulate(bool& a, bool f) { a = a || f; } +void Accumulate(size_t& a, const std::string& f) { a += f.size(); } +void Accumulate(size_t& a, const std::vector<std::string>& f) { a += f.size(); } +void Accumulate(bool& a, const OptionalInt& f) { a |= f.has_value(); } +void Accumulate(bool& a, const OptionalString& f) { a |= f.has_value(); } +void Accumulate(bool& a, const UDT& f) { + a |= reinterpret_cast<int64_t>(&f) & 0x1; +} + +#define BM_ManyGetFlag(T) \ + void BM_ManyGetFlag_##T(benchmark::State& state) { \ + Accumulator<T>::type res = {}; \ + while (state.KeepRunningBatch(kNumFlags)) { \ + for (auto* flag_ptr : FlagPtrs_##T) { \ + Accumulate(res, absl::GetFlag(*flag_ptr)); \ + } \ + } \ + benchmark::DoNotOptimize(res); \ + } \ + BENCHMARK(BM_ManyGetFlag_##T)->ThreadRange(1, 8); + +BENCHMARKED_TYPES(BM_ManyGetFlag) -BENCHMARKED_TYPES(BM_GetFlag) +void BM_ThreadedFindCommandLineFlag(benchmark::State& state) { + char dummy[] = "dummy"; + char* argv[] = {dummy}; + // We need to ensure that flags have been parsed. That is where the registry + // is finalized. + absl::ParseCommandLine(1, argv); + + while (state.KeepRunningBatch(kNumFlags)) { + for (auto* flag_ptr : FlagPtrs_bool) { + benchmark::DoNotOptimize(absl::FindCommandLineFlag(flag_ptr->Name())); + } + } +} +BENCHMARK(BM_ThreadedFindCommandLineFlag)->ThreadRange(1, 16); } // namespace + +#define InvokeGetFlag(T) \ + T AbslInvokeGetFlag##T() { return absl::GetFlag(SINGLE_FLAG(T)); } \ + int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1); + +BENCHMARKED_TYPES(InvokeGetFlag) + +// To veiw disassembly use: gdb ${BINARY} -batch -ex "disassemble /s $FUNC" diff --git a/third_party/abseil-cpp/absl/flags/flag_benchmark.lds b/third_party/abseil-cpp/absl/flags/flag_benchmark.lds new file mode 100644 index 0000000000..af115dfc00 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/flag_benchmark.lds @@ -0,0 +1,13 @@ +/* This linker script forces the flags used by flags_benchmark + * into a separate page-aligned section. This isn't necessary for + * correctness but ensures that the benchmark results are more + * reproducible across unrelated code changes. + */ +SECTIONS { + .benchmark_flags : { + . = ALIGN(0x1000); + * (.benchmark_flags); + } +} + +INSERT AFTER .data diff --git a/third_party/abseil-cpp/absl/flags/flag_test.cc b/third_party/abseil-cpp/absl/flags/flag_test.cc index 4984d28403..6e974a5b5e 100644 --- a/third_party/abseil-cpp/absl/flags/flag_test.cc +++ b/third_party/abseil-cpp/absl/flags/flag_test.cc @@ -15,25 +15,31 @@ #include "absl/flags/flag.h" +#include <stddef.h> #include <stdint.h> +#include <atomic> #include <cmath> +#include <new> #include <string> +#include <thread> // NOLINT #include <vector> #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/macros.h" #include "absl/flags/config.h" #include "absl/flags/declare.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" -#include "absl/flags/internal/registry.h" +#include "absl/flags/marshalling.h" +#include "absl/flags/reflection.h" #include "absl/flags/usage_config.h" #include "absl/strings/match.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag); ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag); @@ -43,37 +49,19 @@ namespace { namespace flags = absl::flags_internal; std::string TestHelpMsg() { return "dynamic help"; } +#if defined(_MSC_VER) && !defined(__clang__) +std::string TestLiteralHelpMsg() { return "literal help"; } +#endif template <typename T> -void* TestMakeDflt() { - return new T{}; +void TestMakeDflt(void* dst) { + new (dst) T{}; } void TestCallback() {} -template <typename T> -bool TestConstructionFor() { - constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"), - flags::FlagHelpKind::kLiteral}; - constexpr flags::Flag<T> f1("f1", "file", help_arg, &TestMakeDflt<T>); - EXPECT_EQ(f1.Name(), "f1"); - EXPECT_EQ(f1.Help(), "literal help"); - EXPECT_EQ(f1.Filename(), "file"); - - ABSL_CONST_INIT static flags::Flag<T> f2( - "f2", "file", - {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, - &TestMakeDflt<T>); - flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback); - - EXPECT_EQ(f2.Name(), "f2"); - EXPECT_EQ(f2.Help(), "dynamic help"); - EXPECT_EQ(f2.Filename(), "file"); - - return true; -} - struct UDT { UDT() = default; UDT(const UDT&) = default; + UDT& operator=(const UDT&) = default; }; bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } std::string AbslUnparseFlag(const UDT&) { return ""; } @@ -96,21 +84,124 @@ class FlagTest : public testing::Test { #endif return std::string(fname); } + absl::FlagSaver flag_saver_; }; -TEST_F(FlagTest, TestConstruction) { - TestConstructionFor<bool>(); - TestConstructionFor<int16_t>(); - TestConstructionFor<uint16_t>(); - TestConstructionFor<int32_t>(); - TestConstructionFor<uint32_t>(); - TestConstructionFor<int64_t>(); - TestConstructionFor<uint64_t>(); - TestConstructionFor<double>(); - TestConstructionFor<float>(); - TestConstructionFor<std::string>(); +struct S1 { + S1() = default; + S1(const S1&) = default; + int32_t f1; + int64_t f2; +}; + +struct S2 { + S2() = default; + S2(const S2&) = default; + int64_t f1; + double f2; +}; + +TEST_F(FlagTest, Traits) { + EXPECT_EQ(flags::StorageKind<int>(), + flags::FlagValueStorageKind::kValueAndInitBit); + EXPECT_EQ(flags::StorageKind<bool>(), + flags::FlagValueStorageKind::kValueAndInitBit); + EXPECT_EQ(flags::StorageKind<double>(), + flags::FlagValueStorageKind::kOneWordAtomic); + EXPECT_EQ(flags::StorageKind<int64_t>(), + flags::FlagValueStorageKind::kOneWordAtomic); + + EXPECT_EQ(flags::StorageKind<S1>(), + flags::FlagValueStorageKind::kSequenceLocked); + EXPECT_EQ(flags::StorageKind<S2>(), + flags::FlagValueStorageKind::kSequenceLocked); +// Make sure absl::Duration uses the sequence-locked code path. MSVC 2015 +// doesn't consider absl::Duration to be trivially-copyable so we just +// restrict this to clang as it seems to be a well-behaved compiler. +#ifdef __clang__ + EXPECT_EQ(flags::StorageKind<absl::Duration>(), + flags::FlagValueStorageKind::kSequenceLocked); +#endif + + EXPECT_EQ(flags::StorageKind<std::string>(), + flags::FlagValueStorageKind::kAlignedBuffer); + EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(), + flags::FlagValueStorageKind::kAlignedBuffer); +} + +// -------------------------------------------------------------------- + +constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"), + flags::FlagHelpKind::kLiteral}; + +using String = std::string; + +#if !defined(_MSC_VER) || defined(__clang__) +#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \ + constexpr flags::FlagDefaultArg f1default##T{ \ + flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \ + constexpr absl::Flag<T> f1##T{"f1", "file", help_arg, f1default##T}; \ + ABSL_CONST_INIT absl::Flag<T> f2##T { \ + "f2", "file", \ + {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \ + flags::FlagDefaultArg { \ + flags::FlagDefaultSrc(&TestMakeDflt<T>), \ + flags::FlagDefaultKind::kGenFunc \ + } \ + } +#else +#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \ + constexpr flags::FlagDefaultArg f1default##T{ \ + flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \ + constexpr absl::Flag<T> f1##T{"f1", "file", &TestLiteralHelpMsg, \ + &TestMakeDflt<T>}; \ + ABSL_CONST_INIT absl::Flag<T> f2##T { \ + "f2", "file", &TestHelpMsg, &TestMakeDflt<T> \ + } +#endif + +DEFINE_CONSTRUCTED_FLAG(bool, true, kOneWord); +DEFINE_CONSTRUCTED_FLAG(int16_t, 1, kOneWord); +DEFINE_CONSTRUCTED_FLAG(uint16_t, 2, kOneWord); +DEFINE_CONSTRUCTED_FLAG(int32_t, 3, kOneWord); +DEFINE_CONSTRUCTED_FLAG(uint32_t, 4, kOneWord); +DEFINE_CONSTRUCTED_FLAG(int64_t, 5, kOneWord); +DEFINE_CONSTRUCTED_FLAG(uint64_t, 6, kOneWord); +DEFINE_CONSTRUCTED_FLAG(float, 7.8, kOneWord); +DEFINE_CONSTRUCTED_FLAG(double, 9.10, kOneWord); +DEFINE_CONSTRUCTED_FLAG(String, &TestMakeDflt<String>, kGenFunc); +DEFINE_CONSTRUCTED_FLAG(UDT, &TestMakeDflt<UDT>, kGenFunc); + +template <typename T> +bool TestConstructionFor(const absl::Flag<T>& f1, absl::Flag<T>& f2) { + EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Name(), "f1"); + EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Help(), "literal help"); + EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Filename(), "file"); + + flags::FlagRegistrar<T, false>(ABSL_FLAG_IMPL_FLAG_PTR(f2), nullptr) + .OnUpdate(TestCallback); + + EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Name(), "f2"); + EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Help(), "dynamic help"); + EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Filename(), "file"); + + return true; +} + +#define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, f2##T); - TestConstructionFor<UDT>(); +TEST_F(FlagTest, TestConstruction) { + TEST_CONSTRUCTED_FLAG(bool); + TEST_CONSTRUCTED_FLAG(int16_t); + TEST_CONSTRUCTED_FLAG(uint16_t); + TEST_CONSTRUCTED_FLAG(int32_t); + TEST_CONSTRUCTED_FLAG(uint32_t); + TEST_CONSTRUCTED_FLAG(int64_t); + TEST_CONSTRUCTED_FLAG(uint64_t); + TEST_CONSTRUCTED_FLAG(float); + TEST_CONSTRUCTED_FLAG(double); + TEST_CONSTRUCTED_FLAG(String); + TEST_CONSTRUCTED_FLAG(UDT); } // -------------------------------------------------------------------- @@ -128,6 +219,7 @@ ABSL_DECLARE_FLAG(uint64_t, test_flag_08); ABSL_DECLARE_FLAG(double, test_flag_09); ABSL_DECLARE_FLAG(float, test_flag_10); ABSL_DECLARE_FLAG(std::string, test_flag_11); +ABSL_DECLARE_FLAG(absl::Duration, test_flag_12); namespace { @@ -135,17 +227,30 @@ namespace { TEST_F(FlagTest, TestFlagDeclaration) { // test that we can access flag objects. - EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); - EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); - EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); - EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); - EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); - EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); - EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); - EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); - EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); - EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); - EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(), + "test_flag_01"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(), + "test_flag_02"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(), + "test_flag_03"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(), + "test_flag_04"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(), + "test_flag_05"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(), + "test_flag_06"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(), + "test_flag_07"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(), + "test_flag_08"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(), + "test_flag_09"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(), + "test_flag_10"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(), + "test_flag_11"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(), + "test_flag_12"); } #endif // !ABSL_FLAGS_STRIP_NAMES @@ -164,6 +269,7 @@ ABSL_FLAG(uint64_t, test_flag_08, 9876543, "test flag 08"); ABSL_FLAG(double, test_flag_09, -9.876e-50, "test flag 09"); ABSL_FLAG(float, test_flag_10, 1.234e12f, "test flag 10"); ABSL_FLAG(std::string, test_flag_11, "", "test flag 11"); +ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), "test flag 12"); namespace { @@ -171,66 +277,169 @@ namespace { TEST_F(FlagTest, TestFlagDefinition) { absl::string_view expected_file_name = "absl/flags/flag_test.cc"; - EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); - EXPECT_EQ(FLAGS_test_flag_01.Help(), "test flag 01"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name)) - << FLAGS_test_flag_01.Filename(); - - EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); - EXPECT_EQ(FLAGS_test_flag_02.Help(), "test flag 02"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name)) - << FLAGS_test_flag_02.Filename(); - - EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); - EXPECT_EQ(FLAGS_test_flag_03.Help(), "test flag 03"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name)) - << FLAGS_test_flag_03.Filename(); - - EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); - EXPECT_EQ(FLAGS_test_flag_04.Help(), "test flag 04"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name)) - << FLAGS_test_flag_04.Filename(); - - EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); - EXPECT_EQ(FLAGS_test_flag_05.Help(), "test flag 05"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name)) - << FLAGS_test_flag_05.Filename(); - - EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); - EXPECT_EQ(FLAGS_test_flag_06.Help(), "test flag 06"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name)) - << FLAGS_test_flag_06.Filename(); - - EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); - EXPECT_EQ(FLAGS_test_flag_07.Help(), "test flag 07"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name)) - << FLAGS_test_flag_07.Filename(); - - EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); - EXPECT_EQ(FLAGS_test_flag_08.Help(), "test flag 08"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name)) - << FLAGS_test_flag_08.Filename(); - - EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); - EXPECT_EQ(FLAGS_test_flag_09.Help(), "test flag 09"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name)) - << FLAGS_test_flag_09.Filename(); - - EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); - EXPECT_EQ(FLAGS_test_flag_10.Help(), "test flag 10"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name)) - << FLAGS_test_flag_10.Filename(); - - EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); - EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11"); - EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name)) - << FLAGS_test_flag_11.Filename(); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(), + "test_flag_01"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Help(), + "test flag 01"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(), + "test_flag_02"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Help(), + "test flag 02"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(), + "test_flag_03"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Help(), + "test flag 03"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(), + "test_flag_04"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Help(), + "test flag 04"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(), + "test_flag_05"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Help(), + "test flag 05"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(), + "test_flag_06"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Help(), + "test flag 06"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(), + "test_flag_07"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Help(), + "test flag 07"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(), + "test_flag_08"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Help(), + "test flag 08"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(), + "test_flag_09"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Help(), + "test flag 09"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(), + "test_flag_10"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Help(), + "test flag 10"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(), + "test_flag_11"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Help(), + "test flag 11"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename(); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(), + "test_flag_12"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Help(), + "test flag 12"); + EXPECT_TRUE(absl::EndsWith( + absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename(), + expected_file_name)) + << absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename(); } #endif // !ABSL_FLAGS_STRIP_NAMES // -------------------------------------------------------------------- TEST_F(FlagTest, TestDefault) { + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).DefaultValue(), + "true"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).DefaultValue(), + "1234"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).DefaultValue(), + "-34"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).DefaultValue(), + "189"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).DefaultValue(), + "10765"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).DefaultValue(), + "40000"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).DefaultValue(), + "-1234567"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).DefaultValue(), + "9876543"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).DefaultValue(), + "-9.876e-50"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).DefaultValue(), + "1.234e+12"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).DefaultValue(), + ""); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).DefaultValue(), + "10m"); + + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).CurrentValue(), + "true"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).CurrentValue(), + "1234"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).CurrentValue(), + "-34"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).CurrentValue(), + "189"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).CurrentValue(), + "10765"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).CurrentValue(), + "40000"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).CurrentValue(), + "-1234567"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).CurrentValue(), + "9876543"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).CurrentValue(), + "-9.876e-50"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).CurrentValue(), + "1.234e+12"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).CurrentValue(), + ""); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).CurrentValue(), + "10m"); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34); @@ -242,6 +451,68 @@ TEST_F(FlagTest, TestDefault) { EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55); EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), ""); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10)); +} + +// -------------------------------------------------------------------- + +struct NonTriviallyCopyableAggregate { + NonTriviallyCopyableAggregate() = default; + NonTriviallyCopyableAggregate(const NonTriviallyCopyableAggregate& rhs) + : value(rhs.value) {} + NonTriviallyCopyableAggregate& operator=( + const NonTriviallyCopyableAggregate& rhs) { + value = rhs.value; + return *this; + } + + int value; +}; +bool AbslParseFlag(absl::string_view src, NonTriviallyCopyableAggregate* f, + std::string* e) { + return absl::ParseFlag(src, &f->value, e); +} +std::string AbslUnparseFlag(const NonTriviallyCopyableAggregate& ntc) { + return absl::StrCat(ntc.value); +} + +bool operator==(const NonTriviallyCopyableAggregate& ntc1, + const NonTriviallyCopyableAggregate& ntc2) { + return ntc1.value == ntc2.value; +} + +} // namespace + +ABSL_FLAG(bool, test_flag_eb_01, {}, ""); +ABSL_FLAG(int32_t, test_flag_eb_02, {}, ""); +ABSL_FLAG(int64_t, test_flag_eb_03, {}, ""); +ABSL_FLAG(double, test_flag_eb_04, {}, ""); +ABSL_FLAG(std::string, test_flag_eb_05, {}, ""); +ABSL_FLAG(NonTriviallyCopyableAggregate, test_flag_eb_06, {}, ""); + +namespace { + +TEST_F(FlagTest, TestEmptyBracesDefault) { + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_01).DefaultValue(), + "false"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_02).DefaultValue(), + "0"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_03).DefaultValue(), + "0"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_04).DefaultValue(), + "0"); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_05).DefaultValue(), + ""); + EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_06).DefaultValue(), + "0"); + + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_01), false); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_02), 0); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_03), 0); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_04), 0.0); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_05), ""); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_06), + NonTriviallyCopyableAggregate{}); } // -------------------------------------------------------------------- @@ -279,6 +550,75 @@ TEST_F(FlagTest, TestGetSet) { absl::SetFlag(&FLAGS_test_flag_11, "asdf"); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "asdf"); + + absl::SetFlag(&FLAGS_test_flag_12, absl::Seconds(110)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Seconds(110)); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagTest, TestGetViaReflection) { + auto* handle = absl::FindCommandLineFlag("test_flag_01"); + EXPECT_EQ(*handle->TryGet<bool>(), true); + handle = absl::FindCommandLineFlag("test_flag_02"); + EXPECT_EQ(*handle->TryGet<int>(), 1234); + handle = absl::FindCommandLineFlag("test_flag_03"); + EXPECT_EQ(*handle->TryGet<int16_t>(), -34); + handle = absl::FindCommandLineFlag("test_flag_04"); + EXPECT_EQ(*handle->TryGet<uint16_t>(), 189); + handle = absl::FindCommandLineFlag("test_flag_05"); + EXPECT_EQ(*handle->TryGet<int32_t>(), 10765); + handle = absl::FindCommandLineFlag("test_flag_06"); + EXPECT_EQ(*handle->TryGet<uint32_t>(), 40000); + handle = absl::FindCommandLineFlag("test_flag_07"); + EXPECT_EQ(*handle->TryGet<int64_t>(), -1234567); + handle = absl::FindCommandLineFlag("test_flag_08"); + EXPECT_EQ(*handle->TryGet<uint64_t>(), 9876543); + handle = absl::FindCommandLineFlag("test_flag_09"); + EXPECT_NEAR(*handle->TryGet<double>(), -9.876e-50, 1e-55); + handle = absl::FindCommandLineFlag("test_flag_10"); + EXPECT_NEAR(*handle->TryGet<float>(), 1.234e12f, 1e5f); + handle = absl::FindCommandLineFlag("test_flag_11"); + EXPECT_EQ(*handle->TryGet<std::string>(), ""); + handle = absl::FindCommandLineFlag("test_flag_12"); + EXPECT_EQ(*handle->TryGet<absl::Duration>(), absl::Minutes(10)); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagTest, ConcurrentSetAndGet) { + static constexpr int kNumThreads = 8; + // Two arbitrary durations. One thread will concurrently flip the flag + // between these two values, while the other threads read it and verify + // that no other value is seen. + static const absl::Duration kValidDurations[] = { + absl::Seconds(int64_t{0x6cebf47a9b68c802}) + absl::Nanoseconds(229702057), + absl::Seconds(int64_t{0x23fec0307e4e9d3}) + absl::Nanoseconds(44555374)}; + absl::SetFlag(&FLAGS_test_flag_12, kValidDurations[0]); + + std::atomic<bool> stop{false}; + std::vector<std::thread> threads; + auto* handle = absl::FindCommandLineFlag("test_flag_12"); + for (int i = 0; i < kNumThreads; i++) { + threads.emplace_back([&]() { + while (!stop.load(std::memory_order_relaxed)) { + // Try loading the flag both directly and via a reflection + // handle. + absl::Duration v = absl::GetFlag(FLAGS_test_flag_12); + EXPECT_TRUE(v == kValidDurations[0] || v == kValidDurations[1]); + v = *handle->TryGet<absl::Duration>(); + EXPECT_TRUE(v == kValidDurations[0] || v == kValidDurations[1]); + } + }); + } + absl::Time end_time = absl::Now() + absl::Seconds(1); + int i = 0; + while (absl::Now() < end_time) { + absl::SetFlag(&FLAGS_test_flag_12, + kValidDurations[i++ % ABSL_ARRAYSIZE(kValidDurations)]); + } + stop.store(true, std::memory_order_relaxed); + for (auto& t : threads) t.join(); } // -------------------------------------------------------------------- @@ -287,28 +627,33 @@ int GetDflt1() { return 1; } } // namespace -ABSL_FLAG(int, test_flag_12, GetDflt1(), "test flag 12"); -ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"), - "test flag 13"); +ABSL_FLAG(int, test_int_flag_with_non_const_default, GetDflt1(), + "test int flag non const default"); +ABSL_FLAG(std::string, test_string_flag_with_non_const_default, + absl::StrCat("AAA", "BBB"), "test string flag non const default"); namespace { TEST_F(FlagTest, TestNonConstexprDefault) { - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1); - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB"); + EXPECT_EQ(absl::GetFlag(FLAGS_test_int_flag_with_non_const_default), 1); + EXPECT_EQ(absl::GetFlag(FLAGS_test_string_flag_with_non_const_default), + "AAABBB"); } // -------------------------------------------------------------------- } // namespace -ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14")); +ABSL_FLAG(bool, test_flag_with_non_const_help, true, + absl::StrCat("test ", "flag ", "non const help")); namespace { #if !ABSL_FLAGS_STRIP_HELP TEST_F(FlagTest, TestNonConstexprHelp) { - EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14"); + EXPECT_EQ( + absl::GetFlagReflectionHandle(FLAGS_test_flag_with_non_const_help).Help(), + "test flag non const help"); } #endif //! ABSL_FLAGS_STRIP_HELP @@ -374,14 +719,16 @@ std::string AbslUnparseFlag(const CustomUDT& f) { } // namespace -ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15"); +ABSL_FLAG(CustomUDT, test_flag_custom_udt, CustomUDT(), "test flag custom UDT"); namespace { TEST_F(FlagTest, TestCustomUDT) { - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(1, 1)); - absl::SetFlag(&FLAGS_test_flag_15, CustomUDT(2, 3)); - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(2, 3)); + EXPECT_EQ(flags::StorageKind<CustomUDT>(), + flags::FlagValueStorageKind::kOneWordAtomic); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1)); + absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3)); } // MSVC produces link error on the type mismatch. @@ -391,18 +738,22 @@ TEST_F(FlagTest, TestCustomUDT) { using FlagDeathTest = FlagTest; TEST_F(FlagDeathTest, TestTypeMismatchValidations) { - EXPECT_DEBUG_DEATH( +#if !defined(NDEBUG) + EXPECT_DEATH_IF_SUPPORTED( static_cast<void>(absl::GetFlag(FLAGS_mistyped_int_flag)), "Flag 'mistyped_int_flag' is defined as one type and declared " "as another"); - EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 1), - "Flag 'mistyped_int_flag' is defined as one type and declared " - "as another"); - - EXPECT_DEATH(static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)), - "Flag 'mistyped_string_flag' is defined as one type and " - "declared as another"); - EXPECT_DEATH( + EXPECT_DEATH_IF_SUPPORTED( + static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)), + "Flag 'mistyped_string_flag' is defined as one type and " + "declared as another"); +#endif + + EXPECT_DEATH_IF_SUPPORTED( + absl::SetFlag(&FLAGS_mistyped_int_flag, 1), + "Flag 'mistyped_int_flag' is defined as one type and declared " + "as another"); + EXPECT_DEATH_IF_SUPPORTED( absl::SetFlag(&FLAGS_mistyped_string_flag, std::vector<std::string>{}), "Flag 'mistyped_string_flag' is defined as one type and declared as " "another"); @@ -440,16 +791,17 @@ std::string AbslUnparseFlag(const ConversionTestVal& val) { // Flag default values can be specified with a value that converts to the flag // value type implicitly. -ABSL_FLAG(ConversionTestVal, test_flag_16, - ConversionTestVal::ViaImplicitConv::kTen, "test flag 16"); +ABSL_FLAG(ConversionTestVal, test_flag_implicit_conv, + ConversionTestVal::ViaImplicitConv::kTen, + "test flag init via implicit conversion"); namespace { TEST_F(FlagTest, CanSetViaImplicitConversion) { - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10); - absl::SetFlag(&FLAGS_test_flag_16, + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 10); + absl::SetFlag(&FLAGS_test_flag_implicit_conv, ConversionTestVal::ViaImplicitConv::kEleven); - EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 11); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 11); } // -------------------------------------------------------------------- @@ -494,25 +846,134 @@ TEST_F(FlagTest, TestNonDefaultConstructibleType) { EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); } -// -------------------------------------------------------------------- - } // namespace +// -------------------------------------------------------------------- + ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr"); ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); +bool initializaion_order_fiasco_test = [] { + // Iterate over all the flags during static initialization. + // This should not trigger ASan's initialization-order-fiasco. + auto* handle1 = absl::FindCommandLineFlag("flag_on_separate_file"); + auto* handle2 = absl::FindCommandLineFlag("retired_flag_on_separate_file"); + if (handle1 != nullptr && handle2 != nullptr) { + return handle1->Name() == handle2->Name(); + } + return true; +}(); + namespace { TEST_F(FlagTest, TestRetiredFlagRegistration) { - bool is_bool = false; - EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool)); - EXPECT_TRUE(is_bool); - EXPECT_TRUE(flags::IsRetiredFlag("old_int_flag", &is_bool)); - EXPECT_FALSE(is_bool); - EXPECT_TRUE(flags::IsRetiredFlag("old_str_flag", &is_bool)); - EXPECT_FALSE(is_bool); - EXPECT_FALSE(flags::IsRetiredFlag("some_other_flag", &is_bool)); + auto* handle = absl::FindCommandLineFlag("old_bool_flag"); + EXPECT_TRUE(handle->IsOfType<bool>()); + EXPECT_TRUE(handle->IsRetired()); + handle = absl::FindCommandLineFlag("old_int_flag"); + EXPECT_TRUE(handle->IsOfType<int>()); + EXPECT_TRUE(handle->IsRetired()); + handle = absl::FindCommandLineFlag("old_str_flag"); + EXPECT_TRUE(handle->IsOfType<std::string>()); + EXPECT_TRUE(handle->IsRetired()); } } // namespace + +// -------------------------------------------------------------------- + +namespace { + +// User-defined type with small alignment, but size exceeding 16. +struct SmallAlignUDT { + SmallAlignUDT() : c('A'), s(12) {} + char c; + int16_t s; + char bytes[14]; +}; + +bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) { + return true; +} +std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; } + +// User-defined type with small size, but not trivially copyable. +struct NonTriviallyCopyableUDT { + NonTriviallyCopyableUDT() : c('A') {} + NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {} + NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) { + c = rhs.c; + return *this; + } + + char c; +}; + +bool AbslParseFlag(absl::string_view, NonTriviallyCopyableUDT*, std::string*) { + return true; +} +std::string AbslUnparseFlag(const NonTriviallyCopyableUDT&) { return ""; } + +} // namespace + +ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help"); +ABSL_FLAG(NonTriviallyCopyableUDT, test_flag_ntc_udt, {}, "help"); + +namespace { + +TEST_F(FlagTest, TestSmallAlignUDT) { + SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt); + EXPECT_EQ(value.c, 'A'); + EXPECT_EQ(value.s, 12); + + value.c = 'B'; + value.s = 45; + absl::SetFlag(&FLAGS_test_flag_sa_udt, value); + value = absl::GetFlag(FLAGS_test_flag_sa_udt); + EXPECT_EQ(value.c, 'B'); + EXPECT_EQ(value.s, 45); +} + +TEST_F(FlagTest, TestNonTriviallyCopyableUDT) { + NonTriviallyCopyableUDT value = absl::GetFlag(FLAGS_test_flag_ntc_udt); + EXPECT_EQ(value.c, 'A'); + + value.c = 'B'; + absl::SetFlag(&FLAGS_test_flag_ntc_udt, value); + value = absl::GetFlag(FLAGS_test_flag_ntc_udt); + EXPECT_EQ(value.c, 'B'); +} + +} // namespace + +// -------------------------------------------------------------------- + +namespace { + +enum TestE { A = 1, B = 2, C = 3 }; + +struct EnumWrapper { + EnumWrapper() : e(A) {} + + TestE e; +}; + +bool AbslParseFlag(absl::string_view, EnumWrapper*, std::string*) { + return true; +} +std::string AbslUnparseFlag(const EnumWrapper&) { return ""; } + +} // namespace + +ABSL_FLAG(EnumWrapper, test_enum_wrapper_flag, {}, "help"); + +TEST_F(FlagTest, TesTypeWrappingEnum) { + EnumWrapper value = absl::GetFlag(FLAGS_test_enum_wrapper_flag); + EXPECT_EQ(value.e, A); + + value.e = B; + absl::SetFlag(&FLAGS_test_enum_wrapper_flag, value); + value = absl::GetFlag(FLAGS_test_enum_wrapper_flag); + EXPECT_EQ(value.e, B); +} diff --git a/third_party/abseil-cpp/absl/flags/flag_test_defs.cc b/third_party/abseil-cpp/absl/flags/flag_test_defs.cc index 49f91dee39..4e1693cdb9 100644 --- a/third_party/abseil-cpp/absl/flags/flag_test_defs.cc +++ b/third_party/abseil-cpp/absl/flags/flag_test_defs.cc @@ -20,5 +20,5 @@ ABSL_FLAG(int, mistyped_int_flag, 0, ""); ABSL_FLAG(std::string, mistyped_string_flag, "", ""); -ABSL_RETIRED_FLAG(bool, old_bool_flag, true, - "repetition of retired flag definition"); +ABSL_FLAG(bool, flag_on_separate_file, false, ""); +ABSL_RETIRED_FLAG(bool, retired_flag_on_separate_file, false, ""); diff --git a/third_party/abseil-cpp/absl/random/mocking_bit_gen.cc b/third_party/abseil-cpp/absl/flags/internal/commandlineflag.cc index 6bb1e414ae..4482955c4c 100644 --- a/third_party/abseil-cpp/absl/random/mocking_bit_gen.cc +++ b/third_party/abseil-cpp/absl/flags/internal/commandlineflag.cc @@ -1,5 +1,5 @@ // -// Copyright 2018 The Abseil Authors. +// Copyright 2020 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,19 +12,15 @@ // 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/random/mocking_bit_gen.h" -#include <string> +#include "absl/flags/internal/commandlineflag.h" namespace absl { ABSL_NAMESPACE_BEGIN -MockingBitGen::~MockingBitGen() { +namespace flags_internal { - for (const auto& del : deleters_) { - del(); - } -} +FlagStateInterface::~FlagStateInterface() {} +} // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/internal/commandlineflag.h b/third_party/abseil-cpp/absl/flags/internal/commandlineflag.h index 6363c6615b..ebfe81ba1e 100644 --- a/third_party/abseil-cpp/absl/flags/internal/commandlineflag.h +++ b/third_party/abseil-cpp/absl/flags/internal/commandlineflag.h @@ -16,41 +16,19 @@ #ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ -#include <stddef.h> -#include <stdint.h> - -#include <memory> -#include <string> -#include <typeinfo> - #include "absl/base/config.h" -#include "absl/base/macros.h" -#include "absl/flags/config.h" -#include "absl/flags/marshalling.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" +#include "absl/base/internal/fast_type_id.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace flags_internal { -// An alias for flag static type id. Values of type identify the flag value type -// simialarly to typeid(T), but without relying on RTTI being available. In most +// An alias for flag fast type id. This value identifies the flag value type +// similarly to typeid(T), without relying on RTTI being available. In most // cases this id is enough to uniquely identify the flag's value type. In a few // cases we'll have to resort to using actual RTTI implementation if it is // available. -using FlagStaticTypeId = void* (*)(); - -// Address of this function template is used in current implementation as a flag -// static type id. -template <typename T> -void* FlagStaticTypeIdGen() { -#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) - return const_cast<std::type_info*>(&typeid(T)); -#else - return nullptr; -#endif -} +using FlagFastTypeId = absl::base_internal::FastTypeIdType; // Options that control SetCommandLineOptionWithMode. enum FlagSettingMode { @@ -65,7 +43,7 @@ enum FlagSettingMode { SET_FLAGS_DEFAULT }; -// Options that control SetFromString: Source of a value. +// Options that control ParseFrom: Source of a value. enum ValueSource { // Flag is being set by value specified on a command line. kCommandLine, @@ -77,135 +55,12 @@ enum ValueSource { // of a flag produced this flag state from method CommandLineFlag::SaveState(). class FlagStateInterface { public: - virtual ~FlagStateInterface() {} + virtual ~FlagStateInterface(); // Restores the flag originated this object to the saved state. virtual void Restore() const = 0; }; -// Holds all information for a flag. -class CommandLineFlag { - public: - constexpr CommandLineFlag() = default; - - // Not copyable/assignable. - CommandLineFlag(const CommandLineFlag&) = delete; - CommandLineFlag& operator=(const CommandLineFlag&) = delete; - - // Non-polymorphic access methods. - - // Return true iff flag has type T. - template <typename T> - inline bool IsOfType() const { - return TypeId() == &flags_internal::FlagStaticTypeIdGen<T>; - } - - // Attempts to retrieve the flag value. Returns value on success, - // absl::nullopt otherwise. - template <typename T> - absl::optional<T> Get() const { - if (IsRetired() || !IsOfType<T>()) { - return absl::nullopt; - } - - // Implementation notes: - // - // We are wrapping a union around the value of `T` to serve three purposes: - // - // 1. `U.value` has correct size and alignment for a value of type `T` - // 2. The `U.value` constructor is not invoked since U's constructor does - // not do it explicitly. - // 3. The `U.value` destructor is invoked since U's destructor does it - // explicitly. This makes `U` a kind of RAII wrapper around non default - // constructible value of T, which is destructed when we leave the - // scope. We do need to destroy U.value, which is constructed by - // CommandLineFlag::Read even though we left it in a moved-from state - // after std::move. - // - // All of this serves to avoid requiring `T` being default constructible. - union U { - T value; - U() {} - ~U() { value.~T(); } - }; - U u; - - Read(&u.value); - return std::move(u.value); - } - - // Polymorphic access methods - - // Returns name of this flag. - virtual absl::string_view Name() const = 0; - // Returns name of the file where this flag is defined. - virtual std::string Filename() const = 0; - // Returns name of the flag's value type for some built-in types or empty - // std::string. - virtual absl::string_view Typename() const = 0; - // Returns help message associated with this flag. - virtual std::string Help() const = 0; - // Returns true iff this object corresponds to retired flag. - virtual bool IsRetired() const { return false; } - // Returns true iff this is a handle to an Abseil Flag. - virtual bool IsAbseilFlag() const { return true; } - // Returns id of the flag's value type. - virtual FlagStaticTypeId TypeId() const = 0; - virtual bool IsModified() const = 0; - virtual bool IsSpecifiedOnCommandLine() const = 0; - virtual std::string DefaultValue() const = 0; - virtual std::string CurrentValue() const = 0; - - // Interfaces to operate on validators. - virtual bool ValidateInputValue(absl::string_view value) const = 0; - - // Interface to save flag to some persistent state. Returns current flag state - // or nullptr if flag does not support saving and restoring a state. - virtual std::unique_ptr<FlagStateInterface> SaveState() = 0; - - // Sets the value of the flag based on specified std::string `value`. If the flag - // was successfully set to new value, it returns true. Otherwise, sets `error` - // to indicate the error, leaves the flag unchanged, and returns false. There - // are three ways to set the flag's value: - // * Update the current flag value - // * Update the flag's default value - // * Update the current flag value if it was never set before - // The mode is selected based on `set_mode` parameter. - virtual bool SetFromString(absl::string_view value, - flags_internal::FlagSettingMode set_mode, - flags_internal::ValueSource source, - std::string* error) = 0; - - // Checks that flags default value can be converted to std::string and back to the - // flag's value type. - virtual void CheckDefaultValueParsingRoundtrip() const = 0; - - protected: - ~CommandLineFlag() = default; - - private: - // Copy-construct a new value of the flag's type in a memory referenced by - // the dst based on the current flag's value. - virtual void Read(void* dst) const = 0; -}; - -// This macro is the "source of truth" for the list of supported flag built-in -// types. -#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \ - A(bool) \ - A(short) \ - A(unsigned short) \ - A(int) \ - A(unsigned int) \ - A(long) \ - A(unsigned long) \ - A(long long) \ - A(unsigned long long) \ - A(double) \ - A(float) \ - A(std::string) \ - A(std::vector<std::string>) - } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/internal/commandlineflag_test.cc b/third_party/abseil-cpp/absl/flags/internal/commandlineflag_test.cc deleted file mode 100644 index 0e8bc3133b..0000000000 --- a/third_party/abseil-cpp/absl/flags/internal/commandlineflag_test.cc +++ /dev/null @@ -1,219 +0,0 @@ -// -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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/flags/internal/commandlineflag.h" - -#include <memory> -#include <string> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/flags/internal/registry.h" -#include "absl/flags/usage_config.h" -#include "absl/memory/memory.h" -#include "absl/strings/match.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" - -ABSL_FLAG(int, int_flag, 201, "int_flag help"); -ABSL_FLAG(std::string, string_flag, "dflt", - absl::StrCat("string_flag", " help")); -ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); - -namespace { - -namespace flags = absl::flags_internal; - -class CommandLineFlagTest : public testing::Test { - protected: - static void SetUpTestSuite() { - // Install a function to normalize filenames before this test is run. - absl::FlagsUsageConfig default_config; - default_config.normalize_filename = &CommandLineFlagTest::NormalizeFileName; - absl::SetFlagsUsageConfig(default_config); - } - - void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); } - void TearDown() override { flag_saver_.reset(); } - - private: - static std::string NormalizeFileName(absl::string_view fname) { -#ifdef _WIN32 - std::string normalized(fname); - std::replace(normalized.begin(), normalized.end(), '\\', '/'); - fname = normalized; -#endif - return std::string(fname); - } - - std::unique_ptr<flags::FlagSaver> flag_saver_; -}; - -TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { - auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - - ASSERT_TRUE(flag_01); - EXPECT_EQ(flag_01->Name(), "int_flag"); - EXPECT_EQ(flag_01->Help(), "int_flag help"); - EXPECT_EQ(flag_01->Typename(), ""); - EXPECT_TRUE(!flag_01->IsRetired()); - EXPECT_TRUE(flag_01->IsOfType<int>()); - EXPECT_TRUE( - absl::EndsWith(flag_01->Filename(), - "absl/flags/internal/commandlineflag_test.cc")) - << flag_01->Filename(); - - auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - - ASSERT_TRUE(flag_02); - EXPECT_EQ(flag_02->Name(), "string_flag"); - EXPECT_EQ(flag_02->Help(), "string_flag help"); - EXPECT_EQ(flag_02->Typename(), ""); - EXPECT_TRUE(!flag_02->IsRetired()); - EXPECT_TRUE(flag_02->IsOfType<std::string>()); - EXPECT_TRUE( - absl::EndsWith(flag_02->Filename(), - "absl/flags/internal/commandlineflag_test.cc")) - << flag_02->Filename(); - - auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag"); - - ASSERT_TRUE(flag_03); - EXPECT_EQ(flag_03->Name(), "bool_retired_flag"); - EXPECT_EQ(flag_03->Help(), ""); - EXPECT_EQ(flag_03->Typename(), ""); - EXPECT_TRUE(flag_03->IsRetired()); - EXPECT_TRUE(flag_03->IsOfType<bool>()); - EXPECT_EQ(flag_03->Filename(), "RETIRED"); -} - -// -------------------------------------------------------------------- - -TEST_F(CommandLineFlagTest, TestValueAccessMethods) { - absl::SetFlag(&FLAGS_int_flag, 301); - auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - - ASSERT_TRUE(flag_01); - EXPECT_EQ(flag_01->CurrentValue(), "301"); - EXPECT_EQ(flag_01->DefaultValue(), "201"); - - absl::SetFlag(&FLAGS_string_flag, "new_str_value"); - auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - - ASSERT_TRUE(flag_02); - EXPECT_EQ(flag_02->CurrentValue(), "new_str_value"); - EXPECT_EQ(flag_02->DefaultValue(), "dflt"); -} - -// -------------------------------------------------------------------- - -TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) { - std::string err; - - auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - - EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); - EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - - EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); - EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - - EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); - EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'"); - EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - - EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); - EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'"); - EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - - EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16); - EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - - EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE, - flags::kCommandLine, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); - EXPECT_TRUE(flag_01->IsSpecifiedOnCommandLine()); - - EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); - - auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - EXPECT_TRUE(flag_02->SetFromString("xyz", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz"); - - EXPECT_TRUE(flag_02->SetFromString("", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), ""); -} - -// -------------------------------------------------------------------- - -TEST_F(CommandLineFlagTest, TestSetFromStringDefaultValue) { - std::string err; - - auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - - EXPECT_TRUE(flag_01->SetFromString("111", flags::SET_FLAGS_DEFAULT, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(flag_01->DefaultValue(), "111"); - - auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - - EXPECT_TRUE(flag_02->SetFromString("abc", flags::SET_FLAGS_DEFAULT, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(flag_02->DefaultValue(), "abc"); -} - -// -------------------------------------------------------------------- - -TEST_F(CommandLineFlagTest, TestSetFromStringIfDefault) { - std::string err; - - auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - - EXPECT_TRUE(flag_01->SetFromString("22", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)) - << err; - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); - - EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); - // EXPECT_EQ(err, "ERROR: int_flag is already set to 22"); - - // Reset back to default value - EXPECT_TRUE(flag_01->SetFromString("201", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); - - EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201); - // EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); -} - -} // namespace diff --git a/third_party/abseil-cpp/absl/flags/internal/flag.cc b/third_party/abseil-cpp/absl/flags/internal/flag.cc index 5a921e28d7..1515022d11 100644 --- a/third_party/abseil-cpp/absl/flags/internal/flag.cc +++ b/third_party/abseil-cpp/absl/flags/internal/flag.cc @@ -15,21 +15,26 @@ #include "absl/flags/internal/flag.h" +#include <assert.h> #include <stddef.h> #include <stdint.h> #include <string.h> +#include <array> #include <atomic> #include <memory> +#include <new> #include <string> -#include <vector> +#include <typeinfo> -#include "absl/base/attributes.h" +#include "absl/base/call_once.h" +#include "absl/base/casts.h" #include "absl/base/config.h" -#include "absl/base/const_init.h" #include "absl/base/optimization.h" +#include "absl/flags/config.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/usage_config.h" +#include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" @@ -47,10 +52,10 @@ const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; namespace { // Currently we only validate flag values for user-defined flag types. -bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) { -#define DONT_VALIDATE(T) \ - if (flag_type_id == &FlagStaticTypeIdGen<T>) return false; - ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE) +bool ShouldValidateFlagValue(FlagFastTypeId flag_type_id) { +#define DONT_VALIDATE(T, _) \ + if (flag_type_id == base_internal::FastTypeId<T>()) return false; + ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(DONT_VALIDATE) #undef DONT_VALIDATE return true; @@ -62,55 +67,146 @@ bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) { // need to acquire these locks themselves. class MutexRelock { public: - explicit MutexRelock(absl::Mutex* mu) : mu_(mu) { mu_->Unlock(); } - ~MutexRelock() { mu_->Lock(); } + explicit MutexRelock(absl::Mutex& mu) : mu_(mu) { mu_.Unlock(); } + ~MutexRelock() { mu_.Lock(); } MutexRelock(const MutexRelock&) = delete; MutexRelock& operator=(const MutexRelock&) = delete; private: - absl::Mutex* mu_; + absl::Mutex& mu_; }; } // namespace +/////////////////////////////////////////////////////////////////////////////// +// Persistent state of the flag data. + +class FlagImpl; + +class FlagState : public flags_internal::FlagStateInterface { + public: + template <typename V> + FlagState(FlagImpl& flag_impl, const V& v, bool modified, + bool on_command_line, int64_t counter) + : flag_impl_(flag_impl), + value_(v), + modified_(modified), + on_command_line_(on_command_line), + counter_(counter) {} + + ~FlagState() override { + if (flag_impl_.ValueStorageKind() != FlagValueStorageKind::kAlignedBuffer && + flag_impl_.ValueStorageKind() != FlagValueStorageKind::kSequenceLocked) + return; + flags_internal::Delete(flag_impl_.op_, value_.heap_allocated); + } + + private: + friend class FlagImpl; + + // Restores the flag to the saved state. + void Restore() const override { + if (!flag_impl_.RestoreState(*this)) return; + + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("Restore saved value of ", flag_impl_.Name(), + " to: ", flag_impl_.CurrentValue())); + } + + // Flag and saved flag data. + FlagImpl& flag_impl_; + union SavedValue { + explicit SavedValue(void* v) : heap_allocated(v) {} + explicit SavedValue(int64_t v) : one_word(v) {} + + void* heap_allocated; + int64_t one_word; + } value_; + bool modified_; + bool on_command_line_; + int64_t counter_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Flag implementation, which does not depend on flag value type. + +DynValueDeleter::DynValueDeleter(FlagOpFn op_arg) : op(op_arg) {} + +void DynValueDeleter::operator()(void* ptr) const { + if (op == nullptr) return; + + Delete(op, ptr); +} + void FlagImpl::Init() { new (&data_guard_) absl::Mutex; - absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_)); + auto def_kind = static_cast<FlagDefaultKind>(def_kind_); - value_.dynamic = MakeInitValue().release(); - StoreAtomic(); + switch (ValueStorageKind()) { + case FlagValueStorageKind::kValueAndInitBit: + case FlagValueStorageKind::kOneWordAtomic: { + alignas(int64_t) std::array<char, sizeof(int64_t)> buf{}; + if (def_kind == FlagDefaultKind::kGenFunc) { + (*default_value_.gen_func)(buf.data()); + } else { + assert(def_kind != FlagDefaultKind::kDynamicValue); + std::memcpy(buf.data(), &default_value_, Sizeof(op_)); + } + if (ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit) { + // We presume here the memory layout of FlagValueAndInitBit struct. + uint8_t initialized = 1; + std::memcpy(buf.data() + Sizeof(op_), &initialized, + sizeof(initialized)); + } + OneWordValue().store(absl::bit_cast<int64_t>(buf), + std::memory_order_release); + break; + } + case FlagValueStorageKind::kSequenceLocked: { + // For this storage kind the default_value_ always points to gen_func + // during initialization. + assert(def_kind == FlagDefaultKind::kGenFunc); + (*default_value_.gen_func)(AtomicBufferValue()); + break; + } + case FlagValueStorageKind::kAlignedBuffer: + // For this storage kind the default_value_ always points to gen_func + // during initialization. + assert(def_kind == FlagDefaultKind::kGenFunc); + (*default_value_.gen_func)(AlignedBufferValue()); + break; + } + seq_lock_.MarkInitialized(); } -// Ensures that the lazily initialized data is initialized, -// and returns pointer to the mutex guarding flags data. absl::Mutex* FlagImpl::DataGuard() const { absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init, const_cast<FlagImpl*>(this)); - // data_guard_ is initialized. + // data_guard_ is initialized inside Init. return reinterpret_cast<absl::Mutex*>(&data_guard_); } -void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const { - FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_); +void FlagImpl::AssertValidType(FlagFastTypeId rhs_type_id, + const std::type_info* (*gen_rtti)()) const { + FlagFastTypeId lhs_type_id = flags_internal::FastTypeId(op_); - // `type_id` is the type id corresponding to the declaration visibile at the - // call site. `this_type_id` is the type id corresponding to the type stored - // during flag definition. They must match for this operation to be - // well-defined. - if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return; + // `rhs_type_id` is the fast type id corresponding to the declaration + // visibile at the call site. `lhs_type_id` is the fast type id + // corresponding to the type specified in flag definition. They must match + // for this operation to be well-defined. + if (ABSL_PREDICT_TRUE(lhs_type_id == rhs_type_id)) return; - void* lhs_runtime_type_id = type_id(); - void* rhs_runtime_type_id = this_type_id(); + const std::type_info* lhs_runtime_type_id = + flags_internal::RuntimeTypeId(op_); + const std::type_info* rhs_runtime_type_id = (*gen_rtti)(); if (lhs_runtime_type_id == rhs_runtime_type_id) return; #if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) - if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) == - *reinterpret_cast<std::type_info*>(rhs_runtime_type_id)) - return; + if (*lhs_runtime_type_id == *rhs_runtime_type_id) return; #endif ABSL_INTERNAL_LOG( @@ -120,19 +216,42 @@ void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const { std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { void* res = nullptr; - if (DefaultKind() == FlagDefaultKind::kDynamicValue) { - res = flags_internal::Clone(op_, default_value_.dynamic_value); - } else { - res = (*default_value_.gen_func)(); + switch (DefaultKind()) { + case FlagDefaultKind::kDynamicValue: + res = flags_internal::Clone(op_, default_value_.dynamic_value); + break; + case FlagDefaultKind::kGenFunc: + res = flags_internal::Alloc(op_); + (*default_value_.gen_func)(res); + break; + default: + res = flags_internal::Clone(op_, &default_value_); + break; } return {res, DynValueDeleter{op_}}; } void FlagImpl::StoreValue(const void* src) { - flags_internal::Copy(op_, src, value_.dynamic); - StoreAtomic(); + switch (ValueStorageKind()) { + case FlagValueStorageKind::kValueAndInitBit: + case FlagValueStorageKind::kOneWordAtomic: { + // Load the current value to avoid setting 'init' bit manualy. + int64_t one_word_val = OneWordValue().load(std::memory_order_acquire); + std::memcpy(&one_word_val, src, Sizeof(op_)); + OneWordValue().store(one_word_val, std::memory_order_release); + seq_lock_.IncrementModificationCount(); + break; + } + case FlagValueStorageKind::kSequenceLocked: { + seq_lock_.Write(AtomicBufferValue(), src, Sizeof(op_)); + break; + } + case FlagValueStorageKind::kAlignedBuffer: + Copy(op_, src, AlignedBufferValue()); + seq_lock_.IncrementModificationCount(); + break; + } modified_ = true; - ++counter_; InvokeCallback(); } @@ -147,9 +266,12 @@ std::string FlagImpl::Help() const { : help_.gen_func(); } -bool FlagImpl::IsModified() const { - absl::MutexLock l(DataGuard()); - return modified_; +FlagFastTypeId FlagImpl::TypeId() const { + return flags_internal::FastTypeId(op_); +} + +int64_t FlagImpl::ModificationCount() const { + return seq_lock_.ModificationCount(); } bool FlagImpl::IsSpecifiedOnCommandLine() const { @@ -165,9 +287,28 @@ std::string FlagImpl::DefaultValue() const { } std::string FlagImpl::CurrentValue() const { - absl::MutexLock l(DataGuard()); + auto* guard = DataGuard(); // Make sure flag initialized + switch (ValueStorageKind()) { + case FlagValueStorageKind::kValueAndInitBit: + case FlagValueStorageKind::kOneWordAtomic: { + const auto one_word_val = + absl::bit_cast<std::array<char, sizeof(int64_t)>>( + OneWordValue().load(std::memory_order_acquire)); + return flags_internal::Unparse(op_, one_word_val.data()); + } + case FlagValueStorageKind::kSequenceLocked: { + std::unique_ptr<void, DynValueDeleter> cloned(flags_internal::Alloc(op_), + DynValueDeleter{op_}); + ReadSequenceLockedData(cloned.get()); + return flags_internal::Unparse(op_, cloned.get()); + } + case FlagValueStorageKind::kAlignedBuffer: { + absl::MutexLock l(guard); + return flags_internal::Unparse(op_, AlignedBufferValue()); + } + } - return flags_internal::Unparse(op_, value_.dynamic); + return ""; } void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) { @@ -199,44 +340,103 @@ void FlagImpl::InvokeCallback() const { // and it also can be different by the time the callback invocation is // completed. Requires that *primary_lock be held in exclusive mode; it may be // released and reacquired by the implementation. - MutexRelock relock(DataGuard()); + MutexRelock relock(*DataGuard()); absl::MutexLock lock(&callback_->guard); cb(); } -bool FlagImpl::RestoreState(const void* value, bool modified, - bool on_command_line, int64_t counter) { - { - absl::MutexLock l(DataGuard()); +std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() { + absl::MutexLock l(DataGuard()); - if (counter_ == counter) return false; + bool modified = modified_; + bool on_command_line = on_command_line_; + switch (ValueStorageKind()) { + case FlagValueStorageKind::kValueAndInitBit: + case FlagValueStorageKind::kOneWordAtomic: { + return absl::make_unique<FlagState>( + *this, OneWordValue().load(std::memory_order_acquire), modified, + on_command_line, ModificationCount()); + } + case FlagValueStorageKind::kSequenceLocked: { + void* cloned = flags_internal::Alloc(op_); + // Read is guaranteed to be successful because we hold the lock. + bool success = + seq_lock_.TryRead(cloned, AtomicBufferValue(), Sizeof(op_)); + assert(success); + static_cast<void>(success); + return absl::make_unique<FlagState>(*this, cloned, modified, + on_command_line, ModificationCount()); + } + case FlagValueStorageKind::kAlignedBuffer: { + return absl::make_unique<FlagState>( + *this, flags_internal::Clone(op_, AlignedBufferValue()), modified, + on_command_line, ModificationCount()); + } } + return nullptr; +} - Write(value); - - { - absl::MutexLock l(DataGuard()); +bool FlagImpl::RestoreState(const FlagState& flag_state) { + absl::MutexLock l(DataGuard()); + if (flag_state.counter_ == ModificationCount()) { + return false; + } - modified_ = modified; - on_command_line_ = on_command_line; + switch (ValueStorageKind()) { + case FlagValueStorageKind::kValueAndInitBit: + case FlagValueStorageKind::kOneWordAtomic: + StoreValue(&flag_state.value_.one_word); + break; + case FlagValueStorageKind::kSequenceLocked: + case FlagValueStorageKind::kAlignedBuffer: + StoreValue(flag_state.value_.heap_allocated); + break; } + modified_ = flag_state.modified_; + on_command_line_ = flag_state.on_command_line_; + return true; } +template <typename StorageT> +StorageT* FlagImpl::OffsetValue() const { + char* p = reinterpret_cast<char*>(const_cast<FlagImpl*>(this)); + // The offset is deduced via Flag value type specific op_. + size_t offset = flags_internal::ValueOffset(op_); + + return reinterpret_cast<StorageT*>(p + offset); +} + +void* FlagImpl::AlignedBufferValue() const { + assert(ValueStorageKind() == FlagValueStorageKind::kAlignedBuffer); + return OffsetValue<void>(); +} + +std::atomic<uint64_t>* FlagImpl::AtomicBufferValue() const { + assert(ValueStorageKind() == FlagValueStorageKind::kSequenceLocked); + return OffsetValue<std::atomic<uint64_t>>(); +} + +std::atomic<int64_t>& FlagImpl::OneWordValue() const { + assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic || + ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); + return OffsetValue<FlagOneWordValue>()->value; +} + // Attempts to parse supplied `value` string using parsing routine in the `flag` // argument. If parsing successful, this function replaces the dst with newly // parsed value. In case if any error is encountered in either step, the error // message is stored in 'err' std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse( - absl::string_view value, std::string* err) const { + absl::string_view value, std::string& err) const { std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue(); std::string parse_err; if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) { absl::string_view err_sep = parse_err.empty() ? "" : "; "; - *err = absl::StrCat("Illegal value '", value, "' specified for flag '", - Name(), "'", err_sep, parse_err); + err = absl::StrCat("Illegal value '", value, "' specified for flag '", + Name(), "'", err_sep, parse_err); return nullptr; } @@ -244,32 +444,62 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse( } void FlagImpl::Read(void* dst) const { - absl::ReaderMutexLock l(DataGuard()); + auto* guard = DataGuard(); // Make sure flag initialized + switch (ValueStorageKind()) { + case FlagValueStorageKind::kValueAndInitBit: + case FlagValueStorageKind::kOneWordAtomic: { + const int64_t one_word_val = + OneWordValue().load(std::memory_order_acquire); + std::memcpy(dst, &one_word_val, Sizeof(op_)); + break; + } + case FlagValueStorageKind::kSequenceLocked: { + ReadSequenceLockedData(dst); + break; + } + case FlagValueStorageKind::kAlignedBuffer: { + absl::MutexLock l(guard); + flags_internal::CopyConstruct(op_, AlignedBufferValue(), dst); + break; + } + } +} - flags_internal::CopyConstruct(op_, value_.dynamic, dst); +int64_t FlagImpl::ReadOneWord() const { + assert(ValueStorageKind() == FlagValueStorageKind::kOneWordAtomic || + ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); + auto* guard = DataGuard(); // Make sure flag initialized + (void)guard; + return OneWordValue().load(std::memory_order_acquire); } -void FlagImpl::StoreAtomic() { - size_t data_size = flags_internal::Sizeof(op_); +bool FlagImpl::ReadOneBool() const { + assert(ValueStorageKind() == FlagValueStorageKind::kValueAndInitBit); + auto* guard = DataGuard(); // Make sure flag initialized + (void)guard; + return absl::bit_cast<FlagValueAndInitBit<bool>>( + OneWordValue().load(std::memory_order_acquire)) + .value; +} - if (data_size <= sizeof(int64_t)) { - int64_t t = 0; - std::memcpy(&t, value_.dynamic, data_size); - value_.atomics.small_atomic.store(t, std::memory_order_release); - } -#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) - else if (data_size <= sizeof(FlagsInternalTwoWordsType)) { - FlagsInternalTwoWordsType t{0, 0}; - std::memcpy(&t, value_.dynamic, data_size); - value_.atomics.big_atomic.store(t, std::memory_order_release); +void FlagImpl::ReadSequenceLockedData(void* dst) const { + int size = Sizeof(op_); + // Attempt to read using the sequence lock. + if (ABSL_PREDICT_TRUE(seq_lock_.TryRead(dst, AtomicBufferValue(), size))) { + return; } -#endif + // We failed due to contention. Acquire the lock to prevent contention + // and try again. + absl::ReaderMutexLock l(DataGuard()); + bool success = seq_lock_.TryRead(dst, AtomicBufferValue(), size); + assert(success); + static_cast<void>(success); } void FlagImpl::Write(const void* src) { absl::MutexLock l(DataGuard()); - if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) { + if (ShouldValidateFlagValue(flags_internal::FastTypeId(op_))) { std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src), DynValueDeleter{op_}}; std::string ignored_error; @@ -291,8 +521,8 @@ void FlagImpl::Write(const void* src) { // * Update the flag's default value // * Update the current flag value if it was never set before // The mode is selected based on 'set_mode' parameter. -bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, - ValueSource source, std::string* err) { +bool FlagImpl::ParseFrom(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string& err) { absl::MutexLock l(DataGuard()); switch (set_mode) { @@ -339,7 +569,7 @@ bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode, } if (!modified_) { - // Need to set both default value *and* current, in this case + // Need to set both default value *and* current, in this case. StoreValue(default_value_.dynamic_value); modified_ = false; } @@ -361,7 +591,7 @@ void FlagImpl::CheckDefaultValueParsingRoundtrip() const { ABSL_INTERNAL_LOG( FATAL, absl::StrCat("Flag ", Name(), " (from ", Filename(), - "): std::string form of default value '", v, + "): string form of default value '", v, "' could not be parsed; error=", error)); } diff --git a/third_party/abseil-cpp/absl/flags/internal/flag.h b/third_party/abseil-cpp/absl/flags/internal/flag.h index 35a148cf66..124a2f1c03 100644 --- a/third_party/abseil-cpp/absl/flags/internal/flag.h +++ b/third_party/abseil-cpp/absl/flags/internal/flag.h @@ -16,94 +16,95 @@ #ifndef ABSL_FLAGS_INTERNAL_FLAG_H_ #define ABSL_FLAGS_INTERNAL_FLAG_H_ +#include <stddef.h> #include <stdint.h> #include <atomic> #include <cstring> #include <memory> +#include <new> #include <string> #include <type_traits> +#include <typeinfo> +#include "absl/base/attributes.h" #include "absl/base/call_once.h" +#include "absl/base/casts.h" #include "absl/base/config.h" +#include "absl/base/optimization.h" #include "absl/base/thread_annotations.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/config.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" +#include "absl/flags/internal/sequence_lock.h" +#include "absl/flags/marshalling.h" +#include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Forward declaration of absl::Flag<T> public API. namespace flags_internal { +template <typename T> +class Flag; +} // namespace flags_internal +#if defined(_MSC_VER) && !defined(__clang__) template <typename T> class Flag; +#else +template <typename T> +using Flag = flags_internal::Flag<T>; +#endif + +template <typename T> +ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag); + +template <typename T> +void SetFlag(absl::Flag<T>* flag, const T& v); + +template <typename T, typename V> +void SetFlag(absl::Flag<T>* flag, const V& v); + +template <typename U> +const CommandLineFlag& GetFlagReflectionHandle(const absl::Flag<U>& f); /////////////////////////////////////////////////////////////////////////////// // Flag value type operations, eg., parsing, copying, etc. are provided // by function specific to that type with a signature matching FlagOpFn. +namespace flags_internal { + enum class FlagOp { + kAlloc, kDelete, - kClone, kCopy, kCopyConstruct, kSizeof, - kStaticTypeId, + kFastTypeId, + kRuntimeTypeId, kParse, kUnparse, + kValueOffset, }; using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*); -// Flag value specific operations routine. +// Forward declaration for Flag value specific operations. template <typename T> -void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { - switch (op) { - case FlagOp::kDelete: - delete static_cast<const T*>(v1); - return nullptr; - case FlagOp::kClone: - return new T(*static_cast<const T*>(v1)); - case FlagOp::kCopy: - *static_cast<T*>(v2) = *static_cast<const T*>(v1); - return nullptr; - case FlagOp::kCopyConstruct: - new (v2) T(*static_cast<const T*>(v1)); - return nullptr; - case FlagOp::kSizeof: - return reinterpret_cast<void*>(sizeof(T)); - case FlagOp::kStaticTypeId: - return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>); - case FlagOp::kParse: { - // Initialize the temporary instance of type T based on current value in - // destination (which is going to be flag's default value). - T temp(*static_cast<T*>(v2)); - if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, - static_cast<std::string*>(v3))) { - return nullptr; - } - *static_cast<T*>(v2) = std::move(temp); - return v2; - } - case FlagOp::kUnparse: - *static_cast<std::string*>(v2) = - absl::UnparseFlag<T>(*static_cast<const T*>(v1)); - return nullptr; - default: - return nullptr; - } -} +void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3); -// Deletes memory interpreting obj as flag value type pointer. -inline void Delete(FlagOpFn op, const void* obj) { - op(FlagOp::kDelete, obj, nullptr, nullptr); +// Allocate aligned memory for a flag value. +inline void* Alloc(FlagOpFn op) { + return op(FlagOp::kAlloc, nullptr, nullptr, nullptr); } -// Makes a copy of flag value pointed by obj. -inline void* Clone(FlagOpFn op, const void* obj) { - return op(FlagOp::kClone, obj, nullptr, nullptr); +// Deletes memory interpreting obj as flag value type pointer. +inline void Delete(FlagOpFn op, void* obj) { + op(FlagOp::kDelete, nullptr, obj, nullptr); } // Copies src to dst interpreting as flag value type pointers. inline void Copy(FlagOpFn op, const void* src, void* dst) { @@ -114,6 +115,12 @@ inline void Copy(FlagOpFn op, const void* src, void* dst) { inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { op(FlagOp::kCopyConstruct, src, dst, nullptr); } +// Makes a copy of flag value pointed by obj. +inline void* Clone(FlagOpFn op, const void* obj) { + void* res = flags_internal::Alloc(op); + flags_internal::CopyConstruct(op, obj, res); + return res; +} // Returns true if parsing of input text is successfull. inline bool Parse(FlagOpFn op, absl::string_view text, void* dst, std::string* error) { @@ -132,41 +139,36 @@ inline size_t Sizeof(FlagOpFn op) { return static_cast<size_t>(reinterpret_cast<intptr_t>( op(FlagOp::kSizeof, nullptr, nullptr, nullptr))); } -// Returns static type id coresponding to the value type. -inline FlagStaticTypeId StaticTypeId(FlagOpFn op) { - return reinterpret_cast<FlagStaticTypeId>( - op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr)); +// Returns fast type id coresponding to the value type. +inline FlagFastTypeId FastTypeId(FlagOpFn op) { + return reinterpret_cast<FlagFastTypeId>( + op(FlagOp::kFastTypeId, nullptr, nullptr, nullptr)); +} +// Returns fast type id coresponding to the value type. +inline const std::type_info* RuntimeTypeId(FlagOpFn op) { + return reinterpret_cast<const std::type_info*>( + op(FlagOp::kRuntimeTypeId, nullptr, nullptr, nullptr)); +} +// Returns offset of the field value_ from the field impl_ inside of +// absl::Flag<T> data. Given FlagImpl pointer p you can get the +// location of the corresponding value as: +// reinterpret_cast<char*>(p) + ValueOffset(). +inline ptrdiff_t ValueOffset(FlagOpFn op) { + // This sequence of casts reverses the sequence from + // `flags_internal::FlagOps()` + return static_cast<ptrdiff_t>(reinterpret_cast<intptr_t>( + op(FlagOp::kValueOffset, nullptr, nullptr, nullptr))); } -/////////////////////////////////////////////////////////////////////////////// -// Persistent state of the flag data. - +// Returns an address of RTTI's typeid(T). template <typename T> -class FlagState : public flags_internal::FlagStateInterface { - public: - FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line, - int64_t counter) - : flag_(flag), - cur_value_(std::move(cur)), - modified_(modified), - on_command_line_(on_command_line), - counter_(counter) {} - - ~FlagState() override = default; - - private: - friend class Flag<T>; - - // Restores the flag to the saved state. - void Restore() const override; - - // Flag and saved flag data. - Flag<T>* flag_; - T cur_value_; - bool modified_; - bool on_command_line_; - int64_t counter_; -}; +inline const std::type_info* GenRuntimeTypeId() { +#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI) + return &typeid(T); +#else + return nullptr; +#endif +} /////////////////////////////////////////////////////////////////////////////// // Flag help auxiliary structs. @@ -176,6 +178,28 @@ class FlagState : public flags_internal::FlagStateInterface { // cases. using HelpGenFunc = std::string (*)(); +template <size_t N> +struct FixedCharArray { + char value[N]; + + template <size_t... I> + static constexpr FixedCharArray<N> FromLiteralString( + absl::string_view str, absl::index_sequence<I...>) { + return (void)str, FixedCharArray<N>({{str[I]..., '\0'}}); + } +}; + +template <typename Gen, size_t N = Gen::Value().size()> +constexpr FixedCharArray<N + 1> HelpStringAsArray(int) { + return FixedCharArray<N + 1>::FromLiteralString( + Gen::Value(), absl::make_index_sequence<N>{}); +} + +template <typename Gen> +constexpr std::false_type HelpStringAsArray(char) { + return std::false_type{}; +} + union FlagHelpMsg { constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {} constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {} @@ -193,40 +217,28 @@ struct FlagHelpArg { extern const char kStrippedFlagHelp[]; -// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by -// ABSL_FLAG macro. It is only used to silence the compiler in the case where -// help message expression is not constexpr and does not have type const char*. -// If help message expression is indeed constexpr const char* HelpConstexprWrap -// is just a trivial identity function. -template <typename T> -const char* HelpConstexprWrap(const T&) { - return nullptr; -} -constexpr const char* HelpConstexprWrap(const char* p) { return p; } -constexpr const char* HelpConstexprWrap(char* p) { return p; } - // These two HelpArg overloads allows us to select at compile time one of two // way to pass Help argument to absl::Flag. We'll be passing -// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer -// first overload if possible. If T::Const is evaluatable on constexpr -// context (see non template int parameter below) we'll choose first overload. -// In this case the help message expression is immediately evaluated and is used -// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG. -// Otherwise SFINAE kicks in and first overload is dropped from the +// AbslFlagHelpGenFor##name as Gen and integer 0 as a single argument to prefer +// first overload if possible. If help message is evaluatable on constexpr +// context We'll be able to make FixedCharArray out of it and we'll choose first +// overload. In this case the help message expression is immediately evaluated +// and is used to construct the absl::Flag. No additionl code is generated by +// ABSL_FLAG Otherwise SFINAE kicks in and first overload is dropped from the // consideration, in which case the second overload will be used. The second // overload does not attempt to evaluate the help message expression // immediately and instead delays the evaluation by returing the function // pointer (&T::NonConst) genering the help message when necessary. This is // evaluatable in constexpr context, but the cost is an extra function being // generated in the ABSL_FLAG code. -template <typename T, int = (T::Const(), 1)> -constexpr FlagHelpArg HelpArg(int) { - return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral}; +template <typename Gen, size_t N> +constexpr FlagHelpArg HelpArg(const FixedCharArray<N>& value) { + return {FlagHelpMsg(value.value), FlagHelpKind::kLiteral}; } -template <typename T> -constexpr FlagHelpArg HelpArg(char) { - return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc}; +template <typename Gen> +constexpr FlagHelpArg HelpArg(std::false_type) { + return {FlagHelpMsg(&Gen::NonConst), FlagHelpKind::kGenFunc}; } /////////////////////////////////////////////////////////////////////////////// @@ -234,110 +246,159 @@ constexpr FlagHelpArg HelpArg(char) { // Signature for the function generating the initial flag value (usually // based on default value supplied in flag's definition) -using FlagDfltGenFunc = void* (*)(); +using FlagDfltGenFunc = void (*)(void*); union FlagDefaultSrc { constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg) : gen_func(gen_func_arg) {} +#define ABSL_FLAGS_INTERNAL_DFLT_FOR_TYPE(T, name) \ + T name##_value; \ + constexpr explicit FlagDefaultSrc(T value) : name##_value(value) {} // NOLINT + ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(ABSL_FLAGS_INTERNAL_DFLT_FOR_TYPE) +#undef ABSL_FLAGS_INTERNAL_DFLT_FOR_TYPE + void* dynamic_value; FlagDfltGenFunc gen_func; }; -enum class FlagDefaultKind : uint8_t { kDynamicValue = 0, kGenFunc = 1 }; +enum class FlagDefaultKind : uint8_t { + kDynamicValue = 0, + kGenFunc = 1, + kOneWord = 2 // for default values UP to one word in size +}; + +struct FlagDefaultArg { + FlagDefaultSrc source; + FlagDefaultKind kind; +}; + +// This struct and corresponding overload to InitDefaultValue are used to +// facilitate usage of {} as default value in ABSL_FLAG macro. +// TODO(rogeeff): Fix handling types with explicit constructors. +struct EmptyBraces {}; + +template <typename T> +constexpr T InitDefaultValue(T t) { + return t; +} + +template <typename T> +constexpr T InitDefaultValue(EmptyBraces) { + return T{}; +} + +template <typename ValueT, typename GenT, + typename std::enable_if<std::is_integral<ValueT>::value, int>::type = + ((void)GenT{}, 0)> +constexpr FlagDefaultArg DefaultArg(int) { + return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kOneWord}; +} + +template <typename ValueT, typename GenT> +constexpr FlagDefaultArg DefaultArg(char) { + return {FlagDefaultSrc(&GenT::Gen), FlagDefaultKind::kGenFunc}; +} /////////////////////////////////////////////////////////////////////////////// // Flag current value auxiliary structs. -// The minimum atomic size we believe to generate lock free code, i.e. all -// trivially copyable types not bigger this size generate lock free code. -static constexpr int kMinLockFreeAtomicSize = 8; +constexpr int64_t UninitializedFlagValue() { return 0xababababababababll; } -// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words -// might use two registers, we want to dispatch the logic for them. -#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) -static constexpr int kMaxLockFreeAtomicSize = 16; -#else -static constexpr int kMaxLockFreeAtomicSize = 8; -#endif - -// We can use atomic in cases when it fits in the register, trivially copyable -// in order to make memcpy operations. template <typename T> -struct IsAtomicFlagTypeTrait { - static constexpr bool value = - (sizeof(T) <= kMaxLockFreeAtomicSize && - type_traits_internal::is_trivially_copyable<T>::value); -}; +using FlagUseValueAndInitBitStorage = std::integral_constant< + bool, absl::type_traits_internal::is_trivially_copyable<T>::value && + std::is_default_constructible<T>::value && (sizeof(T) < 8)>; -// Clang does not always produce cmpxchg16b instruction when alignment of a 16 -// bytes type is not 16. -struct alignas(16) FlagsInternalTwoWordsType { - int64_t first; - int64_t second; +template <typename T> +using FlagUseOneWordStorage = std::integral_constant< + bool, absl::type_traits_internal::is_trivially_copyable<T>::value && + (sizeof(T) <= 8)>; + +template <class T> +using FlagUseSequenceLockStorage = std::integral_constant< + bool, absl::type_traits_internal::is_trivially_copyable<T>::value && + (sizeof(T) > 8)>; + +enum class FlagValueStorageKind : uint8_t { + kValueAndInitBit = 0, + kOneWordAtomic = 1, + kSequenceLocked = 2, + kAlignedBuffer = 3, }; -constexpr bool operator==(const FlagsInternalTwoWordsType& that, - const FlagsInternalTwoWordsType& other) { - return that.first == other.first && that.second == other.second; -} -constexpr bool operator!=(const FlagsInternalTwoWordsType& that, - const FlagsInternalTwoWordsType& other) { - return !(that == other); +template <typename T> +static constexpr FlagValueStorageKind StorageKind() { + return FlagUseValueAndInitBitStorage<T>::value + ? FlagValueStorageKind::kValueAndInitBit + : FlagUseOneWordStorage<T>::value + ? FlagValueStorageKind::kOneWordAtomic + : FlagUseSequenceLockStorage<T>::value + ? FlagValueStorageKind::kSequenceLocked + : FlagValueStorageKind::kAlignedBuffer; } -constexpr int64_t SmallAtomicInit() { return 0xababababababababll; } +struct FlagOneWordValue { + constexpr explicit FlagOneWordValue(int64_t v) : value(v) {} + std::atomic<int64_t> value; +}; -template <typename T, typename S = void> -struct BestAtomicType { - using type = int64_t; - static constexpr int64_t AtomicInit() { return SmallAtomicInit(); } +template <typename T> +struct alignas(8) FlagValueAndInitBit { + T value; + // Use an int instead of a bool to guarantee that a non-zero value has + // a bit set. + uint8_t init; }; +template <typename T, + FlagValueStorageKind Kind = flags_internal::StorageKind<T>()> +struct FlagValue; + template <typename T> -struct BestAtomicType< - T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) && - sizeof(T) <= kMaxLockFreeAtomicSize), - void>::type> { - using type = FlagsInternalTwoWordsType; - static constexpr FlagsInternalTwoWordsType AtomicInit() { - return {SmallAtomicInit(), SmallAtomicInit()}; +struct FlagValue<T, FlagValueStorageKind::kValueAndInitBit> : FlagOneWordValue { + constexpr FlagValue() : FlagOneWordValue(0) {} + bool Get(const SequenceLock&, T& dst) const { + int64_t storage = value.load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(storage == 0)) { + return false; + } + dst = absl::bit_cast<FlagValueAndInitBit<T>>(storage).value; + return true; } }; -struct FlagValue { - // Heap allocated value. - void* dynamic = nullptr; - // For some types, a copy of the current value is kept in an atomically - // accessible field. - union Atomics { - // Using small atomic for small types. - std::atomic<int64_t> small_atomic; - template <typename T, - typename K = typename std::enable_if< - (sizeof(T) <= kMinLockFreeAtomicSize), void>::type> - int64_t load() const { - return small_atomic.load(std::memory_order_acquire); +template <typename T> +struct FlagValue<T, FlagValueStorageKind::kOneWordAtomic> : FlagOneWordValue { + constexpr FlagValue() : FlagOneWordValue(UninitializedFlagValue()) {} + bool Get(const SequenceLock&, T& dst) const { + int64_t one_word_val = value.load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(one_word_val == UninitializedFlagValue())) { + return false; } + std::memcpy(&dst, static_cast<const void*>(&one_word_val), sizeof(T)); + return true; + } +}; -#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD) - // Using big atomics for big types. - std::atomic<FlagsInternalTwoWordsType> big_atomic; - template <typename T, typename K = typename std::enable_if< - (kMinLockFreeAtomicSize < sizeof(T) && - sizeof(T) <= kMaxLockFreeAtomicSize), - void>::type> - FlagsInternalTwoWordsType load() const { - return big_atomic.load(std::memory_order_acquire); - } - constexpr Atomics() - : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(), - SmallAtomicInit()}} {} -#else - constexpr Atomics() : small_atomic{SmallAtomicInit()} {} -#endif - }; - Atomics atomics{}; +template <typename T> +struct FlagValue<T, FlagValueStorageKind::kSequenceLocked> { + bool Get(const SequenceLock& lock, T& dst) const { + return lock.TryRead(&dst, value_words, sizeof(T)); + } + + static constexpr int kNumWords = + flags_internal::AlignUp(sizeof(T), sizeof(uint64_t)) / sizeof(uint64_t); + + alignas(T) alignas( + std::atomic<uint64_t>) std::atomic<uint64_t> value_words[kNumWords]; +}; + +template <typename T> +struct FlagValue<T, FlagValueStorageKind::kAlignedBuffer> { + bool Get(const SequenceLock&, T&) const { return false; } + + alignas(T) char value[sizeof(T)]; }; /////////////////////////////////////////////////////////////////////////////// @@ -358,134 +419,161 @@ struct FlagCallback { // The class encapsulates the Flag's data and access to it. struct DynValueDeleter { - explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {} - void operator()(void* ptr) const { - if (op != nullptr) Delete(op, ptr); - } + explicit DynValueDeleter(FlagOpFn op_arg = nullptr); + void operator()(void* ptr) const; FlagOpFn op; }; -class FlagImpl { +class FlagState; + +class FlagImpl final : public CommandLineFlag { public: constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, - FlagHelpArg help, FlagDfltGenFunc default_value_gen) + FlagHelpArg help, FlagValueStorageKind value_kind, + FlagDefaultArg default_arg) : name_(name), filename_(filename), op_(op), help_(help.source), help_source_kind_(static_cast<uint8_t>(help.kind)), - def_kind_(static_cast<uint8_t>(FlagDefaultKind::kGenFunc)), + value_storage_kind_(static_cast<uint8_t>(value_kind)), + def_kind_(static_cast<uint8_t>(default_arg.kind)), modified_(false), on_command_line_(false), - counter_(0), callback_(nullptr), - default_value_(default_value_gen), + default_value_(default_arg.source), data_guard_{} {} // Constant access methods - absl::string_view Name() const; - std::string Filename() const; - std::string Help() const; - bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); - void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard()); - - template <typename T, typename std::enable_if< - !IsAtomicFlagTypeTrait<T>::value, int>::type = 0> - void Get(T* dst) const { - AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); - Read(dst); + int64_t ReadOneWord() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool ReadOneBool() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard()); + void Read(bool* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { + *value = ReadOneBool(); } - // Overload for `GetFlag()` for types that support lock-free reads. - template <typename T, typename std::enable_if<IsAtomicFlagTypeTrait<T>::value, - int>::type = 0> - void Get(T* dst) const { - // For flags of types which can be accessed "atomically" we want to avoid - // slowing down flag value access due to type validation. That's why - // this validation is hidden behind !NDEBUG -#ifndef NDEBUG - AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); -#endif - using U = flags_internal::BestAtomicType<T>; - typename U::type r = value_.atomics.template load<T>(); - if (r != U::AtomicInit()) { - std::memcpy(static_cast<void*>(dst), &r, sizeof(T)); - } else { - Read(dst); - } + template <typename T, + absl::enable_if_t<flags_internal::StorageKind<T>() == + FlagValueStorageKind::kOneWordAtomic, + int> = 0> + void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { + int64_t v = ReadOneWord(); + std::memcpy(value, static_cast<const void*>(&v), sizeof(T)); } - template <typename T> - void Set(const T& src) { - AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>); - Write(&src); + template <typename T, + typename std::enable_if<flags_internal::StorageKind<T>() == + FlagValueStorageKind::kValueAndInitBit, + int>::type = 0> + void Read(T* value) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { + *value = absl::bit_cast<FlagValueAndInitBit<T>>(ReadOneWord()).value; } // Mutating access methods void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); - bool SetFromString(absl::string_view value, FlagSettingMode set_mode, - ValueSource source, std::string* err) - ABSL_LOCKS_EXCLUDED(*DataGuard()); - // If possible, updates copy of the Flag's value that is stored in an - // atomic word. - void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Interfaces to operate on callbacks. void SetCallback(const FlagCallbackFunc mutation_callback) ABSL_LOCKS_EXCLUDED(*DataGuard()); void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); - // Interfaces to save/restore mutable flag data - template <typename T> - std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const - ABSL_LOCKS_EXCLUDED(*DataGuard()) { - T&& cur_value = flag->Get(); - absl::MutexLock l(DataGuard()); - - return absl::make_unique<FlagState<T>>( - flag, std::move(cur_value), modified_, on_command_line_, counter_); - } - bool RestoreState(const void* value, bool modified, bool on_command_line, - int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard()); - - // Value validation interfaces. - void CheckDefaultValueParsingRoundtrip() const - ABSL_LOCKS_EXCLUDED(*DataGuard()); - bool ValidateInputValue(absl::string_view value) const - ABSL_LOCKS_EXCLUDED(*DataGuard()); + // Used in read/write operations to validate source/target has correct type. + // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to + // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed + // int. To do that we pass the "assumed" type id (which is deduced from type + // int) as an argument `type_id`, which is in turn is validated against the + // type id stored in flag object by flag definition statement. + void AssertValidType(FlagFastTypeId type_id, + const std::type_info* (*gen_rtti)()) const; private: + template <typename T> + friend class Flag; + friend class FlagState; + // Ensures that `data_guard_` is initialized and returns it. - absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_); + absl::Mutex* DataGuard() const + ABSL_LOCK_RETURNED(reinterpret_cast<absl::Mutex*>(data_guard_)); // Returns heap allocated value of type T initialized with default value. std::unique_ptr<void, DynValueDeleter> MakeInitValue() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Flag initialization called via absl::call_once. void Init(); - // Attempts to parse supplied `value` std::string. If parsing is successful, + + // Offset value access methods. One per storage kind. These methods to not + // respect const correctness, so be very carefull using them. + + // This is a shared helper routine which encapsulates most of the magic. Since + // it is only used inside the three routines below, which are defined in + // flag.cc, we can define it in that file as well. + template <typename StorageT> + StorageT* OffsetValue() const; + // This is an accessor for a value stored in an aligned buffer storage + // used for non-trivially-copyable data types. + // Returns a mutable pointer to the start of a buffer. + void* AlignedBufferValue() const; + + // The same as above, but used for sequencelock-protected storage. + std::atomic<uint64_t>* AtomicBufferValue() const; + + // This is an accessor for a value stored as one word atomic. Returns a + // mutable reference to an atomic value. + std::atomic<int64_t>& OneWordValue() const; + + // Attempts to parse supplied `value` string. If parsing is successful, // returns new value. Otherwise returns nullptr. std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value, - std::string* err) const + std::string& err) const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Stores the flag value based on the pointer to the source. void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + // Copy the flag data, protected by `seq_lock_` into `dst`. + // + // REQUIRES: ValueStorageKind() == kSequenceLocked. + void ReadSequenceLockedData(void* dst) const + ABSL_LOCKS_EXCLUDED(*DataGuard()); + FlagHelpKind HelpSourceKind() const { return static_cast<FlagHelpKind>(help_source_kind_); } + FlagValueStorageKind ValueStorageKind() const { + return static_cast<FlagValueStorageKind>(value_storage_kind_); + } FlagDefaultKind DefaultKind() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()) { return static_cast<FlagDefaultKind>(def_kind_); } - // Used in read/write operations to validate source/target has correct type. - // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to - // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed - // int. To do that we pass the "assumed" type id (which is deduced from type - // int) as an argument `op`, which is in turn is validated against the type id - // stored in flag object by flag definition statement. - void AssertValidType(FlagStaticTypeId type_id) const; + + // CommandLineFlag interface implementation + absl::string_view Name() const override; + std::string Filename() const override; + std::string Help() const override; + FlagFastTypeId TypeId() const override; + bool IsSpecifiedOnCommandLine() const override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string DefaultValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string CurrentValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool ValidateInputValue(absl::string_view value) const override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + void CheckDefaultValueParsingRoundtrip() const override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + int64_t ModificationCount() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + + // Interfaces to save and restore flags to/from persistent state. + // Returns current flag state or nullptr if flag does not support + // saving and restoring a state. + std::unique_ptr<FlagStateInterface> SaveState() override + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + // Restores the flag state to the supplied state object. If there is + // nothing to restore returns false. Otherwise returns true. + bool RestoreState(const FlagState& flag_state) + ABSL_LOCKS_EXCLUDED(*DataGuard()); + + bool ParseFrom(absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string& error) override + ABSL_LOCKS_EXCLUDED(*DataGuard()); // Immutable flag's state. @@ -499,40 +587,35 @@ class FlagImpl { const FlagHelpMsg help_; // Indicates if help message was supplied as literal or generator func. const uint8_t help_source_kind_ : 1; + // Kind of storage this flag is using for the flag's value. + const uint8_t value_storage_kind_ : 2; - // ------------------------------------------------------------------------ - // The bytes containing the const bitfields must not be shared with bytes - // containing the mutable bitfields. - // ------------------------------------------------------------------------ - - // Unique tag for absl::call_once call to initialize this flag. - // - // The placement of this variable between the immutable and mutable bitfields - // is important as prevents them from occupying the same byte. If you remove - // this variable, make sure to maintain this property. - absl::once_flag init_control_; + uint8_t : 0; // The bytes containing the const bitfields must not be + // shared with bytes containing the mutable bitfields. // Mutable flag's state (guarded by `data_guard_`). - // If def_kind_ == kDynamicValue, default_value_ holds a dynamically allocated - // value. - uint8_t def_kind_ : 1 ABSL_GUARDED_BY(*DataGuard()); + // def_kind_ is not guard by DataGuard() since it is accessed in Init without + // locks. + uint8_t def_kind_ : 2; // Has this flag's value been modified? bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard()); // Has this flag been specified on command line. bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard()); - // Mutation counter - int64_t counter_ ABSL_GUARDED_BY(*DataGuard()); + // Unique tag for absl::call_once call to initialize this flag. + absl::once_flag init_control_; + + // Sequence lock / mutation counter. + flags_internal::SequenceLock seq_lock_; + // Optional flag's callback and absl::Mutex to guard the invocations. FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard()); // Either a pointer to the function generating the default value based on the // value specified in ABSL_FLAG or pointer to the dynamically set default // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish // these two cases. - FlagDefaultSrc default_value_ ABSL_GUARDED_BY(*DataGuard()); - // Current Flag Value - FlagValue value_; + FlagDefaultSrc default_value_; // This is reserved space for an absl::Mutex to guard flag data. It will be // initialized in FlagImpl::Init via placement new. @@ -549,11 +632,29 @@ class FlagImpl { // flag reflection handle interface. template <typename T> -class Flag final : public flags_internal::CommandLineFlag { +class Flag { public: - constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, - const FlagDfltGenFunc default_value_gen) - : impl_(name, filename, &FlagOps<T>, help, default_value_gen) {} + constexpr Flag(const char* name, const char* filename, FlagHelpArg help, + const FlagDefaultArg default_arg) + : impl_(name, filename, &FlagOps<T>, help, + flags_internal::StorageKind<T>(), default_arg), + value_() {} + + // CommandLineFlag interface + absl::string_view Name() const { return impl_.Name(); } + std::string Filename() const { return impl_.Filename(); } + std::string Help() const { return impl_.Help(); } + // Do not use. To be removed. + bool IsSpecifiedOnCommandLine() const { + return impl_.IsSpecifiedOnCommandLine(); + } + std::string DefaultValue() const { return impl_.DefaultValue(); } + std::string CurrentValue() const { return impl_.CurrentValue(); } + + private: + template <typename, bool> + friend class FlagRegistrar; + friend class FlagImplPeer; T Get() const { // See implementation notes in CommandLineFlag::Get(). @@ -564,106 +665,132 @@ class Flag final : public flags_internal::CommandLineFlag { }; U u; - impl_.Get(&u.value); +#if !defined(NDEBUG) + impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>); +#endif + + if (ABSL_PREDICT_FALSE(!value_.Get(impl_.seq_lock_, u.value))) { + impl_.Read(&u.value); + } return std::move(u.value); } - void Set(const T& v) { impl_.Set(v); } - void SetCallback(const FlagCallbackFunc mutation_callback) { - impl_.SetCallback(mutation_callback); + void Set(const T& v) { + impl_.AssertValidType(base_internal::FastTypeId<T>(), &GenRuntimeTypeId<T>); + impl_.Write(&v); } - // CommandLineFlag interface - absl::string_view Name() const override { return impl_.Name(); } - std::string Filename() const override { return impl_.Filename(); } - absl::string_view Typename() const override { return ""; } - std::string Help() const override { return impl_.Help(); } - bool IsModified() const override { return impl_.IsModified(); } - bool IsSpecifiedOnCommandLine() const override { - return impl_.IsSpecifiedOnCommandLine(); - } - std::string DefaultValue() const override { return impl_.DefaultValue(); } - std::string CurrentValue() const override { return impl_.CurrentValue(); } - bool ValidateInputValue(absl::string_view value) const override { - return impl_.ValidateInputValue(value); - } + // Access to the reflection. + const CommandLineFlag& Reflect() const { return impl_; } - // Interfaces to save and restore flags to/from persistent state. - // Returns current flag state or nullptr if flag does not support - // saving and restoring a state. - std::unique_ptr<FlagStateInterface> SaveState() override { - return impl_.SaveState(this); - } + // Flag's data + // The implementation depends on value_ field to be placed exactly after the + // impl_ field, so that impl_ can figure out the offset to the value and + // access it. + FlagImpl impl_; + FlagValue<T> value_; +}; - // Restores the flag state to the supplied state object. If there is - // nothing to restore returns false. Otherwise returns true. - bool RestoreState(const FlagState<T>& flag_state) { - return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_, - flag_state.on_command_line_, flag_state.counter_); +/////////////////////////////////////////////////////////////////////////////// +// Trampoline for friend access + +class FlagImplPeer { + public: + template <typename T, typename FlagType> + static T InvokeGet(const FlagType& flag) { + return flag.Get(); } - bool SetFromString(absl::string_view value, FlagSettingMode set_mode, - ValueSource source, std::string* error) override { - return impl_.SetFromString(value, set_mode, source, error); + template <typename FlagType, typename T> + static void InvokeSet(FlagType& flag, const T& v) { + flag.Set(v); } - void CheckDefaultValueParsingRoundtrip() const override { - impl_.CheckDefaultValueParsingRoundtrip(); + template <typename FlagType> + static const CommandLineFlag& InvokeReflect(const FlagType& f) { + return f.Reflect(); } - - private: - friend class FlagState<T>; - - void Read(void* dst) const override { impl_.Read(dst); } - FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; } - - // Flag's data - FlagImpl impl_; }; +/////////////////////////////////////////////////////////////////////////////// +// Implementation of Flag value specific operations routine. template <typename T> -inline void FlagState<T>::Restore() const { - if (flag_->RestoreState(*this)) { - ABSL_INTERNAL_LOG(INFO, - absl::StrCat("Restore saved value of ", flag_->Name(), - " to: ", flag_->CurrentValue())); +void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) { + switch (op) { + case FlagOp::kAlloc: { + std::allocator<T> alloc; + return std::allocator_traits<std::allocator<T>>::allocate(alloc, 1); + } + case FlagOp::kDelete: { + T* p = static_cast<T*>(v2); + p->~T(); + std::allocator<T> alloc; + std::allocator_traits<std::allocator<T>>::deallocate(alloc, p, 1); + return nullptr; + } + case FlagOp::kCopy: + *static_cast<T*>(v2) = *static_cast<const T*>(v1); + return nullptr; + case FlagOp::kCopyConstruct: + new (v2) T(*static_cast<const T*>(v1)); + return nullptr; + case FlagOp::kSizeof: + return reinterpret_cast<void*>(static_cast<uintptr_t>(sizeof(T))); + case FlagOp::kFastTypeId: + return const_cast<void*>(base_internal::FastTypeId<T>()); + case FlagOp::kRuntimeTypeId: + return const_cast<std::type_info*>(GenRuntimeTypeId<T>()); + case FlagOp::kParse: { + // Initialize the temporary instance of type T based on current value in + // destination (which is going to be flag's default value). + T temp(*static_cast<T*>(v2)); + if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, + static_cast<std::string*>(v3))) { + return nullptr; + } + *static_cast<T*>(v2) = std::move(temp); + return v2; + } + case FlagOp::kUnparse: + *static_cast<std::string*>(v2) = + absl::UnparseFlag<T>(*static_cast<const T*>(v1)); + return nullptr; + case FlagOp::kValueOffset: { + // Round sizeof(FlagImp) to a multiple of alignof(FlagValue<T>) to get the + // offset of the data. + ptrdiff_t round_to = alignof(FlagValue<T>); + ptrdiff_t offset = + (sizeof(FlagImpl) + round_to - 1) / round_to * round_to; + return reinterpret_cast<void*>(offset); + } } + return nullptr; } +/////////////////////////////////////////////////////////////////////////////// // This class facilitates Flag object registration and tail expression-based // flag definition, for example: // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); +struct FlagRegistrarEmpty {}; template <typename T, bool do_register> class FlagRegistrar { public: - explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) { - if (do_register) flags_internal::RegisterCommandLineFlag(flag_); + explicit FlagRegistrar(Flag<T>& flag, const char* filename) : flag_(flag) { + if (do_register) + flags_internal::RegisterCommandLineFlag(flag_.impl_, filename); } - FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && { - flag_->SetCallback(cb); + FlagRegistrar OnUpdate(FlagCallbackFunc cb) && { + flag_.impl_.SetCallback(cb); return *this; } - // Make the registrar "die" gracefully as a bool on a line where registration - // happens. Registrar objects are intended to live only as temporary. - operator bool() const { return true; } // NOLINT + // Make the registrar "die" gracefully as an empty struct on a line where + // registration happens. Registrar objects are intended to live only as + // temporary. + operator FlagRegistrarEmpty() const { return {}; } // NOLINT private: - Flag<T>* flag_; // Flag being registered (not owned). + Flag<T>& flag_; // Flag being registered (not owned). }; -// This struct and corresponding overload to MakeDefaultValue are used to -// facilitate usage of {} as default value in ABSL_FLAG macro. -struct EmptyBraces {}; - -template <typename T> -T* MakeFromDefaultValue(T t) { - return new T(std::move(t)); -} - -template <typename T> -T* MakeFromDefaultValue(EmptyBraces) { - return new T; -} - } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/internal/flag_msvc.inc b/third_party/abseil-cpp/absl/flags/internal/flag_msvc.inc new file mode 100644 index 0000000000..c31bd27fd8 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/internal/flag_msvc.inc @@ -0,0 +1,116 @@ +// +// Copyright 2021 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +// Do not include this file directly. +// Include absl/flags/flag.h instead. + +// MSVC debug builds do not implement initialization with constexpr constructors +// correctly. To work around this we add a level of indirection, so that the +// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias +// to that class) and dynamically allocates an instance when necessary. We also +// forward all calls to internal::Flag methods via trampoline methods. In this +// setup the `absl::Flag` class does not have constructor and virtual methods, +// all the data members are public and thus MSVC is able to initialize it at +// link time. To deal with multiple threads accessing the flag for the first +// time concurrently we use an atomic boolean indicating if flag object is +// initialized. We also employ the double-checked locking pattern where the +// second level of protection is a global Mutex, so if two threads attempt to +// construct the flag concurrently only one wins. +// +// This solution is based on a recomendation here: +// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 + +namespace flags_internal { +absl::Mutex* GetGlobalConstructionGuard(); +} // namespace flags_internal + +// Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. +// See https://abseil.io/docs/cpp/guides/flags +template <typename T> +class Flag { + public: + // No constructor and destructor to ensure this is an aggregate type. + // Visual Studio 2015 still requires the constructor for class to be + // constexpr initializable. +#if _MSC_VER <= 1900 + constexpr Flag(const char* name, const char* filename, + const flags_internal::HelpGenFunc help_gen, + const flags_internal::FlagDfltGenFunc default_value_gen) + : name_(name), + filename_(filename), + help_gen_(help_gen), + default_value_gen_(default_value_gen), + inited_(false), + impl_(nullptr) {} +#endif + + flags_internal::Flag<T>& GetImpl() const { + if (!inited_.load(std::memory_order_acquire)) { + absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); + + if (inited_.load(std::memory_order_acquire)) { + return *impl_; + } + + impl_ = new flags_internal::Flag<T>( + name_, filename_, + {flags_internal::FlagHelpMsg(help_gen_), + flags_internal::FlagHelpKind::kGenFunc}, + {flags_internal::FlagDefaultSrc(default_value_gen_), + flags_internal::FlagDefaultKind::kGenFunc}); + inited_.store(true, std::memory_order_release); + } + + return *impl_; + } + + // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. + // See https://abseil.io/docs/cpp/guides/flags + bool IsRetired() const { return GetImpl().IsRetired(); } + absl::string_view Name() const { return GetImpl().Name(); } + std::string Help() const { return GetImpl().Help(); } + bool IsModified() const { return GetImpl().IsModified(); } + bool IsSpecifiedOnCommandLine() const { + return GetImpl().IsSpecifiedOnCommandLine(); + } + std::string Filename() const { return GetImpl().Filename(); } + std::string DefaultValue() const { return GetImpl().DefaultValue(); } + std::string CurrentValue() const { return GetImpl().CurrentValue(); } + template <typename U> + inline bool IsOfType() const { + return GetImpl().template IsOfType<U>(); + } + T Get() const { + return flags_internal::FlagImplPeer::InvokeGet<T>(GetImpl()); + } + void Set(const T& v) { + flags_internal::FlagImplPeer::InvokeSet(GetImpl(), v); + } + void InvokeCallback() { GetImpl().InvokeCallback(); } + + const CommandLineFlag& Reflect() const { + return flags_internal::FlagImplPeer::InvokeReflect(GetImpl()); + } + + // The data members are logically private, but they need to be public for + // this to be an aggregate type. + const char* name_; + const char* filename_; + const flags_internal::HelpGenFunc help_gen_; + const flags_internal::FlagDfltGenFunc default_value_gen_; + + mutable std::atomic<bool> inited_; + mutable flags_internal::Flag<T>* impl_; +}; diff --git a/third_party/abseil-cpp/absl/flags/internal/parse.h b/third_party/abseil-cpp/absl/flags/internal/parse.h index 03e8a07bf3..de706c8984 100644 --- a/third_party/abseil-cpp/absl/flags/internal/parse.h +++ b/third_party/abseil-cpp/absl/flags/internal/parse.h @@ -21,6 +21,7 @@ #include "absl/base/config.h" #include "absl/flags/declare.h" +#include "absl/strings/string_view.h" ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile); ABSL_DECLARE_FLAG(std::vector<std::string>, fromenv); @@ -44,6 +45,13 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], UsageFlagsAction usage_flag_act, OnUndefinedFlag on_undef_flag); +// -------------------------------------------------------------------- +// Inspect original command line + +// Returns true if flag with specified name was either present on the original +// command line or specified in flag file present on the original command line. +bool WasPresentOnCommandLine(absl::string_view flag_name); + } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/internal/path_util.h b/third_party/abseil-cpp/absl/flags/internal/path_util.h index 365c830522..a6594d3347 100644 --- a/third_party/abseil-cpp/absl/flags/internal/path_util.h +++ b/third_party/abseil-cpp/absl/flags/internal/path_util.h @@ -17,7 +17,6 @@ #define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ #include "absl/base/config.h" -#include "absl/strings/match.h" #include "absl/strings/string_view.h" namespace absl { diff --git a/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.cc b/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.cc new file mode 100644 index 0000000000..a7eb58b6d4 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.cc @@ -0,0 +1,65 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/flags/internal/private_handle_accessor.h" + +#include <memory> +#include <string> + +#include "absl/base/config.h" +#include "absl/flags/commandlineflag.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +FlagFastTypeId PrivateHandleAccessor::TypeId(const CommandLineFlag& flag) { + return flag.TypeId(); +} + +std::unique_ptr<FlagStateInterface> PrivateHandleAccessor::SaveState( + CommandLineFlag& flag) { + return flag.SaveState(); +} + +bool PrivateHandleAccessor::IsSpecifiedOnCommandLine( + const CommandLineFlag& flag) { + return flag.IsSpecifiedOnCommandLine(); +} + +bool PrivateHandleAccessor::ValidateInputValue(const CommandLineFlag& flag, + absl::string_view value) { + return flag.ValidateInputValue(value); +} + +void PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip( + const CommandLineFlag& flag) { + flag.CheckDefaultValueParsingRoundtrip(); +} + +bool PrivateHandleAccessor::ParseFrom(CommandLineFlag& flag, + absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string& error) { + return flag.ParseFrom(value, set_mode, source, error); +} + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + diff --git a/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.h b/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.h new file mode 100644 index 0000000000..c64435cd61 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/internal/private_handle_accessor.h @@ -0,0 +1,61 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ +#define ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ + +#include <memory> +#include <string> + +#include "absl/base/config.h" +#include "absl/flags/commandlineflag.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// This class serves as a trampoline to access private methods of +// CommandLineFlag. This class is intended for use exclusively internally inside +// of the Abseil Flags implementation. +class PrivateHandleAccessor { + public: + // Access to CommandLineFlag::TypeId. + static FlagFastTypeId TypeId(const CommandLineFlag& flag); + + // Access to CommandLineFlag::SaveState. + static std::unique_ptr<FlagStateInterface> SaveState(CommandLineFlag& flag); + + // Access to CommandLineFlag::IsSpecifiedOnCommandLine. + static bool IsSpecifiedOnCommandLine(const CommandLineFlag& flag); + + // Access to CommandLineFlag::ValidateInputValue. + static bool ValidateInputValue(const CommandLineFlag& flag, + absl::string_view value); + + // Access to CommandLineFlag::CheckDefaultValueParsingRoundtrip. + static void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag); + + static bool ParseFrom(CommandLineFlag& flag, absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, std::string& error); +}; + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ diff --git a/third_party/abseil-cpp/absl/flags/internal/program_name_test.cc b/third_party/abseil-cpp/absl/flags/internal/program_name_test.cc index 269142f225..aff9f6315e 100644 --- a/third_party/abseil-cpp/absl/flags/internal/program_name_test.cc +++ b/third_party/abseil-cpp/absl/flags/internal/program_name_test.cc @@ -25,7 +25,7 @@ namespace { namespace flags = absl::flags_internal; -TEST(FlagsPathUtilTest, TestInitialProgamName) { +TEST(FlagsPathUtilTest, TestProgamNameInterfaces) { flags::SetProgramInvocationName("absl/flags/program_name_test"); std::string program_name = flags::ProgramInvocationName(); for (char& c : program_name) @@ -43,9 +43,7 @@ TEST(FlagsPathUtilTest, TestInitialProgamName) { EXPECT_TRUE(absl::EndsWith(program_name, expect_name)) << program_name; EXPECT_EQ(flags::ShortProgramInvocationName(), expect_basename); -} -TEST(FlagsPathUtilTest, TestProgamNameInterfaces) { flags::SetProgramInvocationName("a/my_test"); EXPECT_EQ(flags::ProgramInvocationName(), "a/my_test"); diff --git a/third_party/abseil-cpp/absl/flags/internal/registry.cc b/third_party/abseil-cpp/absl/flags/internal/registry.cc deleted file mode 100644 index e434a85912..0000000000 --- a/third_party/abseil-cpp/absl/flags/internal/registry.cc +++ /dev/null @@ -1,351 +0,0 @@ -// -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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/flags/internal/registry.h" - -#include <assert.h> -#include <stdlib.h> - -#include <functional> -#include <map> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "absl/base/config.h" -#include "absl/base/internal/raw_logging.h" -#include "absl/base/thread_annotations.h" -#include "absl/flags/internal/commandlineflag.h" -#include "absl/flags/usage_config.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" - -// -------------------------------------------------------------------- -// FlagRegistry implementation -// A FlagRegistry holds all flag objects indexed -// by their names so that if you know a flag's name you can access or -// set it. - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace flags_internal { - -// -------------------------------------------------------------------- -// FlagRegistry -// A FlagRegistry singleton object holds all flag objects indexed -// by their names so that if you know a flag's name (as a C -// string), you can access or set it. If the function is named -// FooLocked(), you must own the registry lock before calling -// the function; otherwise, you should *not* hold the lock, and -// the function will acquire it itself if needed. -// -------------------------------------------------------------------- - -class FlagRegistry { - public: - FlagRegistry() = default; - ~FlagRegistry() = default; - - // Store a flag in this registry. Takes ownership of *flag. - void RegisterFlag(CommandLineFlag* flag); - - void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); } - void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); } - - // Returns the flag object for the specified name, or nullptr if not found. - // Will emit a warning if a 'retired' flag is specified. - CommandLineFlag* FindFlagLocked(absl::string_view name); - - // Returns the retired flag object for the specified name, or nullptr if not - // found or not retired. Does not emit a warning. - CommandLineFlag* FindRetiredFlagLocked(absl::string_view name); - - static FlagRegistry* GlobalRegistry(); // returns a singleton registry - - private: - friend class FlagSaverImpl; // reads all the flags in order to copy them - friend void ForEachFlagUnlocked( - std::function<void(CommandLineFlag*)> visitor); - - // The map from name to flag, for FindFlagLocked(). - using FlagMap = std::map<absl::string_view, CommandLineFlag*>; - using FlagIterator = FlagMap::iterator; - using FlagConstIterator = FlagMap::const_iterator; - FlagMap flags_; - - absl::Mutex lock_; - - // Disallow - FlagRegistry(const FlagRegistry&); - FlagRegistry& operator=(const FlagRegistry&); -}; - -FlagRegistry* FlagRegistry::GlobalRegistry() { - static FlagRegistry* global_registry = new FlagRegistry; - return global_registry; -} - -namespace { - -class FlagRegistryLock { - public: - explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); } - ~FlagRegistryLock() { fr_->Unlock(); } - - private: - FlagRegistry* const fr_; -}; - -void DestroyRetiredFlag(CommandLineFlag* flag); -} // namespace - -void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { - FlagRegistryLock registry_lock(this); - std::pair<FlagIterator, bool> ins = - flags_.insert(FlagMap::value_type(flag->Name(), flag)); - if (ins.second == false) { // means the name was already in the map - CommandLineFlag* old_flag = ins.first->second; - if (flag->IsRetired() != old_flag->IsRetired()) { - // All registrations must agree on the 'retired' flag. - flags_internal::ReportUsageError( - absl::StrCat( - "Retired flag '", flag->Name(), - "' was defined normally in file '", - (flag->IsRetired() ? old_flag->Filename() : flag->Filename()), - "'."), - true); - } else if (flag->TypeId() != old_flag->TypeId()) { - flags_internal::ReportUsageError( - absl::StrCat("Flag '", flag->Name(), - "' was defined more than once but with " - "differing types. Defined in files '", - old_flag->Filename(), "' and '", flag->Filename(), - "' with types '", old_flag->Typename(), "' and '", - flag->Typename(), "', respectively."), - true); - } else if (old_flag->IsRetired()) { - // Retired flag can just be deleted. - DestroyRetiredFlag(flag); - return; - } else if (old_flag->Filename() != flag->Filename()) { - flags_internal::ReportUsageError( - absl::StrCat("Flag '", flag->Name(), - "' was defined more than once (in files '", - old_flag->Filename(), "' and '", flag->Filename(), - "')."), - true); - } else { - flags_internal::ReportUsageError( - absl::StrCat( - "Something wrong with flag '", flag->Name(), "' in file '", - flag->Filename(), "'. One possibility: file '", flag->Filename(), - "' is being linked both statically and dynamically into this " - "executable. e.g. some files listed as srcs to a test and also " - "listed as srcs of some shared lib deps of the same test."), - true); - } - // All cases above are fatal, except for the retired flags. - std::exit(1); - } -} - -CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) { - FlagConstIterator i = flags_.find(name); - if (i == flags_.end()) { - return nullptr; - } - - if (i->second->IsRetired()) { - flags_internal::ReportUsageError( - absl::StrCat("Accessing retired flag '", name, "'"), false); - } - - return i->second; -} - -CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) { - FlagConstIterator i = flags_.find(name); - if (i == flags_.end() || !i->second->IsRetired()) { - return nullptr; - } - - return i->second; -} - -// -------------------------------------------------------------------- -// FlagSaver -// FlagSaverImpl -// This class stores the states of all flags at construct time, -// and restores all flags to that state at destruct time. -// Its major implementation challenge is that it never modifies -// pointers in the 'main' registry, so global FLAG_* vars always -// point to the right place. -// -------------------------------------------------------------------- - -class FlagSaverImpl { - public: - FlagSaverImpl() = default; - FlagSaverImpl(const FlagSaverImpl&) = delete; - void operator=(const FlagSaverImpl&) = delete; - - // Saves the flag states from the flag registry into this object. - // It's an error to call this more than once. - void SaveFromRegistry() { - assert(backup_registry_.empty()); // call only once! - flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { - if (auto flag_state = flag->SaveState()) { - backup_registry_.emplace_back(std::move(flag_state)); - } - }); - } - - // Restores the saved flag states into the flag registry. - void RestoreToRegistry() { - for (const auto& flag_state : backup_registry_) { - flag_state->Restore(); - } - } - - private: - std::vector<std::unique_ptr<flags_internal::FlagStateInterface>> - backup_registry_; -}; - -FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); } - -void FlagSaver::Ignore() { - delete impl_; - impl_ = nullptr; -} - -FlagSaver::~FlagSaver() { - if (!impl_) return; - - impl_->RestoreToRegistry(); - delete impl_; -} - -// -------------------------------------------------------------------- - -CommandLineFlag* FindCommandLineFlag(absl::string_view name) { - if (name.empty()) return nullptr; - FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - FlagRegistryLock frl(registry); - - return registry->FindFlagLocked(name); -} - -CommandLineFlag* FindRetiredFlag(absl::string_view name) { - FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - FlagRegistryLock frl(registry); - - return registry->FindRetiredFlagLocked(name); -} - -// -------------------------------------------------------------------- - -void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) { - FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - for (FlagRegistry::FlagConstIterator i = registry->flags_.begin(); - i != registry->flags_.end(); ++i) { - visitor(i->second); - } -} - -void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) { - FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - FlagRegistryLock frl(registry); - ForEachFlagUnlocked(visitor); -} - -// -------------------------------------------------------------------- - -bool RegisterCommandLineFlag(CommandLineFlag* flag) { - FlagRegistry::GlobalRegistry()->RegisterFlag(flag); - return true; -} - -// -------------------------------------------------------------------- - -namespace { - -class RetiredFlagObj final : public flags_internal::CommandLineFlag { - public: - constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id) - : name_(name), type_id_(type_id) {} - - private: - absl::string_view Name() const override { return name_; } - std::string Filename() const override { return "RETIRED"; } - absl::string_view Typename() const override { return ""; } - FlagStaticTypeId TypeId() const override { return type_id_; } - std::string Help() const override { return ""; } - bool IsRetired() const override { return true; } - bool IsModified() const override { return false; } - bool IsSpecifiedOnCommandLine() const override { return false; } - std::string DefaultValue() const override { return ""; } - std::string CurrentValue() const override { return ""; } - - // Any input is valid - bool ValidateInputValue(absl::string_view) const override { return true; } - - std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override { - return nullptr; - } - - bool SetFromString(absl::string_view, flags_internal::FlagSettingMode, - flags_internal::ValueSource, std::string*) override { - return false; - } - - void CheckDefaultValueParsingRoundtrip() const override {} - - void Read(void*) const override {} - - // Data members - const char* const name_; - const FlagStaticTypeId type_id_; -}; - -void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) { - assert(flag->IsRetired()); - delete static_cast<RetiredFlagObj*>(flag); -} - -} // namespace - -bool Retire(const char* name, FlagStaticTypeId type_id) { - auto* flag = new flags_internal::RetiredFlagObj(name, type_id); - FlagRegistry::GlobalRegistry()->RegisterFlag(flag); - return true; -} - -// -------------------------------------------------------------------- - -bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) { - assert(!name.empty()); - CommandLineFlag* flag = flags_internal::FindRetiredFlag(name); - if (flag == nullptr) { - return false; - } - assert(type_is_bool); - *type_is_bool = flag->IsOfType<bool>(); - return true; -} - -} // namespace flags_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/internal/registry.h b/third_party/abseil-cpp/absl/flags/internal/registry.h index 69ff889fb1..4b68c85f5c 100644 --- a/third_party/abseil-cpp/absl/flags/internal/registry.h +++ b/third_party/abseil-cpp/absl/flags/internal/registry.h @@ -17,11 +17,9 @@ #define ABSL_FLAGS_INTERNAL_REGISTRY_H_ #include <functional> -#include <map> -#include <string> #include "absl/base/config.h" -#include "absl/base/macros.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/strings/string_view.h" @@ -32,19 +30,15 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace flags_internal { -CommandLineFlag* FindCommandLineFlag(absl::string_view name); -CommandLineFlag* FindRetiredFlag(absl::string_view name); - -// Executes specified visitor for each non-retired flag in the registry. -// Requires the caller hold the registry lock. -void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor); // Executes specified visitor for each non-retired flag in the registry. While // callback are executed, the registry is locked and can't be changed. -void ForEachFlag(std::function<void(CommandLineFlag*)> visitor); +void ForEachFlag(std::function<void(CommandLineFlag&)> visitor); //----------------------------------------------------------------------------- -bool RegisterCommandLineFlag(CommandLineFlag*); +bool RegisterCommandLineFlag(CommandLineFlag&, const char* filename); + +void FinalizeRegistry(); //----------------------------------------------------------------------------- // Retired registrations: @@ -79,42 +73,21 @@ bool RegisterCommandLineFlag(CommandLineFlag*); // // Retire flag with name "name" and type indicated by ops. -bool Retire(const char* name, FlagStaticTypeId type_id); +void Retire(const char* name, FlagFastTypeId type_id, char* buf); + +constexpr size_t kRetiredFlagObjSize = 3 * sizeof(void*); +constexpr size_t kRetiredFlagObjAlignment = alignof(void*); // Registered a retired flag with name 'flag_name' and type 'T'. template <typename T> -inline bool RetiredFlag(const char* flag_name) { - return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen<T>); -} - -// If the flag is retired, returns true and indicates in |*type_is_bool| -// whether the type of the retired flag is a bool. -// Only to be called by code that needs to explicitly ignore retired flags. -bool IsRetiredFlag(absl::string_view name, bool* type_is_bool); - -//----------------------------------------------------------------------------- -// Saves the states (value, default value, whether the user has set -// the flag, registered validators, etc) of all flags, and restores -// them when the FlagSaver is destroyed. -// -// This class is thread-safe. However, its destructor writes to -// exactly the set of flags that have changed value during its -// lifetime, so concurrent _direct_ access to those flags -// (i.e. FLAGS_foo instead of {Get,Set}CommandLineOption()) is unsafe. - -class FlagSaver { +class RetiredFlag { public: - FlagSaver(); - ~FlagSaver(); - - FlagSaver(const FlagSaver&) = delete; - void operator=(const FlagSaver&) = delete; - - // Prevents saver from restoring the saved state of flags. - void Ignore(); + void Retire(const char* flag_name) { + flags_internal::Retire(flag_name, base_internal::FastTypeId<T>(), buf_); + } private: - class FlagSaverImpl* impl_; // we use pimpl here to keep API steady + alignas(kRetiredFlagObjAlignment) char buf_[kRetiredFlagObjSize]; }; } // namespace flags_internal diff --git a/third_party/abseil-cpp/absl/flags/internal/sequence_lock.h b/third_party/abseil-cpp/absl/flags/internal/sequence_lock.h new file mode 100644 index 0000000000..36318ab9d3 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/internal/sequence_lock.h @@ -0,0 +1,187 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_ +#define ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <atomic> +#include <cassert> +#include <cstring> + +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// Align 'x' up to the nearest 'align' bytes. +inline constexpr size_t AlignUp(size_t x, size_t align) { + return align * ((x + align - 1) / align); +} + +// A SequenceLock implements lock-free reads. A sequence counter is incremented +// before and after each write, and readers access the counter before and after +// accessing the protected data. If the counter is verified to not change during +// the access, and the sequence counter value was even, then the reader knows +// that the read was race-free and valid. Otherwise, the reader must fall back +// to a Mutex-based code path. +// +// This particular SequenceLock starts in an "uninitialized" state in which +// TryRead() returns false. It must be enabled by calling MarkInitialized(). +// This serves as a marker that the associated flag value has not yet been +// initialized and a slow path needs to be taken. +// +// The memory reads and writes protected by this lock must use the provided +// `TryRead()` and `Write()` functions. These functions behave similarly to +// `memcpy()`, with one oddity: the protected data must be an array of +// `std::atomic<uint64>`. This is to comply with the C++ standard, which +// considers data races on non-atomic objects to be undefined behavior. See "Can +// Seqlocks Get Along With Programming Language Memory Models?"[1] by Hans J. +// Boehm for more details. +// +// [1] https://www.hpl.hp.com/techreports/2012/HPL-2012-68.pdf +class SequenceLock { + public: + constexpr SequenceLock() : lock_(kUninitialized) {} + + // Mark that this lock is ready for use. + void MarkInitialized() { + assert(lock_.load(std::memory_order_relaxed) == kUninitialized); + lock_.store(0, std::memory_order_release); + } + + // Copy "size" bytes of data from "src" to "dst", protected as a read-side + // critical section of the sequence lock. + // + // Unlike traditional sequence lock implementations which loop until getting a + // clean read, this implementation returns false in the case of concurrent + // calls to `Write`. In such a case, the caller should fall back to a + // locking-based slow path. + // + // Returns false if the sequence lock was not yet marked as initialized. + // + // NOTE: If this returns false, "dst" may be overwritten with undefined + // (potentially uninitialized) data. + bool TryRead(void* dst, const std::atomic<uint64_t>* src, size_t size) const { + // Acquire barrier ensures that no loads done by f() are reordered + // above the first load of the sequence counter. + int64_t seq_before = lock_.load(std::memory_order_acquire); + if (ABSL_PREDICT_FALSE(seq_before & 1) == 1) return false; + RelaxedCopyFromAtomic(dst, src, size); + // Another acquire fence ensures that the load of 'lock_' below is + // strictly ordered after the RelaxedCopyToAtomic call above. + std::atomic_thread_fence(std::memory_order_acquire); + int64_t seq_after = lock_.load(std::memory_order_relaxed); + return ABSL_PREDICT_TRUE(seq_before == seq_after); + } + + // Copy "size" bytes from "src" to "dst" as a write-side critical section + // of the sequence lock. Any concurrent readers will be forced to retry + // until they get a read that does not conflict with this write. + // + // This call must be externally synchronized against other calls to Write, + // but may proceed concurrently with reads. + void Write(std::atomic<uint64_t>* dst, const void* src, size_t size) { + // We can use relaxed instructions to increment the counter since we + // are extenally synchronized. The std::atomic_thread_fence below + // ensures that the counter updates don't get interleaved with the + // copy to the data. + int64_t orig_seq = lock_.load(std::memory_order_relaxed); + assert((orig_seq & 1) == 0); // Must be initially unlocked. + lock_.store(orig_seq + 1, std::memory_order_relaxed); + + // We put a release fence between update to lock_ and writes to shared data. + // Thus all stores to shared data are effectively release operations and + // update to lock_ above cannot be re-ordered past any of them. Note that + // this barrier is not for the fetch_add above. A release barrier for the + // fetch_add would be before it, not after. + std::atomic_thread_fence(std::memory_order_release); + RelaxedCopyToAtomic(dst, src, size); + // "Release" semantics ensure that none of the writes done by + // RelaxedCopyToAtomic() can be reordered after the following modification. + lock_.store(orig_seq + 2, std::memory_order_release); + } + + // Return the number of times that Write() has been called. + // + // REQUIRES: This must be externally synchronized against concurrent calls to + // `Write()` or `IncrementModificationCount()`. + // REQUIRES: `MarkInitialized()` must have been previously called. + int64_t ModificationCount() const { + int64_t val = lock_.load(std::memory_order_relaxed); + assert(val != kUninitialized && (val & 1) == 0); + return val / 2; + } + + // REQUIRES: This must be externally synchronized against concurrent calls to + // `Write()` or `ModificationCount()`. + // REQUIRES: `MarkInitialized()` must have been previously called. + void IncrementModificationCount() { + int64_t val = lock_.load(std::memory_order_relaxed); + assert(val != kUninitialized); + lock_.store(val + 2, std::memory_order_relaxed); + } + + private: + // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed + // atomics. + static void RelaxedCopyFromAtomic(void* dst, const std::atomic<uint64_t>* src, + size_t size) { + char* dst_byte = static_cast<char*>(dst); + while (size >= sizeof(uint64_t)) { + uint64_t word = src->load(std::memory_order_relaxed); + std::memcpy(dst_byte, &word, sizeof(word)); + dst_byte += sizeof(word); + src++; + size -= sizeof(word); + } + if (size > 0) { + uint64_t word = src->load(std::memory_order_relaxed); + std::memcpy(dst_byte, &word, size); + } + } + + // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed + // atomics. + static void RelaxedCopyToAtomic(std::atomic<uint64_t>* dst, const void* src, + size_t size) { + const char* src_byte = static_cast<const char*>(src); + while (size >= sizeof(uint64_t)) { + uint64_t word; + std::memcpy(&word, src_byte, sizeof(word)); + dst->store(word, std::memory_order_relaxed); + src_byte += sizeof(word); + dst++; + size -= sizeof(word); + } + if (size > 0) { + uint64_t word = 0; + std::memcpy(&word, src_byte, size); + dst->store(word, std::memory_order_relaxed); + } + } + + static constexpr int64_t kUninitialized = -1; + std::atomic<int64_t> lock_; +}; + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_ diff --git a/third_party/abseil-cpp/absl/flags/internal/sequence_lock_test.cc b/third_party/abseil-cpp/absl/flags/internal/sequence_lock_test.cc new file mode 100644 index 0000000000..c3ec372ed8 --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/internal/sequence_lock_test.cc @@ -0,0 +1,169 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/flags/internal/sequence_lock.h" + +#include <algorithm> +#include <atomic> +#include <thread> // NOLINT(build/c++11) +#include <tuple> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/base/internal/sysinfo.h" +#include "absl/container/fixed_array.h" +#include "absl/time/clock.h" + +namespace { + +namespace flags = absl::flags_internal; + +class ConcurrentSequenceLockTest + : public testing::TestWithParam<std::tuple<int, int>> { + public: + ConcurrentSequenceLockTest() + : buf_bytes_(std::get<0>(GetParam())), + num_threads_(std::get<1>(GetParam())) {} + + protected: + const int buf_bytes_; + const int num_threads_; +}; + +TEST_P(ConcurrentSequenceLockTest, ReadAndWrite) { + const int buf_words = + flags::AlignUp(buf_bytes_, sizeof(uint64_t)) / sizeof(uint64_t); + + // The buffer that will be protected by the SequenceLock. + absl::FixedArray<std::atomic<uint64_t>> protected_buf(buf_words); + for (auto& v : protected_buf) v = -1; + + flags::SequenceLock seq_lock; + std::atomic<bool> stop{false}; + std::atomic<int64_t> bad_reads{0}; + std::atomic<int64_t> good_reads{0}; + std::atomic<int64_t> unsuccessful_reads{0}; + + // Start a bunch of threads which read 'protected_buf' under the sequence + // lock. The main thread will concurrently update 'protected_buf'. The updates + // always consist of an array of identical integers. The reader ensures that + // any data it reads matches that pattern (i.e. the reads are not "torn"). + std::vector<std::thread> threads; + for (int i = 0; i < num_threads_; i++) { + threads.emplace_back([&]() { + absl::FixedArray<char> local_buf(buf_bytes_); + while (!stop.load(std::memory_order_relaxed)) { + if (seq_lock.TryRead(local_buf.data(), protected_buf.data(), + buf_bytes_)) { + bool good = true; + for (const auto& v : local_buf) { + if (v != local_buf[0]) good = false; + } + if (good) { + good_reads.fetch_add(1, std::memory_order_relaxed); + } else { + bad_reads.fetch_add(1, std::memory_order_relaxed); + } + } else { + unsuccessful_reads.fetch_add(1, std::memory_order_relaxed); + } + } + }); + } + while (unsuccessful_reads.load(std::memory_order_relaxed) < num_threads_) { + absl::SleepFor(absl::Milliseconds(1)); + } + seq_lock.MarkInitialized(); + + // Run a maximum of 5 seconds. On Windows, the scheduler behavior seems + // somewhat unfair and without an explicit timeout for this loop, the tests + // can run a long time. + absl::Time deadline = absl::Now() + absl::Seconds(5); + for (int i = 0; i < 100 && absl::Now() < deadline; i++) { + absl::FixedArray<char> writer_buf(buf_bytes_); + for (auto& v : writer_buf) v = i; + seq_lock.Write(protected_buf.data(), writer_buf.data(), buf_bytes_); + absl::SleepFor(absl::Microseconds(10)); + } + stop.store(true, std::memory_order_relaxed); + for (auto& t : threads) t.join(); + ASSERT_GE(good_reads, 0); + ASSERT_EQ(bad_reads, 0); +} + +// Simple helper for generating a range of thread counts. +// Generates [low, low*scale, low*scale^2, ...high) +// (even if high is between low*scale^k and low*scale^(k+1)). +std::vector<int> MultiplicativeRange(int low, int high, int scale) { + std::vector<int> result; + for (int current = low; current < high; current *= scale) { + result.push_back(current); + } + result.push_back(high); + return result; +} + +#ifndef ABSL_HAVE_THREAD_SANITIZER +const int kMaxThreads = absl::base_internal::NumCPUs(); +#else +// With TSAN, a lot of threads contending for atomic access on the sequence +// lock make this test run too slowly. +const int kMaxThreads = std::min(absl::base_internal::NumCPUs(), 4); +#endif + +// Return all of the interesting buffer sizes worth testing: +// powers of two and adjacent values. +std::vector<int> InterestingBufferSizes() { + std::vector<int> ret; + for (int v : MultiplicativeRange(1, 128, 2)) { + ret.push_back(v); + if (v > 1) { + ret.push_back(v - 1); + } + ret.push_back(v + 1); + } + return ret; +} + +INSTANTIATE_TEST_SUITE_P( + TestManyByteSizes, ConcurrentSequenceLockTest, + testing::Combine( + // Buffer size (bytes). + testing::ValuesIn(InterestingBufferSizes()), + // Number of reader threads. + testing::ValuesIn(MultiplicativeRange(1, kMaxThreads, 2)))); + +// Simple single-threaded test, parameterized by the size of the buffer to be +// protected. +class SequenceLockTest : public testing::TestWithParam<int> {}; + +TEST_P(SequenceLockTest, SingleThreaded) { + const int size = GetParam(); + absl::FixedArray<std::atomic<uint64_t>> protected_buf( + flags::AlignUp(size, sizeof(uint64_t)) / sizeof(uint64_t)); + + flags::SequenceLock seq_lock; + seq_lock.MarkInitialized(); + + std::vector<char> src_buf(size, 'x'); + seq_lock.Write(protected_buf.data(), src_buf.data(), size); + + std::vector<char> dst_buf(size, '0'); + ASSERT_TRUE(seq_lock.TryRead(dst_buf.data(), protected_buf.data(), size)); + ASSERT_EQ(src_buf, dst_buf); +} +INSTANTIATE_TEST_SUITE_P(TestManyByteSizes, SequenceLockTest, + // Buffer size (bytes). + testing::Range(1, 128)); + +} // namespace diff --git a/third_party/abseil-cpp/absl/flags/internal/type_erased.cc b/third_party/abseil-cpp/absl/flags/internal/type_erased.cc deleted file mode 100644 index 490bc4ebae..0000000000 --- a/third_party/abseil-cpp/absl/flags/internal/type_erased.cc +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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/flags/internal/type_erased.h" - -#include <assert.h> - -#include <string> - -#include "absl/base/config.h" -#include "absl/base/internal/raw_logging.h" -#include "absl/flags/internal/commandlineflag.h" -#include "absl/flags/internal/registry.h" -#include "absl/flags/usage_config.h" -#include "absl/strings/string_view.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace flags_internal { - -bool GetCommandLineOption(absl::string_view name, std::string* value) { - if (name.empty()) return false; - assert(value); - - CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); - if (flag == nullptr || flag->IsRetired()) { - return false; - } - - *value = flag->CurrentValue(); - return true; -} - -bool SetCommandLineOption(absl::string_view name, absl::string_view value) { - return SetCommandLineOptionWithMode(name, value, - flags_internal::SET_FLAGS_VALUE); -} - -bool SetCommandLineOptionWithMode(absl::string_view name, - absl::string_view value, - FlagSettingMode set_mode) { - CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); - - if (!flag || flag->IsRetired()) return false; - - std::string error; - if (!flag->SetFromString(value, set_mode, kProgrammaticChange, &error)) { - // Errors here are all of the form: the provided name was a recognized - // flag, but the value was invalid (bad type, or validation failed). - flags_internal::ReportUsageError(error, false); - return false; - } - - return true; -} - -// -------------------------------------------------------------------- - -bool IsValidFlagValue(absl::string_view name, absl::string_view value) { - CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); - - return flag != nullptr && - (flag->IsRetired() || flag->ValidateInputValue(value)); -} - -// -------------------------------------------------------------------- - -bool SpecifiedOnCommandLine(absl::string_view name) { - CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); - if (flag != nullptr && !flag->IsRetired()) { - return flag->IsSpecifiedOnCommandLine(); - } - return false; -} - -} // namespace flags_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/internal/type_erased.h b/third_party/abseil-cpp/absl/flags/internal/type_erased.h deleted file mode 100644 index 188429c771..0000000000 --- a/third_party/abseil-cpp/absl/flags/internal/type_erased.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. - -#ifndef ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ -#define ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ - -#include <string> - -#include "absl/base/config.h" -#include "absl/flags/internal/commandlineflag.h" -#include "absl/flags/internal/registry.h" -#include "absl/strings/string_view.h" - -// -------------------------------------------------------------------- -// Registry interfaces operating on type erased handles. - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace flags_internal { - -// If a flag named "name" exists, store its current value in *OUTPUT -// and return true. Else return false without changing *OUTPUT. -// Thread-safe. -bool GetCommandLineOption(absl::string_view name, std::string* value); - -// Set the value of the flag named "name" to value. If successful, -// returns true. If not successful (e.g., the flag was not found or -// the value is not a valid value), returns false. -// Thread-safe. -bool SetCommandLineOption(absl::string_view name, absl::string_view value); - -bool SetCommandLineOptionWithMode(absl::string_view name, - absl::string_view value, - FlagSettingMode set_mode); - -//----------------------------------------------------------------------------- - -// Returns true iff all of the following conditions are true: -// (a) "name" names a registered flag -// (b) "value" can be parsed succesfully according to the type of the flag -// (c) parsed value passes any validator associated with the flag -bool IsValidFlagValue(absl::string_view name, absl::string_view value); - -//----------------------------------------------------------------------------- - -// Returns true iff a flag named "name" was specified on the command line -// (either directly, or via one of --flagfile or --fromenv or --tryfromenv). -// -// Any non-command-line modification of the flag does not affect the -// result of this function. So for example, if a flag was passed on -// the command line but then reset via SET_FLAGS_DEFAULT, this -// function will still return true. -bool SpecifiedOnCommandLine(absl::string_view name); - -//----------------------------------------------------------------------------- - -// If a flag with specified "name" exists and has type T, store -// its current value in *dst and return true. Else return false -// without touching *dst. T must obey all of the requirements for -// types passed to DEFINE_FLAG. -template <typename T> -inline bool GetByName(absl::string_view name, T* dst) { - CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); - if (!flag) return false; - - if (auto val = flag->Get<T>()) { - *dst = *val; - return true; - } - - return false; -} - -} // namespace flags_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ diff --git a/third_party/abseil-cpp/absl/flags/internal/type_erased_test.cc b/third_party/abseil-cpp/absl/flags/internal/type_erased_test.cc deleted file mode 100644 index 4ce5981047..0000000000 --- a/third_party/abseil-cpp/absl/flags/internal/type_erased_test.cc +++ /dev/null @@ -1,157 +0,0 @@ -// -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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/flags/internal/type_erased.h" - -#include <memory> -#include <string> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/flags/internal/commandlineflag.h" -#include "absl/flags/internal/registry.h" -#include "absl/flags/marshalling.h" -#include "absl/memory/memory.h" - -ABSL_FLAG(int, int_flag, 1, "int_flag help"); -ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help"); -ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); - -namespace { - -namespace flags = absl::flags_internal; - -class TypeErasedTest : public testing::Test { - protected: - void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); } - void TearDown() override { flag_saver_.reset(); } - - private: - std::unique_ptr<flags::FlagSaver> flag_saver_; -}; - -// -------------------------------------------------------------------- - -TEST_F(TypeErasedTest, TestGetCommandLineOption) { - std::string value; - EXPECT_TRUE(flags::GetCommandLineOption("int_flag", &value)); - EXPECT_EQ(value, "1"); - - EXPECT_TRUE(flags::GetCommandLineOption("string_flag", &value)); - EXPECT_EQ(value, "dflt"); - - EXPECT_FALSE(flags::GetCommandLineOption("bool_retired_flag", &value)); - - EXPECT_FALSE(flags::GetCommandLineOption("unknown_flag", &value)); -} - -// -------------------------------------------------------------------- - -TEST_F(TypeErasedTest, TestSetCommandLineOption) { - EXPECT_TRUE(flags::SetCommandLineOption("int_flag", "101")); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); - - EXPECT_TRUE(flags::SetCommandLineOption("string_flag", "asdfgh")); - EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); - - EXPECT_FALSE(flags::SetCommandLineOption("bool_retired_flag", "true")); - - EXPECT_FALSE(flags::SetCommandLineOption("unknown_flag", "true")); -} - -// -------------------------------------------------------------------- - -TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_VALUE) { - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", - flags::SET_FLAGS_VALUE)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); - - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", - flags::SET_FLAGS_VALUE)); - EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); - - EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", - flags::SET_FLAGS_VALUE)); - - EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", - flags::SET_FLAGS_VALUE)); -} - -// -------------------------------------------------------------------- - -TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAG_IF_DEFAULT) { - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", - flags::SET_FLAG_IF_DEFAULT)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); - - // This semantic is broken. We return true instead of false. Value is not - // updated. - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202", - flags::SET_FLAG_IF_DEFAULT)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); - - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", - flags::SET_FLAG_IF_DEFAULT)); - EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); - - EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", - flags::SET_FLAG_IF_DEFAULT)); - - EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", - flags::SET_FLAG_IF_DEFAULT)); -} - -// -------------------------------------------------------------------- - -TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) { - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", - flags::SET_FLAGS_DEFAULT)); - - // Set it again to ensure that resetting logic is covered. - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "102", - flags::SET_FLAGS_DEFAULT)); - - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "103", - flags::SET_FLAGS_DEFAULT)); - - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", - flags::SET_FLAGS_DEFAULT)); - EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); - - EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", - flags::SET_FLAGS_DEFAULT)); - - EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", - flags::SET_FLAGS_DEFAULT)); - - // This should be successfull, since flag is still is not set - EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202", - flags::SET_FLAG_IF_DEFAULT)); - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 202); -} - -// -------------------------------------------------------------------- - -TEST_F(TypeErasedTest, TestIsValidFlagValue) { - EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "57")); - EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "-101")); - EXPECT_FALSE(flags::IsValidFlagValue("int_flag", "1.1")); - - EXPECT_TRUE(flags::IsValidFlagValue("string_flag", "#%^#%^$%DGHDG$W%adsf")); - - EXPECT_TRUE(flags::IsValidFlagValue("bool_retired_flag", "true")); -} - -} // namespace diff --git a/third_party/abseil-cpp/absl/flags/internal/usage.cc b/third_party/abseil-cpp/absl/flags/internal/usage.cc index ff90716194..949709e883 100644 --- a/third_party/abseil-cpp/absl/flags/internal/usage.cc +++ b/third_party/abseil-cpp/absl/flags/internal/usage.cc @@ -15,6 +15,8 @@ #include "absl/flags/internal/usage.h" +#include <stdint.h> + #include <functional> #include <map> #include <ostream> @@ -23,10 +25,11 @@ #include <vector> #include "absl/base/config.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/flag.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/program_name.h" #include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" @@ -34,46 +37,25 @@ #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" -ABSL_FLAG(bool, help, false, - "show help on important flags for this binary [tip: all flags can " - "have two dashes]"); -ABSL_FLAG(bool, helpfull, false, "show help on all flags"); -ABSL_FLAG(bool, helpshort, false, - "show help on only the main module for this program"); -ABSL_FLAG(bool, helppackage, false, - "show help on all modules in the main package"); -ABSL_FLAG(bool, version, false, "show version and build info and exit"); -ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags"); -ABSL_FLAG(std::string, helpon, "", - "show help on the modules named by this flag value"); -ABSL_FLAG(std::string, helpmatch, "", - "show help on modules whose name contains the specified substr"); +// Dummy global variables to prevent anyone else defining these. +bool FLAGS_help = false; +bool FLAGS_helpfull = false; +bool FLAGS_helpshort = false; +bool FLAGS_helppackage = false; +bool FLAGS_version = false; +bool FLAGS_only_check_args = false; +bool FLAGS_helpon = false; +bool FLAGS_helpmatch = false; namespace absl { ABSL_NAMESPACE_BEGIN namespace flags_internal { namespace { -absl::string_view TypenameForHelp(const flags_internal::CommandLineFlag& flag) { - // Only report names of v1 built-in types -#define HANDLE_V1_BUILTIN_TYPE(t) \ - if (flag.IsOfType<t>()) { \ - return #t; \ - } - - HANDLE_V1_BUILTIN_TYPE(bool); - HANDLE_V1_BUILTIN_TYPE(int32_t); - HANDLE_V1_BUILTIN_TYPE(int64_t); - HANDLE_V1_BUILTIN_TYPE(uint64_t); - HANDLE_V1_BUILTIN_TYPE(double); -#undef HANDLE_V1_BUILTIN_TYPE +using PerFlagFilter = std::function<bool(const absl::CommandLineFlag&)>; - if (flag.IsOfType<std::string>()) { - return "string"; - } - - return ""; -} +// Maximum length size in a human readable format. +constexpr size_t kHrfMaxLineLength = 80; // This class is used to emit an XML element with `tag` and `text`. // It adds opening and closing tags and escapes special characters in the text. @@ -127,21 +109,24 @@ class FlagHelpPrettyPrinter { public: // Pretty printer holds on to the std::ostream& reference to direct an output // to that stream. - FlagHelpPrettyPrinter(int max_line_len, std::ostream* out) - : out_(*out), + FlagHelpPrettyPrinter(size_t max_line_len, size_t min_line_len, + size_t wrapped_line_indent, std::ostream& out) + : out_(out), max_line_len_(max_line_len), + min_line_len_(min_line_len), + wrapped_line_indent_(wrapped_line_indent), line_len_(0), first_line_(true) {} void Write(absl::string_view str, bool wrap_line = false) { - // Empty std::string - do nothing. + // Empty string - do nothing. if (str.empty()) return; std::vector<absl::string_view> tokens; if (wrap_line) { for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) { if (!tokens.empty()) { - // Keep line separators in the input std::string. + // Keep line separators in the input string. tokens.push_back("\n"); } for (auto token : @@ -156,14 +141,15 @@ class FlagHelpPrettyPrinter { for (auto token : tokens) { bool new_line = (line_len_ == 0); - // Respect line separators in the input std::string. + // Respect line separators in the input string. if (token == "\n") { EndLine(); continue; } - // Write the token, ending the std::string first if necessary/possible. - if (!new_line && (line_len_ + token.size() >= max_line_len_)) { + // Write the token, ending the string first if necessary/possible. + if (!new_line && + (line_len_ + static_cast<int>(token.size()) >= max_line_len_)) { EndLine(); new_line = true; } @@ -182,13 +168,12 @@ class FlagHelpPrettyPrinter { void StartLine() { if (first_line_) { - out_ << " "; - line_len_ = 4; + line_len_ = min_line_len_; first_line_ = false; } else { - out_ << " "; - line_len_ = 6; + line_len_ = min_line_len_ + wrapped_line_indent_; } + out_ << std::string(line_len_, ' '); } void EndLine() { out_ << '\n'; @@ -197,14 +182,15 @@ class FlagHelpPrettyPrinter { private: std::ostream& out_; - const int max_line_len_; - int line_len_; + const size_t max_line_len_; + const size_t min_line_len_; + const size_t wrapped_line_indent_; + size_t line_len_; bool first_line_; }; -void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, - std::ostream* out) { - FlagHelpPrettyPrinter printer(80, out); // Max line length is 80. +void FlagHelpHumanReadable(const CommandLineFlag& flag, std::ostream& out) { + FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 4, 2, out); // Flag name. printer.Write(absl::StrCat("--", flag.Name())); @@ -212,23 +198,20 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, // Flag help. printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true); - // Flag data type (for V1 flags only). - if (!flag.IsAbseilFlag() && !flag.IsRetired()) { - printer.Write(absl::StrCat("type: ", TypenameForHelp(flag), ";")); - } - // The listed default value will be the actual default from the flag // definition in the originating source file, unless the value has // subsequently been modified using SetCommandLineOption() with mode // SET_FLAGS_DEFAULT. std::string dflt_val = flag.DefaultValue(); + std::string curr_val = flag.CurrentValue(); + bool is_modified = curr_val != dflt_val; + if (flag.IsOfType<std::string>()) { dflt_val = absl::StrCat("\"", dflt_val, "\""); } printer.Write(absl::StrCat("default: ", dflt_val, ";")); - if (flag.IsModified()) { - std::string curr_val = flag.CurrentValue(); + if (is_modified) { if (flag.IsOfType<std::string>()) { curr_val = absl::StrCat("\"", curr_val, "\""); } @@ -243,7 +226,7 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, // If a flag's help message has been stripped (e.g. by adding '#define // STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help' // and its variants. -void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, +void FlagsHelpImpl(std::ostream& out, PerFlagFilter filter_cb, HelpFormat format, absl::string_view program_usage_message) { if (format == HelpFormat::kHumanReadable) { out << flags_internal::ShortProgramInvocationName() << ": " @@ -262,50 +245,54 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, << XMLElement("usage", program_usage_message) << '\n'; } - // Map of package name to + // Ordered map of package name to // map of file name to // vector of flags in the file. // This map is used to output matching flags grouped by package and file // name. std::map<std::string, - std::map<std::string, - std::vector<const flags_internal::CommandLineFlag*>>> + std::map<std::string, std::vector<const absl::CommandLineFlag*>>> matching_flags; - flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { - std::string flag_filename = flag->Filename(); - + flags_internal::ForEachFlag([&](absl::CommandLineFlag& flag) { // Ignore retired flags. - if (flag->IsRetired()) return; + if (flag.IsRetired()) return; // If the flag has been stripped, pretend that it doesn't exist. - if (flag->Help() == flags_internal::kStrippedFlagHelp) return; + if (flag.Help() == flags_internal::kStrippedFlagHelp) return; // Make sure flag satisfies the filter - if (!filter_cb || !filter_cb(flag_filename)) return; + if (!filter_cb(flag)) return; + + std::string flag_filename = flag.Filename(); matching_flags[std::string(flags_internal::Package(flag_filename))] [flag_filename] - .push_back(flag); + .push_back(&flag); }); - absl::string_view - package_separator; // controls blank lines between packages. - absl::string_view file_separator; // controls blank lines between files. - for (const auto& package : matching_flags) { + absl::string_view package_separator; // controls blank lines between packages + absl::string_view file_separator; // controls blank lines between files + for (auto& package : matching_flags) { if (format == HelpFormat::kHumanReadable) { out << package_separator; package_separator = "\n\n"; } file_separator = ""; - for (const auto& flags_in_file : package.second) { + for (auto& flags_in_file : package.second) { if (format == HelpFormat::kHumanReadable) { out << file_separator << " Flags from " << flags_in_file.first << ":\n"; file_separator = "\n"; } + std::sort(std::begin(flags_in_file.second), + std::end(flags_in_file.second), + [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) { + return lhs->Name() < rhs->Name(); + }); + for (const auto* flag : flags_in_file.second) { flags_internal::FlagHelp(out, *flag, format); } @@ -313,27 +300,46 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, } if (format == HelpFormat::kHumanReadable) { + FlagHelpPrettyPrinter printer(kHrfMaxLineLength, 0, 0, out); + if (filter_cb && matching_flags.empty()) { - out << " No modules matched: use -helpfull\n"; + printer.Write("No flags matched.\n", true); } + printer.EndLine(); + printer.Write( + "Try --helpfull to get a list of all flags or --help=substring " + "shows help for flags which include specified substring in either " + "in the name, or description or path.\n", + true); } else { // The end of the document. out << "</AllFlags>\n"; } } +void FlagsHelpImpl(std::ostream& out, + flags_internal::FlagKindFilter filename_filter_cb, + HelpFormat format, absl::string_view program_usage_message) { + FlagsHelpImpl( + out, + [&](const absl::CommandLineFlag& flag) { + return filename_filter_cb && filename_filter_cb(flag.Filename()); + }, + format, program_usage_message); +} + } // namespace // -------------------------------------------------------------------- // Produces the help message describing specific flag. -void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, +void FlagHelp(std::ostream& out, const CommandLineFlag& flag, HelpFormat format) { if (format == HelpFormat::kHumanReadable) - flags_internal::FlagHelpHumanReadable(flag, &out); + flags_internal::FlagHelpHumanReadable(flag, out); } // -------------------------------------------------------------------- -// Produces the help messages for all flags matching the filter. +// Produces the help messages for all flags matching the filename filter. // If filter is empty produces help messages for all flags. void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format, absl::string_view program_usage_message) { @@ -348,66 +354,169 @@ void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format, // If so, handles them appropriately. int HandleUsageFlags(std::ostream& out, absl::string_view program_usage_message) { - if (absl::GetFlag(FLAGS_helpshort)) { - flags_internal::FlagsHelpImpl( - out, flags_internal::GetUsageConfig().contains_helpshort_flags, - HelpFormat::kHumanReadable, program_usage_message); - return 1; - } + switch (GetFlagsHelpMode()) { + case HelpMode::kNone: + break; + case HelpMode::kImportant: + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_help_flags, + GetFlagsHelpFormat(), program_usage_message); + return 1; + + case HelpMode::kShort: + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_helpshort_flags, + GetFlagsHelpFormat(), program_usage_message); + return 1; + + case HelpMode::kFull: + flags_internal::FlagsHelp(out, "", GetFlagsHelpFormat(), + program_usage_message); + return 1; + + case HelpMode::kPackage: + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_helppackage_flags, + GetFlagsHelpFormat(), program_usage_message); + + return 1; + + case HelpMode::kMatch: { + std::string substr = GetFlagsHelpMatchSubstr(); + if (substr.empty()) { + // show all options + flags_internal::FlagsHelp(out, substr, GetFlagsHelpFormat(), + program_usage_message); + } else { + auto filter_cb = [&substr](const absl::CommandLineFlag& flag) { + if (absl::StrContains(flag.Name(), substr)) return true; + if (absl::StrContains(flag.Filename(), substr)) return true; + if (absl::StrContains(flag.Help(), substr)) return true; + + return false; + }; + flags_internal::FlagsHelpImpl( + out, filter_cb, HelpFormat::kHumanReadable, program_usage_message); + } - if (absl::GetFlag(FLAGS_helpfull)) { - // show all options - flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable, - program_usage_message); - return 1; + return 1; + } + case HelpMode::kVersion: + if (flags_internal::GetUsageConfig().version_string) + out << flags_internal::GetUsageConfig().version_string(); + // Unlike help, we may be asking for version in a script, so return 0 + return 0; + + case HelpMode::kOnlyCheckArgs: + return 0; } - if (!absl::GetFlag(FLAGS_helpon).empty()) { - flags_internal::FlagsHelp( - out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."), - HelpFormat::kHumanReadable, program_usage_message); - return 1; - } + return -1; +} - if (!absl::GetFlag(FLAGS_helpmatch).empty()) { - flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch), - HelpFormat::kHumanReadable, - program_usage_message); - return 1; - } +// -------------------------------------------------------------------- +// Globals representing usage reporting flags + +namespace { - if (absl::GetFlag(FLAGS_help)) { - flags_internal::FlagsHelpImpl( - out, flags_internal::GetUsageConfig().contains_help_flags, - HelpFormat::kHumanReadable, program_usage_message); +ABSL_CONST_INIT absl::Mutex help_attributes_guard(absl::kConstInit); +ABSL_CONST_INIT std::string* match_substr + ABSL_GUARDED_BY(help_attributes_guard) = nullptr; +ABSL_CONST_INIT HelpMode help_mode ABSL_GUARDED_BY(help_attributes_guard) = + HelpMode::kNone; +ABSL_CONST_INIT HelpFormat help_format ABSL_GUARDED_BY(help_attributes_guard) = + HelpFormat::kHumanReadable; - out << "\nTry --helpfull to get a list of all flags.\n"; +} // namespace - return 1; - } +std::string GetFlagsHelpMatchSubstr() { + absl::MutexLock l(&help_attributes_guard); + if (match_substr == nullptr) return ""; + return *match_substr; +} + +void SetFlagsHelpMatchSubstr(absl::string_view substr) { + absl::MutexLock l(&help_attributes_guard); + if (match_substr == nullptr) match_substr = new std::string; + match_substr->assign(substr.data(), substr.size()); +} + +HelpMode GetFlagsHelpMode() { + absl::MutexLock l(&help_attributes_guard); + return help_mode; +} - if (absl::GetFlag(FLAGS_helppackage)) { - flags_internal::FlagsHelpImpl( - out, flags_internal::GetUsageConfig().contains_helppackage_flags, - HelpFormat::kHumanReadable, program_usage_message); +void SetFlagsHelpMode(HelpMode mode) { + absl::MutexLock l(&help_attributes_guard); + help_mode = mode; +} - out << "\nTry --helpfull to get a list of all flags.\n"; +HelpFormat GetFlagsHelpFormat() { + absl::MutexLock l(&help_attributes_guard); + return help_format; +} - return 1; +void SetFlagsHelpFormat(HelpFormat format) { + absl::MutexLock l(&help_attributes_guard); + help_format = format; +} + +// Deduces usage flags from the input argument in a form --name=value or +// --name. argument is already split into name and value before we call this +// function. +bool DeduceUsageFlags(absl::string_view name, absl::string_view value) { + if (absl::ConsumePrefix(&name, "help")) { + if (name == "") { + if (value.empty()) { + SetFlagsHelpMode(HelpMode::kImportant); + } else { + SetFlagsHelpMode(HelpMode::kMatch); + SetFlagsHelpMatchSubstr(value); + } + return true; + } + + if (name == "match") { + SetFlagsHelpMode(HelpMode::kMatch); + SetFlagsHelpMatchSubstr(value); + return true; + } + + if (name == "on") { + SetFlagsHelpMode(HelpMode::kMatch); + SetFlagsHelpMatchSubstr(absl::StrCat("/", value, ".")); + return true; + } + + if (name == "full") { + SetFlagsHelpMode(HelpMode::kFull); + return true; + } + + if (name == "short") { + SetFlagsHelpMode(HelpMode::kShort); + return true; + } + + if (name == "package") { + SetFlagsHelpMode(HelpMode::kPackage); + return true; + } + + return false; } - if (absl::GetFlag(FLAGS_version)) { - if (flags_internal::GetUsageConfig().version_string) - out << flags_internal::GetUsageConfig().version_string(); - // Unlike help, we may be asking for version in a script, so return 0 - return 0; + if (name == "version") { + SetFlagsHelpMode(HelpMode::kVersion); + return true; } - if (absl::GetFlag(FLAGS_only_check_args)) { - return 0; + if (name == "only_check_args") { + SetFlagsHelpMode(HelpMode::kOnlyCheckArgs); + return true; } - return -1; + return false; } } // namespace flags_internal diff --git a/third_party/abseil-cpp/absl/flags/internal/usage.h b/third_party/abseil-cpp/absl/flags/internal/usage.h index 6b080fd1ee..c0bcac5762 100644 --- a/third_party/abseil-cpp/absl/flags/internal/usage.h +++ b/third_party/abseil-cpp/absl/flags/internal/usage.h @@ -20,8 +20,8 @@ #include <string> #include "absl/base/config.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/declare.h" -#include "absl/flags/internal/commandlineflag.h" #include "absl/strings/string_view.h" // -------------------------------------------------------------------- @@ -36,8 +36,9 @@ enum class HelpFormat { kHumanReadable, }; -// Outputs the help message describing specific flag. -void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, +// Streams the help message describing `flag` to `out`. +// The default value for `flag` is included in the output. +void FlagHelp(std::ostream& out, const CommandLineFlag& flag, HelpFormat format = HelpFormat::kHumanReadable); // Produces the help messages for all flags matching the filter. A flag matches @@ -65,17 +66,39 @@ void FlagsHelp(std::ostream& out, absl::string_view filter, int HandleUsageFlags(std::ostream& out, absl::string_view program_usage_message); +// -------------------------------------------------------------------- +// Globals representing usage reporting flags + +enum class HelpMode { + kNone, + kImportant, + kShort, + kFull, + kPackage, + kMatch, + kVersion, + kOnlyCheckArgs +}; + +// Returns substring to filter help output (--help=substr argument) +std::string GetFlagsHelpMatchSubstr(); +// Returns the requested help mode. +HelpMode GetFlagsHelpMode(); +// Returns the requested help format. +HelpFormat GetFlagsHelpFormat(); + +// These are corresponding setters to the attributes above. +void SetFlagsHelpMatchSubstr(absl::string_view); +void SetFlagsHelpMode(HelpMode); +void SetFlagsHelpFormat(HelpFormat); + +// Deduces usage flags from the input argument in a form --name=value or +// --name. argument is already split into name and value before we call this +// function. +bool DeduceUsageFlags(absl::string_view name, absl::string_view value); + } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl -ABSL_DECLARE_FLAG(bool, help); -ABSL_DECLARE_FLAG(bool, helpfull); -ABSL_DECLARE_FLAG(bool, helpshort); -ABSL_DECLARE_FLAG(bool, helppackage); -ABSL_DECLARE_FLAG(bool, version); -ABSL_DECLARE_FLAG(bool, only_check_args); -ABSL_DECLARE_FLAG(std::string, helpon); -ABSL_DECLARE_FLAG(std::string, helpmatch); - #endif // ABSL_FLAGS_INTERNAL_USAGE_H_ diff --git a/third_party/abseil-cpp/absl/flags/internal/usage_test.cc b/third_party/abseil-cpp/absl/flags/internal/usage_test.cc index e1e57e5570..044d71c87d 100644 --- a/third_party/abseil-cpp/absl/flags/internal/usage_test.cc +++ b/third_party/abseil-cpp/absl/flags/internal/usage_test.cc @@ -21,15 +21,13 @@ #include <string> #include "gtest/gtest.h" -#include "absl/flags/declare.h" #include "absl/flags/flag.h" #include "absl/flags/internal/parse.h" #include "absl/flags/internal/path_util.h" #include "absl/flags/internal/program_name.h" -#include "absl/flags/internal/registry.h" +#include "absl/flags/reflection.h" #include "absl/flags/usage.h" #include "absl/flags/usage_config.h" -#include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" @@ -47,6 +45,7 @@ static const char kTestUsageMessage[] = "Custom usage message"; struct UDT { UDT() = default; UDT(const UDT&) = default; + UDT& operator=(const UDT&) = default; }; bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; } @@ -89,9 +88,14 @@ class UsageReportingTest : public testing::Test { default_config.normalize_filename = &NormalizeFileName; absl::SetFlagsUsageConfig(default_config); } + ~UsageReportingTest() override { + flags::SetFlagsHelpMode(flags::HelpMode::kNone); + flags::SetFlagsHelpMatchSubstr(""); + flags::SetFlagsHelpFormat(flags::HelpFormat::kHumanReadable); + } private: - flags::FlagSaver flag_saver_; + absl::FlagSaver flag_saver_; }; // -------------------------------------------------------------------- @@ -103,15 +107,16 @@ TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) { #ifndef _WIN32 // TODO(rogeeff): figure out why this does not work on Windows. - EXPECT_DEATH(absl::SetProgramUsageMessage("custom usage message"), - ".*SetProgramUsageMessage\\(\\) called twice.*"); + EXPECT_DEATH_IF_SUPPORTED( + absl::SetProgramUsageMessage("custom usage message"), + ".*SetProgramUsageMessage\\(\\) called twice.*"); #endif } // -------------------------------------------------------------------- TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { - const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_01"); + const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_01"); std::stringstream test_buf; flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); @@ -123,7 +128,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { } TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { - const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_02"); + const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_02"); std::stringstream test_buf; flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); @@ -135,7 +140,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { } TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { - const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_03"); + const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_03"); std::stringstream test_buf; flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); @@ -147,7 +152,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { } TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { - const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_04"); + const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_04"); std::stringstream test_buf; flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); @@ -159,7 +164,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { } TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) { - const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_05"); + const auto* flag = absl::FindCommandLineFlag("usage_reporting_test_flag_05"); std::stringstream test_buf; flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); @@ -192,6 +197,10 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) { Some more help. Even more long long long long long long long long long long long long help message.); default: ""; + +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. )"; std::stringstream test_buf_01; @@ -215,7 +224,11 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) { EXPECT_EQ(test_buf_04.str(), R"(usage_test: Custom usage message - No modules matched: use -helpfull +No flags matched. + +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. )"); std::stringstream test_buf_05; @@ -227,12 +240,8 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) { absl::StartsWith(test_out_str, "usage_test: Custom usage message")); EXPECT_TRUE(absl::StrContains( test_out_str, "Flags from absl/flags/internal/usage_test.cc:")); - EXPECT_TRUE(absl::StrContains(test_out_str, - "Flags from absl/flags/internal/usage.cc:")); EXPECT_TRUE( absl::StrContains(test_out_str, "-usage_reporting_test_flag_01 ")); - EXPECT_TRUE(absl::StrContains(test_out_str, "-help (show help")) - << test_out_str; } // -------------------------------------------------------------------- @@ -245,7 +254,40 @@ TEST_F(UsageReportingTest, TestNoUsageFlags) { // -------------------------------------------------------------------- TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { - absl::SetFlag(&FLAGS_helpshort, true); + flags::SetFlagsHelpMode(flags::HelpMode::kShort); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; + +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_help_simple) { + flags::SetFlagsHelpMode(flags::HelpMode::kImportant); std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); @@ -268,13 +310,42 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { Some more help. Even more long long long long long long long long long long long long help message.); default: ""; + +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_help_one_flag) { + flags::SetFlagsHelpMode(flags::HelpMode::kMatch); + flags::SetFlagsHelpMatchSubstr("usage_reporting_test_flag_06"); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; + +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. )"); } // -------------------------------------------------------------------- -TEST_F(UsageReportingTest, TestUsageFlag_help) { - absl::SetFlag(&FLAGS_help, true); +TEST_F(UsageReportingTest, TestUsageFlag_help_multiple_flag) { + flags::SetFlagsHelpMode(flags::HelpMode::kMatch); + flags::SetFlagsHelpMatchSubstr("test_flag"); std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); @@ -298,14 +369,16 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) { Even more long long long long long long long long long long long long help message.); default: ""; -Try --helpfull to get a list of all flags. +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. )"); } // -------------------------------------------------------------------- TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { - absl::SetFlag(&FLAGS_helppackage, true); + flags::SetFlagsHelpMode(flags::HelpMode::kPackage); std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); @@ -329,14 +402,16 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { Even more long long long long long long long long long long long long help message.); default: ""; -Try --helpfull to get a list of all flags. +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. )"); } // -------------------------------------------------------------------- TEST_F(UsageReportingTest, TestUsageFlag_version) { - absl::SetFlag(&FLAGS_version, true); + flags::SetFlagsHelpMode(flags::HelpMode::kVersion); std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); @@ -350,7 +425,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_version) { // -------------------------------------------------------------------- TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) { - absl::SetFlag(&FLAGS_only_check_args, true); + flags::SetFlagsHelpMode(flags::HelpMode::kOnlyCheckArgs); std::stringstream test_buf; EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); @@ -360,17 +435,22 @@ TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) { // -------------------------------------------------------------------- TEST_F(UsageReportingTest, TestUsageFlag_helpon) { - absl::SetFlag(&FLAGS_helpon, "bla-bla"); + flags::SetFlagsHelpMode(flags::HelpMode::kMatch); + flags::SetFlagsHelpMatchSubstr("/bla-bla."); std::stringstream test_buf_01; EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1); EXPECT_EQ(test_buf_01.str(), R"(usage_test: Custom usage message - No modules matched: use -helpfull +No flags matched. + +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. )"); - absl::SetFlag(&FLAGS_helpon, "usage_test"); + flags::SetFlagsHelpMatchSubstr("/usage_test."); std::stringstream test_buf_02; EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1); @@ -393,6 +473,10 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) { Some more help. Even more long long long long long long long long long long long long help message.); default: ""; + +Try --helpfull to get a list of all flags or --help=substring shows help for +flags which include specified substring in either in the name, or description or +path. )"); } diff --git a/third_party/abseil-cpp/absl/flags/marshalling.cc b/third_party/abseil-cpp/absl/flags/marshalling.cc index 6f2ddda8c3..81f9cebd6f 100644 --- a/third_party/abseil-cpp/absl/flags/marshalling.cc +++ b/third_party/abseil-cpp/absl/flags/marshalling.cc @@ -74,15 +74,16 @@ static int NumericBase(absl::string_view text) { } template <typename IntType> -inline bool ParseFlagImpl(absl::string_view text, IntType* dst) { +inline bool ParseFlagImpl(absl::string_view text, IntType& dst) { text = absl::StripAsciiWhitespace(text); - return absl::numbers_internal::safe_strtoi_base(text, dst, NumericBase(text)); + return absl::numbers_internal::safe_strtoi_base(text, &dst, + NumericBase(text)); } bool AbslParseFlag(absl::string_view text, short* dst, std::string*) { int val; - if (!ParseFlagImpl(text, &val)) return false; + if (!ParseFlagImpl(text, val)) return false; if (static_cast<short>(val) != val) // worked, but number out of range return false; *dst = static_cast<short>(val); @@ -91,7 +92,7 @@ bool AbslParseFlag(absl::string_view text, short* dst, std::string*) { bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) { unsigned int val; - if (!ParseFlagImpl(text, &val)) return false; + if (!ParseFlagImpl(text, val)) return false; if (static_cast<unsigned short>(val) != val) // worked, but number out of range return false; @@ -100,28 +101,28 @@ bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) { } bool AbslParseFlag(absl::string_view text, int* dst, std::string*) { - return ParseFlagImpl(text, dst); + return ParseFlagImpl(text, *dst); } bool AbslParseFlag(absl::string_view text, unsigned int* dst, std::string*) { - return ParseFlagImpl(text, dst); + return ParseFlagImpl(text, *dst); } bool AbslParseFlag(absl::string_view text, long* dst, std::string*) { - return ParseFlagImpl(text, dst); + return ParseFlagImpl(text, *dst); } bool AbslParseFlag(absl::string_view text, unsigned long* dst, std::string*) { - return ParseFlagImpl(text, dst); + return ParseFlagImpl(text, *dst); } bool AbslParseFlag(absl::string_view text, long long* dst, std::string*) { - return ParseFlagImpl(text, dst); + return ParseFlagImpl(text, *dst); } bool AbslParseFlag(absl::string_view text, unsigned long long* dst, std::string*) { - return ParseFlagImpl(text, dst); + return ParseFlagImpl(text, *dst); } // -------------------------------------------------------------------- @@ -172,7 +173,7 @@ std::string Unparse(long long v) { return absl::StrCat(v); } std::string Unparse(unsigned long long v) { return absl::StrCat(v); } template <typename T> std::string UnparseFloatingPointVal(T v) { - // digits10 is guaranteed to roundtrip correctly in std::string -> value -> std::string + // digits10 is guaranteed to roundtrip correctly in string -> value -> string // conversions, but may not be enough to represent all the values correctly. std::string digit10_str = absl::StrFormat("%.*g", std::numeric_limits<T>::digits10, v); diff --git a/third_party/abseil-cpp/absl/flags/marshalling.h b/third_party/abseil-cpp/absl/flags/marshalling.h index 0b5033547e..7cbc136d57 100644 --- a/third_party/abseil-cpp/absl/flags/marshalling.h +++ b/third_party/abseil-cpp/absl/flags/marshalling.h @@ -83,7 +83,7 @@ // // AbslParseFlag converts from a string to OutputMode. // // Must be in same namespace as OutputMode. // -// // Parses an OutputMode from the command line flag value `text. Returns +// // Parses an OutputMode from the command line flag value `text`. Returns // // `true` and sets `*mode` on success; returns `false` and sets `*error` // // on failure. // bool AbslParseFlag(absl::string_view text, @@ -139,7 +139,7 @@ // // // Within the implementation, `AbslParseFlag()` will, in turn invoke // // `absl::ParseFlag()` on its constituent `int` and `std::string` types -// // (which have built-in Abseil flag support. +// // (which have built-in Abseil flag support). // // bool AbslParseFlag(absl::string_view text, MyFlagType* flag, // std::string* err) { diff --git a/third_party/abseil-cpp/absl/flags/parse.cc b/third_party/abseil-cpp/absl/flags/parse.cc index 812e498189..dd1a6796ca 100644 --- a/third_party/abseil-cpp/absl/flags/parse.cc +++ b/third_party/abseil-cpp/absl/flags/parse.cc @@ -34,14 +34,16 @@ #include "absl/base/config.h" #include "absl/base/const_init.h" #include "absl/base/thread_annotations.h" +#include "absl/flags/commandlineflag.h" #include "absl/flags/config.h" #include "absl/flags/flag.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/parse.h" +#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/program_name.h" -#include "absl/flags/internal/registry.h" #include "absl/flags/internal/usage.h" +#include "absl/flags/reflection.h" #include "absl/flags/usage.h" #include "absl/flags/usage_config.h" #include "absl/strings/ascii.h" @@ -66,6 +68,22 @@ ABSL_CONST_INIT bool fromenv_needs_processing ABSL_CONST_INIT bool tryfromenv_needs_processing ABSL_GUARDED_BY(processing_checks_guard) = false; +ABSL_CONST_INIT absl::Mutex specified_flags_guard(absl::kConstInit); +ABSL_CONST_INIT std::vector<const CommandLineFlag*>* specified_flags + ABSL_GUARDED_BY(specified_flags_guard) = nullptr; + +struct SpecifiedFlagsCompare { + bool operator()(const CommandLineFlag* a, const CommandLineFlag* b) const { + return a->Name() < b->Name(); + } + bool operator()(const CommandLineFlag* a, absl::string_view b) const { + return a->Name() < b; + } + bool operator()(absl::string_view a, const CommandLineFlag* b) const { + return a < b->Name(); + } +}; + } // namespace } // namespace flags_internal ABSL_NAMESPACE_END @@ -205,7 +223,7 @@ bool ArgsList::ReadFromFlagfile(const std::string& flag_file_name) { // Reads the environment variable with name `name` and stores results in // `value`. If variable is not present in environment returns false, otherwise // returns true. -bool GetEnvVar(const char* var_name, std::string* var_value) { +bool GetEnvVar(const char* var_name, std::string& var_value) { #ifdef _WIN32 char buf[1024]; auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf)); @@ -217,14 +235,14 @@ bool GetEnvVar(const char* var_name, std::string* var_value) { return false; } - *var_value = std::string(buf, get_res); + var_value = std::string(buf, get_res); #else const char* val = ::getenv(var_name); if (val == nullptr) { return false; } - *var_value = val; + var_value = val; #endif return true; @@ -272,11 +290,11 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue( // found flag or nullptr // is negative in case of --nofoo std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) { - CommandLineFlag* flag = flags_internal::FindCommandLineFlag(flag_name); + CommandLineFlag* flag = absl::FindCommandLineFlag(flag_name); bool is_negative = false; if (!flag && absl::ConsumePrefix(&flag_name, "no")) { - flag = flags_internal::FindCommandLineFlag(flag_name); + flag = absl::FindCommandLineFlag(flag_name); is_negative = true; } @@ -289,16 +307,17 @@ std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) { // back. void CheckDefaultValuesParsingRoundtrip() { #ifndef NDEBUG - flags_internal::ForEachFlag([&](CommandLineFlag* flag) { - if (flag->IsRetired()) return; + flags_internal::ForEachFlag([&](CommandLineFlag& flag) { + if (flag.IsRetired()) return; -#define IGNORE_TYPE(T) \ - if (flag->IsOfType<T>()) return; +#define ABSL_FLAGS_INTERNAL_IGNORE_TYPE(T, _) \ + if (flag.IsOfType<T>()) return; - ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(IGNORE_TYPE) -#undef IGNORE_TYPE + ABSL_FLAGS_INTERNAL_SUPPORTED_TYPES(ABSL_FLAGS_INTERNAL_IGNORE_TYPE) +#undef ABSL_FLAGS_INTERNAL_IGNORE_TYPE - flag->CheckDefaultValueParsingRoundtrip(); + flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip( + flag); }); #endif } @@ -311,13 +330,13 @@ void CheckDefaultValuesParsingRoundtrip() { // the first flagfile in the input list are processed before the second flagfile // etc. bool ReadFlagfiles(const std::vector<std::string>& flagfiles, - std::vector<ArgsList>* input_args) { + std::vector<ArgsList>& input_args) { bool success = true; for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) { ArgsList al; if (al.ReadFromFlagfile(*it)) { - input_args->push_back(al); + input_args.push_back(al); } else { success = false; } @@ -332,7 +351,7 @@ bool ReadFlagfiles(const std::vector<std::string>& flagfiles, // `flag_name` is a string from the input flag_names list. If successful we // append a single ArgList at the end of the input_args. bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names, - std::vector<ArgsList>* input_args, + std::vector<ArgsList>& input_args, bool fail_on_absent_in_env) { bool success = true; std::vector<std::string> args; @@ -353,7 +372,7 @@ bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names, const std::string envname = absl::StrCat("FLAGS_", flag_name); std::string envval; - if (!GetEnvVar(envname.c_str(), &envval)) { + if (!GetEnvVar(envname.c_str(), envval)) { if (fail_on_absent_in_env) { flags_internal::ReportUsageError( absl::StrCat(envname, " not found in environment"), true); @@ -368,7 +387,7 @@ bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names, } if (success) { - input_args->emplace_back(args); + input_args.emplace_back(args); } return success; @@ -378,8 +397,8 @@ bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names, // Returns success status, which is true if were able to handle all generator // flags (flagfile, fromenv, tryfromemv) successfully. -bool HandleGeneratorFlags(std::vector<ArgsList>* input_args, - std::vector<std::string>* flagfile_value) { +bool HandleGeneratorFlags(std::vector<ArgsList>& input_args, + std::vector<std::string>& flagfile_value) { bool success = true; absl::MutexLock l(&flags_internal::processing_checks_guard); @@ -404,9 +423,9 @@ bool HandleGeneratorFlags(std::vector<ArgsList>* input_args, if (flags_internal::flagfile_needs_processing) { auto flagfiles = absl::GetFlag(FLAGS_flagfile); - if (input_args->size() == 1) { - flagfile_value->insert(flagfile_value->end(), flagfiles.begin(), - flagfiles.end()); + if (input_args.size() == 1) { + flagfile_value.insert(flagfile_value.end(), flagfiles.begin(), + flagfiles.end()); } success &= ReadFlagfiles(flagfiles, input_args); @@ -533,10 +552,10 @@ std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag, curr_list->PopFront(); value = curr_list->Front(); - // Heuristic to detect the case where someone treats a std::string arg + // Heuristic to detect the case where someone treats a string arg // like a bool or just forgets to pass a value: // --my_string_var --foo=bar - // We look for a flag of std::string type, whose value begins with a + // We look for a flag of string type, whose value begins with a // dash and corresponds to known flag or standalone --. if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) { auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1))); @@ -575,12 +594,28 @@ bool CanIgnoreUndefinedFlag(absl::string_view flag_name) { // -------------------------------------------------------------------- +bool WasPresentOnCommandLine(absl::string_view flag_name) { + absl::MutexLock l(&specified_flags_guard); + ABSL_INTERNAL_CHECK(specified_flags != nullptr, + "ParseCommandLine is not invoked yet"); + + return std::binary_search(specified_flags->begin(), specified_flags->end(), + flag_name, SpecifiedFlagsCompare{}); +} + +// -------------------------------------------------------------------- + std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], ArgvListAction arg_list_act, UsageFlagsAction usage_flag_act, OnUndefinedFlag on_undef_flag) { ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]"); + // Once parsing has started we will not have more flag registrations. + // If we did, they would be missing during parsing, which is a problem on + // itself. + flags_internal::FinalizeRegistry(); + // This routine does not return anything since we abort on failure. CheckDefaultValuesParsingRoundtrip(); @@ -605,13 +640,20 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], } output_args.push_back(argv[0]); + absl::MutexLock l(&specified_flags_guard); + if (specified_flags == nullptr) { + specified_flags = new std::vector<const CommandLineFlag*>; + } else { + specified_flags->clear(); + } + // Iterate through the list of the input arguments. First level are arguments // originated from argc/argv. Following levels are arguments originated from // recursive parsing of flagfile(s). bool success = true; while (!input_args.empty()) { // 10. First we process the built-in generator flags. - success &= HandleGeneratorFlags(&input_args, &flagfile_value); + success &= HandleGeneratorFlags(input_args, flagfile_value); // 30. Select top-most (most recent) arguments list. If it is empty drop it // and re-try. @@ -646,7 +688,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], // 60. Split the current argument on '=' to figure out the argument // name and value. If flag name is empty it means we've got "--". value - // can be empty either if there were no '=' in argument std::string at all or + // can be empty either if there were no '=' in argument string at all or // an argument looked like "--foo=". In a latter case is_empty_value is // true. absl::string_view flag_name; @@ -671,6 +713,11 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], std::tie(flag, is_negative) = LocateFlag(flag_name); if (flag == nullptr) { + // Usage flags are not modeled as Abseil flags. Locate them separately. + if (flags_internal::DeduceUsageFlags(flag_name, value)) { + continue; + } + if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) { undefined_flag_names.emplace_back(arg_from_argv, std::string(flag_name)); @@ -692,13 +739,17 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], } // 100. Set the located flag to a new new value, unless it is retired. - // Setting retired flag fails, but we ignoring it here. - if (flag->IsRetired()) continue; - + // Setting retired flag fails, but we ignoring it here while also reporting + // access to retired flag. std::string error; - if (!flag->SetFromString(value, SET_FLAGS_VALUE, kCommandLine, &error)) { + if (!flags_internal::PrivateHandleAccessor::ParseFrom( + *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) { + if (flag->IsRetired()) continue; + flags_internal::ReportUsageError(error, true); success = false; + } else { + specified_flags->push_back(flag); } } @@ -750,6 +801,10 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], } } + // Trim and sort the vector. + specified_flags->shrink_to_fit(); + std::sort(specified_flags->begin(), specified_flags->end(), + SpecifiedFlagsCompare{}); return output_args; } diff --git a/third_party/abseil-cpp/absl/flags/parse.h b/third_party/abseil-cpp/absl/flags/parse.h index f37b0602e6..929de2cb40 100644 --- a/third_party/abseil-cpp/absl/flags/parse.h +++ b/third_party/abseil-cpp/absl/flags/parse.h @@ -23,7 +23,6 @@ #ifndef ABSL_FLAGS_PARSE_H_ #define ABSL_FLAGS_PARSE_H_ -#include <string> #include <vector> #include "absl/base/config.h" diff --git a/third_party/abseil-cpp/absl/flags/parse_test.cc b/third_party/abseil-cpp/absl/flags/parse_test.cc index 6f49377a93..8dc91db2b3 100644 --- a/third_party/abseil-cpp/absl/flags/parse_test.cc +++ b/third_party/abseil-cpp/absl/flags/parse_test.cc @@ -28,7 +28,8 @@ #include "absl/flags/declare.h" #include "absl/flags/flag.h" #include "absl/flags/internal/parse.h" -#include "absl/flags/internal/registry.h" +#include "absl/flags/internal/usage.h" +#include "absl/flags/reflection.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" @@ -45,6 +46,7 @@ using absl::base_internal::ScopedSetEnv; struct UDT { UDT() = default; UDT(const UDT&) = default; + UDT& operator=(const UDT&) = default; UDT(int v) : value(v) {} // NOLINT int value; @@ -171,8 +173,8 @@ constexpr const char* const ff2_data[] = { // temporary directory location. This way we can test inclusion of one flagfile // from another flagfile. const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd, - std::string* flagfile_flag) { - *flagfile_flag = "--flagfile="; + std::string& flagfile_flag) { + flagfile_flag = "--flagfile="; absl::string_view separator; for (const auto& flagfile_data : ffd) { std::string flagfile_name = @@ -183,11 +185,11 @@ const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd, flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n"; } - absl::StrAppend(flagfile_flag, separator, flagfile_name); + absl::StrAppend(&flagfile_flag, separator, flagfile_name); separator = ","; } - return flagfile_flag->c_str(); + return flagfile_flag.c_str(); } } // namespace @@ -207,8 +209,11 @@ namespace flags = absl::flags_internal; using testing::ElementsAreArray; class ParseTest : public testing::Test { + public: + ~ParseTest() override { flags::SetFlagsHelpMode(flags::HelpMode::kNone); } + private: - flags::FlagSaver flag_saver_; + absl::FlagSaver flag_saver_; }; // -------------------------------------------------------------------- @@ -481,21 +486,22 @@ TEST_F(ParseDeathTest, TestUndefinedArg) { "testbin", "--undefined_flag", }; - EXPECT_DEATH(InvokeParse(in_args1), - "Unknown command line flag 'undefined_flag'"); + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), + "Unknown command line flag 'undefined_flag'"); const char* in_args2[] = { "testbin", "--noprefixed_flag", }; - EXPECT_DEATH(InvokeParse(in_args2), - "Unknown command line flag 'noprefixed_flag'"); + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), + "Unknown command line flag 'noprefixed_flag'"); const char* in_args3[] = { "testbin", "--Int_flag=1", }; - EXPECT_DEATH(InvokeParse(in_args3), "Unknown command line flag 'Int_flag'"); + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3), + "Unknown command line flag 'Int_flag'"); } // -------------------------------------------------------------------- @@ -505,7 +511,7 @@ TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) { "testbin", "--bool_flag=", }; - EXPECT_DEATH( + EXPECT_DEATH_IF_SUPPORTED( InvokeParse(in_args1), "Missing the value after assignment for the boolean flag 'bool_flag'"); @@ -513,7 +519,7 @@ TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) { "testbin", "--nobool_flag=true", }; - EXPECT_DEATH(InvokeParse(in_args2), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), "Negative form with assignment is not valid for the boolean " "flag 'bool_flag'"); } @@ -525,14 +531,14 @@ TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) { "testbin", "--nostring_flag", }; - EXPECT_DEATH(InvokeParse(in_args1), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), "Negative form is not valid for the flag 'string_flag'"); const char* in_args2[] = { "testbin", "--int_flag", }; - EXPECT_DEATH(InvokeParse(in_args2), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), "Missing the value for the flag 'int_flag'"); } @@ -543,7 +549,7 @@ TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) { "testbin", "--udt_flag=1", }; - EXPECT_DEATH(InvokeParse(in_args1), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), "Illegal value '1' specified for flag 'udt_flag'; Use values A, " "AAA instead"); @@ -552,7 +558,7 @@ TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) { "--udt_flag", "AA", }; - EXPECT_DEATH(InvokeParse(in_args2), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), "Illegal value 'AA' specified for flag 'udt_flag'; Use values " "A, AAA instead"); } @@ -587,14 +593,14 @@ TEST_F(ParseTest, TestSimpleValidFlagfile) { const char* in_args1[] = { "testbin", GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, - &flagfile_flag), + flagfile_flag), }; TestParse(in_args1, -1, 0.1, "q2w2 ", true); const char* in_args2[] = { "testbin", GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}}, - &flagfile_flag), + flagfile_flag), }; TestParse(in_args2, 100, 0.1, "q2w2 ", false); } @@ -608,7 +614,7 @@ TEST_F(ParseTest, TestValidMultiFlagfile) { "testbin", GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, - &flagfile_flag), + flagfile_flag), }; TestParse(in_args1, -1, 0.1, "q2w2 ", true); } @@ -621,7 +627,7 @@ TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) { const char* in_args1[] = { "testbin", "--int_flag=3", GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, - &flagfile_flag), + flagfile_flag), "-double_flag=0.2"}; TestParse(in_args1, -1, 0.2, "q2w2 ", true); } @@ -636,10 +642,14 @@ TEST_F(ParseTest, TestFlagfileInFlagfile) { "--flagfile=$0/parse_test.ff2", }; + GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, + {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, + flagfile_flag); + const char* in_args1[] = { "testbin", GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}}, - &flagfile_flag), + flagfile_flag), }; TestParse(in_args1, 100, 0.1, "q2w2 ", false); } @@ -656,9 +666,9 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) { const char* in_args1[] = { "testbin", GetFlagfileFlag({{"parse_test.ff4", - absl::MakeConstSpan(ff4_data)}}, &flagfile_flag), + absl::MakeConstSpan(ff4_data)}}, flagfile_flag), }; - EXPECT_DEATH(InvokeParse(in_args1), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), "Unknown command line flag 'unknown_flag'"); constexpr const char* const ff5_data[] = { @@ -668,9 +678,9 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) { const char* in_args2[] = { "testbin", GetFlagfileFlag({{"parse_test.ff5", - absl::MakeConstSpan(ff5_data)}}, &flagfile_flag), + absl::MakeConstSpan(ff5_data)}}, flagfile_flag), }; - EXPECT_DEATH(InvokeParse(in_args2), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args2), "Unknown command line flag 'int_flag 10'"); constexpr const char* const ff6_data[] = { @@ -680,16 +690,17 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) { const char* in_args3[] = { "testbin", GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}}, - &flagfile_flag), + flagfile_flag), }; - EXPECT_DEATH(InvokeParse(in_args3), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args3), "Flagfile can't contain position arguments or --"); const char* in_args4[] = { "testbin", "--flagfile=invalid_flag_file", }; - EXPECT_DEATH(InvokeParse(in_args4), "Can't open flagfile invalid_flag_file"); + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args4), + "Can't open flagfile invalid_flag_file"); constexpr const char* const ff7_data[] = { "--int_flag=10", @@ -700,9 +711,9 @@ TEST_F(ParseDeathTest, TestInvalidFlagfiles) { const char* in_args5[] = { "testbin", GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}}, - &flagfile_flag), + flagfile_flag), }; - EXPECT_DEATH(InvokeParse(in_args5), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args5), "Unexpected line in the flagfile .*: \\*bin\\*"); } @@ -724,7 +735,7 @@ TEST_F(ParseTest, TestReadingRequiredFlagsFromEnv) { TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) { const char* in_args1[] = {"testbin", "--fromenv=int_flag"}; - EXPECT_DEATH(InvokeParse(in_args1), + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), "FLAGS_int_flag not found in environment"); } @@ -735,7 +746,8 @@ TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) { ScopedSetEnv set_tryfromenv("FLAGS_tryfromenv", "int_flag"); - EXPECT_DEATH(InvokeParse(in_args1), "Infinite recursion on flag tryfromenv"); + EXPECT_DEATH_IF_SUPPORTED(InvokeParse(in_args1), + "Infinite recursion on flag tryfromenv"); } // -------------------------------------------------------------------- @@ -844,7 +856,7 @@ TEST_F(ParseTest, TestIgnoreUndefinedFlags) { // -------------------------------------------------------------------- -TEST_F(ParseDeathTest, TestHelpFlagHandling) { +TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { const char* in_args1[] = { "testbin", "--help", @@ -863,7 +875,56 @@ TEST_F(ParseDeathTest, TestHelpFlagHandling) { flags::UsageFlagsAction::kIgnoreUsage, flags::OnUndefinedFlag::kAbortIfUndefined); + EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3); } +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) { + const char* in_args1[] = { + "testbin", + "--help=abcd", + }; + + auto out_args1 = flags::ParseCommandLineImpl( + 2, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs, + flags::UsageFlagsAction::kIgnoreUsage, + flags::OnUndefinedFlag::kAbortIfUndefined); + + EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch); + EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd"); + + const char* in_args2[] = {"testbin", "--help", "some_positional_arg"}; + + auto out_args2 = flags::ParseCommandLineImpl( + 3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs, + flags::UsageFlagsAction::kIgnoreUsage, + flags::OnUndefinedFlag::kAbortIfUndefined); + + EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, WasPresentOnCommandLine) { + const char* in_args1[] = { + "testbin", "arg1", "--bool_flag", + "--int_flag=211", "arg2", "--double_flag=1.1", + "--string_flag", "asd", "--", + "--some_flag", "arg4", + }; + + InvokeParse(in_args1); + + EXPECT_TRUE(flags::WasPresentOnCommandLine("bool_flag")); + EXPECT_TRUE(flags::WasPresentOnCommandLine("int_flag")); + EXPECT_TRUE(flags::WasPresentOnCommandLine("double_flag")); + EXPECT_TRUE(flags::WasPresentOnCommandLine("string_flag")); + EXPECT_FALSE(flags::WasPresentOnCommandLine("some_flag")); + EXPECT_FALSE(flags::WasPresentOnCommandLine("another_flag")); +} + +// -------------------------------------------------------------------- + } // namespace diff --git a/third_party/abseil-cpp/absl/flags/reflection.cc b/third_party/abseil-cpp/absl/flags/reflection.cc new file mode 100644 index 0000000000..dbce4032ab --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/reflection.cc @@ -0,0 +1,354 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/flags/reflection.h" + +#include <assert.h> + +#include <atomic> +#include <string> + +#include "absl/base/config.h" +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/flags/commandlineflag.h" +#include "absl/flags/internal/private_handle_accessor.h" +#include "absl/flags/internal/registry.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// -------------------------------------------------------------------- +// FlagRegistry +// A FlagRegistry singleton object holds all flag objects indexed by their +// names so that if you know a flag's name, you can access or set it. If the +// function is named FooLocked(), you must own the registry lock before +// calling the function; otherwise, you should *not* hold the lock, and the +// function will acquire it itself if needed. +// -------------------------------------------------------------------- + +class FlagRegistry { + public: + FlagRegistry() = default; + ~FlagRegistry() = default; + + // Store a flag in this registry. Takes ownership of *flag. + void RegisterFlag(CommandLineFlag& flag, const char* filename); + + void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); } + void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); } + + // Returns the flag object for the specified name, or nullptr if not found. + // Will emit a warning if a 'retired' flag is specified. + CommandLineFlag* FindFlag(absl::string_view name); + + static FlagRegistry& GlobalRegistry(); // returns a singleton registry + + private: + friend class flags_internal::FlagSaverImpl; // reads all the flags in order + // to copy them + friend void ForEachFlag(std::function<void(CommandLineFlag&)> visitor); + friend void FinalizeRegistry(); + + // The map from name to flag, for FindFlag(). + using FlagMap = absl::flat_hash_map<absl::string_view, CommandLineFlag*>; + using FlagIterator = FlagMap::iterator; + using FlagConstIterator = FlagMap::const_iterator; + FlagMap flags_; + std::vector<CommandLineFlag*> flat_flags_; + std::atomic<bool> finalized_flags_{false}; + + absl::Mutex lock_; + + // Disallow + FlagRegistry(const FlagRegistry&); + FlagRegistry& operator=(const FlagRegistry&); +}; + +namespace { + +class FlagRegistryLock { + public: + explicit FlagRegistryLock(FlagRegistry& fr) : fr_(fr) { fr_.Lock(); } + ~FlagRegistryLock() { fr_.Unlock(); } + + private: + FlagRegistry& fr_; +}; + +} // namespace + +CommandLineFlag* FlagRegistry::FindFlag(absl::string_view name) { + if (finalized_flags_.load(std::memory_order_acquire)) { + // We could save some gcus here if we make `Name()` be non-virtual. + // We could move the `const char*` name to the base class. + auto it = std::partition_point( + flat_flags_.begin(), flat_flags_.end(), + [=](CommandLineFlag* f) { return f->Name() < name; }); + if (it != flat_flags_.end() && (*it)->Name() == name) return *it; + } + + FlagRegistryLock frl(*this); + auto it = flags_.find(name); + return it != flags_.end() ? it->second : nullptr; +} + +void FlagRegistry::RegisterFlag(CommandLineFlag& flag, const char* filename) { + if (filename != nullptr && + flag.Filename() != GetUsageConfig().normalize_filename(filename)) { + flags_internal::ReportUsageError( + absl::StrCat( + "Inconsistency between flag object and registration for flag '", + flag.Name(), + "', likely due to duplicate flags or an ODR violation. Relevant " + "files: ", + flag.Filename(), " and ", filename), + true); + std::exit(1); + } + + FlagRegistryLock registry_lock(*this); + + std::pair<FlagIterator, bool> ins = + flags_.insert(FlagMap::value_type(flag.Name(), &flag)); + if (ins.second == false) { // means the name was already in the map + CommandLineFlag& old_flag = *ins.first->second; + if (flag.IsRetired() != old_flag.IsRetired()) { + // All registrations must agree on the 'retired' flag. + flags_internal::ReportUsageError( + absl::StrCat( + "Retired flag '", flag.Name(), "' was defined normally in file '", + (flag.IsRetired() ? old_flag.Filename() : flag.Filename()), "'."), + true); + } else if (flags_internal::PrivateHandleAccessor::TypeId(flag) != + flags_internal::PrivateHandleAccessor::TypeId(old_flag)) { + flags_internal::ReportUsageError( + absl::StrCat("Flag '", flag.Name(), + "' was defined more than once but with " + "differing types. Defined in files '", + old_flag.Filename(), "' and '", flag.Filename(), "'."), + true); + } else if (old_flag.IsRetired()) { + return; + } else if (old_flag.Filename() != flag.Filename()) { + flags_internal::ReportUsageError( + absl::StrCat("Flag '", flag.Name(), + "' was defined more than once (in files '", + old_flag.Filename(), "' and '", flag.Filename(), "')."), + true); + } else { + flags_internal::ReportUsageError( + absl::StrCat( + "Something is wrong with flag '", flag.Name(), "' in file '", + flag.Filename(), "'. One possibility: file '", flag.Filename(), + "' is being linked both statically and dynamically into this " + "executable. e.g. some files listed as srcs to a test and also " + "listed as srcs of some shared lib deps of the same test."), + true); + } + // All cases above are fatal, except for the retired flags. + std::exit(1); + } +} + +FlagRegistry& FlagRegistry::GlobalRegistry() { + static FlagRegistry* global_registry = new FlagRegistry; + return *global_registry; +} + +// -------------------------------------------------------------------- + +void ForEachFlag(std::function<void(CommandLineFlag&)> visitor) { + FlagRegistry& registry = FlagRegistry::GlobalRegistry(); + + if (registry.finalized_flags_.load(std::memory_order_acquire)) { + for (const auto& i : registry.flat_flags_) visitor(*i); + } + + FlagRegistryLock frl(registry); + for (const auto& i : registry.flags_) visitor(*i.second); +} + +// -------------------------------------------------------------------- + +bool RegisterCommandLineFlag(CommandLineFlag& flag, const char* filename) { + FlagRegistry::GlobalRegistry().RegisterFlag(flag, filename); + return true; +} + +void FinalizeRegistry() { + auto& registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + if (registry.finalized_flags_.load(std::memory_order_relaxed)) { + // Was already finalized. Ignore the second time. + return; + } + registry.flat_flags_.reserve(registry.flags_.size()); + for (const auto& f : registry.flags_) { + registry.flat_flags_.push_back(f.second); + } + std::sort(std::begin(registry.flat_flags_), std::end(registry.flat_flags_), + [](const CommandLineFlag* lhs, const CommandLineFlag* rhs) { + return lhs->Name() < rhs->Name(); + }); + registry.flags_.clear(); + registry.finalized_flags_.store(true, std::memory_order_release); +} + +// -------------------------------------------------------------------- + +namespace { + +class RetiredFlagObj final : public CommandLineFlag { + public: + constexpr RetiredFlagObj(const char* name, FlagFastTypeId type_id) + : name_(name), type_id_(type_id) {} + + private: + absl::string_view Name() const override { return name_; } + std::string Filename() const override { + OnAccess(); + return "RETIRED"; + } + FlagFastTypeId TypeId() const override { return type_id_; } + std::string Help() const override { + OnAccess(); + return ""; + } + bool IsRetired() const override { return true; } + bool IsSpecifiedOnCommandLine() const override { + OnAccess(); + return false; + } + std::string DefaultValue() const override { + OnAccess(); + return ""; + } + std::string CurrentValue() const override { + OnAccess(); + return ""; + } + + // Any input is valid + bool ValidateInputValue(absl::string_view) const override { + OnAccess(); + return true; + } + + std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override { + return nullptr; + } + + bool ParseFrom(absl::string_view, flags_internal::FlagSettingMode, + flags_internal::ValueSource, std::string&) override { + OnAccess(); + return false; + } + + void CheckDefaultValueParsingRoundtrip() const override { OnAccess(); } + + void Read(void*) const override { OnAccess(); } + + void OnAccess() const { + flags_internal::ReportUsageError( + absl::StrCat("Accessing retired flag '", name_, "'"), false); + } + + // Data members + const char* const name_; + const FlagFastTypeId type_id_; +}; + +} // namespace + +void Retire(const char* name, FlagFastTypeId type_id, char* buf) { + static_assert(sizeof(RetiredFlagObj) == kRetiredFlagObjSize, ""); + static_assert(alignof(RetiredFlagObj) == kRetiredFlagObjAlignment, ""); + auto* flag = ::new (static_cast<void*>(buf)) + flags_internal::RetiredFlagObj(name, type_id); + FlagRegistry::GlobalRegistry().RegisterFlag(*flag, nullptr); +} + +// -------------------------------------------------------------------- + +class FlagSaverImpl { + public: + FlagSaverImpl() = default; + FlagSaverImpl(const FlagSaverImpl&) = delete; + void operator=(const FlagSaverImpl&) = delete; + + // Saves the flag states from the flag registry into this object. + // It's an error to call this more than once. + void SaveFromRegistry() { + assert(backup_registry_.empty()); // call only once! + flags_internal::ForEachFlag([&](CommandLineFlag& flag) { + if (auto flag_state = + flags_internal::PrivateHandleAccessor::SaveState(flag)) { + backup_registry_.emplace_back(std::move(flag_state)); + } + }); + } + + // Restores the saved flag states into the flag registry. + void RestoreToRegistry() { + for (const auto& flag_state : backup_registry_) { + flag_state->Restore(); + } + } + + private: + std::vector<std::unique_ptr<flags_internal::FlagStateInterface>> + backup_registry_; +}; + +} // namespace flags_internal + +FlagSaver::FlagSaver() : impl_(new flags_internal::FlagSaverImpl) { + impl_->SaveFromRegistry(); +} + +FlagSaver::~FlagSaver() { + if (!impl_) return; + + impl_->RestoreToRegistry(); + delete impl_; +} + +// -------------------------------------------------------------------- + +CommandLineFlag* FindCommandLineFlag(absl::string_view name) { + if (name.empty()) return nullptr; + flags_internal::FlagRegistry& registry = + flags_internal::FlagRegistry::GlobalRegistry(); + return registry.FindFlag(name); +} + +// -------------------------------------------------------------------- + +absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> GetAllFlags() { + absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> res; + flags_internal::ForEachFlag([&](CommandLineFlag& flag) { + if (!flag.IsRetired()) res.insert({flag.Name(), &flag}); + }); + return res; +} + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/flags/reflection.h b/third_party/abseil-cpp/absl/flags/reflection.h new file mode 100644 index 0000000000..e6baf5de4b --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/reflection.h @@ -0,0 +1,90 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// File: reflection.h +// ----------------------------------------------------------------------------- +// +// This file defines the routines to access and operate on an Abseil Flag's +// reflection handle. + +#ifndef ABSL_FLAGS_REFLECTION_H_ +#define ABSL_FLAGS_REFLECTION_H_ + +#include <string> + +#include "absl/base/config.h" +#include "absl/container/flat_hash_map.h" +#include "absl/flags/commandlineflag.h" +#include "absl/flags/internal/commandlineflag.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { +class FlagSaverImpl; +} // namespace flags_internal + +// FindCommandLineFlag() +// +// Returns the reflection handle of an Abseil flag of the specified name, or +// `nullptr` if not found. This function will emit a warning if the name of a +// 'retired' flag is specified. +absl::CommandLineFlag* FindCommandLineFlag(absl::string_view name); + +// Returns current state of the Flags registry in a form of mapping from flag +// name to a flag reflection handle. +absl::flat_hash_map<absl::string_view, absl::CommandLineFlag*> GetAllFlags(); + +//------------------------------------------------------------------------------ +// FlagSaver +//------------------------------------------------------------------------------ +// +// A FlagSaver object stores the state of flags in the scope where the FlagSaver +// is defined, allowing modification of those flags within that scope and +// automatic restoration of the flags to their previous state upon leaving the +// scope. +// +// A FlagSaver can be used within tests to temporarily change the test +// environment and restore the test case to its previous state. +// +// Example: +// +// void MyFunc() { +// absl::FlagSaver fs; +// ... +// absl::SetFlag(&FLAGS_myFlag, otherValue); +// ... +// } // scope of FlagSaver left, flags return to previous state +// +// This class is thread-safe. + +class FlagSaver { + public: + FlagSaver(); + ~FlagSaver(); + + FlagSaver(const FlagSaver&) = delete; + void operator=(const FlagSaver&) = delete; + + private: + flags_internal::FlagSaverImpl* impl_; +}; + +//----------------------------------------------------------------------------- + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_REFLECTION_H_ diff --git a/third_party/abseil-cpp/absl/flags/reflection_test.cc b/third_party/abseil-cpp/absl/flags/reflection_test.cc new file mode 100644 index 0000000000..79cfa90c3a --- /dev/null +++ b/third_party/abseil-cpp/absl/flags/reflection_test.cc @@ -0,0 +1,265 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/flags/reflection.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/flags/declare.h" +#include "absl/flags/flag.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/marshalling.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" + +ABSL_FLAG(int, int_flag, 1, "int_flag help"); +ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help"); +ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); + +namespace { + +class ReflectionTest : public testing::Test { + protected: + void SetUp() override { flag_saver_ = absl::make_unique<absl::FlagSaver>(); } + void TearDown() override { flag_saver_.reset(); } + + private: + std::unique_ptr<absl::FlagSaver> flag_saver_; +}; + +// -------------------------------------------------------------------- + +TEST_F(ReflectionTest, TestFindCommandLineFlag) { + auto* handle = absl::FindCommandLineFlag("some_flag"); + EXPECT_EQ(handle, nullptr); + + handle = absl::FindCommandLineFlag("int_flag"); + EXPECT_NE(handle, nullptr); + + handle = absl::FindCommandLineFlag("string_flag"); + EXPECT_NE(handle, nullptr); + + handle = absl::FindCommandLineFlag("bool_retired_flag"); + EXPECT_NE(handle, nullptr); +} + +// -------------------------------------------------------------------- + +TEST_F(ReflectionTest, TestGetAllFlags) { + auto all_flags = absl::GetAllFlags(); + EXPECT_NE(all_flags.find("int_flag"), all_flags.end()); + EXPECT_EQ(all_flags.find("bool_retired_flag"), all_flags.end()); + EXPECT_EQ(all_flags.find("some_undefined_flag"), all_flags.end()); + + std::vector<absl::string_view> flag_names_first_attempt; + auto all_flags_1 = absl::GetAllFlags(); + for (auto f : all_flags_1) { + flag_names_first_attempt.push_back(f.first); + } + + std::vector<absl::string_view> flag_names_second_attempt; + auto all_flags_2 = absl::GetAllFlags(); + for (auto f : all_flags_2) { + flag_names_second_attempt.push_back(f.first); + } + + EXPECT_THAT(flag_names_first_attempt, + ::testing::UnorderedElementsAreArray(flag_names_second_attempt)); +} + +// -------------------------------------------------------------------- + +struct CustomUDT { + CustomUDT() : a(1), b(1) {} + CustomUDT(int a_, int b_) : a(a_), b(b_) {} + + friend bool operator==(const CustomUDT& f1, const CustomUDT& f2) { + return f1.a == f2.a && f1.b == f2.b; + } + + int a; + int b; +}; +bool AbslParseFlag(absl::string_view in, CustomUDT* f, std::string*) { + std::vector<absl::string_view> parts = + absl::StrSplit(in, ':', absl::SkipWhitespace()); + + if (parts.size() != 2) return false; + + if (!absl::SimpleAtoi(parts[0], &f->a)) return false; + + if (!absl::SimpleAtoi(parts[1], &f->b)) return false; + + return true; +} +std::string AbslUnparseFlag(const CustomUDT& f) { + return absl::StrCat(f.a, ":", f.b); +} + +} // namespace + +// -------------------------------------------------------------------- + +ABSL_FLAG(bool, test_flag_01, true, ""); +ABSL_FLAG(int, test_flag_02, 1234, ""); +ABSL_FLAG(int16_t, test_flag_03, -34, ""); +ABSL_FLAG(uint16_t, test_flag_04, 189, ""); +ABSL_FLAG(int32_t, test_flag_05, 10765, ""); +ABSL_FLAG(uint32_t, test_flag_06, 40000, ""); +ABSL_FLAG(int64_t, test_flag_07, -1234567, ""); +ABSL_FLAG(uint64_t, test_flag_08, 9876543, ""); +ABSL_FLAG(double, test_flag_09, -9.876e-50, ""); +ABSL_FLAG(float, test_flag_10, 1.234e12f, ""); +ABSL_FLAG(std::string, test_flag_11, "", ""); +ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), ""); +static int counter = 0; +ABSL_FLAG(int, test_flag_13, 200, "").OnUpdate([]() { counter++; }); +ABSL_FLAG(CustomUDT, test_flag_14, {}, ""); + +namespace { + +TEST_F(ReflectionTest, TestFlagSaverInScope) { + { + absl::FlagSaver s; + counter = 0; + absl::SetFlag(&FLAGS_test_flag_01, false); + absl::SetFlag(&FLAGS_test_flag_02, -1021); + absl::SetFlag(&FLAGS_test_flag_03, 6009); + absl::SetFlag(&FLAGS_test_flag_04, 44); + absl::SetFlag(&FLAGS_test_flag_05, +800); + absl::SetFlag(&FLAGS_test_flag_06, -40978756); + absl::SetFlag(&FLAGS_test_flag_07, 23405); + absl::SetFlag(&FLAGS_test_flag_08, 975310); + absl::SetFlag(&FLAGS_test_flag_09, 1.00001); + absl::SetFlag(&FLAGS_test_flag_10, -3.54f); + absl::SetFlag(&FLAGS_test_flag_11, "asdf"); + absl::SetFlag(&FLAGS_test_flag_12, absl::Hours(20)); + absl::SetFlag(&FLAGS_test_flag_13, 4); + absl::SetFlag(&FLAGS_test_flag_14, CustomUDT{-1, -2}); + } + + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), ""); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), 200); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), CustomUDT{}); + EXPECT_EQ(counter, 2); +} + +// -------------------------------------------------------------------- + +TEST_F(ReflectionTest, TestFlagSaverVsUpdateViaReflection) { + { + absl::FlagSaver s; + counter = 0; + std::string error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_01")->ParseFrom("false", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_02")->ParseFrom("-4536", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_03")->ParseFrom("111", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_04")->ParseFrom("909", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_05")->ParseFrom("-2004", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_06")->ParseFrom("1000023", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_07")->ParseFrom("69305", &error)) + << error; + EXPECT_TRUE(absl::FindCommandLineFlag("test_flag_08") + ->ParseFrom("1000000001", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_09")->ParseFrom("2.09021", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_10")->ParseFrom("-33.1", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_11")->ParseFrom("ADD_FOO", &error)) + << error; + EXPECT_TRUE(absl::FindCommandLineFlag("test_flag_12") + ->ParseFrom("3h11m16s", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_13")->ParseFrom("0", &error)) + << error; + EXPECT_TRUE( + absl::FindCommandLineFlag("test_flag_14")->ParseFrom("10:1", &error)) + << error; + } + + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), ""); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), 200); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), CustomUDT{}); + EXPECT_EQ(counter, 2); +} + +// -------------------------------------------------------------------- + +TEST_F(ReflectionTest, TestMultipleFlagSaversInEnclosedScopes) { + { + absl::FlagSaver s; + absl::SetFlag(&FLAGS_test_flag_08, 10); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 10); + { + absl::FlagSaver s; + absl::SetFlag(&FLAGS_test_flag_08, 20); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 20); + { + absl::FlagSaver s; + absl::SetFlag(&FLAGS_test_flag_08, -200); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), -200); + } + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 20); + } + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 10); + } + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543); +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/flags/usage_config.cc b/third_party/abseil-cpp/absl/flags/usage_config.cc index 2d837ec590..5d7426db31 100644 --- a/third_party/abseil-cpp/absl/flags/usage_config.cc +++ b/third_party/abseil-cpp/absl/flags/usage_config.cc @@ -15,6 +15,7 @@ #include "absl/flags/usage_config.h" +#include <functional> #include <iostream> #include <string> @@ -33,7 +34,8 @@ extern "C" { // Additional report of fatal usage error message before we std::exit. Error is // fatal if is_fatal argument to ReportUsageError is true. -ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {} +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL( + AbslInternalReportFatalUsageError)(absl::string_view) {} } // extern "C" @@ -50,10 +52,15 @@ namespace { bool ContainsHelpshortFlags(absl::string_view filename) { // By default we only want flags in binary's main. We expect the main // routine to reside in <program>.cc or <program>-main.cc or - // <program>_main.cc, where the <program> is the name of the binary. + // <program>_main.cc, where the <program> is the name of the binary + // (without .exe on Windows). auto suffix = flags_internal::Basename(filename); - if (!absl::ConsumePrefix(&suffix, - flags_internal::ShortProgramInvocationName())) + auto program_name = flags_internal::ShortProgramInvocationName(); + absl::string_view program_name_ref = program_name; +#if defined(_WIN32) + absl::ConsumeSuffix(&program_name_ref, ".exe"); +#endif + if (!absl::ConsumePrefix(&suffix, program_name_ref)) return false; return absl::StartsWith(suffix, ".") || absl::StartsWith(suffix, "-main.") || absl::StartsWith(suffix, "_main."); @@ -122,7 +129,7 @@ void ReportUsageError(absl::string_view msg, bool is_fatal) { std::cerr << "ERROR: " << msg << std::endl; if (is_fatal) { - AbslInternalReportFatalUsageError(msg); + ABSL_INTERNAL_C_SYMBOL(AbslInternalReportFatalUsageError)(msg); } } diff --git a/third_party/abseil-cpp/absl/flags/usage_config.h b/third_party/abseil-cpp/absl/flags/usage_config.h index 0ed7e1b47c..ded70300f0 100644 --- a/third_party/abseil-cpp/absl/flags/usage_config.h +++ b/third_party/abseil-cpp/absl/flags/usage_config.h @@ -90,7 +90,7 @@ struct FlagsUsageConfig { // program output. flags_internal::FlagKindFilter contains_helppackage_flags; - // Generates std::string containing program version. This is the std::string reported + // Generates string containing program version. This is the string reported // when user specifies --version in a command line. std::function<std::string()> version_string; @@ -127,7 +127,8 @@ extern "C" { // Additional report of fatal usage error message before we std::exit. Error is // fatal if is_fatal argument to ReportUsageError is true. -void AbslInternalReportFatalUsageError(absl::string_view); +void ABSL_INTERNAL_C_SYMBOL(AbslInternalReportFatalUsageError)( + absl::string_view); } // extern "C" diff --git a/third_party/abseil-cpp/absl/flags/usage_config_test.cc b/third_party/abseil-cpp/absl/flags/usage_config_test.cc index 70eca30b8f..e57a8832f6 100644 --- a/third_party/abseil-cpp/absl/flags/usage_config_test.cc +++ b/third_party/abseil-cpp/absl/flags/usage_config_test.cc @@ -84,7 +84,11 @@ TEST_F(FlagsUsageConfigTest, TestGetSetFlagsUsageConfig) { // -------------------------------------------------------------------- TEST_F(FlagsUsageConfigTest, TestContainsHelpshortFlags) { +#if defined(_WIN32) + flags::SetProgramInvocationName("usage_config_test.exe"); +#else flags::SetProgramInvocationName("usage_config_test"); +#endif auto config = flags::GetUsageConfig(); EXPECT_TRUE(config.contains_helpshort_flags("adir/cd/usage_config_test.cc")); diff --git a/third_party/abseil-cpp/absl/functional/BUILD.bazel b/third_party/abseil-cpp/absl/functional/BUILD.bazel index 432546ce0c..f9f2b9c2f5 100644 --- a/third_party/abseil-cpp/absl/functional/BUILD.bazel +++ b/third_party/abseil-cpp/absl/functional/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "bind_front", @@ -60,6 +59,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:base_internal", + "//absl/base:core_headers", "//absl/meta:type_traits", ], ) diff --git a/third_party/abseil-cpp/absl/functional/CMakeLists.txt b/third_party/abseil-cpp/absl/functional/CMakeLists.txt index cda914f2cd..338ddc6c6c 100644 --- a/third_party/abseil-cpp/absl/functional/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/functional/CMakeLists.txt @@ -39,7 +39,7 @@ absl_cc_test( DEPS absl::bind_front absl::memory - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -53,6 +53,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base_internal + absl::core_headers absl::meta PUBLIC ) @@ -68,5 +69,5 @@ absl_cc_test( absl::function_ref absl::memory absl::test_instance_tracker - gmock_main + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/functional/function_ref.h b/third_party/abseil-cpp/absl/functional/function_ref.h index 370acc55b0..824e3cea9d 100644 --- a/third_party/abseil-cpp/absl/functional/function_ref.h +++ b/third_party/abseil-cpp/absl/functional/function_ref.h @@ -50,6 +50,7 @@ #include <functional> #include <type_traits> +#include "absl/base/attributes.h" #include "absl/functional/internal/function_ref.h" #include "absl/meta/type_traits.h" @@ -90,7 +91,7 @@ class FunctionRef<R(Args...)> { // Used to disable constructors for objects that are not compatible with the // signature of this FunctionRef. template <typename F, - typename FR = absl::base_internal::InvokeT<F, Args&&...>> + typename FR = absl::base_internal::invoke_result_t<F, Args&&...>> using EnableIfCompatible = typename std::enable_if<std::is_void<R>::value || std::is_convertible<FR, R>::value>::type; @@ -98,7 +99,8 @@ class FunctionRef<R(Args...)> { public: // Constructs a FunctionRef from any invokable type. template <typename F, typename = EnableIfCompatible<const F&>> - FunctionRef(const F& f) // NOLINT(runtime/explicit) + // NOLINTNEXTLINE(runtime/explicit) + FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND) : invoker_(&absl::functional_internal::InvokeObject<F, R, Args...>) { absl::functional_internal::AssertNonNull(f); ptr_.obj = &f; @@ -122,6 +124,7 @@ class FunctionRef<R(Args...)> { // To help prevent subtle lifetime bugs, FunctionRef is not assignable. // Typically, it should only be used as an argument type. FunctionRef& operator=(const FunctionRef& rhs) = delete; + FunctionRef(const FunctionRef& rhs) = default; // Call the underlying object. R operator()(Args... args) const { diff --git a/third_party/abseil-cpp/absl/functional/internal/front_binder.h b/third_party/abseil-cpp/absl/functional/internal/front_binder.h index a4d95da44a..45f52de73d 100644 --- a/third_party/abseil-cpp/absl/functional/internal/front_binder.h +++ b/third_party/abseil-cpp/absl/functional/internal/front_binder.h @@ -33,7 +33,7 @@ namespace functional_internal { // Invoke the method, expanding the tuple of bound arguments. template <class R, class Tuple, size_t... Idx, class... Args> R Apply(Tuple&& bound, absl::index_sequence<Idx...>, Args&&... free) { - return base_internal::Invoke( + return base_internal::invoke( absl::forward<Tuple>(bound).template get<Idx>()..., absl::forward<Args>(free)...); } @@ -50,22 +50,22 @@ class FrontBinder { constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts) : bound_args_(absl::forward<Ts>(ts)...) {} - template <class... FreeArgs, - class R = base_internal::InvokeT<F&, BoundArgs&..., FreeArgs&&...>> + template <class... FreeArgs, class R = base_internal::invoke_result_t< + F&, BoundArgs&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) & { return functional_internal::Apply<R>(bound_args_, Idx(), absl::forward<FreeArgs>(free_args)...); } template <class... FreeArgs, - class R = base_internal::InvokeT<const F&, const BoundArgs&..., - FreeArgs&&...>> + class R = base_internal::invoke_result_t< + const F&, const BoundArgs&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) const& { return functional_internal::Apply<R>(bound_args_, Idx(), absl::forward<FreeArgs>(free_args)...); } - template <class... FreeArgs, class R = base_internal::InvokeT< + template <class... FreeArgs, class R = base_internal::invoke_result_t< F&&, BoundArgs&&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) && { // This overload is called when *this is an rvalue. If some of the bound @@ -75,8 +75,8 @@ class FrontBinder { } template <class... FreeArgs, - class R = base_internal::InvokeT<const F&&, const BoundArgs&&..., - FreeArgs&&...>> + class R = base_internal::invoke_result_t< + const F&&, const BoundArgs&&..., FreeArgs&&...>> R operator()(FreeArgs&&... free_args) const&& { // This overload is called when *this is an rvalue. If some of the bound // arguments are stored by value or rvalue reference, we move them. diff --git a/third_party/abseil-cpp/absl/functional/internal/function_ref.h b/third_party/abseil-cpp/absl/functional/internal/function_ref.h index d1575054ea..b5bb8b430a 100644 --- a/third_party/abseil-cpp/absl/functional/internal/function_ref.h +++ b/third_party/abseil-cpp/absl/functional/internal/function_ref.h @@ -71,14 +71,14 @@ template <typename Obj, typename R, typename... Args> R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) { auto o = static_cast<const Obj*>(ptr.obj); return static_cast<R>( - absl::base_internal::Invoke(*o, std::forward<Args>(args)...)); + absl::base_internal::invoke(*o, std::forward<Args>(args)...)); } template <typename Fun, typename R, typename... Args> R InvokeFunction(VoidPtr ptr, typename ForwardT<Args>::type... args) { auto f = reinterpret_cast<Fun>(ptr.fun); return static_cast<R>( - absl::base_internal::Invoke(f, std::forward<Args>(args)...)); + absl::base_internal::invoke(f, std::forward<Args>(args)...)); } template <typename Sig> diff --git a/third_party/abseil-cpp/absl/hash/BUILD.bazel b/third_party/abseil-cpp/absl/hash/BUILD.bazel index ffe8c294a0..f0640d34e4 100644 --- a/third_party/abseil-cpp/absl/hash/BUILD.bazel +++ b/third_party/abseil-cpp/absl/hash/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "hash", @@ -37,6 +36,8 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", + ":low_level_hash", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", "//absl/container:fixed_array", @@ -76,10 +77,30 @@ cc_test( "//absl/container:flat_hash_set", "//absl/meta:type_traits", "//absl/numeric:int128", + "//absl/strings:cord_test_helpers", "@com_google_googletest//:gtest_main", ], ) +cc_binary( + name = "hash_benchmark", + testonly = 1, + srcs = ["hash_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":hash", + "//absl/base:core_headers", + "//absl/random", + "//absl/strings", + "//absl/strings:cord", + "//absl/strings:cord_test_helpers", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_library( name = "spy_hash_state", testonly = 1, @@ -119,3 +140,31 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "low_level_hash", + srcs = ["internal/low_level_hash.cc"], + hdrs = ["internal/low_level_hash.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + "//absl/base:config", + "//absl/base:endian", + "//absl/numeric:bits", + "//absl/numeric:int128", + ], +) + +cc_test( + name = "low_level_hash_test", + srcs = ["internal/low_level_hash_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":low_level_hash", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/third_party/abseil-cpp/absl/hash/CMakeLists.txt b/third_party/abseil-cpp/absl/hash/CMakeLists.txt index febc551fce..5916ae3cf0 100644 --- a/third_party/abseil-cpp/absl/hash/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/hash/CMakeLists.txt @@ -24,7 +24,9 @@ absl_cc_library( "internal/hash.h" COPTS ${ABSL_DEFAULT_COPTS} - DEPS + DEPS + absl::city + absl::config absl::core_headers absl::endian absl::fixed_array @@ -34,7 +36,7 @@ absl_cc_library( absl::optional absl::variant absl::utility - absl::city + absl::low_level_hash PUBLIC ) @@ -50,7 +52,7 @@ absl_cc_library( absl::meta absl::strings absl::variant - gmock + GTest::gmock TESTONLY ) @@ -62,6 +64,7 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::cord_test_helpers absl::hash absl::hash_testing absl::core_headers @@ -69,7 +72,7 @@ absl_cc_test( absl::spy_hash_state absl::meta absl::int128 - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -110,6 +113,34 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::city - gmock_main + GTest::gmock_main +) + +absl_cc_library( + NAME + low_level_hash + HDRS + "internal/low_level_hash.h" + SRCS + "internal/low_level_hash.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::bits + absl::config + absl::endian + absl::int128 ) +absl_cc_test( + NAME + low_level_hash_test + SRCS + "internal/low_level_hash_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::low_level_hash + absl::strings + GTest::gmock_main +) diff --git a/third_party/abseil-cpp/absl/hash/hash.h b/third_party/abseil-cpp/absl/hash/hash.h index 23a65ea868..8282ea53c6 100644 --- a/third_party/abseil-cpp/absl/hash/hash.h +++ b/third_party/abseil-cpp/absl/hash/hash.h @@ -37,8 +37,11 @@ // types. Hashing of that combined state is separately done by `absl::Hash`. // // One should assume that a hash algorithm is chosen randomly at the start of -// each process. E.g., absl::Hash<int>()(9) in one process and -// absl::Hash<int>()(9) in another process are likely to differ. +// each process. E.g., `absl::Hash<int>{}(9)` in one process and +// `absl::Hash<int>{}(9)` in another process are likely to differ. +// +// `absl::Hash` is intended to strongly mix input bits with a target of passing +// an [Avalanche Test](https://en.wikipedia.org/wiki/Avalanche_effect). // // Example: // @@ -70,6 +73,8 @@ #ifndef ABSL_HASH_HASH_H_ #define ABSL_HASH_HASH_H_ +#include <tuple> + #include "absl/hash/internal/hash.h" namespace absl { @@ -85,7 +90,6 @@ ABSL_NAMESPACE_BEGIN // * T is an arithmetic or pointer type // * T defines an overload for `AbslHashValue(H, const T&)` for an arbitrary // hash state `H`. -// - T defines a specialization of `HASH_NAMESPACE::hash<T>` // - T defines a specialization of `std::hash<T>` // // `absl::Hash` intrinsically supports the following types: @@ -98,6 +102,7 @@ ABSL_NAMESPACE_BEGIN // * std::tuple<Ts...>, if all the Ts... are hashable // * std::unique_ptr and std::shared_ptr // * All string-like types including: +// * absl::Cord // * std::string // * std::string_view (as well as any instance of std::basic_string that // uses char and std::char_traits) @@ -124,8 +129,6 @@ ABSL_NAMESPACE_BEGIN // * Natively supported types out of the box (see above) // * Types for which an `AbslHashValue()` overload is provided (such as // user-defined types). See "Adding Type Support to `absl::Hash`" below. -// * Types which define a `HASH_NAMESPACE::hash<T>` specialization (aka -// `__gnu_cxx::hash<T>` for gcc/Clang or `stdext::hash<T>` for MSVC) // * Types which define a `std::hash<T>` specialization // // The fallback to legacy hash functions exists mainly for backwards @@ -213,6 +216,26 @@ ABSL_NAMESPACE_BEGIN template <typename T> using Hash = absl::hash_internal::Hash<T>; +// HashOf +// +// absl::HashOf() is a helper that generates a hash from the values of its +// arguments. It dispatches to absl::Hash directly, as follows: +// * HashOf(t) == absl::Hash<T>{}(t) +// * HashOf(a, b, c) == HashOf(std::make_tuple(a, b, c)) +// +// HashOf(a1, a2, ...) == HashOf(b1, b2, ...) is guaranteed when +// * The argument lists have pairwise identical C++ types +// * a1 == b1 && a2 == b2 && ... +// +// The requirement that the arguments match in both type and value is critical. +// It means that `a == b` does not necessarily imply `HashOf(a) == HashOf(b)` if +// `a` and `b` have different types. For example, `HashOf(2) != HashOf(2.0)`. +template <int&... ExplicitArgumentBarrier, typename... Types> +size_t HashOf(const Types&... values) { + auto tuple = std::tie(values...); + return absl::Hash<decltype(tuple)>{}(tuple); +} + // HashState // // A type erased version of the hash state concept, for use in user-defined diff --git a/third_party/abseil-cpp/absl/hash/hash_benchmark.cc b/third_party/abseil-cpp/absl/hash/hash_benchmark.cc new file mode 100644 index 0000000000..d498ac29c0 --- /dev/null +++ b/third_party/abseil-cpp/absl/hash/hash_benchmark.cc @@ -0,0 +1,254 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <string> +#include <type_traits> +#include <typeindex> +#include <utility> +#include <vector> + +#include "absl/base/attributes.h" +#include "absl/hash/hash.h" +#include "absl/random/random.h" +#include "absl/strings/cord.h" +#include "absl/strings/cord_test_helpers.h" +#include "absl/strings/string_view.h" +#include "benchmark/benchmark.h" + +namespace { + +using absl::Hash; + +template <template <typename> class H, typename T> +void RunBenchmark(benchmark::State& state, T value) { + H<T> h; + for (auto _ : state) { + benchmark::DoNotOptimize(value); + benchmark::DoNotOptimize(h(value)); + } +} + +} // namespace + +template <typename T> +using AbslHash = absl::Hash<T>; + +class TypeErasedInterface { + public: + virtual ~TypeErasedInterface() = default; + + template <typename H> + friend H AbslHashValue(H state, const TypeErasedInterface& wrapper) { + state = H::combine(std::move(state), std::type_index(typeid(wrapper))); + wrapper.HashValue(absl::HashState::Create(&state)); + return state; + } + + private: + virtual void HashValue(absl::HashState state) const = 0; +}; + +template <typename T> +struct TypeErasedAbslHash { + class Wrapper : public TypeErasedInterface { + public: + explicit Wrapper(const T& value) : value_(value) {} + + private: + void HashValue(absl::HashState state) const override { + absl::HashState::combine(std::move(state), value_); + } + + const T& value_; + }; + + size_t operator()(const T& value) { + return absl::Hash<Wrapper>{}(Wrapper(value)); + } +}; + +template <typename FuncType> +inline FuncType* ODRUseFunction(FuncType* ptr) { + volatile FuncType* dummy = ptr; + return dummy; +} + +absl::Cord FlatCord(size_t size) { + absl::Cord result(std::string(size, 'a')); + result.Flatten(); + return result; +} + +absl::Cord FragmentedCord(size_t size) { + const size_t orig_size = size; + std::vector<std::string> chunks; + size_t chunk_size = std::max<size_t>(1, size / 10); + while (size > chunk_size) { + chunks.push_back(std::string(chunk_size, 'a')); + size -= chunk_size; + } + if (size > 0) { + chunks.push_back(std::string(size, 'a')); + } + absl::Cord result = absl::MakeFragmentedCord(chunks); + (void) orig_size; + assert(result.size() == orig_size); + return result; +} + +// Generates a benchmark and a codegen method for the provided types. The +// codegen method provides a well known entrypoint for dumping assembly. +#define MAKE_BENCHMARK(hash, name, ...) \ + namespace { \ + void BM_##hash##_##name(benchmark::State& state) { \ + RunBenchmark<hash>(state, __VA_ARGS__); \ + } \ + BENCHMARK(BM_##hash##_##name); \ + } \ + size_t Codegen##hash##name(const decltype(__VA_ARGS__)& arg); \ + size_t Codegen##hash##name(const decltype(__VA_ARGS__)& arg) { \ + return hash<decltype(__VA_ARGS__)>{}(arg); \ + } \ + bool absl_hash_test_odr_use##hash##name = \ + ODRUseFunction(&Codegen##hash##name); + +MAKE_BENCHMARK(AbslHash, Int32, int32_t{}); +MAKE_BENCHMARK(AbslHash, Int64, int64_t{}); +MAKE_BENCHMARK(AbslHash, Double, 1.2); +MAKE_BENCHMARK(AbslHash, DoubleZero, 0.0); +MAKE_BENCHMARK(AbslHash, PairInt32Int32, std::pair<int32_t, int32_t>{}); +MAKE_BENCHMARK(AbslHash, PairInt64Int64, std::pair<int64_t, int64_t>{}); +MAKE_BENCHMARK(AbslHash, TupleInt32BoolInt64, + std::tuple<int32_t, bool, int64_t>{}); +MAKE_BENCHMARK(AbslHash, String_0, std::string()); +MAKE_BENCHMARK(AbslHash, String_10, std::string(10, 'a')); +MAKE_BENCHMARK(AbslHash, String_30, std::string(30, 'a')); +MAKE_BENCHMARK(AbslHash, String_90, std::string(90, 'a')); +MAKE_BENCHMARK(AbslHash, String_200, std::string(200, 'a')); +MAKE_BENCHMARK(AbslHash, String_5000, std::string(5000, 'a')); +MAKE_BENCHMARK(AbslHash, Cord_Flat_0, absl::Cord()); +MAKE_BENCHMARK(AbslHash, Cord_Flat_10, FlatCord(10)); +MAKE_BENCHMARK(AbslHash, Cord_Flat_30, FlatCord(30)); +MAKE_BENCHMARK(AbslHash, Cord_Flat_90, FlatCord(90)); +MAKE_BENCHMARK(AbslHash, Cord_Flat_200, FlatCord(200)); +MAKE_BENCHMARK(AbslHash, Cord_Flat_5000, FlatCord(5000)); +MAKE_BENCHMARK(AbslHash, Cord_Fragmented_200, FragmentedCord(200)); +MAKE_BENCHMARK(AbslHash, Cord_Fragmented_5000, FragmentedCord(5000)); +MAKE_BENCHMARK(AbslHash, VectorInt64_10, std::vector<int64_t>(10)); +MAKE_BENCHMARK(AbslHash, VectorInt64_100, std::vector<int64_t>(100)); +MAKE_BENCHMARK(AbslHash, VectorDouble_10, std::vector<double>(10, 1.1)); +MAKE_BENCHMARK(AbslHash, VectorDouble_100, std::vector<double>(100, 1.1)); +MAKE_BENCHMARK(AbslHash, PairStringString_0, + std::make_pair(std::string(), std::string())); +MAKE_BENCHMARK(AbslHash, PairStringString_10, + std::make_pair(std::string(10, 'a'), std::string(10, 'b'))); +MAKE_BENCHMARK(AbslHash, PairStringString_30, + std::make_pair(std::string(30, 'a'), std::string(30, 'b'))); +MAKE_BENCHMARK(AbslHash, PairStringString_90, + std::make_pair(std::string(90, 'a'), std::string(90, 'b'))); +MAKE_BENCHMARK(AbslHash, PairStringString_200, + std::make_pair(std::string(200, 'a'), std::string(200, 'b'))); +MAKE_BENCHMARK(AbslHash, PairStringString_5000, + std::make_pair(std::string(5000, 'a'), std::string(5000, 'b'))); + +MAKE_BENCHMARK(TypeErasedAbslHash, Int32, int32_t{}); +MAKE_BENCHMARK(TypeErasedAbslHash, Int64, int64_t{}); +MAKE_BENCHMARK(TypeErasedAbslHash, PairInt32Int32, + std::pair<int32_t, int32_t>{}); +MAKE_BENCHMARK(TypeErasedAbslHash, PairInt64Int64, + std::pair<int64_t, int64_t>{}); +MAKE_BENCHMARK(TypeErasedAbslHash, TupleInt32BoolInt64, + std::tuple<int32_t, bool, int64_t>{}); +MAKE_BENCHMARK(TypeErasedAbslHash, String_0, std::string()); +MAKE_BENCHMARK(TypeErasedAbslHash, String_10, std::string(10, 'a')); +MAKE_BENCHMARK(TypeErasedAbslHash, String_30, std::string(30, 'a')); +MAKE_BENCHMARK(TypeErasedAbslHash, String_90, std::string(90, 'a')); +MAKE_BENCHMARK(TypeErasedAbslHash, String_200, std::string(200, 'a')); +MAKE_BENCHMARK(TypeErasedAbslHash, String_5000, std::string(5000, 'a')); +MAKE_BENCHMARK(TypeErasedAbslHash, VectorDouble_10, + std::vector<double>(10, 1.1)); +MAKE_BENCHMARK(TypeErasedAbslHash, VectorDouble_100, + std::vector<double>(100, 1.1)); + +// The latency benchmark attempts to model the speed of the hash function in +// production. When a hash function is used for hashtable lookups it is rarely +// used to hash N items in a tight loop nor on constant sized strings. Instead, +// after hashing there is a potential equality test plus a (usually) large +// amount of user code. To simulate this effectively we introduce a data +// dependency between elements we hash by using the hash of the Nth element as +// the selector of the N+1th element to hash. This isolates the hash function +// code much like in production. As a bonus we use the hash to generate strings +// of size [1,N] (instead of fixed N) to disable perfect branch predictions in +// hash function implementations. +namespace { +// 16kb fits in L1 cache of most CPUs we care about. Keeping memory latency low +// will allow us to attribute most time to CPU which means more accurate +// measurements. +static constexpr size_t kEntropySize = 16 << 10; +static char entropy[kEntropySize + 1024]; +ABSL_ATTRIBUTE_UNUSED static const bool kInitialized = [] { + absl::BitGen gen; + static_assert(sizeof(entropy) % sizeof(uint64_t) == 0, ""); + for (int i = 0; i != sizeof(entropy); i += sizeof(uint64_t)) { + auto rand = absl::Uniform<uint64_t>(gen); + memcpy(&entropy[i], &rand, sizeof(uint64_t)); + } + return true; +}(); +} // namespace + +template <class T> +struct PodRand { + static_assert(std::is_pod<T>::value, ""); + static_assert(kEntropySize + sizeof(T) < sizeof(entropy), ""); + + T Get(size_t i) const { + T v; + memcpy(&v, &entropy[i % kEntropySize], sizeof(T)); + return v; + } +}; + +template <size_t N> +struct StringRand { + static_assert(kEntropySize + N < sizeof(entropy), ""); + + absl::string_view Get(size_t i) const { + // This has a small bias towards small numbers. Because max N is ~200 this + // is very small and prefer to be very fast instead of absolutely accurate. + // Also we pass N = 2^K+1 so that mod reduces to a bitand. + size_t s = (i % (N - 1)) + 1; + return {&entropy[i % kEntropySize], s}; + } +}; + +#define MAKE_LATENCY_BENCHMARK(hash, name, ...) \ + namespace { \ + void BM_latency_##hash##_##name(benchmark::State& state) { \ + __VA_ARGS__ r; \ + hash<decltype(r.Get(0))> h; \ + size_t i = 871401241; \ + for (auto _ : state) { \ + benchmark::DoNotOptimize(i = h(r.Get(i))); \ + } \ + } \ + BENCHMARK(BM_latency_##hash##_##name); \ + } // namespace + +MAKE_LATENCY_BENCHMARK(AbslHash, Int32, PodRand<int32_t>); +MAKE_LATENCY_BENCHMARK(AbslHash, Int64, PodRand<int64_t>); +MAKE_LATENCY_BENCHMARK(AbslHash, String9, StringRand<9>); +MAKE_LATENCY_BENCHMARK(AbslHash, String33, StringRand<33>); +MAKE_LATENCY_BENCHMARK(AbslHash, String65, StringRand<65>); +MAKE_LATENCY_BENCHMARK(AbslHash, String257, StringRand<257>); diff --git a/third_party/abseil-cpp/absl/hash/hash_test.cc b/third_party/abseil-cpp/absl/hash/hash_test.cc index f02a537ae8..b3ddebdd42 100644 --- a/third_party/abseil-cpp/absl/hash/hash_test.cc +++ b/third_party/abseil-cpp/absl/hash/hash_test.cc @@ -42,6 +42,7 @@ #include "absl/hash/internal/spy_hash_state.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" +#include "absl/strings/cord_test_helpers.h" namespace { @@ -81,8 +82,8 @@ TYPED_TEST_P(HashValueIntTest, FastPath) { } REGISTER_TYPED_TEST_CASE_P(HashValueIntTest, BasicUsage, FastPath); -using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t, uint32_t, - uint64_t, size_t>; +using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t, + uint32_t, uint64_t, size_t>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueIntTest, IntTypes); enum LegacyEnum { kValue1, kValue2, kValue3 }; @@ -269,6 +270,22 @@ struct WrapInTuple { } }; +absl::Cord FlatCord(absl::string_view sv) { + absl::Cord c(sv); + c.Flatten(); + return c; +} + +absl::Cord FragmentedCord(absl::string_view sv) { + if (sv.size() < 2) { + return absl::Cord(sv); + } + size_t halfway = sv.size() / 2; + std::vector<absl::string_view> parts = {sv.substr(0, halfway), + sv.substr(halfway)}; + return absl::MakeFragmentedCord(parts); +} + TEST(HashValueTest, Strings) { EXPECT_TRUE((is_hashable<std::string>::value)); @@ -277,25 +294,29 @@ TEST(HashValueTest, Strings) { const std::string large = std::string(2048, 'x'); // multiple of chunk size const std::string huge = std::string(5000, 'a'); // not a multiple - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( - std::string(), absl::string_view(), - std::string(""), absl::string_view(""), - std::string(small), absl::string_view(small), - std::string(dup), absl::string_view(dup), - std::string(large), absl::string_view(large), - std::string(huge), absl::string_view(huge)))); + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( // + std::string(), absl::string_view(), absl::Cord(), // + std::string(""), absl::string_view(""), absl::Cord(""), // + std::string(small), absl::string_view(small), absl::Cord(small), // + std::string(dup), absl::string_view(dup), absl::Cord(dup), // + std::string(large), absl::string_view(large), absl::Cord(large), // + std::string(huge), absl::string_view(huge), FlatCord(huge), // + FragmentedCord(huge)))); // Also check that nested types maintain the same hash. const WrapInTuple t{}; - EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( - t(std::string()), t(absl::string_view()), - t(std::string("")), t(absl::string_view("")), - t(std::string(small)), t(absl::string_view(small)), - t(std::string(dup)), t(absl::string_view(dup)), - t(std::string(large)), t(absl::string_view(large)), - t(std::string(huge)), t(absl::string_view(huge))))); - - // Make sure that hashing a `const char*` does not use its std::string-value. + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( // + t(std::string()), t(absl::string_view()), t(absl::Cord()), // + t(std::string("")), t(absl::string_view("")), t(absl::Cord("")), // + t(std::string(small)), t(absl::string_view(small)), // + t(absl::Cord(small)), // + t(std::string(dup)), t(absl::string_view(dup)), t(absl::Cord(dup)), // + t(std::string(large)), t(absl::string_view(large)), // + t(absl::Cord(large)), // + t(std::string(huge)), t(absl::string_view(huge)), // + t(FlatCord(huge)), t(FragmentedCord(huge))))); + + // Make sure that hashing a `const char*` does not use its string-value. EXPECT_NE(SpyHash(static_cast<const char*>("ABC")), SpyHash(absl::string_view("ABC"))); } @@ -386,7 +407,7 @@ using IntSequenceTypes = INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueSequenceTest, IntSequenceTypes); // Private type that only supports AbslHashValue to make sure our chosen hash -// implentation is recursive within absl::Hash. +// implementation is recursive within absl::Hash. // It uses std::abs() on the value to provide different bitwise representations // of the same logical value. struct Private { @@ -491,7 +512,7 @@ TEST(HashValueTest, CombinePiecewiseBuffer) { SCOPED_TRACE(big_buffer_size); std::string big_buffer; for (int i = 0; i < big_buffer_size; ++i) { - // Arbitrary std::string + // Arbitrary string big_buffer.push_back(32 + (i * (i / 3)) % 64); } auto big_buffer_hash = hash(PiecewiseHashTester(big_buffer)); @@ -560,6 +581,24 @@ TEST(HashValueTest, Maps) { MM{{1, "foo"}, {1, "foo"}, {43, "bar"}}, MM{{1, "foo"}, {43, "baz"}}))); } +TEST(HashValueTest, ReferenceWrapper) { + EXPECT_TRUE(is_hashable<std::reference_wrapper<Private>>::value); + + Private p1{1}, p10{10}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + p1, p10, std::ref(p1), std::ref(p10), std::cref(p1), std::cref(p10)))); + + EXPECT_TRUE(is_hashable<std::reference_wrapper<int>>::value); + int one = 1, ten = 10; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + one, ten, std::ref(one), std::ref(ten), std::cref(one), std::cref(ten)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(std::tuple<std::reference_wrapper<int>>(std::ref(one)), + std::tuple<std::reference_wrapper<int>>(std::ref(ten)), + std::tuple<int>(one), std::tuple<int>(ten)))); +} + template <typename T, typename = void> struct IsHashCallable : std::false_type {}; @@ -780,8 +819,8 @@ TYPED_TEST_P(HashIntTest, BasicUsage) { } REGISTER_TYPED_TEST_CASE_P(HashIntTest, BasicUsage); -using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t, uint32_t, - uint64_t, size_t>; +using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t, + uint32_t, uint64_t, size_t>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashIntTest, IntTypes); struct StructWithPadding { @@ -934,4 +973,39 @@ TEST(HashTest, DoesNotUseImplicitConversionsToBool) { absl::Hash<ValueWithBoolConversion>()(ValueWithBoolConversion{1})); } +TEST(HashOf, MatchesHashForSingleArgument) { + std::string s = "forty two"; + int i = 42; + double d = 42.0; + std::tuple<int, int> t{4, 2}; + + EXPECT_EQ(absl::HashOf(s), absl::Hash<std::string>{}(s)); + EXPECT_EQ(absl::HashOf(i), absl::Hash<int>{}(i)); + EXPECT_EQ(absl::HashOf(d), absl::Hash<double>{}(d)); + EXPECT_EQ(absl::HashOf(t), (absl::Hash<std::tuple<int, int>>{}(t))); +} + +TEST(HashOf, MatchesHashOfTupleForMultipleArguments) { + std::string hello = "hello"; + std::string world = "world"; + + EXPECT_EQ(absl::HashOf(), absl::HashOf(std::make_tuple())); + EXPECT_EQ(absl::HashOf(hello), absl::HashOf(std::make_tuple(hello))); + EXPECT_EQ(absl::HashOf(hello, world), + absl::HashOf(std::make_tuple(hello, world))); +} + +template <typename T> +std::true_type HashOfExplicitParameter(decltype(absl::HashOf<T>(0))) { + return {}; +} +template <typename T> +std::false_type HashOfExplicitParameter(size_t) { + return {}; +} + +TEST(HashOf, CantPassExplicitTemplateParameters) { + EXPECT_FALSE(HashOfExplicitParameter<int>(0)); +} + } // namespace diff --git a/third_party/abseil-cpp/absl/hash/internal/city.cc b/third_party/abseil-cpp/absl/hash/internal/city.cc index e122c184b6..5460134e57 100644 --- a/third_party/abseil-cpp/absl/hash/internal/city.cc +++ b/third_party/abseil-cpp/absl/hash/internal/city.cc @@ -200,10 +200,6 @@ static uint64_t Rotate(uint64_t val, int shift) { static uint64_t ShiftMix(uint64_t val) { return val ^ (val >> 47); } -static uint64_t HashLen16(uint64_t u, uint64_t v) { - return Hash128to64(uint128(u, v)); -} - static uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) { // Murmur-inspired hashing. uint64_t a = (u ^ v) * mul; @@ -214,6 +210,11 @@ static uint64_t HashLen16(uint64_t u, uint64_t v, uint64_t mul) { return b; } +static uint64_t HashLen16(uint64_t u, uint64_t v) { + const uint64_t kMul = 0x9ddfea08eb382d69ULL; + return HashLen16(u, v, kMul); +} + static uint64_t HashLen0to16(const char *s, size_t len) { if (len >= 8) { uint64_t mul = k2 + len * 2; @@ -253,9 +254,8 @@ static uint64_t HashLen17to32(const char *s, size_t len) { // Return a 16-byte hash for 48 bytes. Quick and dirty. // Callers do best to use "random-looking" values for a and b. -static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(uint64_t w, uint64_t x, - uint64_t y, uint64_t z, - uint64_t a, uint64_t b) { +static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds( + uint64_t w, uint64_t x, uint64_t y, uint64_t z, uint64_t a, uint64_t b) { a += w; b = Rotate(b + a + z, 21); uint64_t c = a; @@ -266,8 +266,9 @@ static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(uint64_t w, uint64_t } // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. -static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(const char *s, uint64_t a, - uint64_t b) { +static std::pair<uint64_t, uint64_t> WeakHashLen32WithSeeds(const char *s, + uint64_t a, + uint64_t b) { return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, b); } @@ -310,8 +311,10 @@ uint64_t CityHash64(const char *s, size_t len) { uint64_t x = Fetch64(s + len - 40); uint64_t y = Fetch64(s + len - 16) + Fetch64(s + len - 56); uint64_t z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); - std::pair<uint64_t, uint64_t> v = WeakHashLen32WithSeeds(s + len - 64, len, z); - std::pair<uint64_t, uint64_t> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); + std::pair<uint64_t, uint64_t> v = + WeakHashLen32WithSeeds(s + len - 64, len, z); + std::pair<uint64_t, uint64_t> w = + WeakHashLen32WithSeeds(s + len - 32, y + k1, x); x = x * k1 + Fetch64(s); // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. @@ -337,7 +340,7 @@ uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed) { } uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0, - uint64_t seed1) { + uint64_t seed1) { return HashLen16(CityHash64(s, len) - seed0, seed1); } diff --git a/third_party/abseil-cpp/absl/hash/internal/city.h b/third_party/abseil-cpp/absl/hash/internal/city.h index 161c7748ec..393da0b95d 100644 --- a/third_party/abseil-cpp/absl/hash/internal/city.h +++ b/third_party/abseil-cpp/absl/hash/internal/city.h @@ -56,11 +56,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace hash_internal { -typedef std::pair<uint64_t, uint64_t> uint128; - -inline uint64_t Uint128Low64(const uint128 &x) { return x.first; } -inline uint64_t Uint128High64(const uint128 &x) { return x.second; } - // Hash function for a byte array. uint64_t CityHash64(const char *s, size_t len); @@ -71,24 +66,11 @@ uint64_t CityHash64WithSeed(const char *s, size_t len, uint64_t seed); // Hash function for a byte array. For convenience, two seeds are also // hashed into the result. uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0, - uint64_t seed1); + uint64_t seed1); // Hash function for a byte array. Most useful in 32-bit binaries. uint32_t CityHash32(const char *s, size_t len); -// Hash 128 input bits down to 64 bits of output. -// This is intended to be a reasonably good hash function. -inline uint64_t Hash128to64(const uint128 &x) { - // Murmur-inspired hashing. - const uint64_t kMul = 0x9ddfea08eb382d69ULL; - uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; - a ^= (a >> 47); - uint64_t b = (Uint128High64(x) ^ a) * kMul; - b ^= (b >> 47); - b *= kMul; - return b; -} - } // namespace hash_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/hash/internal/hash.cc b/third_party/abseil-cpp/absl/hash/internal/hash.cc index b44ecb3a6b..11451e575c 100644 --- a/third_party/abseil-cpp/absl/hash/internal/hash.cc +++ b/third_party/abseil-cpp/absl/hash/internal/hash.cc @@ -18,13 +18,12 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace hash_internal { -uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state, - const unsigned char* first, - size_t len) { +uint64_t MixingHashState::CombineLargeContiguousImpl32( + uint64_t state, const unsigned char* first, size_t len) { while (len >= PiecewiseChunkSize()) { - state = - Mix(state, absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), - PiecewiseChunkSize())); + state = Mix(state, + hash_internal::CityHash32(reinterpret_cast<const char*>(first), + PiecewiseChunkSize())); len -= PiecewiseChunkSize(); first += PiecewiseChunkSize(); } @@ -33,13 +32,10 @@ uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state, std::integral_constant<int, 4>{}); } -uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state, - const unsigned char* first, - size_t len) { +uint64_t MixingHashState::CombineLargeContiguousImpl64( + uint64_t state, const unsigned char* first, size_t len) { while (len >= PiecewiseChunkSize()) { - state = - Mix(state, absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), - PiecewiseChunkSize())); + state = Mix(state, Hash64(first, PiecewiseChunkSize())); len -= PiecewiseChunkSize(); first += PiecewiseChunkSize(); } @@ -48,7 +44,25 @@ uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state, std::integral_constant<int, 8>{}); } -ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed; +ABSL_CONST_INIT const void* const MixingHashState::kSeed = &kSeed; + +// The salt array used by LowLevelHash. This array is NOT the mechanism used to +// make absl::Hash non-deterministic between program invocations. See `Seed()` +// for that mechanism. +// +// Any random values are fine. These values are just digits from the decimal +// part of pi. +// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number +constexpr uint64_t kHashSalt[5] = { + uint64_t{0x243F6A8885A308D3}, uint64_t{0x13198A2E03707344}, + uint64_t{0xA4093822299F31D0}, uint64_t{0x082EFA98EC4E6C89}, + uint64_t{0x452821E638D01377}, +}; + +uint64_t MixingHashState::LowLevelHashImpl(const unsigned char* data, + size_t len) { + return LowLevelHash(data, len, Seed(), kHashSalt); +} } // namespace hash_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/hash/internal/hash.h b/third_party/abseil-cpp/absl/hash/internal/hash.h index ae7a60cd55..b1e33caf4c 100644 --- a/third_party/abseil-cpp/absl/hash/internal/hash.h +++ b/third_party/abseil-cpp/absl/hash/internal/hash.h @@ -21,6 +21,7 @@ #include <algorithm> #include <array> +#include <bitset> #include <cmath> #include <cstring> #include <deque> @@ -38,27 +39,82 @@ #include <utility> #include <vector> -#include "absl/base/internal/endian.h" +#include "absl/base/config.h" +#include "absl/base/internal/unaligned_access.h" #include "absl/base/port.h" #include "absl/container/fixed_array.h" +#include "absl/hash/internal/city.h" +#include "absl/hash/internal/low_level_hash.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "absl/types/variant.h" #include "absl/utility/utility.h" -#include "absl/hash/internal/city.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace hash_internal { -class PiecewiseCombiner; - // Internal detail: Large buffers are hashed in smaller chunks. This function // returns the size of these chunks. constexpr size_t PiecewiseChunkSize() { return 1024; } +// PiecewiseCombiner +// +// PiecewiseCombiner is an internal-only helper class for hashing a piecewise +// buffer of `char` or `unsigned char` as though it were contiguous. This class +// provides two methods: +// +// H add_buffer(state, data, size) +// H finalize(state) +// +// `add_buffer` can be called zero or more times, followed by a single call to +// `finalize`. This will produce the same hash expansion as concatenating each +// buffer piece into a single contiguous buffer, and passing this to +// `H::combine_contiguous`. +// +// Example usage: +// PiecewiseCombiner combiner; +// for (const auto& piece : pieces) { +// state = combiner.add_buffer(std::move(state), piece.data, piece.size); +// } +// return combiner.finalize(std::move(state)); +class PiecewiseCombiner { + public: + PiecewiseCombiner() : position_(0) {} + PiecewiseCombiner(const PiecewiseCombiner&) = delete; + PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete; + + // PiecewiseCombiner::add_buffer() + // + // Appends the given range of bytes to the sequence to be hashed, which may + // modify the provided hash state. + template <typename H> + H add_buffer(H state, const unsigned char* data, size_t size); + template <typename H> + H add_buffer(H state, const char* data, size_t size) { + return add_buffer(std::move(state), + reinterpret_cast<const unsigned char*>(data), size); + } + + // PiecewiseCombiner::finalize() + // + // Finishes combining the hash sequence, which may may modify the provided + // hash state. + // + // Once finalize() is called, add_buffer() may no longer be called. The + // resulting hash state will be the same as if the pieces passed to + // add_buffer() were concatenated into a single flat buffer, and then provided + // to H::combine_contiguous(). + template <typename H> + H finalize(H state); + + private: + unsigned char buf_[PiecewiseChunkSize()]; + size_t position_; +}; + // HashStateBase // // A hash state object represents an intermediate state in the computation @@ -125,8 +181,7 @@ class HashStateBase { template <typename T> static H combine_contiguous(H state, const T* data, size_t size); - private: - friend class PiecewiseCombiner; + using AbslInternalPiecewiseCombiner = PiecewiseCombiner; }; // is_uniquely_represented @@ -197,61 +252,6 @@ H hash_bytes(H hash_state, const T& value) { return H::combine_contiguous(std::move(hash_state), start, sizeof(value)); } -// PiecewiseCombiner -// -// PiecewiseCombiner is an internal-only helper class for hashing a piecewise -// buffer of `char` or `unsigned char` as though it were contiguous. This class -// provides two methods: -// -// H add_buffer(state, data, size) -// H finalize(state) -// -// `add_buffer` can be called zero or more times, followed by a single call to -// `finalize`. This will produce the same hash expansion as concatenating each -// buffer piece into a single contiguous buffer, and passing this to -// `H::combine_contiguous`. -// -// Example usage: -// PiecewiseCombiner combiner; -// for (const auto& piece : pieces) { -// state = combiner.add_buffer(std::move(state), piece.data, piece.size); -// } -// return combiner.finalize(std::move(state)); -class PiecewiseCombiner { - public: - PiecewiseCombiner() : position_(0) {} - PiecewiseCombiner(const PiecewiseCombiner&) = delete; - PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete; - - // PiecewiseCombiner::add_buffer() - // - // Appends the given range of bytes to the sequence to be hashed, which may - // modify the provided hash state. - template <typename H> - H add_buffer(H state, const unsigned char* data, size_t size); - template <typename H> - H add_buffer(H state, const char* data, size_t size) { - return add_buffer(std::move(state), - reinterpret_cast<const unsigned char*>(data), size); - } - - // PiecewiseCombiner::finalize() - // - // Finishes combining the hash sequence, which may may modify the provided - // hash state. - // - // Once finalize() is called, add_buffer() may no longer be called. The - // resulting hash state will be the same as if the pieces passed to - // add_buffer() were concatenated into a single flat buffer, and then provided - // to H::combine_contiguous(). - template <typename H> - H finalize(H state); - - private: - unsigned char buf_[PiecewiseChunkSize()]; - size_t position_; -}; - // ----------------------------------------------------------------------------- // AbslHashValue for Basic Types // ----------------------------------------------------------------------------- @@ -380,7 +380,7 @@ template <typename H, typename... Ts> // This SFINAE gets MSVC confused under some conditions. Let's just disable it // for now. H -#else // _MSC_VER +#else // _MSC_VER typename std::enable_if<absl::conjunction<is_hashable<Ts>...>::value, H>::type #endif // _MSC_VER AbslHashValue(H hash_state, const std::tuple<Ts...>& t) { @@ -413,6 +413,7 @@ H AbslHashValue(H hash_state, const std::shared_ptr<T>& ptr) { // All the string-like types supported here provide the same hash expansion for // the same character sequence. These types are: // +// - `absl::Cord` // - `std::string` (and std::basic_string<char, std::char_traits<char>, A> for // any allocator A) // - `absl::string_view` and `std::string_view` @@ -489,8 +490,9 @@ typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue( // AbslHashValue for hashing std::vector // -// Do not use this for vector<bool>. It does not have a .data(), and a fallback -// for std::hash<> is most likely faster. +// Do not use this for vector<bool> on platforms that have a working +// implementation of std::hash. It does not have a .data(), and a fallback for +// std::hash<> is most likely faster. template <typename H, typename T, typename Allocator> typename std::enable_if<is_hashable<T>::value && !std::is_same<T, bool>::value, H>::type @@ -500,6 +502,27 @@ AbslHashValue(H hash_state, const std::vector<T, Allocator>& vector) { vector.size()); } +#if defined(ABSL_IS_BIG_ENDIAN) && \ + (defined(__GLIBCXX__) || defined(__GLIBCPP__)) +// AbslHashValue for hashing std::vector<bool> +// +// std::hash in libstdc++ does not work correctly with vector<bool> on Big +// Endian platforms therefore we need to implement a custom AbslHashValue for +// it. More details on the bug: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 +template <typename H, typename T, typename Allocator> +typename std::enable_if<is_hashable<T>::value && std::is_same<T, bool>::value, + H>::type +AbslHashValue(H hash_state, const std::vector<T, Allocator>& vector) { + typename H::AbslInternalPiecewiseCombiner combiner; + for (const auto& i : vector) { + unsigned char c = static_cast<unsigned char>(i); + hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c)); + } + return H::combine(combiner.finalize(std::move(hash_state)), vector.size()); +} +#endif + // ----------------------------------------------------------------------------- // AbslHashValue for Ordered Associative Containers // ----------------------------------------------------------------------------- @@ -553,6 +576,13 @@ typename std::enable_if<is_hashable<Key>::value, H>::type AbslHashValue( // AbslHashValue for Wrapper Types // ----------------------------------------------------------------------------- +// AbslHashValue for hashing std::reference_wrapper +template <typename H, typename T> +typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue( + H hash_state, std::reference_wrapper<T> opt) { + return H::combine(std::move(hash_state), opt.get()); +} + // AbslHashValue for hashing absl::optional template <typename H, typename T> typename std::enable_if<is_hashable<T>::value, H>::type AbslHashValue( @@ -585,9 +615,28 @@ AbslHashValue(H hash_state, const absl::variant<T...>& v) { // AbslHashValue for Other Types // ----------------------------------------------------------------------------- -// AbslHashValue for hashing std::bitset is not defined, for the same reason as -// for vector<bool> (see std::vector above): It does not expose the raw bytes, -// and a fallback to std::hash<> is most likely faster. +// AbslHashValue for hashing std::bitset is not defined on Little Endian +// platforms, for the same reason as for vector<bool> (see std::vector above): +// It does not expose the raw bytes, and a fallback to std::hash<> is most +// likely faster. + +#if defined(ABSL_IS_BIG_ENDIAN) && \ + (defined(__GLIBCXX__) || defined(__GLIBCPP__)) +// AbslHashValue for hashing std::bitset +// +// std::hash in libstdc++ does not work correctly with std::bitset on Big Endian +// platforms therefore we need to implement a custom AbslHashValue for it. More +// details on the bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102531 +template <typename H, size_t N> +H AbslHashValue(H hash_state, const std::bitset<N>& set) { + typename H::AbslInternalPiecewiseCombiner combiner; + for (int i = 0; i < N; i++) { + unsigned char c = static_cast<unsigned char>(set[i]); + hash_state = combiner.add_buffer(std::move(hash_state), &c, sizeof(c)); + } + return H::combine(combiner.finalize(std::move(hash_state)), N); +} +#endif // ----------------------------------------------------------------------------- @@ -707,9 +756,8 @@ template <typename T> struct is_hashable : std::integral_constant<bool, HashSelect::template Apply<T>::value> {}; -// CityHashState -class ABSL_DLL CityHashState - : public HashStateBase<CityHashState> { +// MixingHashState +class ABSL_DLL MixingHashState : public HashStateBase<MixingHashState> { // absl::uint128 is not an alias or a thin wrapper around the intrinsic. // We use the intrinsic when available to improve performance. #ifdef ABSL_HAVE_INTRINSIC_INT128 @@ -728,23 +776,23 @@ class ABSL_DLL CityHashState public: // Move only - CityHashState(CityHashState&&) = default; - CityHashState& operator=(CityHashState&&) = default; + MixingHashState(MixingHashState&&) = default; + MixingHashState& operator=(MixingHashState&&) = default; - // CityHashState::combine_contiguous() + // MixingHashState::combine_contiguous() // // Fundamental base case for hash recursion: mixes the given range of bytes // into the hash state. - static CityHashState combine_contiguous(CityHashState hash_state, - const unsigned char* first, - size_t size) { - return CityHashState( + static MixingHashState combine_contiguous(MixingHashState hash_state, + const unsigned char* first, + size_t size) { + return MixingHashState( CombineContiguousImpl(hash_state.state_, first, size, std::integral_constant<int, sizeof(size_t)>{})); } - using CityHashState::HashStateBase::combine_contiguous; + using MixingHashState::HashStateBase::combine_contiguous; - // CityHashState::hash() + // MixingHashState::hash() // // For performance reasons in non-opt mode, we specialize this for // integral types. @@ -756,24 +804,24 @@ class ABSL_DLL CityHashState return static_cast<size_t>(Mix(Seed(), static_cast<uint64_t>(value))); } - // Overload of CityHashState::hash() + // Overload of MixingHashState::hash() template <typename T, absl::enable_if_t<!IntegralFastPath<T>::value, int> = 0> static size_t hash(const T& value) { - return static_cast<size_t>(combine(CityHashState{}, value).state_); + return static_cast<size_t>(combine(MixingHashState{}, value).state_); } private: // Invoked only once for a given argument; that plus the fact that this is // move-only ensures that there is only one non-moved-from object. - CityHashState() : state_(Seed()) {} + MixingHashState() : state_(Seed()) {} // Workaround for MSVC bug. // We make the type copyable to fix the calling convention, even though we // never actually copy it. Keep it private to not affect the public API of the // type. - CityHashState(const CityHashState&) = default; + MixingHashState(const MixingHashState&) = default; - explicit CityHashState(uint64_t state) : state_(state) {} + explicit MixingHashState(uint64_t state) : state_(state) {} // Implementation of the base case for combine_contiguous where we actually // mix the bytes into the state. @@ -786,7 +834,7 @@ class ABSL_DLL CityHashState static uint64_t CombineContiguousImpl(uint64_t state, const unsigned char* first, size_t len, std::integral_constant<int, 8> - /* sizeof_size_t*/); + /* sizeof_size_t */); // Slow dispatch path for calls to CombineContiguousImpl with a size argument // larger than PiecewiseChunkSize(). Has the same effect as calling @@ -799,31 +847,66 @@ class ABSL_DLL CityHashState size_t len); // Reads 9 to 16 bytes from p. - // The first 8 bytes are in .first, the rest (zero padded) bytes are in - // .second. + // The least significant 8 bytes are in .first, the rest (zero padded) bytes + // are in .second. static std::pair<uint64_t, uint64_t> Read9To16(const unsigned char* p, size_t len) { - uint64_t high = little_endian::Load64(p + len - 8); - return {little_endian::Load64(p), high >> (128 - len * 8)}; + uint64_t low_mem = absl::base_internal::UnalignedLoad64(p); + uint64_t high_mem = absl::base_internal::UnalignedLoad64(p + len - 8); +#ifdef ABSL_IS_LITTLE_ENDIAN + uint64_t most_significant = high_mem; + uint64_t least_significant = low_mem; +#else + uint64_t most_significant = low_mem; + uint64_t least_significant = high_mem; +#endif + return {least_significant, most_significant >> (128 - len * 8)}; } // Reads 4 to 8 bytes from p. Zero pads to fill uint64_t. static uint64_t Read4To8(const unsigned char* p, size_t len) { - return (static_cast<uint64_t>(little_endian::Load32(p + len - 4)) - << (len - 4) * 8) | - little_endian::Load32(p); + uint32_t low_mem = absl::base_internal::UnalignedLoad32(p); + uint32_t high_mem = absl::base_internal::UnalignedLoad32(p + len - 4); +#ifdef ABSL_IS_LITTLE_ENDIAN + uint32_t most_significant = high_mem; + uint32_t least_significant = low_mem; +#else + uint32_t most_significant = low_mem; + uint32_t least_significant = high_mem; +#endif + return (static_cast<uint64_t>(most_significant) << (len - 4) * 8) | + least_significant; } // Reads 1 to 3 bytes from p. Zero pads to fill uint32_t. static uint32_t Read1To3(const unsigned char* p, size_t len) { - return static_cast<uint32_t>((p[0]) | // - (p[len / 2] << (len / 2 * 8)) | // - (p[len - 1] << ((len - 1) * 8))); + unsigned char mem0 = p[0]; + unsigned char mem1 = p[len / 2]; + unsigned char mem2 = p[len - 1]; +#ifdef ABSL_IS_LITTLE_ENDIAN + unsigned char significant2 = mem2; + unsigned char significant1 = mem1; + unsigned char significant0 = mem0; +#else + unsigned char significant2 = mem0; + unsigned char significant1 = mem1; + unsigned char significant0 = mem2; +#endif + return static_cast<uint32_t>(significant0 | // + (significant1 << (len / 2 * 8)) | // + (significant2 << ((len - 1) * 8))); } ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Mix(uint64_t state, uint64_t v) { +#if defined(__aarch64__) + // On AArch64, calculating a 128-bit product is inefficient, because it + // requires a sequence of two instructions to calculate the upper and lower + // halves of the result. + using MultType = uint64_t; +#else using MultType = absl::conditional_t<sizeof(size_t) == 4, uint64_t, uint128>; +#endif // We do the addition in 64-bit space to make sure the 128-bit // multiplication is fast. If we were to do it as MultType the compiler has // to assume that the high word is non-zero and needs to perform 2 @@ -833,6 +916,19 @@ class ABSL_DLL CityHashState return static_cast<uint64_t>(m ^ (m >> (sizeof(m) * 8 / 2))); } + // An extern to avoid bloat on a direct call to LowLevelHash() with fixed + // values for both the seed and salt parameters. + static uint64_t LowLevelHashImpl(const unsigned char* data, size_t len); + + ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Hash64(const unsigned char* data, + size_t len) { +#ifdef ABSL_HAVE_INTRINSIC_INT128 + return LowLevelHashImpl(data, len); +#else + return hash_internal::CityHash64(reinterpret_cast<const char*>(data), len); +#endif + } + // Seed() // // A non-deterministic seed. @@ -850,15 +946,22 @@ class ABSL_DLL CityHashState // On other platforms this is still going to be non-deterministic but most // probably per-build and not per-process. ABSL_ATTRIBUTE_ALWAYS_INLINE static uint64_t Seed() { +#if (!defined(__clang__) || __clang_major__ > 11) && \ + !defined(__apple_build_version__) + return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&kSeed)); +#else + // Workaround the absence of + // https://github.com/llvm/llvm-project/commit/bc15bf66dcca76cc06fe71fca35b74dc4d521021. return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(kSeed)); +#endif } static const void* const kSeed; uint64_t state_; }; -// CityHashState::CombineContiguousImpl() -inline uint64_t CityHashState::CombineContiguousImpl( +// MixingHashState::CombineContiguousImpl() +inline uint64_t MixingHashState::CombineContiguousImpl( uint64_t state, const unsigned char* first, size_t len, std::integral_constant<int, 4> /* sizeof_size_t */) { // For large values we use CityHash, for small ones we just use a @@ -868,7 +971,7 @@ inline uint64_t CityHashState::CombineContiguousImpl( if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { return CombineLargeContiguousImpl32(state, first, len); } - v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len); + v = hash_internal::CityHash32(reinterpret_cast<const char*>(first), len); } else if (len >= 4) { v = Read4To8(first, len); } else if (len > 0) { @@ -880,18 +983,18 @@ inline uint64_t CityHashState::CombineContiguousImpl( return Mix(state, v); } -// Overload of CityHashState::CombineContiguousImpl() -inline uint64_t CityHashState::CombineContiguousImpl( +// Overload of MixingHashState::CombineContiguousImpl() +inline uint64_t MixingHashState::CombineContiguousImpl( uint64_t state, const unsigned char* first, size_t len, std::integral_constant<int, 8> /* sizeof_size_t */) { - // For large values we use CityHash, for small ones we just use a - // multiplicative hash. + // For large values we use LowLevelHash or CityHash depending on the platform, + // for small ones we just use a multiplicative hash. uint64_t v; if (len > 16) { if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { return CombineLargeContiguousImpl64(state, first, len); } - v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len); + v = Hash64(first, len); } else if (len > 8) { auto p = Read9To16(first, len); state = Mix(state, p.first); @@ -922,7 +1025,9 @@ struct PoisonedHash : private AggregateBarrier { template <typename T> struct HashImpl { - size_t operator()(const T& value) const { return CityHashState::hash(value); } + size_t operator()(const T& value) const { + return MixingHashState::hash(value); + } }; template <typename T> @@ -955,12 +1060,15 @@ H PiecewiseCombiner::add_buffer(H state, const unsigned char* data, return state; } - // Complete the buffer and hash it - const size_t bytes_needed = PiecewiseChunkSize() - position_; - memcpy(buf_ + position_, data, bytes_needed); - state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize()); - data += bytes_needed; - size -= bytes_needed; + // If the buffer is partially filled we need to complete the buffer + // and hash it. + if (position_ != 0) { + const size_t bytes_needed = PiecewiseChunkSize() - position_; + memcpy(buf_ + position_, data, bytes_needed); + state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize()); + data += bytes_needed; + size -= bytes_needed; + } // Hash whatever chunks we can without copying while (size >= PiecewiseChunkSize()) { diff --git a/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc b/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc new file mode 100644 index 0000000000..6f9cb9c7bf --- /dev/null +++ b/third_party/abseil-cpp/absl/hash/internal/low_level_hash.cc @@ -0,0 +1,123 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/hash/internal/low_level_hash.h" + +#include "absl/base/internal/unaligned_access.h" +#include "absl/numeric/bits.h" +#include "absl/numeric/int128.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_internal { + +static uint64_t Mix(uint64_t v0, uint64_t v1) { +#if !defined(__aarch64__) + // The default bit-mixer uses 64x64->128-bit multiplication. + absl::uint128 p = v0; + p *= v1; + return absl::Uint128Low64(p) ^ absl::Uint128High64(p); +#else + // The default bit-mixer above would perform poorly on some ARM microarchs, + // where calculating a 128-bit product requires a sequence of two + // instructions with a high combined latency and poor throughput. + // Instead, we mix bits using only 64-bit arithmetic, which is faster. + uint64_t p = v0 ^ absl::rotl(v1, 40); + p *= v1 ^ absl::rotl(v0, 39); + return p ^ (p >> 11); +#endif +} + +uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, + const uint64_t salt[]) { + const uint8_t* ptr = static_cast<const uint8_t*>(data); + uint64_t starting_length = static_cast<uint64_t>(len); + uint64_t current_state = seed ^ salt[0]; + + if (len > 64) { + // If we have more than 64 bytes, we're going to handle chunks of 64 + // bytes at a time. We're going to build up two separate hash states + // which we will then hash together. + uint64_t duplicated_state = current_state; + + do { + uint64_t a = absl::base_internal::UnalignedLoad64(ptr); + uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); + uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16); + uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24); + uint64_t e = absl::base_internal::UnalignedLoad64(ptr + 32); + uint64_t f = absl::base_internal::UnalignedLoad64(ptr + 40); + uint64_t g = absl::base_internal::UnalignedLoad64(ptr + 48); + uint64_t h = absl::base_internal::UnalignedLoad64(ptr + 56); + + uint64_t cs0 = Mix(a ^ salt[1], b ^ current_state); + uint64_t cs1 = Mix(c ^ salt[2], d ^ current_state); + current_state = (cs0 ^ cs1); + + uint64_t ds0 = Mix(e ^ salt[3], f ^ duplicated_state); + uint64_t ds1 = Mix(g ^ salt[4], h ^ duplicated_state); + duplicated_state = (ds0 ^ ds1); + + ptr += 64; + len -= 64; + } while (len > 64); + + current_state = current_state ^ duplicated_state; + } + + // We now have a data `ptr` with at most 64 bytes and the current state + // of the hashing state machine stored in current_state. + while (len > 16) { + uint64_t a = absl::base_internal::UnalignedLoad64(ptr); + uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); + + current_state = Mix(a ^ salt[1], b ^ current_state); + + ptr += 16; + len -= 16; + } + + // We now have a data `ptr` with at most 16 bytes. + uint64_t a = 0; + uint64_t b = 0; + if (len > 8) { + // When we have at least 9 and at most 16 bytes, set A to the first 64 + // bits of the input and B to the last 64 bits of the input. Yes, they will + // overlap in the middle if we are working with less than the full 16 + // bytes. + a = absl::base_internal::UnalignedLoad64(ptr); + b = absl::base_internal::UnalignedLoad64(ptr + len - 8); + } else if (len > 3) { + // If we have at least 4 and at most 8 bytes, set A to the first 32 + // bits and B to the last 32 bits. + a = absl::base_internal::UnalignedLoad32(ptr); + b = absl::base_internal::UnalignedLoad32(ptr + len - 4); + } else if (len > 0) { + // If we have at least 1 and at most 3 bytes, read all of the provided + // bits into A, with some adjustments. + a = ((ptr[0] << 16) | (ptr[len >> 1] << 8) | ptr[len - 1]); + b = 0; + } else { + a = 0; + b = 0; + } + + uint64_t w = Mix(a ^ salt[1], b ^ current_state); + uint64_t z = salt[1] ^ starting_length; + return Mix(w, z); +} + +} // namespace hash_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/hash/internal/low_level_hash.h b/third_party/abseil-cpp/absl/hash/internal/low_level_hash.h new file mode 100644 index 0000000000..439968aa98 --- /dev/null +++ b/third_party/abseil-cpp/absl/hash/internal/low_level_hash.h @@ -0,0 +1,50 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// This file provides the Google-internal implementation of LowLevelHash. +// +// LowLevelHash is a fast hash function for hash tables, the fastest we've +// currently (late 2020) found that passes the SMHasher tests. The algorithm +// relies on intrinsic 128-bit multiplication for speed. This is not meant to be +// secure - just fast. +// +// It is closely based on a version of wyhash, but does not maintain or +// guarantee future compatibility with it. + +#ifndef ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_ +#define ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_ + +#include <stdint.h> +#include <stdlib.h> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace hash_internal { + +// Hash function for a byte array. A 64-bit seed and a set of five 64-bit +// integers are hashed into the result. +// +// To allow all hashable types (including string_view and Span) to depend on +// this algorithm, we keep the API low-level, with as few dependencies as +// possible. +uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, + const uint64_t salt[5]); + +} // namespace hash_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_HASH_INTERNAL_LOW_LEVEL_HASH_H_ diff --git a/third_party/abseil-cpp/absl/hash/internal/low_level_hash_test.cc b/third_party/abseil-cpp/absl/hash/internal/low_level_hash_test.cc new file mode 100644 index 0000000000..ae930b349b --- /dev/null +++ b/third_party/abseil-cpp/absl/hash/internal/low_level_hash_test.cc @@ -0,0 +1,580 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/hash/internal/low_level_hash.h" + +#include <cinttypes> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/escaping.h" + +#define UPDATE_GOLDEN 0 + +namespace { + +static const uint64_t kSalt[5] = {0xa0761d6478bd642f, 0xe7037ed1a0b428dbl, + 0x8ebc6af09c88c6e3, 0x589965cc75374cc3l, + 0x1d8e4e27c47d124f}; + +TEST(LowLevelHashTest, VerifyGolden) { + constexpr size_t kNumGoldenOutputs = 134; + static struct { + absl::string_view base64_data; + uint64_t seed; + } cases[] = { + {"", uint64_t{0xec42b7ab404b8acb}}, + {"ICAg", uint64_t{0}}, + {"YWFhYQ==", uint64_t{0}}, + {"AQID", uint64_t{0}}, + {"AQIDBA==", uint64_t{0}}, + {"dGhpcmRfcGFydHl8d3loYXNofDY0", uint64_t{0}}, + {"Zw==", uint64_t{0xeeee074043a3ee0f}}, + {"xmk=", uint64_t{0x857902089c393de}}, + {"c1H/", uint64_t{0x993df040024ca3af}}, + {"SuwpzQ==", uint64_t{0xc4e4c2acea740e96}}, + {"uqvy++M=", uint64_t{0x6a214b3db872d0cf}}, + {"RnzCVPgb", uint64_t{0x44343db6a89dba4d}}, + {"6OeNdlouYw==", uint64_t{0x77b5d6d1ae1dd483}}, + {"M5/JmmYyDbc=", uint64_t{0x89ab8ecb44d221f1}}, + {"MVijWiVdBRdY", uint64_t{0x60244b17577ca81b}}, + {"6V7Uq7LNxpu0VA==", uint64_t{0x59a08dcee0717067}}, + {"EQ6CdEEhPdyHcOk=", uint64_t{0xf5f20db3ade57396}}, + {"PqFB4fxnPgF+l+rc", uint64_t{0xbf8dee0751ad3efb}}, + {"a5aPOFwq7LA7+zKvPA==", uint64_t{0x6b7a06b268d63e30}}, + {"VOwY21wCGv5D+/qqOvs=", uint64_t{0xb8c37f0ae0f54c82}}, + {"KdHmBTx8lHXYvmGJ+Vy7", uint64_t{0x9fcbed0c38e50eef}}, + {"qJkPlbHr8bMF7/cA6aE65Q==", uint64_t{0x2af4bade1d8e3a1d}}, + {"ygvL0EhHZL0fIx6oHHtkxRQ=", uint64_t{0x714e3aa912da2f2c}}, + {"c1rFXkt5YztwZCQRngncqtSs", uint64_t{0xf5ee75e3cbb82c1c}}, + {"8hsQrzszzeNQSEcVXLtvIhm6mw==", uint64_t{0x620e7007321b93b9}}, + {"ffUL4RocfyP4KfikGxO1yk7omDI=", uint64_t{0xc08528cac2e551fc}}, + {"OOB5TT00vF9Od/rLbAWshiErqhpV", uint64_t{0x6a1debf9cc3ad39}}, + {"or5wtXM7BFzTNpSzr+Lw5J5PMhVJ/Q==", uint64_t{0x7e0a3c88111fc226}}, + {"gk6pCHDUsoopVEiaCrzVDhioRKxb844=", uint64_t{0x1301fef15df39edb}}, + {"TNctmwlC5QbEM6/No4R/La3UdkfeMhzs", uint64_t{0x64e181f3d5817ab}}, + {"SsQw9iAjhWz7sgcE9OwLuSC6hsM+BfHs2Q==", uint64_t{0xafafc44961078ecb}}, + {"ZzO3mVCj4xTT2TT3XqDyEKj2BZQBvrS8RHg=", uint64_t{0x4f7bb45549250094}}, + {"+klp5iPQGtppan5MflEls0iEUzqU+zGZkDJX", uint64_t{0xa30061abaa2818c}}, + {"RO6bvOnlJc8I9eniXlNgqtKy0IX6VNg16NRmgg==", + uint64_t{0xd902ee3e44a5705f}}, + {"ZJjZqId1ZXBaij9igClE3nyliU5XWdNRrayGlYA=", uint64_t{0x316d36da516f583}}, + {"7BfkhfGMDGbxfMB8uyL85GbaYQtjr2K8g7RpLzr/", + uint64_t{0x402d83f9f834f616}}, + {"rycWk6wHH7htETQtje9PidS2YzXBx+Qkg2fY7ZYS7A==", + uint64_t{0x9c604164c016b72c}}, + {"RTkC2OUK+J13CdGllsH0H5WqgspsSa6QzRZouqx6pvI=", + uint64_t{0x3f4507e01f9e73ba}}, + {"tKjKmbLCNyrLCM9hycOAXm4DKNpM12oZ7dLTmUx5iwAi", + uint64_t{0xc3fe0d5be8d2c7c7}}, + {"VprUGNH+5NnNRaORxgH/ySrZFQFDL+4VAodhfBNinmn8cg==", + uint64_t{0x531858a40bfa7ea1}}, + {"gc1xZaY+q0nPcUvOOnWnT3bqfmT/geth/f7Dm2e/DemMfk4=", + uint64_t{0x86689478a7a7e8fa}}, + {"Mr35fIxqx1ukPAL0su1yFuzzAU3wABCLZ8+ZUFsXn47UmAph", + uint64_t{0x4ec948b8e7f27288}}, + {"A9G8pw2+m7+rDtWYAdbl8tb2fT7FFo4hLi2vAsa5Y8mKH3CX3g==", + uint64_t{0xce46c7213c10032}}, + {"DFaJGishGwEHDdj9ixbCoaTjz9KS0phLNWHVVdFsM93CvPft3hM=", + uint64_t{0xf63e96ee6f32a8b6}}, + {"7+Ugx+Kr3aRNgYgcUxru62YkTDt5Hqis+2po81hGBkcrJg4N0uuy", + uint64_t{0x1cfe85e65fc5225}}, + {"H2w6O8BUKqu6Tvj2xxaecxEI2wRgIgqnTTG1WwOgDSINR13Nm4d4Vg==", + uint64_t{0x45c474f1cee1d2e8}}, + {"1XBMnIbqD5jy65xTDaf6WtiwtdtQwv1dCVoqpeKj+7cTR1SaMWMyI04=", + uint64_t{0x6e024e14015f329c}}, + {"znZbdXG2TSFrKHEuJc83gPncYpzXGbAebUpP0XxzH0rpe8BaMQ17nDbt", + uint64_t{0x760c40502103ae1c}}, + {"ylu8Atu13j1StlcC1MRMJJXIl7USgDDS22HgVv0WQ8hx/8pNtaiKB17hCQ==", + uint64_t{0x17fd05c3c560c320}}, + {"M6ZVVzsd7vAvbiACSYHioH/440dp4xG2mLlBnxgiqEvI/aIEGpD0Sf4VS0g=", + uint64_t{0x8b34200a6f8e90d9}}, + {"li3oFSXLXI+ubUVGJ4blP6mNinGKLHWkvGruun85AhVn6iuMtocbZPVhqxzn", + uint64_t{0x6be89e50818bdf69}}, + {"kFuQHuUCqBF3Tc3hO4dgdIp223ShaCoog48d5Do5zMqUXOh5XpGK1t5XtxnfGA==", + uint64_t{0xfb389773315b47d8}}, + {"jWmOad0v0QhXVJd1OdGuBZtDYYS8wBVHlvOeTQx9ZZnm8wLEItPMeihj72E0nWY=", + uint64_t{0x4f2512a23f61efee}}, + {"z+DHU52HaOQdW4JrZwDQAebEA6rm13Zg/9lPYA3txt3NjTBqFZlOMvTRnVzRbl23", + uint64_t{0x59ccd92fc16c6fda}}, + {"MmBiGDfYeTayyJa/tVycg+rN7f9mPDFaDc+23j0TlW9094er0ADigsl4QX7V3gG/qw==", + uint64_t{0x25c5a7f5bd330919}}, + {"774RK+9rOL4iFvs1q2qpo/JVc/I39buvNjqEFDtDvyoB0FXxPI2vXqOrk08VPfIHkmU=", + uint64_t{0x51df4174d34c97d7}}, + {"+slatXiQ7/2lK0BkVUI1qzNxOOLP3I1iK6OfHaoxgqT63FpzbElwEXSwdsryq3UlHK0I", + uint64_t{0x80ce6d76f89cb57}}, + {"64mVTbQ47dHjHlOHGS/hjJwr/" + "K2frCNpn87exOqMzNUVYiPKmhCbfS7vBUce5tO6Ec9osQ==", + uint64_t{0x20961c911965f684}}, + {"fIsaG1r530SFrBqaDj1kqE0AJnvvK8MNEZbII2Yw1OK77v0V59xabIh0B5axaz/" + "+a2V5WpA=", + uint64_t{0x4e5b926ec83868e7}}, + {"PGih0zDEOWCYGxuHGDFu9Ivbff/" + "iE7BNUq65tycTR2R76TerrXALRosnzaNYO5fjFhTi+CiS", + uint64_t{0x3927b30b922eecef}}, + {"RnpA/" + "zJnEnnLjmICORByRVb9bCOgxF44p3VMiW10G7PvW7IhwsWajlP9kIwNA9FjAD2GoQHk2Q=" + "=", + uint64_t{0xbd0291284a49b61c}}, + {"qFklMceaTHqJpy2qavJE+EVBiNFOi6OxjOA3LeIcBop1K7w8xQi3TrDk+" + "BrWPRIbfprszSaPfrI=", + uint64_t{0x73a77c575bcc956}}, + {"cLbfUtLl3EcQmITWoTskUR8da/VafRDYF/ylPYwk7/" + "zazk6ssyrzxMN3mmSyvrXR2yDGNZ3WDrTT", + uint64_t{0x766a0e2ade6d09a6}}, + {"s/" + "Jf1+" + "FbsbCpXWPTUSeWyMH6e4CvTFvPE5Fs6Z8hvFITGyr0dtukHzkI84oviVLxhM1xMxrMAy1db" + "w==", + uint64_t{0x2599f4f905115869}}, + {"FvyQ00+j7nmYZVQ8hI1Edxd0AWplhTfWuFGiu34AK5X8u2hLX1bE97sZM0CmeLe+" + "7LgoUT1fJ/axybE=", + uint64_t{0xd8256e5444d21e53}}, + {"L8ncxMaYLBH3g9buPu8hfpWZNlOF7nvWLNv9IozH07uQsIBWSKxoPy8+" + "LW4tTuzC6CIWbRGRRD1sQV/4", + uint64_t{0xf664a91333fb8dfd}}, + {"CDK0meI07yrgV2kQlZZ+" + "wuVqhc2NmzqeLH7bmcA6kchsRWFPeVF5Wqjjaj556ABeUoUr3yBmfU3kWOakkg==", + uint64_t{0x9625b859be372cd1}}, + {"d23/vc5ONh/" + "HkMiq+gYk4gaCNYyuFKwUkvn46t+dfVcKfBTYykr4kdvAPNXGYLjM4u1YkAEFpJP+" + "nX7eOvs=", + uint64_t{0x7b99940782e29898}}, + {"NUR3SRxBkxTSbtQORJpu/GdR6b/h6sSGfsMj/KFd99ahbh+9r7LSgSGmkGVB/" + "mGoT0pnMTQst7Lv2q6QN6Vm", + uint64_t{0x4fe12fa5383b51a8}}, + {"2BOFlcI3Z0RYDtS9T9Ie9yJoXlOdigpPeeT+CRujb/" + "O39Ih5LPC9hP6RQk1kYESGyaLZZi3jtabHs7DiVx/VDg==", + uint64_t{0xe2ccb09ac0f5b4b6}}, + {"FF2HQE1FxEvWBpg6Z9zAMH+Zlqx8S1JD/" + "wIlViL6ZDZY63alMDrxB0GJQahmAtjlm26RGLnjW7jmgQ4Ie3I+014=", + uint64_t{0x7d0a37adbd7b753b}}, + {"tHmO7mqVL/PX11nZrz50Hc+M17Poj5lpnqHkEN+4bpMx/" + "YGbkrGOaYjoQjgmt1X2QyypK7xClFrjeWrCMdlVYtbW", + uint64_t{0xd3ae96ef9f7185f2}}, + {"/WiHi9IQcxRImsudkA/KOTqGe8/" + "gXkhKIHkjddv5S9hi02M049dIK3EUyAEjkjpdGLUs+BN0QzPtZqjIYPOgwsYE9g==", + uint64_t{0x4fb88ea63f79a0d8}}, + {"qds+1ExSnU11L4fTSDz/QE90g4Jh6ioqSh3KDOTOAo2pQGL1k/" + "9CCC7J23YF27dUTzrWsCQA2m4epXoCc3yPHb3xElA=", + uint64_t{0xed564e259bb5ebe9}}, + {"8FVYHx40lSQPTHheh08Oq0/" + "pGm2OlG8BEf8ezvAxHuGGdgCkqpXIueJBF2mQJhTfDy5NncO8ntS7vaKs7sCNdDaNGOEi", + uint64_t{0x3e3256b60c428000}}, + {"4ZoEIrJtstiCkeew3oRzmyJHVt/pAs2pj0HgHFrBPztbQ10NsQ/" + "lM6DM439QVxpznnBSiHMgMQJhER+70l72LqFTO1JiIQ==", + uint64_t{0xfb05bad59ec8705}}, + {"hQPtaYI+wJyxXgwD5n8jGIKFKaFA/" + "P83KqCKZfPthnjwdOFysqEOYwAaZuaaiv4cDyi9TyS8hk5cEbNP/jrI7q6pYGBLbsM=", + uint64_t{0xafdc251dbf97b5f8}}, + {"S4gpMSKzMD7CWPsSfLeYyhSpfWOntyuVZdX1xSBjiGvsspwOZcxNKCRIOqAA0moUfOh3I5+" + "juQV4rsqYElMD/gWfDGpsWZKQ", + uint64_t{0x10ec9c92ddb5dcbc}}, + {"oswxop+" + "bthuDLT4j0PcoSKby4LhF47ZKg8K17xxHf74UsGCzTBbOz0MM8hQEGlyqDT1iUiAYnaPaUp" + "L2mRK0rcIUYA4qLt5uOw==", + uint64_t{0x9a767d5822c7dac4}}, + {"0II/" + "697p+" + "BtLSjxj5989OXI004TogEb94VUnDzOVSgMXie72cuYRvTFNIBgtXlKfkiUjeqVpd4a+" + "n5bxNOD1TGrjQtzKU5r7obo=", + uint64_t{0xee46254080d6e2db}}, + {"E84YZW2qipAlMPmctrg7TKlwLZ68l4L+c0xRDUfyyFrA4MAti0q9sHq3TDFviH0Y+" + "Kq3tEE5srWFA8LM9oomtmvm5PYxoaarWPLc", + uint64_t{0xbbb669588d8bf398}}, + {"x3pa4HIElyZG0Nj7Vdy9IdJIR4izLmypXw5PCmZB5y68QQ4uRaVVi3UthsoJROvbjDJkP2D" + "Q6L/eN8pFeLFzNPKBYzcmuMOb5Ull7w==", + uint64_t{0xdc2afaa529beef44}}, + {"jVDKGYIuWOP/" + "QKLdd2wi8B2VJA8Wh0c8PwrXJVM8FOGM3voPDVPyDJOU6QsBDPseoR8uuKd19OZ/" + "zAvSCB+zlf6upAsBlheUKgCfKww=", + uint64_t{0xf1f67391d45013a8}}, + {"mkquunhmYe1aR2wmUz4vcvLEcKBoe6H+kjUok9VUn2+eTSkWs4oDDtJvNCWtY5efJwg/" + "j4PgjRYWtqnrCkhaqJaEvkkOwVfgMIwF3e+d", + uint64_t{0x16fce2b8c65a3429}}, + {"fRelvKYonTQ+s+rnnvQw+JzGfFoPixtna0vzcSjiDqX5s2Kg2//" + "UGrK+AVCyMUhO98WoB1DDbrsOYSw2QzrcPe0+3ck9sePvb+Q/IRaHbw==", + uint64_t{0xf4b096699f49fe67}}, + {"DUwXFJzagljo44QeJ7/" + "6ZKw4QXV18lhkYT2jglMr8WB3CHUU4vdsytvw6AKv42ZcG6fRkZkq9fpnmXy6xG0aO3WPT1" + "eHuyFirAlkW+zKtwg=", + uint64_t{0xca584c4bc8198682}}, + {"cYmZCrOOBBongNTr7e4nYn52uQUy2mfe48s50JXx2AZ6cRAt/" + "xRHJ5QbEoEJOeOHsJyM4nbzwFm++SlT6gFZZHJpkXJ92JkR86uS/eV1hJUR", + uint64_t{0xed269fc3818b6aad}}, + {"EXeHBDfhwzAKFhsMcH9+2RHwV+mJaN01+9oacF6vgm8mCXRd6jeN9U2oAb0of5c5cO4i+" + "Vb/LlHZSMI490SnHU0bejhSCC2gsC5d2K30ER3iNA==", + uint64_t{0x33f253cbb8fe66a8}}, + {"FzkzRYoNjkxFhZDso94IHRZaJUP61nFYrh5MwDwv9FNoJ5jyNCY/" + "eazPZk+tbmzDyJIGw2h3GxaWZ9bSlsol/vK98SbkMKCQ/wbfrXRLcDzdd/8=", + uint64_t{0xd0b76b2c1523d99c}}, + {"Re4aXISCMlYY/XsX7zkIFR04ta03u4zkL9dVbLXMa/q6hlY/CImVIIYRN3VKP4pnd0AUr/" + "ugkyt36JcstAInb4h9rpAGQ7GMVOgBniiMBZ/MGU7H", + uint64_t{0xfd28f0811a2a237f}}, + {"ueLyMcqJXX+MhO4UApylCN9WlTQ+" + "ltJmItgG7vFUtqs2qNwBMjmAvr5u0sAKd8jpzV0dDPTwchbIeAW5zbtkA2NABJV6hFM48ib" + "4/J3A5mseA3cS8w==", + uint64_t{0x6261fb136482e84}}, + {"6Si7Yi11L+jZMkwaN+GUuzXMrlvEqviEkGOilNq0h8TdQyYKuFXzkYc/" + "q74gP3pVCyiwz9KpVGMM9vfnq36riMHRknkmhQutxLZs5fbmOgEO69HglCU=", + uint64_t{0x458efc750bca7c3a}}, + {"Q6AbOofGuTJOegPh9Clm/" + "9crtUMQqylKrTc1fhfJo1tqvpXxhU4k08kntL1RG7woRnFrVh2UoMrL1kjin+s9CanT+" + "y4hHwLqRranl9FjvxfVKm3yvg68", + uint64_t{0xa7e69ff84e5e7c27}}, + {"ieQEbIPvqY2YfIjHnqfJiO1/MIVRk0RoaG/WWi3kFrfIGiNLCczYoklgaecHMm/" + "1sZ96AjO+a5stQfZbJQwS7Sc1ODABEdJKcTsxeW2hbh9A6CFzpowP1A==", + uint64_t{0x3c59bfd0c29efe9e}}, + {"zQUv8hFB3zh2GGl3KTvCmnfzE+" + "SUgQPVaSVIELFX5H9cE3FuVFGmymkPQZJLAyzC90Cmi8GqYCvPqTuAAB//" + "XTJxy4bCcVArgZG9zJXpjowpNBfr3ngWrSE=", + uint64_t{0x10befacc6afd298d}}, + {"US4hcC1+op5JKGC7eIs8CUgInjKWKlvKQkapulxW262E/" + "B2ye79QxOexf188u2mFwwe3WTISJHRZzS61IwljqAWAWoBAqkUnW8SHmIDwHUP31J0p5sGd" + "P47L", + uint64_t{0x41d5320b0a38efa7}}, + {"9bHUWFna2LNaGF6fQLlkx1Hkt24nrkLE2CmFdWgTQV3FFbUe747SSqYw6ebpTa07MWSpWRP" + "sHesVo2B9tqHbe7eQmqYebPDFnNqrhSdZwFm9arLQVs+7a3Ic6A==", + uint64_t{0x58db1c7450fe17f3}}, + {"Kb3DpHRUPhtyqgs3RuXjzA08jGb59hjKTOeFt1qhoINfYyfTt2buKhD6YVffRCPsgK9SeqZ" + "qRPJSyaqsa0ovyq1WnWW8jI/NhvAkZTVHUrX2pC+cD3OPYT05Dag=", + uint64_t{0x6098c055a335b7a6}}, + {"gzxyMJIPlU+bJBwhFUCHSofZ/" + "319LxqMoqnt3+L6h2U2+ZXJCSsYpE80xmR0Ta77Jq54o92SMH87HV8dGOaCTuAYF+" + "lDL42SY1P316Cl0sZTS2ow3ZqwGbcPNs/1", + uint64_t{0x1bbacec67845a801}}, + {"uR7V0TW+FGVMpsifnaBAQ3IGlr1wx5sKd7TChuqRe6OvUXTlD4hKWy8S+" + "8yyOw8lQabism19vOQxfmocEOW/" + "vzY0pEa87qHrAZy4s9fH2Bltu8vaOIe+agYohhYORQ==", + uint64_t{0xc419cfc7442190}}, + {"1UR5eoo2aCwhacjZHaCh9bkOsITp6QunUxHQ2SfeHv0imHetzt/" + "Z70mhyWZBalv6eAx+YfWKCUib2SHDtz/" + "A2dc3hqUWX5VfAV7FQsghPUAtu6IiRatq4YSLpDvKZBQ=", + uint64_t{0xc95e510d94ba270c}}, + {"opubR7H63BH7OtY+Avd7QyQ25UZ8kLBdFDsBTwZlY6gA/" + "u+x+" + "czC9AaZMgmQrUy15DH7YMGsvdXnviTtI4eVI4aF1H9Rl3NXMKZgwFOsdTfdcZeeHVRzBBKX" + "8jUfh1il", + uint64_t{0xff1ae05c98089c3f}}, + {"DC0kXcSXtfQ9FbSRwirIn5tgPri0sbzHSa78aDZVDUKCMaBGyFU6BmrulywYX8yzvwprdLs" + "oOwTWN2wMjHlPDqrvVHNEjnmufRDblW+nSS+xtKNs3N5xsxXdv6JXDrAB/Q==", + uint64_t{0x90c02b8dceced493}}, + {"BXRBk+3wEP3Lpm1y75wjoz+PgB0AMzLe8tQ1AYU2/" + "oqrQB2YMC6W+9QDbcOfkGbeH+b7IBkt/" + "gwCMw2HaQsRFEsurXtcQ3YwRuPz5XNaw5NAvrNa67Fm7eRzdE1+hWLKtA8=", + uint64_t{0x9f8a76697ab1aa36}}, + {"RRBSvEGYnzR9E45Aps/+WSnpCo/X7gJLO4DRnUqFrJCV/kzWlusLE/" + "6ZU6RoUf2ROwcgEvUiXTGjLs7ts3t9SXnJHxC1KiOzxHdYLMhVvgNd3hVSAXODpKFSkVXND" + "55G2L1W", + uint64_t{0x6ba1bf3d811a531d}}, + {"jeh6Qazxmdi57pa9S3XSnnZFIRrnc6s8QLrah5OX3SB/V2ErSPoEAumavzQPkdKF1/" + "SfvmdL+qgF1C+Yawy562QaFqwVGq7+tW0yxP8FStb56ZRgNI4IOmI30s1Ei7iops9Uuw==", + uint64_t{0x6a418974109c67b4}}, + {"6QO5nnDrY2/" + "wrUXpltlKy2dSBcmK15fOY092CR7KxAjNfaY+" + "aAmtWbbzQk3MjBg03x39afSUN1fkrWACdyQKRaGxgwq6MGNxI6W+8DLWJBHzIXrntrE/" + "ml6fnNXEpxplWJ1vEs4=", + uint64_t{0x8472f1c2b3d230a3}}, + {"0oPxeEHhqhcFuwonNfLd5jF3RNATGZS6NPoS0WklnzyokbTqcl4BeBkMn07+fDQv83j/" + "BpGUwcWO05f3+DYzocfnizpFjLJemFGsls3gxcBYxcbqWYev51tG3lN9EvRE+X9+Pwww", + uint64_t{0x5e06068f884e73a7}}, + {"naSBSjtOKgAOg8XVbR5cHAW3Y+QL4Pb/JO9/" + "oy6L08wvVRZqo0BrssMwhzBP401Um7A4ppAupbQeJFdMrysY34AuSSNvtNUy5VxjNECwiNt" + "gwYHw7yakDUv8WvonctmnoSPKENegQg==", + uint64_t{0x55290b1a8f170f59}}, + {"vPyl8DxVeRe1OpilKb9KNwpGkQRtA94UpAHetNh+" + "95V7nIW38v7PpzhnTWIml5kw3So1Si0TXtIUPIbsu32BNhoH7QwFvLM+" + "JACgSpc5e3RjsL6Qwxxi11npwxRmRUqATDeMUfRAjxg=", + uint64_t{0x5501cfd83dfe706a}}, + {"QC9i2GjdTMuNC1xQJ74ngKfrlA4w3o58FhvNCltdIpuMhHP1YsDA78scQPLbZ3OCUgeQguY" + "f/vw6zAaVKSgwtaykqg5ka/4vhz4hYqWU5ficdXqClHl+zkWEY26slCNYOM5nnDlly8Cj", + uint64_t{0xe43ed13d13a66990}}, + {"7CNIgQhAHX27nxI0HeB5oUTnTdgKpRDYDKwRcXfSFGP1XeT9nQF6WKCMjL1tBV6x7KuJ91G" + "Zz11F4c+8s+MfqEAEpd4FHzamrMNjGcjCyrVtU6y+7HscMVzr7Q/" + "ODLcPEFztFnwjvCjmHw==", + uint64_t{0xdf43bc375cf5283f}}, + {"Qa/hC2RPXhANSospe+gUaPfjdK/yhQvfm4cCV6/pdvCYWPv8p1kMtKOX3h5/" + "8oZ31fsmx4Axphu5qXJokuhZKkBUJueuMpxRyXpwSWz2wELx5glxF7CM0Fn+" + "OevnkhUn5jsPlG2r5jYlVn8=", + uint64_t{0x8112b806d288d7b5}}, + {"kUw/0z4l3a89jTwN5jpG0SHY5km/" + "IVhTjgM5xCiPRLncg40aqWrJ5vcF891AOq5hEpSq0bUCJUMFXgct7kvnys905HjerV7Vs1G" + "y84tgVJ70/2+pAZTsB/PzNOE/G6sOj4+GbTzkQu819OLB", + uint64_t{0xd52a18abb001cb46}}, + {"VDdfSDbO8Tdj3T5W0XM3EI7iHh5xpIutiM6dvcJ/fhe23V/srFEkDy5iZf/" + "VnA9kfi2C79ENnFnbOReeuZW1b3MUXB9lgC6U4pOTuC+" + "jHK3Qnpyiqzj7h3ISJSuo2pob7vY6VHZo6Fn7exEqHg==", + uint64_t{0xe12b76a2433a1236}}, + {"Ldfvy3ORdquM/R2fIkhH/ONi69mcP1AEJ6n/" + "oropwecAsLJzQSgezSY8bEiEs0VnFTBBsW+RtZY6tDj03fnb3amNUOq1b7jbqyQkL9hpl+" + "2Z2J8IaVSeownWl+bQcsR5/xRktIMckC5AtF4YHfU=", + uint64_t{0x175bf7319cf1fa00}}, + {"BrbNpb42+" + "VzZAjJw6QLirXzhweCVRfwlczzZ0VX2xluskwBqyfnGovz5EuX79JJ31VNXa5hTkAyQat3l" + "YKRADTdAdwE5PqM1N7YaMqqsqoAAAeuYVXuk5eWCykYmClNdSspegwgCuT+403JigBzi", + uint64_t{0xd63d57b3f67525ae}}, + {"gB3NGHJJvVcuPyF0ZSvHwnWSIfmaI7La24VMPQVoIIWF7Z74NltPZZpx2f+cocESM+" + "ILzQW9p+BC8x5IWz7N4Str2WLGKMdgmaBfNkEhSHQDU0IJEOnpUt0HmjhFaBlx0/" + "LTmhua+rQ6Wup8ezLwfg==", + uint64_t{0x933faea858832b73}}, + {"hTKHlRxx6Pl4gjG+6ksvvj0CWFicUg3WrPdSJypDpq91LUWRni2KF6+" + "81ZoHBFhEBrCdogKqeK+hy9bLDnx7g6rAFUjtn1+cWzQ2YjiOpz4+" + "ROBB7lnwjyTGWzJD1rXtlso1g2qVH8XJVigC5M9AIxM=", + uint64_t{0x53d061e5f8e7c04f}}, + {"IWQBelSQnhrr0F3BhUpXUIDauhX6f95Qp+A0diFXiUK7irwPG1oqBiqHyK/SH/" + "9S+" + "rln9DlFROAmeFdH0OCJi2tFm4afxYzJTFR4HnR4cG4x12JqHaZLQx6iiu6CE3rtWBVz99oA" + "wCZUOEXIsLU24o2Y", + uint64_t{0xdb4124556dd515e0}}, + {"TKo+l+" + "1dOXdLvIrFqeLaHdm0HZnbcdEgOoLVcGRiCbAMR0j5pIFw8D36tefckAS1RCFOH5IgP8yiF" + "T0Gd0a2hI3+" + "fTKA7iK96NekxWeoeqzJyctc6QsoiyBlkZerRxs5RplrxoeNg29kKDTM0K94mnhD9g==", + uint64_t{0x4fb31a0dd681ee71}}, + {"YU4e7G6EfQYvxCFoCrrT0EFgVLHFfOWRTJQJ5gxM3G2b+" + "1kJf9YPrpsxF6Xr6nYtS8reEEbDoZJYqnlk9lXSkVArm88Cqn6d25VCx3+" + "49MqC0trIlXtb7SXUUhwpJK16T0hJUfPH7s5cMZXc6YmmbFuBNPE=", + uint64_t{0x27cc72eefa138e4c}}, + {"/I/" + "eImMwPo1U6wekNFD1Jxjk9XQVi1D+" + "FPdqcHifYXQuP5aScNQfxMAmaPR2XhuOQhADV5tTVbBKwCDCX4E3jcDNHzCiPvViZF1W27t" + "xaf2BbFQdwKrNCmrtzcluBFYu0XZfc7RU1RmxK/RtnF1qHsq/O4pp", + uint64_t{0x44bc2dfba4bd3ced}}, + {"CJTT9WGcY2XykTdo8KodRIA29qsqY0iHzWZRjKHb9alwyJ7RZAE3V5Juv4MY3MeYEr1EPCC" + "MxO7yFXqT8XA8YTjaMp3bafRt17Pw8JC4iKJ1zN+WWKOESrj+" + "3aluGQqn8z1EzqY4PH7rLG575PYeWsP98BugdA==", + uint64_t{0x242da1e3a439bed8}}, + {"ZlhyQwLhXQyIUEnMH/" + "AEW27vh9xrbNKJxpWGtrEmKhd+nFqAfbeNBQjW0SfG1YI0xQkQMHXjuTt4P/" + "EpZRtA47ibZDVS8TtaxwyBjuIDwqcN09eCtpC+Ls+" + "vWDTLmBeDM3u4hmzz4DQAYsLiZYSJcldg9Q3wszw=", + uint64_t{0xdc559c746e35c139}}, + {"v2KU8y0sCrBghmnm8lzGJlwo6D6ObccAxCf10heoDtYLosk4ztTpLlpSFEyu23MLA1tJkcg" + "Rko04h19QMG0mOw/" + "wc93EXAweriBqXfvdaP85sZABwiKO+6rtS9pacRVpYYhHJeVTQ5NzrvBvi1huxAr+" + "xswhVMfL", + uint64_t{0xd0b0350275b9989}}, + {"QhKlnIS6BuVCTQsnoE67E/" + "yrgogE8EwO7xLaEGei26m0gEU4OksefJgppDh3X0x0Cs78Dr9IHK5b977CmZlrTRmwhlP8p" + "M+UzXPNRNIZuN3ntOum/QhUWP8SGpirheXENWsXMQ/" + "nxtxakyEtrNkKk471Oov9juP8oQ==", + uint64_t{0xb04489e41d17730c}}, + {"/ZRMgnoRt+Uo6fUPr9FqQvKX7syhgVqWu+" + "WUSsiQ68UlN0efSP6Eced5gJZL6tg9gcYJIkhjuQNITU0Q3TjVAnAcobgbJikCn6qZ6pRxK" + "BY4MTiAlfGD3T7R7hwJwx554MAy++Zb/YUFlnCaCJiwQMnowF7aQzwYFCo=", + uint64_t{0x2217285eb4572156}}, + {"NB7tU5fNE8nI+SXGfipc7sRkhnSkUF1krjeo6k+8FITaAtdyz+" + "o7mONgXmGLulBPH9bEwyYhKNVY0L+njNQrZ9YC2aXsFD3PdZsxAFaBT3VXEzh+" + "NGBTjDASNL3mXyS8Yv1iThGfHoY7T4aR0NYGJ+k+pR6f+KrPC96M", + uint64_t{0x12c2e8e68aede73b}}, + {"8T6wrqCtEO6/rwxF6lvMeyuigVOLwPipX/FULvwyu+1wa5sQGav/" + "2FsLHUVn6cGSi0LlFwLewGHPFJDLR0u4t7ZUyM//" + "x6da0sWgOa5hzDqjsVGmjxEHXiaXKW3i4iSZNuxoNbMQkIbVML+" + "DkYu9ND0O2swg4itGeVSzXA==", + uint64_t{0x4d612125bdc4fd00}}, + {"Ntf1bMRdondtMv1CYr3G80iDJ4WSAlKy5H34XdGruQiCrnRGDBa+" + "eUi7vKp4gp3BBcVGl8eYSasVQQjn7MLvb3BjtXx6c/" + "bCL7JtpzQKaDnPr9GWRxpBXVxKREgMM7d8lm35EODv0w+" + "hQLfVSh8OGs7fsBb68nNWPLeeSOo=", + uint64_t{0x81826b553954464e}}, + {"VsSAw72Ro6xks02kaiLuiTEIWBC5bgqr4WDnmP8vglXzAhixk7td926rm9jNimL+" + "kroPSygZ9gl63aF5DCPOACXmsbmhDrAQuUzoh9ZKhWgElLQsrqo1KIjWoZT5b5QfVUXY9lS" + "IBg3U75SqORoTPq7HalxxoIT5diWOcJQi", + uint64_t{0xc2e5d345dc0ddd2d}}, + {"j+loZ+C87+" + "bJxNVebg94gU0mSLeDulcHs84tQT7BZM2rzDSLiCNxUedHr1ZWJ9ejTiBa0dqy2I2ABc++" + "xzOLcv+//YfibtjKtYggC6/3rv0XCc7xu6d/" + "O6xO+XOBhOWAQ+IHJVHf7wZnDxIXB8AUHsnjEISKj7823biqXjyP3g==", + uint64_t{0x3da6830a9e32631e}}, + {"f3LlpcPElMkspNtDq5xXyWU62erEaKn7RWKlo540gR6mZsNpK1czV/" + "sOmqaq8XAQLEn68LKj6/" + "cFkJukxRzCa4OF1a7cCAXYFp9+wZDu0bw4y63qbpjhdCl8GO6Z2lkcXy7KOzbPE01ukg7+" + "gN+7uKpoohgAhIwpAKQXmX5xtd0=", + uint64_t{0xc9ae5c8759b4877a}}, + }; + +#if defined(ABSL_IS_BIG_ENDIAN) + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8, + 0xa6630143a7e6aa6f, 0x17645cb7318b86b, 0x218b175f30ba61f8, + 0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d, + 0xe7b01610fc22dbb8, 0x99d9f694404af913, 0xf4eecd37464b45c5, + 0x7d2c653d63596d9b, 0x3f15c8544ec5393a, 0x6b9dc0c1704f796c, + 0xf1ded7a7eae5ed5a, 0x2db2fd7c6dd4641b, 0x151ca2d3d4cd33ab, + 0xa5af5994ac2ccd64, 0x2b2a4ca3191d2fce, 0xf89e68c9364e7c05, + 0x71724c70b799c21, 0x70536fabfd157369, 0xdee92794c3c3082b, + 0xac033a6743d3b3eb, 0xed2956b506cd5151, 0xbd669644755264b6, + 0x6ab1ff5d5f549a63, 0xf6bd551a2e3e04e, 0x7b5a8cef6875ea73, + 0x22bccf4d4db0a91c, 0x4f2bc07754c7c7eb, 0xfb6b8342a86725db, + 0x13a1a0d4c5854da, 0x5f6e44655f7dedac, 0x54a9198dff2bdf85, + 0xdb17e6915d4e4042, 0xa69926cf5c3b89f, 0xf77f031bfd74c096, + 0x1d6f916fdd50ec3c, 0x334ac76013ade393, 0x99370f899111de15, + 0x352457a03ada6de, 0x341974d4f42d854d, 0xda89ab02872aeb5, + 0x6ec2b74e143b10d9, 0x6f284c0b5cd60522, 0xf9670de353438f88, + 0xde920913adf0a2b4, 0xb7a07d7c0c17a8ec, 0x879a69f558ba3a98, + 0x360cf6d802df20f9, 0x53530f8046673738, 0xbd8f5f2bcf35e483, + 0x3f171f047144b983, 0x644d04e820823465, 0x50e44773a20b2702, + 0xe584ed4c05c745dd, 0x9a825c85b95ab6c0, 0xbce2931deb74e775, + 0x10468e9e705c7cfe, 0x12e01de3104141e2, 0x5c11ae2ee3713abd, + 0x6ac5ffb0860319e6, 0xc1e6da1849d30fc9, 0xa0e4d247a458b447, + 0x4530d4615c32b89b, 0x116aa09107a76505, 0xf941339d00d9bb73, + 0x573a0fc1615afb33, 0xa975c81dc868b258, 0x3ab2c5250ab54bda, + 0x37f99f208a3e3b11, 0x4b49b0ff706689d, 0x30bafa0b8f0a87fe, + 0xea6787a65cc20cdd, 0x55861729f1fc3ab8, 0xea38e009c5be9b72, + 0xcb8522cba33c3c66, 0x352e77653fe306f3, 0xe0bb760793bac064, + 0xf66ec59322662956, 0x637aa320455d56f8, 0x46ee546be5824a89, + 0x9e6842421e83d8a4, 0xf98ac2bc96b9fb8c, 0xf2c1002fd9a70b99, + 0x4c2b62b1e39e9405, 0x3248555fa3ade9c4, 0xd4d04c37f6417c21, + 0xf40cd506b1bf5653, 0x6c45d6005c760d2f, 0x61d88a7e61ff0d7e, + 0x131591e8a53cc967, 0xdae85cb9bc29bab6, 0xe98835334905e626, + 0x7cce50a2b66b8754, 0x5b0b3d0c5ac498ae, 0xd35a218c974d1756, + 0xfce436ddc1d003c, 0xd183901de90bb741, 0x9378f8f34974a66, + 0x21f11ae0a0402368, 0xf2fbd7c94ef89cb6, 0xc329c69d0f0d080b, + 0xf2841cba16216a61, 0x47aba97b44916df1, 0x724d4e00a8019fcf, + 0x2df9005c2a728d63, 0xc788892a1a5d7515, 0x9e993a65f9df0480, + 0x76876721ff49f969, 0xbe7a796cfba15bf5, 0xa4c8bd54586f5488, + 0xb390a325275501ab, 0x893f11317427ccf1, 0x92f2bb57da5695b9, + 0x30985b90da88269f, 0x2c690e268e086de8, 0x1c02df6097997196, + 0x1f9778f8bbdf6455, 0x7d57378c7bf8416d, 0xba8582a5f8d84d38, + 0xe8ca43b85050be4e, 0x5048cf6bed8a5d9f, 0xfbc5ba80917d0ea4, + 0x8011026525bf1691, 0x26b8dc6aed9fb50d, 0x191f5bfee77c1fe3, + 0xdd497891465a2cc1, 0x6f1fe8c57a33072e, 0x2c9f4ec078c460c0, + 0x9a725bde8f6a1437, 0x6ce545fa3ef61e4d, + }; +#elif defined(__aarch64__) + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0x45c0aadee165dcbe, 0x25ed8587f6f20d06, 0x5f23ae668ce7926d, + 0xfef74d1da0846719, 0x54478408e68cb7d4, 0xee27ddaf88c6fe68, + 0xb7ac7031e81867ca, 0xf1168f818ec6c36d, 0x1dd0b734a83b019a, + 0xd6ae30d4142b54fe, 0xcd860c721ccb80fb, 0x068acf8493794756, + 0xd4ada0be58681307, 0x13ffe0f64ca540ed, 0xffc1d7a3401aec02, + 0xd81c4d865cf95fb9, 0x1dd0793acede62e0, 0xa6722abbca8fe4cf, + 0x5453d3e4111a7e40, 0xf29b3e3204c9dcd2, 0x23be2980e43117f7, + 0x74e2ccbc286f08eb, 0x19ef7c0f9496003a, 0xbfbf1c3e49b27987, + 0x6e6c179eb4a82c70, 0x07f4e184216bc4fc, 0xf17fbc4254927554, + 0xe57696b70a45b1b6, 0x6d3b144631b320e8, 0xccf8729792c75a2d, + 0xe832495b41fa980b, 0x5c96cfdc7b227d34, 0xc4dca234ef4e43f4, + 0x5fc801abf9abe307, 0xe41e3c5076d88f4d, 0x522346200ddec3c3, + 0x72bed1946fd7aaa4, 0x0ac1f84dcc335f96, 0x3af78db5e0a47670, + 0x6100ebf1481f1caf, 0xf5fd10037fc651a3, 0xa01227d8944665f3, + 0x7217681c4bbc9420, 0x4adee538e3eb10d1, 0x35e1761ad96de9a7, + 0x8b370aef9613bfba, 0x824506f749eeaf59, 0x85e805fa04423991, + 0xb61e9c33283c3de7, 0xc79721bbcb039ed6, 0x04e1c19a3a1e6639, + 0x6aaf6346b68dd638, 0x601a4b496be6d0c4, 0x3ece355f91c41787, + 0xd2fc8998448d7888, 0xd7529804f843efa9, 0xabdcc38a288536aa, + 0xdd323e48a9718648, 0x2090279c0030a52a, 0xe2f90faca88a3cd1, + 0x3e0c4e92fc50e4aa, 0xa26d308798e801dd, 0x432eefeedee8c02e, + 0xca4ce494614b77df, 0xbba82911e838066d, 0x4b00821016adee4b, + 0x4cf6e526dfb5a20f, 0x5b8466495142cba2, 0xe28ac1406e88a68c, + 0x8511e5f9d3100999, 0x05acbfe02798890b, 0x74c249c7ce4a8425, + 0xdbe7468d09bc34bc, 0x11079ab10e3b9b58, 0xb7788dec9032035a, + 0xb7e8daa786513f80, 0x34c3288831f46b45, 0x014cce5f0c21ecc6, + 0xc6a8f7b024551a28, 0x49784e902e207fd8, 0x4720d32af0b55158, + 0x8df3ec5de0c1da00, 0xf4db677b2c9e6853, 0xaa419abea78d312d, + 0x181e0f91bd757443, 0xa8c45136fada083b, 0x91303b93f5f0582c, + 0x883b95c6ddc62a08, 0x93186a8875fe952b, 0xd94f533928e957e2, + 0x6ba343003e10c172, 0xc8623b620c715d6a, 0x8ca0c512e180e244, + 0xdc9b74c2536b6216, 0x8eb5fdc61b295d96, 0x2ad83966b37c95ba, + 0xb90bf154ac5edec9, 0x902cf847b326cfb3, 0x7b02d0c0ca7808ca, + 0x492f310d003ea15f, 0x3eb6497a47c95990, 0x5d46b0ced31428b7, + 0x081afa67d1986157, 0x043482ec286b20eb, 0xc103c8f18c1a2a53, + 0xe8e9995a81481e83, 0x6bb3295822bc90b5, 0xeec75297a3fa5672, + 0x591c8440c4857412, 0x74947f455aaf24ad, 0xcf0e571586ec77a9, + 0x0c2553ea8c0400ad, 0x380219118066255f, 0x7595adb88b15ebe2, + 0xb33c00696c64ae23, 0xa143516ddd7c9857, 0x39179af229248d26, + 0x65d387a6f2ee2079, 0x89f8a9b21cd2f195, 0xbfef032d25df92e6, + 0x6b7e18a36c69da71, 0x4b3b15f6c28974e6, 0x032a75917f6c544c, + 0xe3b97ecca6d287cd, 0xa4a563110d3cda81, 0x35e09e8134f4e7f1, + 0xc9419dd03a9a390e, 0x7b86fae9000fd329, 0x1e044f8d54fe74c3, + 0x9c4991d7a47e9666, 0xfb485f3a1df4fdb6, 0xb11519969eeb94ff, + 0x3224ea1c44caeb8d, 0x86570bbd7cc6b80d, + }; +#else + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0xe5a40d39ab796423, 0x1766974bf7527d81, 0x5c3bbbe230db17a8, + 0xa6630143a7e6aa6f, 0x8787cb2d04b0c984, 0x33603654ff574ac2, + 0xa6564b468248c683, 0xef192f401b116e1c, 0xbe8dc0c54617639d, + 0x93d7f665b5521c8e, 0x646d70bb42445f28, 0x96a7b1e3cc9bd426, + 0x76020289ab0790c4, 0x39f842e4133b9b44, 0x2b8d7047be4bcaab, + 0x99628abef6716a97, 0x4432e02ba42b2740, 0x74d810efcad7918a, + 0x88c84e986002507f, 0x4f99acf193cf39b9, 0xd90e7a3655891e37, + 0x3bb378b1d4df8fcf, 0xf78e94045c052d47, 0x26da0b2130da6b40, + 0x30b4d426af8c6986, 0x5413b4aaf3baaeae, 0x756ab265370a1597, + 0xdaf5f4b7d09814fb, 0x8f874ae37742b75e, 0x8fecd03956121ce8, + 0x229c292ea7a08285, 0x0bb4bf0692d14bae, 0x207b24ca3bdac1db, + 0x64f6cd6745d3825b, 0xa2b2e1656b58df1e, 0x0d01d30d9ee7a148, + 0x1cb4cd00ab804e3b, 0x4697f2637fd90999, 0x8383a756b5688c07, + 0x695c29cb3696a975, 0xda2e5a5a5e971521, 0x7935d4befa056b2b, + 0x38dd541ca95420fe, 0xcc06c7a4963f967f, 0xbf0f6f66e232fb20, + 0xf7efb32d373fe71a, 0xe2e64634b1c12660, 0x285b8fd1638e306d, + 0x658e8a4e3b714d6c, 0xf391fb968e0eb398, 0x744a9ea0cc144bf2, + 0x12636f2be11012f1, 0x29c57de825948f80, 0x58c6f99ab0d1c021, + 0x13e7b5a7b82fe3bb, 0x10fbc87901e02b63, 0xa24c9184901b748b, + 0xcac4fd4c5080e581, 0xc38bdb7483ba68e1, 0xdb2a8069b2ceaffa, + 0xdf9fe91d0d1c7887, 0xe83f49e96e2e6a08, 0x0c69e61b62ca2b62, + 0xb4a4f3f85f8298fe, 0x167a1b39e1e95f41, 0xf8a2a5649855ee41, + 0x27992565b595c498, 0x3e08cca5b71f9346, 0xad406b10c770a6d2, + 0xd1713ce6e552bcf2, 0x753b287194c73ad3, 0x5ae41a95f600af1c, + 0x4a61163b86a8bb4c, 0x42eeaa79e760c7e4, 0x698df622ef465b0a, + 0x157583111e1a6026, 0xaa1388f078e793e0, 0xf10d68d0f3309360, + 0x2af056184457a3de, 0x6d0058e1590b2489, 0x638f287f68817f12, + 0xc46b71fecefd5467, 0x2c8e94679d964e0a, 0x8612b797ce22503a, + 0x59f929babfba7170, 0x9527556923fb49a0, 0x1039ab644f5e150b, + 0x7816c83f3aa05e6d, 0xf51d2f564518c619, 0x67d494cff03ac004, + 0x2802d636ced1cfbb, 0xf64e20bad771cb12, 0x0b9a6cf84a83e15e, + 0x8da6630319609301, 0x40946a86e2a996f3, 0xcab7f5997953fa76, + 0x39129ca0e04fc465, 0x5238221fd685e1b8, 0x175130c407dbcaab, + 0x02f20e7536c0b0df, 0x2742cb488a04ad56, 0xd6afb593879ff93b, + 0xf50ad64caac0ca7f, 0x2ade95c4261364ae, 0x5c4f3299faacd07a, + 0xfffe3bff0ae5e9bc, 0x1db785c0005166e4, 0xea000d962ad18418, + 0xe42aef38359362d9, 0xc8e95657348a3891, 0xc162eca864f238c6, + 0xbe1fb373e20579ad, 0x628a1d4f40aa6ffd, 0xa87bdb7456340f90, + 0x5960ef3ba982c801, 0x5026586df9a431ec, 0xfe4b8a20fdf0840b, + 0xdcb761867da7072f, 0xc10d4653667275b7, 0x727720deec13110b, + 0x710b009662858dc9, 0xfbf8f7a3ecac1eb7, 0xb6fc4fcd0722e3df, + 0x7cb86dcc55104aac, 0x19e71e9b45c3a51e, 0x51de38573c2bea48, + 0xa73ab6996d6df158, 0x55ef2b8c930817b2, 0xb2850bf5fae87157, + 0xecf3de1acd04651f, 0xcc0a40552559ff32, 0xc385c374f20315b1, + 0xb90208a4c7234183, 0x58aa1ca7a4c075d9, + }; +#endif + +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + std::string str; + ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str)); + uint64_t h = absl::hash_internal::LowLevelHash(str.data(), str.size(), + cases[i].seed, kSalt); + printf("0x%016" PRIx64 ", ", h); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); + EXPECT_FALSE(true); +#else + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + SCOPED_TRACE(::testing::Message() + << "i = " << i << "; input = " << cases[i].base64_data); + std::string str; + ASSERT_TRUE(absl::Base64Unescape(cases[i].base64_data, &str)); + EXPECT_EQ(absl::hash_internal::LowLevelHash(str.data(), str.size(), + cases[i].seed, kSalt), + kGolden[i]); + } +#endif +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/memory/BUILD.bazel b/third_party/abseil-cpp/absl/memory/BUILD.bazel index 2ba9d7cb98..c16bf8a948 100644 --- a/third_party/abseil-cpp/absl/memory/BUILD.bazel +++ b/third_party/abseil-cpp/absl/memory/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "memory", diff --git a/third_party/abseil-cpp/absl/memory/CMakeLists.txt b/third_party/abseil-cpp/absl/memory/CMakeLists.txt index 78fb7e1b31..9d50e1dcd4 100644 --- a/third_party/abseil-cpp/absl/memory/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/memory/CMakeLists.txt @@ -37,7 +37,7 @@ absl_cc_test( DEPS absl::memory absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -51,5 +51,5 @@ absl_cc_test( absl::memory absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/memory/memory.h b/third_party/abseil-cpp/absl/memory/memory.h index 513f7103a0..d63326068f 100644 --- a/third_party/abseil-cpp/absl/memory/memory.h +++ b/third_party/abseil-cpp/absl/memory/memory.h @@ -420,6 +420,9 @@ struct pointer_traits<T*> { // // A C++11 compatible implementation of C++17's std::allocator_traits. // +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +using std::allocator_traits; +#else // __cplusplus >= 201703L template <typename Alloc> struct allocator_traits { using allocator_type = Alloc; @@ -609,6 +612,7 @@ struct allocator_traits { return a; } }; +#endif // __cplusplus >= 201703L namespace memory_internal { diff --git a/third_party/abseil-cpp/absl/memory/memory_test.cc b/third_party/abseil-cpp/absl/memory/memory_test.cc index c47820e54a..1990c7ba47 100644 --- a/third_party/abseil-cpp/absl/memory/memory_test.cc +++ b/third_party/abseil-cpp/absl/memory/memory_test.cc @@ -17,6 +17,7 @@ #include "absl/memory/memory.h" #include <sys/types.h> + #include <cstddef> #include <memory> #include <string> @@ -36,10 +37,10 @@ using ::testing::Return; // been called, via the instance_count variable. class DestructorVerifier { public: - DestructorVerifier() { ++instance_count_; } + DestructorVerifier() { ++instance_count_; } DestructorVerifier(const DestructorVerifier&) = delete; DestructorVerifier& operator=(const DestructorVerifier&) = delete; - ~DestructorVerifier() { --instance_count_; } + ~DestructorVerifier() { --instance_count_; } // The number of instances of this class currently active. static int instance_count() { return instance_count_; } @@ -156,9 +157,7 @@ struct ArrayWatch { allocs().push_back(n); return ::operator new[](n); } - void operator delete[](void* p) { - return ::operator delete[](p); - } + void operator delete[](void* p) { return ::operator delete[](p); } static std::vector<size_t>& allocs() { static auto& v = *new std::vector<size_t>; return v; @@ -171,8 +170,7 @@ TEST(Make_UniqueTest, Array) { ArrayWatch::allocs().clear(); auto p = absl::make_unique<ArrayWatch[]>(5); - static_assert(std::is_same<decltype(p), - std::unique_ptr<ArrayWatch[]>>::value, + static_assert(std::is_same<decltype(p), std::unique_ptr<ArrayWatch[]>>::value, "unexpected return type"); EXPECT_THAT(ArrayWatch::allocs(), ElementsAre(5 * sizeof(ArrayWatch))); } @@ -181,7 +179,7 @@ TEST(Make_UniqueTest, NotAmbiguousWithStdMakeUnique) { // Ensure that absl::make_unique is not ambiguous with std::make_unique. // In C++14 mode, the below call to make_unique has both types as candidates. struct TakesStdType { - explicit TakesStdType(const std::vector<int> &vec) {} + explicit TakesStdType(const std::vector<int>& vec) {} }; using absl::make_unique; (void)make_unique<TakesStdType>(std::vector<int>()); @@ -541,8 +539,8 @@ struct MinimalMockAllocator { MinimalMockAllocator(const MinimalMockAllocator& other) : value(other.value) {} using value_type = TestValue; - MOCK_METHOD1(allocate, value_type*(size_t)); - MOCK_METHOD2(deallocate, void(value_type*, size_t)); + MOCK_METHOD(value_type*, allocate, (size_t)); + MOCK_METHOD(void, deallocate, (value_type*, size_t)); int value; }; @@ -557,7 +555,7 @@ TEST(AllocatorTraits, FunctionsMinimal) { EXPECT_CALL(mock, deallocate(&x, 7)); EXPECT_EQ(&x, Traits::allocate(mock, 7)); - Traits::allocate(mock, 7, static_cast<const void*>(&hint)); + static_cast<void>(Traits::allocate(mock, 7, static_cast<const void*>(&hint))); EXPECT_EQ(&x, Traits::allocate(mock, 7, static_cast<const void*>(&hint))); Traits::deallocate(mock, &x, 7); @@ -579,13 +577,14 @@ struct FullMockAllocator { explicit FullMockAllocator(int value) : value(value) {} FullMockAllocator(const FullMockAllocator& other) : value(other.value) {} using value_type = TestValue; - MOCK_METHOD1(allocate, value_type*(size_t)); - MOCK_METHOD2(allocate, value_type*(size_t, const void*)); - MOCK_METHOD2(construct, void(value_type*, int*)); - MOCK_METHOD1(destroy, void(value_type*)); - MOCK_CONST_METHOD0(max_size, size_t()); - MOCK_CONST_METHOD0(select_on_container_copy_construction, - FullMockAllocator()); + MOCK_METHOD(value_type*, allocate, (size_t)); + MOCK_METHOD(value_type*, allocate, (size_t, const void*)); + MOCK_METHOD(void, construct, (value_type*, int*)); + MOCK_METHOD(void, destroy, (value_type*)); + MOCK_METHOD(size_t, max_size, (), + (const)); + MOCK_METHOD(FullMockAllocator, select_on_container_copy_construction, (), + (const)); int value; }; @@ -642,8 +641,7 @@ TEST(AllocatorNoThrowTest, CustomAllocator) { struct CanThrowAllocator { using is_nothrow = std::false_type; }; - struct UnspecifiedAllocator { - }; + struct UnspecifiedAllocator {}; EXPECT_TRUE(absl::allocator_is_nothrow<NoThrowAllocator>::value); EXPECT_FALSE(absl::allocator_is_nothrow<CanThrowAllocator>::value); EXPECT_FALSE(absl::allocator_is_nothrow<UnspecifiedAllocator>::value); diff --git a/third_party/abseil-cpp/absl/meta/BUILD.bazel b/third_party/abseil-cpp/absl/meta/BUILD.bazel index c06d2d9708..fb379251a6 100644 --- a/third_party/abseil-cpp/absl/meta/BUILD.bazel +++ b/third_party/abseil-cpp/absl/meta/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "type_traits", diff --git a/third_party/abseil-cpp/absl/meta/CMakeLists.txt b/third_party/abseil-cpp/absl/meta/CMakeLists.txt index 672ead2fd0..9de4bd3751 100644 --- a/third_party/abseil-cpp/absl/meta/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/meta/CMakeLists.txt @@ -35,7 +35,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::type_traits - gmock_main + GTest::gmock_main ) # component target diff --git a/third_party/abseil-cpp/absl/meta/type_traits.h b/third_party/abseil-cpp/absl/meta/type_traits.h index ba87d2f0ed..d886cb30a8 100644 --- a/third_party/abseil-cpp/absl/meta/type_traits.h +++ b/third_party/abseil-cpp/absl/meta/type_traits.h @@ -35,7 +35,7 @@ #ifndef ABSL_META_TYPE_TRAITS_H_ #define ABSL_META_TYPE_TRAITS_H_ -#include <stddef.h> +#include <cstddef> #include <functional> #include <type_traits> @@ -47,6 +47,14 @@ #define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 #endif +// Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17 +// feature. +#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) +#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT __STDCPP_DEFAULT_NEW_ALIGNMENT__ +#else // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) +#define ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT alignof(std::max_align_t) +#endif // defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) + namespace absl { ABSL_NAMESPACE_BEGIN @@ -219,7 +227,7 @@ using void_t = typename type_traits_internal::VoidTImpl<Ts...>::type; // This metafunction is designed to be a drop-in replacement for the C++17 // `std::conjunction` metafunction. template <typename... Ts> -struct conjunction; +struct conjunction : std::true_type {}; template <typename T, typename... Ts> struct conjunction<T, Ts...> @@ -228,9 +236,6 @@ struct conjunction<T, Ts...> template <typename T> struct conjunction<T> : T {}; -template <> -struct conjunction<> : std::true_type {}; - // disjunction // // Performs a compile-time logical OR operation on the passed types (which @@ -241,7 +246,7 @@ struct conjunction<> : std::true_type {}; // This metafunction is designed to be a drop-in replacement for the C++17 // `std::disjunction` metafunction. template <typename... Ts> -struct disjunction; +struct disjunction : std::false_type {}; template <typename T, typename... Ts> struct disjunction<T, Ts...> : @@ -250,9 +255,6 @@ struct disjunction<T, Ts...> : template <typename T> struct disjunction<T> : T {}; -template <> -struct disjunction<> : std::false_type {}; - // negation // // Performs a compile-time logical NOT operation on the passed type (which @@ -505,6 +507,27 @@ struct is_trivially_copy_assignable #endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE }; +#if defined(__cpp_lib_remove_cvref) && __cpp_lib_remove_cvref >= 201711L +template <typename T> +using remove_cvref = std::remove_cvref<T>; + +template <typename T> +using remove_cvref_t = typename std::remove_cvref<T>::type; +#else +// remove_cvref() +// +// C++11 compatible implementation of std::remove_cvref which was added in +// C++20. +template <typename T> +struct remove_cvref { + using type = + typename std::remove_cv<typename std::remove_reference<T>::type>::type; +}; + +template <typename T> +using remove_cvref_t = typename remove_cvref<T>::type; +#endif + namespace type_traits_internal { // is_trivially_copyable() // @@ -616,8 +639,23 @@ using common_type_t = typename std::common_type<T...>::type; template <typename T> using underlying_type_t = typename std::underlying_type<T>::type; -template <typename T> -using result_of_t = typename std::result_of<T>::type; + +namespace type_traits_internal { + +#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +// std::result_of is deprecated (C++17) or removed (C++20) +template<typename> struct result_of; +template<typename F, typename... Args> +struct result_of<F(Args...)> : std::invoke_result<F, Args...> {}; +#else +template<typename F> using result_of = std::result_of<F>; +#endif + +} // namespace type_traits_internal + +template<typename F> +using result_of_t = typename type_traits_internal::result_of<F>::type; namespace type_traits_internal { // In MSVC we can't probe std::hash or stdext::hash because it triggers a diff --git a/third_party/abseil-cpp/absl/meta/type_traits_test.cc b/third_party/abseil-cpp/absl/meta/type_traits_test.cc index 1aafd0d49a..0ef5b66558 100644 --- a/third_party/abseil-cpp/absl/meta/type_traits_test.cc +++ b/third_party/abseil-cpp/absl/meta/type_traits_test.cc @@ -942,6 +942,34 @@ TEST(TypeTraitsTest, TestTriviallyCopyable) { absl::type_traits_internal::is_trivially_copyable<Trivial&>::value); } +TEST(TypeTraitsTest, TestRemoveCVRef) { + EXPECT_TRUE( + (std::is_same<typename absl::remove_cvref<int>::type, int>::value)); + EXPECT_TRUE( + (std::is_same<typename absl::remove_cvref<int&>::type, int>::value)); + EXPECT_TRUE( + (std::is_same<typename absl::remove_cvref<int&&>::type, int>::value)); + EXPECT_TRUE(( + std::is_same<typename absl::remove_cvref<const int&>::type, int>::value)); + EXPECT_TRUE( + (std::is_same<typename absl::remove_cvref<int*>::type, int*>::value)); + // Does not remove const in this case. + EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int*>::type, + const int*>::value)); + EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int[2]>::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&)[2]>::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&&)[2]>::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int[2]>::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&)[2]>::type, + int[2]>::value)); + EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int(&&)[2]>::type, + int[2]>::value)); +} + #define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...) \ EXPECT_TRUE((std::is_same<typename std::trait_name<__VA_ARGS__>::type, \ absl::trait_name##_t<__VA_ARGS__>>::value)) diff --git a/third_party/abseil-cpp/absl/numeric/BUILD.bazel b/third_party/abseil-cpp/absl/numeric/BUILD.bazel index e09e52d21f..1f9e0f2055 100644 --- a/third_party/abseil-cpp/absl/numeric/BUILD.bazel +++ b/third_party/abseil-cpp/absl/numeric/BUILD.bazel @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -22,7 +21,36 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) + +cc_library( + name = "bits", + hdrs = [ + "bits.h", + "internal/bits.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "bits_test", + size = "small", + srcs = [ + "bits_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bits", + "//absl/random", + "@com_google_googletest//:gtest_main", + ], +) cc_library( name = "int128", @@ -35,6 +63,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":bits", "//absl/base:config", "//absl/base:core_headers", ], @@ -71,3 +100,15 @@ cc_test( "@com_github_google_benchmark//:benchmark_main", ], ) + +cc_library( + name = "representation", + hdrs = [ + "internal/representation.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + ], +) diff --git a/third_party/abseil-cpp/absl/numeric/CMakeLists.txt b/third_party/abseil-cpp/absl/numeric/CMakeLists.txt index 242889f088..26df5cf703 100644 --- a/third_party/abseil-cpp/absl/numeric/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/numeric/CMakeLists.txt @@ -16,6 +16,33 @@ absl_cc_library( NAME + bits + HDRS + "bits.h" + "internal/bits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + PUBLIC +) + +absl_cc_test( + NAME + bits_test + SRCS + "bits_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::bits + absl::core_headers + absl::random_random + GTest::gmock_main +) + +absl_cc_library( + NAME int128 HDRS "int128.h" @@ -28,6 +55,7 @@ absl_cc_library( DEPS absl::config absl::core_headers + absl::bits PUBLIC ) @@ -45,7 +73,7 @@ absl_cc_test( absl::core_headers absl::hash_testing absl::type_traits - gmock_main + GTest::gmock_main ) # component target @@ -58,3 +86,15 @@ absl_cc_library( absl::int128 PUBLIC ) + +absl_cc_library( + NAME + numeric_representation + HDRS + "internal/representation.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + PUBLIC +) diff --git a/third_party/abseil-cpp/absl/numeric/bits.h b/third_party/abseil-cpp/absl/numeric/bits.h new file mode 100644 index 0000000000..52013ad49b --- /dev/null +++ b/third_party/abseil-cpp/absl/numeric/bits.h @@ -0,0 +1,177 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// File: bits.h +// ----------------------------------------------------------------------------- +// +// This file contains implementations of C++20's bitwise math functions, as +// defined by: +// +// P0553R4: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html +// P0556R3: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0556r3.html +// P1355R2: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1355r2.html +// P1956R1: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1956r1.pdf +// +// When using a standard library that implements these functions, we use the +// standard library's implementation. + +#ifndef ABSL_NUMERIC_BITS_H_ +#define ABSL_NUMERIC_BITS_H_ + +#include <cstdint> +#include <limits> +#include <type_traits> + +#if (defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L) || \ + (defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L) +#include <bit> +#endif + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/numeric/internal/bits.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +#if !(defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L) +// rotating +template <class T> +ABSL_MUST_USE_RESULT constexpr + typename std::enable_if<std::is_unsigned<T>::value, T>::type + rotl(T x, int s) noexcept { + return numeric_internal::RotateLeft(x, s); +} + +template <class T> +ABSL_MUST_USE_RESULT constexpr + typename std::enable_if<std::is_unsigned<T>::value, T>::type + rotr(T x, int s) noexcept { + return numeric_internal::RotateRight(x, s); +} + +// Counting functions +// +// While these functions are typically constexpr, on some platforms, they may +// not be marked as constexpr due to constraints of the compiler/available +// intrinsics. +template <class T> +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if<std::is_unsigned<T>::value, int>::type + countl_zero(T x) noexcept { + return numeric_internal::CountLeadingZeroes(x); +} + +template <class T> +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if<std::is_unsigned<T>::value, int>::type + countl_one(T x) noexcept { + // Avoid integer promotion to a wider type + return countl_zero(static_cast<T>(~x)); +} + +template <class T> +ABSL_INTERNAL_CONSTEXPR_CTZ inline + typename std::enable_if<std::is_unsigned<T>::value, int>::type + countr_zero(T x) noexcept { + return numeric_internal::CountTrailingZeroes(x); +} + +template <class T> +ABSL_INTERNAL_CONSTEXPR_CTZ inline + typename std::enable_if<std::is_unsigned<T>::value, int>::type + countr_one(T x) noexcept { + // Avoid integer promotion to a wider type + return countr_zero(static_cast<T>(~x)); +} + +template <class T> +ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline + typename std::enable_if<std::is_unsigned<T>::value, int>::type + popcount(T x) noexcept { + return numeric_internal::Popcount(x); +} +#else // defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L + +using std::countl_one; +using std::countl_zero; +using std::countr_one; +using std::countr_zero; +using std::popcount; +using std::rotl; +using std::rotr; + +#endif + +#if !(defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L) +// Returns: true if x is an integral power of two; false otherwise. +template <class T> +constexpr inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type +has_single_bit(T x) noexcept { + return x != 0 && (x & (x - 1)) == 0; +} + +// Returns: If x == 0, 0; otherwise one plus the base-2 logarithm of x, with any +// fractional part discarded. +template <class T> +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if<std::is_unsigned<T>::value, T>::type + bit_width(T x) noexcept { + return std::numeric_limits<T>::digits - countl_zero(x); +} + +// Returns: If x == 0, 0; otherwise the maximal value y such that +// has_single_bit(y) is true and y <= x. +template <class T> +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if<std::is_unsigned<T>::value, T>::type + bit_floor(T x) noexcept { + return x == 0 ? 0 : T{1} << (bit_width(x) - 1); +} + +// Returns: N, where N is the smallest power of 2 greater than or equal to x. +// +// Preconditions: N is representable as a value of type T. +template <class T> +ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if<std::is_unsigned<T>::value, T>::type + bit_ceil(T x) { + // If T is narrower than unsigned, T{1} << bit_width will be promoted. We + // want to force it to wraparound so that bit_ceil of an invalid value are not + // core constant expressions. + // + // BitCeilNonPowerOf2 triggers an overflow in constexpr contexts if we would + // undergo promotion to unsigned but not fit the result into T without + // truncation. + return has_single_bit(x) ? T{1} << (bit_width(x) - 1) + : numeric_internal::BitCeilNonPowerOf2(x); +} +#else // defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L + +using std::bit_ceil; +using std::bit_floor; +using std::bit_width; +using std::has_single_bit; + +#endif + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_NUMERIC_BITS_H_ diff --git a/third_party/abseil-cpp/absl/numeric/bits_test.cc b/third_party/abseil-cpp/absl/numeric/bits_test.cc new file mode 100644 index 0000000000..7c942aaecd --- /dev/null +++ b/third_party/abseil-cpp/absl/numeric/bits_test.cc @@ -0,0 +1,573 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/numeric/bits.h" + +#include <limits> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/random.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +TEST(Rotate, Left) { + static_assert(rotl(uint8_t{0x12}, 0) == uint8_t{0x12}, ""); + static_assert(rotl(uint16_t{0x1234}, 0) == uint16_t{0x1234}, ""); + static_assert(rotl(uint32_t{0x12345678UL}, 0) == uint32_t{0x12345678UL}, ""); + static_assert(rotl(uint64_t{0x12345678ABCDEF01ULL}, 0) == + uint64_t{0x12345678ABCDEF01ULL}, + ""); + + EXPECT_EQ(rotl(uint8_t{0x12}, 0), uint8_t{0x12}); + EXPECT_EQ(rotl(uint16_t{0x1234}, 0), uint16_t{0x1234}); + EXPECT_EQ(rotl(uint32_t{0x12345678UL}, 0), uint32_t{0x12345678UL}); + EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, 0), + uint64_t{0x12345678ABCDEF01ULL}); + + EXPECT_EQ(rotl(uint8_t{0x12}, 8), uint8_t{0x12}); + EXPECT_EQ(rotl(uint16_t{0x1234}, 16), uint16_t{0x1234}); + EXPECT_EQ(rotl(uint32_t{0x12345678UL}, 32), uint32_t{0x12345678UL}); + EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, 64), + uint64_t{0x12345678ABCDEF01ULL}); + + EXPECT_EQ(rotl(uint8_t{0x12}, -8), uint8_t{0x12}); + EXPECT_EQ(rotl(uint16_t{0x1234}, -16), uint16_t{0x1234}); + EXPECT_EQ(rotl(uint32_t{0x12345678UL}, -32), uint32_t{0x12345678UL}); + EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, -64), + uint64_t{0x12345678ABCDEF01ULL}); + + EXPECT_EQ(rotl(uint8_t{0x12}, 4), uint8_t{0x21}); + EXPECT_EQ(rotl(uint16_t{0x1234}, 4), uint16_t{0x2341}); + EXPECT_EQ(rotl(uint32_t{0x12345678UL}, 4), uint32_t{0x23456781UL}); + EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, 4), + uint64_t{0x2345678ABCDEF011ULL}); + + EXPECT_EQ(rotl(uint8_t{0x12}, -4), uint8_t{0x21}); + EXPECT_EQ(rotl(uint16_t{0x1234}, -4), uint16_t{0x4123}); + EXPECT_EQ(rotl(uint32_t{0x12345678UL}, -4), uint32_t{0x81234567UL}); + EXPECT_EQ(rotl(uint64_t{0x12345678ABCDEF01ULL}, -4), + uint64_t{0x112345678ABCDEF0ULL}); +} + +TEST(Rotate, Right) { + static_assert(rotr(uint8_t{0x12}, 0) == uint8_t{0x12}, ""); + static_assert(rotr(uint16_t{0x1234}, 0) == uint16_t{0x1234}, ""); + static_assert(rotr(uint32_t{0x12345678UL}, 0) == uint32_t{0x12345678UL}, ""); + static_assert(rotr(uint64_t{0x12345678ABCDEF01ULL}, 0) == + uint64_t{0x12345678ABCDEF01ULL}, + ""); + + EXPECT_EQ(rotr(uint8_t{0x12}, 0), uint8_t{0x12}); + EXPECT_EQ(rotr(uint16_t{0x1234}, 0), uint16_t{0x1234}); + EXPECT_EQ(rotr(uint32_t{0x12345678UL}, 0), uint32_t{0x12345678UL}); + EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, 0), + uint64_t{0x12345678ABCDEF01ULL}); + + EXPECT_EQ(rotr(uint8_t{0x12}, 8), uint8_t{0x12}); + EXPECT_EQ(rotr(uint16_t{0x1234}, 16), uint16_t{0x1234}); + EXPECT_EQ(rotr(uint32_t{0x12345678UL}, 32), uint32_t{0x12345678UL}); + EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, 64), + uint64_t{0x12345678ABCDEF01ULL}); + + EXPECT_EQ(rotr(uint8_t{0x12}, -8), uint8_t{0x12}); + EXPECT_EQ(rotr(uint16_t{0x1234}, -16), uint16_t{0x1234}); + EXPECT_EQ(rotr(uint32_t{0x12345678UL}, -32), uint32_t{0x12345678UL}); + EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, -64), + uint64_t{0x12345678ABCDEF01ULL}); + + EXPECT_EQ(rotr(uint8_t{0x12}, 4), uint8_t{0x21}); + EXPECT_EQ(rotr(uint16_t{0x1234}, 4), uint16_t{0x4123}); + EXPECT_EQ(rotr(uint32_t{0x12345678UL}, 4), uint32_t{0x81234567UL}); + EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, 4), + uint64_t{0x112345678ABCDEF0ULL}); + + EXPECT_EQ(rotr(uint8_t{0x12}, -4), uint8_t{0x21}); + EXPECT_EQ(rotr(uint16_t{0x1234}, -4), uint16_t{0x2341}); + EXPECT_EQ(rotr(uint32_t{0x12345678UL}, -4), uint32_t{0x23456781UL}); + EXPECT_EQ(rotr(uint64_t{0x12345678ABCDEF01ULL}, -4), + uint64_t{0x2345678ABCDEF011ULL}); +} + +TEST(Rotate, Symmetry) { + // rotr(x, s) is equivalent to rotl(x, -s) + absl::BitGen rng; + constexpr int kTrials = 100; + + for (int i = 0; i < kTrials; ++i) { + uint8_t value = absl::Uniform(rng, std::numeric_limits<uint8_t>::min(), + std::numeric_limits<uint8_t>::max()); + int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint8_t>::digits, + 2 * std::numeric_limits<uint8_t>::digits); + + EXPECT_EQ(rotl(value, shift), rotr(value, -shift)); + } + + for (int i = 0; i < kTrials; ++i) { + uint16_t value = absl::Uniform(rng, std::numeric_limits<uint16_t>::min(), + std::numeric_limits<uint16_t>::max()); + int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint16_t>::digits, + 2 * std::numeric_limits<uint16_t>::digits); + + EXPECT_EQ(rotl(value, shift), rotr(value, -shift)); + } + + for (int i = 0; i < kTrials; ++i) { + uint32_t value = absl::Uniform(rng, std::numeric_limits<uint32_t>::min(), + std::numeric_limits<uint32_t>::max()); + int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint32_t>::digits, + 2 * std::numeric_limits<uint32_t>::digits); + + EXPECT_EQ(rotl(value, shift), rotr(value, -shift)); + } + + for (int i = 0; i < kTrials; ++i) { + uint64_t value = absl::Uniform(rng, std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + int shift = absl::Uniform(rng, -2 * std::numeric_limits<uint64_t>::digits, + 2 * std::numeric_limits<uint64_t>::digits); + + EXPECT_EQ(rotl(value, shift), rotr(value, -shift)); + } +} + +TEST(Counting, LeadingZeroes) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ + static_assert(countl_zero(uint8_t{}) == 8, ""); + static_assert(countl_zero(static_cast<uint8_t>(-1)) == 0, ""); + static_assert(countl_zero(uint16_t{}) == 16, ""); + static_assert(countl_zero(static_cast<uint16_t>(-1)) == 0, ""); + static_assert(countl_zero(uint32_t{}) == 32, ""); + static_assert(countl_zero(~uint32_t{}) == 0, ""); + static_assert(countl_zero(uint64_t{}) == 64, ""); + static_assert(countl_zero(~uint64_t{}) == 0, ""); +#endif + + EXPECT_EQ(countl_zero(uint8_t{}), 8); + EXPECT_EQ(countl_zero(static_cast<uint8_t>(-1)), 0); + EXPECT_EQ(countl_zero(uint16_t{}), 16); + EXPECT_EQ(countl_zero(static_cast<uint16_t>(-1)), 0); + EXPECT_EQ(countl_zero(uint32_t{}), 32); + EXPECT_EQ(countl_zero(~uint32_t{}), 0); + EXPECT_EQ(countl_zero(uint64_t{}), 64); + EXPECT_EQ(countl_zero(~uint64_t{}), 0); + + for (int i = 0; i < 8; i++) { + EXPECT_EQ(countl_zero(static_cast<uint8_t>(1u << i)), 7 - i); + } + + for (int i = 0; i < 16; i++) { + EXPECT_EQ(countl_zero(static_cast<uint16_t>(1u << i)), 15 - i); + } + + for (int i = 0; i < 32; i++) { + EXPECT_EQ(countl_zero(uint32_t{1} << i), 31 - i); + } + + for (int i = 0; i < 64; i++) { + EXPECT_EQ(countl_zero(uint64_t{1} << i), 63 - i); + } +} + +TEST(Counting, LeadingOnes) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ + static_assert(countl_one(uint8_t{}) == 0, ""); + static_assert(countl_one(static_cast<uint8_t>(-1)) == 8, ""); + static_assert(countl_one(uint16_t{}) == 0, ""); + static_assert(countl_one(static_cast<uint16_t>(-1)) == 16, ""); + static_assert(countl_one(uint32_t{}) == 0, ""); + static_assert(countl_one(~uint32_t{}) == 32, ""); + static_assert(countl_one(uint64_t{}) == 0, ""); + static_assert(countl_one(~uint64_t{}) == 64, ""); +#endif + + EXPECT_EQ(countl_one(uint8_t{}), 0); + EXPECT_EQ(countl_one(static_cast<uint8_t>(-1)), 8); + EXPECT_EQ(countl_one(uint16_t{}), 0); + EXPECT_EQ(countl_one(static_cast<uint16_t>(-1)), 16); + EXPECT_EQ(countl_one(uint32_t{}), 0); + EXPECT_EQ(countl_one(~uint32_t{}), 32); + EXPECT_EQ(countl_one(uint64_t{}), 0); + EXPECT_EQ(countl_one(~uint64_t{}), 64); +} + +TEST(Counting, TrailingZeroes) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_CTZ + static_assert(countr_zero(uint8_t{}) == 8, ""); + static_assert(countr_zero(static_cast<uint8_t>(-1)) == 0, ""); + static_assert(countr_zero(uint16_t{}) == 16, ""); + static_assert(countr_zero(static_cast<uint16_t>(-1)) == 0, ""); + static_assert(countr_zero(uint32_t{}) == 32, ""); + static_assert(countr_zero(~uint32_t{}) == 0, ""); + static_assert(countr_zero(uint64_t{}) == 64, ""); + static_assert(countr_zero(~uint64_t{}) == 0, ""); +#endif + + EXPECT_EQ(countr_zero(uint8_t{}), 8); + EXPECT_EQ(countr_zero(static_cast<uint8_t>(-1)), 0); + EXPECT_EQ(countr_zero(uint16_t{}), 16); + EXPECT_EQ(countr_zero(static_cast<uint16_t>(-1)), 0); + EXPECT_EQ(countr_zero(uint32_t{}), 32); + EXPECT_EQ(countr_zero(~uint32_t{}), 0); + EXPECT_EQ(countr_zero(uint64_t{}), 64); + EXPECT_EQ(countr_zero(~uint64_t{}), 0); +} + +TEST(Counting, TrailingOnes) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_CTZ + static_assert(countr_one(uint8_t{}) == 0, ""); + static_assert(countr_one(static_cast<uint8_t>(-1)) == 8, ""); + static_assert(countr_one(uint16_t{}) == 0, ""); + static_assert(countr_one(static_cast<uint16_t>(-1)) == 16, ""); + static_assert(countr_one(uint32_t{}) == 0, ""); + static_assert(countr_one(~uint32_t{}) == 32, ""); + static_assert(countr_one(uint64_t{}) == 0, ""); + static_assert(countr_one(~uint64_t{}) == 64, ""); +#endif + + EXPECT_EQ(countr_one(uint8_t{}), 0); + EXPECT_EQ(countr_one(static_cast<uint8_t>(-1)), 8); + EXPECT_EQ(countr_one(uint16_t{}), 0); + EXPECT_EQ(countr_one(static_cast<uint16_t>(-1)), 16); + EXPECT_EQ(countr_one(uint32_t{}), 0); + EXPECT_EQ(countr_one(~uint32_t{}), 32); + EXPECT_EQ(countr_one(uint64_t{}), 0); + EXPECT_EQ(countr_one(~uint64_t{}), 64); +} + +TEST(Counting, Popcount) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT + static_assert(popcount(uint8_t{}) == 0, ""); + static_assert(popcount(uint8_t{1}) == 1, ""); + static_assert(popcount(static_cast<uint8_t>(-1)) == 8, ""); + static_assert(popcount(uint16_t{}) == 0, ""); + static_assert(popcount(uint16_t{1}) == 1, ""); + static_assert(popcount(static_cast<uint16_t>(-1)) == 16, ""); + static_assert(popcount(uint32_t{}) == 0, ""); + static_assert(popcount(uint32_t{1}) == 1, ""); + static_assert(popcount(~uint32_t{}) == 32, ""); + static_assert(popcount(uint64_t{}) == 0, ""); + static_assert(popcount(uint64_t{1}) == 1, ""); + static_assert(popcount(~uint64_t{}) == 64, ""); +#endif // ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT + + EXPECT_EQ(popcount(uint8_t{}), 0); + EXPECT_EQ(popcount(uint8_t{1}), 1); + EXPECT_EQ(popcount(static_cast<uint8_t>(-1)), 8); + EXPECT_EQ(popcount(uint16_t{}), 0); + EXPECT_EQ(popcount(uint16_t{1}), 1); + EXPECT_EQ(popcount(static_cast<uint16_t>(-1)), 16); + EXPECT_EQ(popcount(uint32_t{}), 0); + EXPECT_EQ(popcount(uint32_t{1}), 1); + EXPECT_EQ(popcount(~uint32_t{}), 32); + EXPECT_EQ(popcount(uint64_t{}), 0); + EXPECT_EQ(popcount(uint64_t{1}), 1); + EXPECT_EQ(popcount(~uint64_t{}), 64); + + for (int i = 0; i < 8; i++) { + EXPECT_EQ(popcount(static_cast<uint8_t>(uint8_t{1} << i)), 1); + EXPECT_EQ(popcount(static_cast<uint8_t>(static_cast<uint8_t>(-1) ^ + (uint8_t{1} << i))), + 7); + } + + for (int i = 0; i < 16; i++) { + EXPECT_EQ(popcount(static_cast<uint16_t>(uint16_t{1} << i)), 1); + EXPECT_EQ(popcount(static_cast<uint16_t>(static_cast<uint16_t>(-1) ^ + (uint16_t{1} << i))), + 15); + } + + for (int i = 0; i < 32; i++) { + EXPECT_EQ(popcount(uint32_t{1} << i), 1); + EXPECT_EQ(popcount(static_cast<uint32_t>(-1) ^ (uint32_t{1} << i)), 31); + } + + for (int i = 0; i < 64; i++) { + EXPECT_EQ(popcount(uint64_t{1} << i), 1); + EXPECT_EQ(popcount(static_cast<uint64_t>(-1) ^ (uint64_t{1} << i)), 63); + } +} + +template <typename T> +struct PopcountInput { + T value = 0; + int expected = 0; +}; + +template <typename T> +PopcountInput<T> GeneratePopcountInput(absl::BitGen& gen) { + PopcountInput<T> ret; + for (int i = 0; i < std::numeric_limits<T>::digits; i++) { + bool coin = absl::Bernoulli(gen, 0.2); + if (coin) { + ret.value |= T{1} << i; + ret.expected++; + } + } + return ret; +} + +TEST(Counting, PopcountFuzz) { + absl::BitGen rng; + constexpr int kTrials = 100; + + for (int i = 0; i < kTrials; ++i) { + auto input = GeneratePopcountInput<uint8_t>(rng); + EXPECT_EQ(popcount(input.value), input.expected); + } + + for (int i = 0; i < kTrials; ++i) { + auto input = GeneratePopcountInput<uint16_t>(rng); + EXPECT_EQ(popcount(input.value), input.expected); + } + + for (int i = 0; i < kTrials; ++i) { + auto input = GeneratePopcountInput<uint32_t>(rng); + EXPECT_EQ(popcount(input.value), input.expected); + } + + for (int i = 0; i < kTrials; ++i) { + auto input = GeneratePopcountInput<uint64_t>(rng); + EXPECT_EQ(popcount(input.value), input.expected); + } +} + +TEST(IntegralPowersOfTwo, SingleBit) { + EXPECT_FALSE(has_single_bit(uint8_t{})); + EXPECT_FALSE(has_single_bit(static_cast<uint8_t>(-1))); + EXPECT_FALSE(has_single_bit(uint16_t{})); + EXPECT_FALSE(has_single_bit(static_cast<uint16_t>(-1))); + EXPECT_FALSE(has_single_bit(uint32_t{})); + EXPECT_FALSE(has_single_bit(~uint32_t{})); + EXPECT_FALSE(has_single_bit(uint64_t{})); + EXPECT_FALSE(has_single_bit(~uint64_t{})); + + static_assert(!has_single_bit(0u), ""); + static_assert(has_single_bit(1u), ""); + static_assert(has_single_bit(2u), ""); + static_assert(!has_single_bit(3u), ""); + static_assert(has_single_bit(4u), ""); + static_assert(!has_single_bit(1337u), ""); + static_assert(has_single_bit(65536u), ""); + static_assert(has_single_bit(uint32_t{1} << 30), ""); + static_assert(has_single_bit(uint64_t{1} << 42), ""); + + EXPECT_FALSE(has_single_bit(0u)); + EXPECT_TRUE(has_single_bit(1u)); + EXPECT_TRUE(has_single_bit(2u)); + EXPECT_FALSE(has_single_bit(3u)); + EXPECT_TRUE(has_single_bit(4u)); + EXPECT_FALSE(has_single_bit(1337u)); + EXPECT_TRUE(has_single_bit(65536u)); + EXPECT_TRUE(has_single_bit(uint32_t{1} << 30)); + EXPECT_TRUE(has_single_bit(uint64_t{1} << 42)); + + EXPECT_TRUE(has_single_bit( + static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() / 2 + 1))); + EXPECT_TRUE(has_single_bit( + static_cast<uint16_t>(std::numeric_limits<uint16_t>::max() / 2 + 1))); + EXPECT_TRUE(has_single_bit( + static_cast<uint32_t>(std::numeric_limits<uint32_t>::max() / 2 + 1))); + EXPECT_TRUE(has_single_bit( + static_cast<uint64_t>(std::numeric_limits<uint64_t>::max() / 2 + 1))); +} + +template <typename T, T arg, T = bit_ceil(arg)> +bool IsBitCeilConstantExpression(int) { + return true; +} +template <typename T, T arg> +bool IsBitCeilConstantExpression(char) { + return false; +} + +TEST(IntegralPowersOfTwo, Ceiling) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ + static_assert(bit_ceil(0u) == 1, ""); + static_assert(bit_ceil(1u) == 1, ""); + static_assert(bit_ceil(2u) == 2, ""); + static_assert(bit_ceil(3u) == 4, ""); + static_assert(bit_ceil(4u) == 4, ""); + static_assert(bit_ceil(1337u) == 2048, ""); + static_assert(bit_ceil(65536u) == 65536, ""); + static_assert(bit_ceil(65536u - 1337u) == 65536, ""); + static_assert(bit_ceil(uint32_t{0x80000000}) == uint32_t{0x80000000}, ""); + static_assert(bit_ceil(uint64_t{0x40000000000}) == uint64_t{0x40000000000}, + ""); + static_assert( + bit_ceil(uint64_t{0x8000000000000000}) == uint64_t{0x8000000000000000}, + ""); + + EXPECT_TRUE((IsBitCeilConstantExpression<uint8_t, uint8_t{0x0}>(0))); + EXPECT_TRUE((IsBitCeilConstantExpression<uint8_t, uint8_t{0x80}>(0))); + EXPECT_FALSE((IsBitCeilConstantExpression<uint8_t, uint8_t{0x81}>(0))); + EXPECT_FALSE((IsBitCeilConstantExpression<uint8_t, uint8_t{0xff}>(0))); + + EXPECT_TRUE((IsBitCeilConstantExpression<uint16_t, uint16_t{0x0}>(0))); + EXPECT_TRUE((IsBitCeilConstantExpression<uint16_t, uint16_t{0x8000}>(0))); + EXPECT_FALSE((IsBitCeilConstantExpression<uint16_t, uint16_t{0x8001}>(0))); + EXPECT_FALSE((IsBitCeilConstantExpression<uint16_t, uint16_t{0xffff}>(0))); + + EXPECT_TRUE((IsBitCeilConstantExpression<uint32_t, uint32_t{0x0}>(0))); + EXPECT_TRUE((IsBitCeilConstantExpression<uint32_t, uint32_t{0x80000000}>(0))); + EXPECT_FALSE( + (IsBitCeilConstantExpression<uint32_t, uint32_t{0x80000001}>(0))); + EXPECT_FALSE( + (IsBitCeilConstantExpression<uint32_t, uint32_t{0xffffffff}>(0))); + + EXPECT_TRUE((IsBitCeilConstantExpression<uint64_t, uint64_t{0x0}>(0))); + EXPECT_TRUE( + (IsBitCeilConstantExpression<uint64_t, uint64_t{0x8000000000000000}>(0))); + EXPECT_FALSE( + (IsBitCeilConstantExpression<uint64_t, uint64_t{0x8000000000000001}>(0))); + EXPECT_FALSE( + (IsBitCeilConstantExpression<uint64_t, uint64_t{0xffffffffffffffff}>(0))); +#endif + + EXPECT_EQ(bit_ceil(0u), 1); + EXPECT_EQ(bit_ceil(1u), 1); + EXPECT_EQ(bit_ceil(2u), 2); + EXPECT_EQ(bit_ceil(3u), 4); + EXPECT_EQ(bit_ceil(4u), 4); + EXPECT_EQ(bit_ceil(1337u), 2048); + EXPECT_EQ(bit_ceil(65536u), 65536); + EXPECT_EQ(bit_ceil(65536u - 1337u), 65536); + EXPECT_EQ(bit_ceil(uint64_t{0x40000000000}), uint64_t{0x40000000000}); +} + +TEST(IntegralPowersOfTwo, Floor) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ + static_assert(bit_floor(0u) == 0, ""); + static_assert(bit_floor(1u) == 1, ""); + static_assert(bit_floor(2u) == 2, ""); + static_assert(bit_floor(3u) == 2, ""); + static_assert(bit_floor(4u) == 4, ""); + static_assert(bit_floor(1337u) == 1024, ""); + static_assert(bit_floor(65536u) == 65536, ""); + static_assert(bit_floor(65536u - 1337u) == 32768, ""); + static_assert(bit_floor(uint64_t{0x40000000000}) == uint64_t{0x40000000000}, + ""); +#endif + + EXPECT_EQ(bit_floor(0u), 0); + EXPECT_EQ(bit_floor(1u), 1); + EXPECT_EQ(bit_floor(2u), 2); + EXPECT_EQ(bit_floor(3u), 2); + EXPECT_EQ(bit_floor(4u), 4); + EXPECT_EQ(bit_floor(1337u), 1024); + EXPECT_EQ(bit_floor(65536u), 65536); + EXPECT_EQ(bit_floor(65536u - 1337u), 32768); + EXPECT_EQ(bit_floor(uint64_t{0x40000000000}), uint64_t{0x40000000000}); + + for (int i = 0; i < 8; i++) { + uint8_t input = uint8_t{1} << i; + EXPECT_EQ(bit_floor(input), input); + if (i > 0) { + EXPECT_EQ(bit_floor(static_cast<uint8_t>(input + 1)), input); + } + } + + for (int i = 0; i < 16; i++) { + uint16_t input = uint16_t{1} << i; + EXPECT_EQ(bit_floor(input), input); + if (i > 0) { + EXPECT_EQ(bit_floor(static_cast<uint16_t>(input + 1)), input); + } + } + + for (int i = 0; i < 32; i++) { + uint32_t input = uint32_t{1} << i; + EXPECT_EQ(bit_floor(input), input); + if (i > 0) { + EXPECT_EQ(bit_floor(input + 1), input); + } + } + + for (int i = 0; i < 64; i++) { + uint64_t input = uint64_t{1} << i; + EXPECT_EQ(bit_floor(input), input); + if (i > 0) { + EXPECT_EQ(bit_floor(input + 1), input); + } + } +} + +TEST(IntegralPowersOfTwo, Width) { +#if ABSL_INTERNAL_HAS_CONSTEXPR_CLZ + static_assert(bit_width(uint8_t{}) == 0, ""); + static_assert(bit_width(uint8_t{1}) == 1, ""); + static_assert(bit_width(uint8_t{3}) == 2, ""); + static_assert(bit_width(static_cast<uint8_t>(-1)) == 8, ""); + static_assert(bit_width(uint16_t{}) == 0, ""); + static_assert(bit_width(uint16_t{1}) == 1, ""); + static_assert(bit_width(uint16_t{3}) == 2, ""); + static_assert(bit_width(static_cast<uint16_t>(-1)) == 16, ""); + static_assert(bit_width(uint32_t{}) == 0, ""); + static_assert(bit_width(uint32_t{1}) == 1, ""); + static_assert(bit_width(uint32_t{3}) == 2, ""); + static_assert(bit_width(~uint32_t{}) == 32, ""); + static_assert(bit_width(uint64_t{}) == 0, ""); + static_assert(bit_width(uint64_t{1}) == 1, ""); + static_assert(bit_width(uint64_t{3}) == 2, ""); + static_assert(bit_width(~uint64_t{}) == 64, ""); +#endif + + EXPECT_EQ(bit_width(uint8_t{}), 0); + EXPECT_EQ(bit_width(uint8_t{1}), 1); + EXPECT_EQ(bit_width(uint8_t{3}), 2); + EXPECT_EQ(bit_width(static_cast<uint8_t>(-1)), 8); + EXPECT_EQ(bit_width(uint16_t{}), 0); + EXPECT_EQ(bit_width(uint16_t{1}), 1); + EXPECT_EQ(bit_width(uint16_t{3}), 2); + EXPECT_EQ(bit_width(static_cast<uint16_t>(-1)), 16); + EXPECT_EQ(bit_width(uint32_t{}), 0); + EXPECT_EQ(bit_width(uint32_t{1}), 1); + EXPECT_EQ(bit_width(uint32_t{3}), 2); + EXPECT_EQ(bit_width(~uint32_t{}), 32); + EXPECT_EQ(bit_width(uint64_t{}), 0); + EXPECT_EQ(bit_width(uint64_t{1}), 1); + EXPECT_EQ(bit_width(uint64_t{3}), 2); + EXPECT_EQ(bit_width(~uint64_t{}), 64); + + for (int i = 0; i < 8; i++) { + EXPECT_EQ(bit_width(static_cast<uint8_t>(uint8_t{1} << i)), i + 1); + } + + for (int i = 0; i < 16; i++) { + EXPECT_EQ(bit_width(static_cast<uint16_t>(uint16_t{1} << i)), i + 1); + } + + for (int i = 0; i < 32; i++) { + EXPECT_EQ(bit_width(uint32_t{1} << i), i + 1); + } + + for (int i = 0; i < 64; i++) { + EXPECT_EQ(bit_width(uint64_t{1} << i), i + 1); + } +} + +// On GCC and Clang, anticiapte that implementations will be constexpr +#if defined(__GNUC__) +static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT, + "popcount should be constexpr"); +static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CLZ, "clz should be constexpr"); +static_assert(ABSL_INTERNAL_HAS_CONSTEXPR_CTZ, "ctz should be constexpr"); +#endif + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/numeric/int128.cc b/third_party/abseil-cpp/absl/numeric/int128.cc index b605a87042..17d88744ae 100644 --- a/third_party/abseil-cpp/absl/numeric/int128.cc +++ b/third_party/abseil-cpp/absl/numeric/int128.cc @@ -15,6 +15,7 @@ #include "absl/numeric/int128.h" #include <stddef.h> + #include <cassert> #include <iomanip> #include <ostream> // NOLINT(readability/streams) @@ -22,6 +23,9 @@ #include <string> #include <type_traits> +#include "absl/base/optimization.h" +#include "absl/numeric/bits.h" + namespace absl { ABSL_NAMESPACE_BEGIN @@ -31,44 +35,26 @@ ABSL_DLL const uint128 kuint128max = MakeUint128( namespace { // Returns the 0-based position of the last set bit (i.e., most significant bit) -// in the given uint64_t. The argument may not be 0. +// in the given uint128. The argument is not 0. // // For example: // Given: 5 (decimal) == 101 (binary) // Returns: 2 -#define STEP(T, n, pos, sh) \ - do { \ - if ((n) >= (static_cast<T>(1) << (sh))) { \ - (n) = (n) >> (sh); \ - (pos) |= (sh); \ - } \ - } while (0) -static inline int Fls64(uint64_t n) { - assert(n != 0); - int pos = 0; - STEP(uint64_t, n, pos, 0x20); - uint32_t n32 = static_cast<uint32_t>(n); - STEP(uint32_t, n32, pos, 0x10); - STEP(uint32_t, n32, pos, 0x08); - STEP(uint32_t, n32, pos, 0x04); - return pos + ((uint64_t{0x3333333322221100} >> (n32 << 2)) & 0x3); -} -#undef STEP - -// Like Fls64() above, but returns the 0-based position of the last set bit -// (i.e., most significant bit) in the given uint128. The argument may not be 0. -static inline int Fls128(uint128 n) { +inline ABSL_ATTRIBUTE_ALWAYS_INLINE int Fls128(uint128 n) { if (uint64_t hi = Uint128High64(n)) { - return Fls64(hi) + 64; + ABSL_INTERNAL_ASSUME(hi != 0); + return 127 - countl_zero(hi); } - return Fls64(Uint128Low64(n)); + const uint64_t low = Uint128Low64(n); + ABSL_INTERNAL_ASSUME(low != 0); + return 63 - countl_zero(low); } // Long division/modulo for uint128 implemented using the shift-subtract // division algorithm adapted from: // https://stackoverflow.com/questions/5386377/division-without-using -void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret, - uint128* remainder_ret) { +inline void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret, + uint128* remainder_ret) { assert(divisor != 0); if (divisor > dividend) { @@ -152,28 +138,21 @@ uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {} uint128::uint128(double v) : uint128(MakeUint128FromFloat(v)) {} uint128::uint128(long double v) : uint128(MakeUint128FromFloat(v)) {} +#if !defined(ABSL_HAVE_INTRINSIC_INT128) uint128 operator/(uint128 lhs, uint128 rhs) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return static_cast<unsigned __int128>(lhs) / - static_cast<unsigned __int128>(rhs); -#else // ABSL_HAVE_INTRINSIC_INT128 uint128 quotient = 0; uint128 remainder = 0; DivModImpl(lhs, rhs, "ient, &remainder); return quotient; -#endif // ABSL_HAVE_INTRINSIC_INT128 } + uint128 operator%(uint128 lhs, uint128 rhs) { -#if defined(ABSL_HAVE_INTRINSIC_INT128) - return static_cast<unsigned __int128>(lhs) % - static_cast<unsigned __int128>(rhs); -#else // ABSL_HAVE_INTRINSIC_INT128 uint128 quotient = 0; uint128 remainder = 0; DivModImpl(lhs, rhs, "ient, &remainder); return remainder; -#endif // ABSL_HAVE_INTRINSIC_INT128 } +#endif // !defined(ABSL_HAVE_INTRINSIC_INT128) namespace { diff --git a/third_party/abseil-cpp/absl/numeric/int128.h b/third_party/abseil-cpp/absl/numeric/int128.h index 636e3a5bc7..c7ad96befd 100644 --- a/third_party/abseil-cpp/absl/numeric/int128.h +++ b/third_party/abseil-cpp/absl/numeric/int128.h @@ -18,6 +18,10 @@ // ----------------------------------------------------------------------------- // // This header file defines 128-bit integer types, `uint128` and `int128`. +// +// TODO(absl-team): This module is inconsistent as many inline `uint128` methods +// are defined in this file, while many inline `int128` methods are defined in +// the `int128_*_intrinsic.inc` files. #ifndef ABSL_NUMERIC_INT128_H_ #define ABSL_NUMERIC_INT128_H_ @@ -582,10 +586,10 @@ inline uint128& uint128::operator=(int128 v) { // Arithmetic operators. -uint128 operator<<(uint128 lhs, int amount); -uint128 operator>>(uint128 lhs, int amount); -uint128 operator+(uint128 lhs, uint128 rhs); -uint128 operator-(uint128 lhs, uint128 rhs); +constexpr uint128 operator<<(uint128 lhs, int amount); +constexpr uint128 operator>>(uint128 lhs, int amount); +constexpr uint128 operator+(uint128 lhs, uint128 rhs); +constexpr uint128 operator-(uint128 lhs, uint128 rhs); uint128 operator*(uint128 lhs, uint128 rhs); uint128 operator/(uint128 lhs, uint128 rhs); uint128 operator%(uint128 lhs, uint128 rhs); @@ -782,137 +786,192 @@ inline uint128::operator long double() const { // Comparison operators. -inline bool operator==(uint128 lhs, uint128 rhs) { +constexpr bool operator==(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast<unsigned __int128>(lhs) == + static_cast<unsigned __int128>(rhs); +#else return (Uint128Low64(lhs) == Uint128Low64(rhs) && Uint128High64(lhs) == Uint128High64(rhs)); +#endif } -inline bool operator!=(uint128 lhs, uint128 rhs) { - return !(lhs == rhs); -} +constexpr bool operator!=(uint128 lhs, uint128 rhs) { return !(lhs == rhs); } -inline bool operator<(uint128 lhs, uint128 rhs) { +constexpr bool operator<(uint128 lhs, uint128 rhs) { +#ifdef ABSL_HAVE_INTRINSIC_INT128 + return static_cast<unsigned __int128>(lhs) < + static_cast<unsigned __int128>(rhs); +#else return (Uint128High64(lhs) == Uint128High64(rhs)) ? (Uint128Low64(lhs) < Uint128Low64(rhs)) : (Uint128High64(lhs) < Uint128High64(rhs)); +#endif } -inline bool operator>(uint128 lhs, uint128 rhs) { - return (Uint128High64(lhs) == Uint128High64(rhs)) - ? (Uint128Low64(lhs) > Uint128Low64(rhs)) - : (Uint128High64(lhs) > Uint128High64(rhs)); -} +constexpr bool operator>(uint128 lhs, uint128 rhs) { return rhs < lhs; } -inline bool operator<=(uint128 lhs, uint128 rhs) { - return (Uint128High64(lhs) == Uint128High64(rhs)) - ? (Uint128Low64(lhs) <= Uint128Low64(rhs)) - : (Uint128High64(lhs) <= Uint128High64(rhs)); -} +constexpr bool operator<=(uint128 lhs, uint128 rhs) { return !(rhs < lhs); } -inline bool operator>=(uint128 lhs, uint128 rhs) { - return (Uint128High64(lhs) == Uint128High64(rhs)) - ? (Uint128Low64(lhs) >= Uint128Low64(rhs)) - : (Uint128High64(lhs) >= Uint128High64(rhs)); -} +constexpr bool operator>=(uint128 lhs, uint128 rhs) { return !(lhs < rhs); } // Unary operators. -inline uint128 operator-(uint128 val) { - uint64_t hi = ~Uint128High64(val); - uint64_t lo = ~Uint128Low64(val) + 1; - if (lo == 0) ++hi; // carry - return MakeUint128(hi, lo); +constexpr inline uint128 operator+(uint128 val) { + return val; } -inline bool operator!(uint128 val) { +constexpr inline int128 operator+(int128 val) { + return val; +} + +constexpr uint128 operator-(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return -static_cast<unsigned __int128>(val); +#else + return MakeUint128( + ~Uint128High64(val) + static_cast<unsigned long>(Uint128Low64(val) == 0), + ~Uint128Low64(val) + 1); +#endif +} + +constexpr inline bool operator!(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return !static_cast<unsigned __int128>(val); +#else return !Uint128High64(val) && !Uint128Low64(val); +#endif } // Logical operators. -inline uint128 operator~(uint128 val) { +constexpr inline uint128 operator~(uint128 val) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return ~static_cast<unsigned __int128>(val); +#else return MakeUint128(~Uint128High64(val), ~Uint128Low64(val)); +#endif } -inline uint128 operator|(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator|(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast<unsigned __int128>(lhs) | + static_cast<unsigned __int128>(rhs); +#else return MakeUint128(Uint128High64(lhs) | Uint128High64(rhs), - Uint128Low64(lhs) | Uint128Low64(rhs)); + Uint128Low64(lhs) | Uint128Low64(rhs)); +#endif } -inline uint128 operator&(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator&(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast<unsigned __int128>(lhs) & + static_cast<unsigned __int128>(rhs); +#else return MakeUint128(Uint128High64(lhs) & Uint128High64(rhs), - Uint128Low64(lhs) & Uint128Low64(rhs)); + Uint128Low64(lhs) & Uint128Low64(rhs)); +#endif } -inline uint128 operator^(uint128 lhs, uint128 rhs) { +constexpr inline uint128 operator^(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast<unsigned __int128>(lhs) ^ + static_cast<unsigned __int128>(rhs); +#else return MakeUint128(Uint128High64(lhs) ^ Uint128High64(rhs), - Uint128Low64(lhs) ^ Uint128Low64(rhs)); + Uint128Low64(lhs) ^ Uint128Low64(rhs)); +#endif } inline uint128& uint128::operator|=(uint128 other) { - hi_ |= other.hi_; - lo_ |= other.lo_; + *this = *this | other; return *this; } inline uint128& uint128::operator&=(uint128 other) { - hi_ &= other.hi_; - lo_ &= other.lo_; + *this = *this & other; return *this; } inline uint128& uint128::operator^=(uint128 other) { - hi_ ^= other.hi_; - lo_ ^= other.lo_; + *this = *this ^ other; return *this; } // Arithmetic operators. -inline uint128 operator<<(uint128 lhs, int amount) { +constexpr uint128 operator<<(uint128 lhs, int amount) { +#ifdef ABSL_HAVE_INTRINSIC_INT128 + return static_cast<unsigned __int128>(lhs) << amount; +#else // uint64_t shifts of >= 64 are undefined, so we will need some // special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeUint128( - (Uint128High64(lhs) << amount) | (Uint128Low64(lhs) >> (64 - amount)), - Uint128Low64(lhs) << amount); - } - return lhs; - } - return MakeUint128(Uint128Low64(lhs) << (amount - 64), 0); + return amount >= 64 ? MakeUint128(Uint128Low64(lhs) << (amount - 64), 0) + : amount == 0 ? lhs + : MakeUint128((Uint128High64(lhs) << amount) | + (Uint128Low64(lhs) >> (64 - amount)), + Uint128Low64(lhs) << amount); +#endif } -inline uint128 operator>>(uint128 lhs, int amount) { +constexpr uint128 operator>>(uint128 lhs, int amount) { +#ifdef ABSL_HAVE_INTRINSIC_INT128 + return static_cast<unsigned __int128>(lhs) >> amount; +#else // uint64_t shifts of >= 64 are undefined, so we will need some // special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeUint128(Uint128High64(lhs) >> amount, - (Uint128Low64(lhs) >> amount) | - (Uint128High64(lhs) << (64 - amount))); - } - return lhs; - } - return MakeUint128(0, Uint128High64(lhs) >> (amount - 64)); + return amount >= 64 ? MakeUint128(0, Uint128High64(lhs) >> (amount - 64)) + : amount == 0 ? lhs + : MakeUint128(Uint128High64(lhs) >> amount, + (Uint128Low64(lhs) >> amount) | + (Uint128High64(lhs) << (64 - amount))); +#endif } -inline uint128 operator+(uint128 lhs, uint128 rhs) { - uint128 result = MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), - Uint128Low64(lhs) + Uint128Low64(rhs)); - if (Uint128Low64(result) < Uint128Low64(lhs)) { // check for carry - return MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)); - } - return result; +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 AddResult(uint128 result, uint128 lhs) { + // check for carry + return (Uint128Low64(result) < Uint128Low64(lhs)) + ? MakeUint128(Uint128High64(result) + 1, Uint128Low64(result)) + : result; +} +} // namespace int128_internal +#endif + +constexpr uint128 operator+(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast<unsigned __int128>(lhs) + + static_cast<unsigned __int128>(rhs); +#else + return int128_internal::AddResult( + MakeUint128(Uint128High64(lhs) + Uint128High64(rhs), + Uint128Low64(lhs) + Uint128Low64(rhs)), + lhs); +#endif } -inline uint128 operator-(uint128 lhs, uint128 rhs) { - uint128 result = MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), - Uint128Low64(lhs) - Uint128Low64(rhs)); - if (Uint128Low64(lhs) < Uint128Low64(rhs)) { // check for carry - return MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)); - } - return result; +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace int128_internal { +constexpr uint128 SubstructResult(uint128 result, uint128 lhs, uint128 rhs) { + // check for carry + return (Uint128Low64(lhs) < Uint128Low64(rhs)) + ? MakeUint128(Uint128High64(result) - 1, Uint128Low64(result)) + : result; +} +} // namespace int128_internal +#endif + +constexpr uint128 operator-(uint128 lhs, uint128 rhs) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return static_cast<unsigned __int128>(lhs) - + static_cast<unsigned __int128>(rhs); +#else + return int128_internal::SubstructResult( + MakeUint128(Uint128High64(lhs) - Uint128High64(rhs), + Uint128Low64(lhs) - Uint128Low64(rhs)), + lhs, rhs); +#endif } inline uint128 operator*(uint128 lhs, uint128 rhs) { @@ -942,6 +1001,18 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { #endif // ABSL_HAVE_INTRINSIC128 } +#if defined(ABSL_HAVE_INTRINSIC_INT128) +inline uint128 operator/(uint128 lhs, uint128 rhs) { + return static_cast<unsigned __int128>(lhs) / + static_cast<unsigned __int128>(rhs); +} + +inline uint128 operator%(uint128 lhs, uint128 rhs) { + return static_cast<unsigned __int128>(lhs) % + static_cast<unsigned __int128>(rhs); +} +#endif + // Increment/decrement operators. inline uint128 uint128::operator++(int) { @@ -999,17 +1070,17 @@ inline int128& int128::operator=(unsigned long long v) { } // Arithmetic operators. - -int128 operator+(int128 lhs, int128 rhs); -int128 operator-(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 v); +constexpr int128 operator+(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 lhs, int128 rhs); int128 operator*(int128 lhs, int128 rhs); int128 operator/(int128 lhs, int128 rhs); int128 operator%(int128 lhs, int128 rhs); -int128 operator|(int128 lhs, int128 rhs); -int128 operator&(int128 lhs, int128 rhs); -int128 operator^(int128 lhs, int128 rhs); -int128 operator<<(int128 lhs, int amount); -int128 operator>>(int128 lhs, int amount); +constexpr int128 operator|(int128 lhs, int128 rhs); +constexpr int128 operator&(int128 lhs, int128 rhs); +constexpr int128 operator^(int128 lhs, int128 rhs); +constexpr int128 operator<<(int128 lhs, int amount); +constexpr int128 operator>>(int128 lhs, int amount); inline int128& int128::operator+=(int128 other) { *this = *this + other; @@ -1061,6 +1132,9 @@ inline int128& int128::operator>>=(int amount) { return *this; } +// Forward declaration for comparison operators. +constexpr bool operator!=(int128 lhs, int128 rhs); + namespace int128_internal { // Casts from unsigned to signed while preserving the underlying binary diff --git a/third_party/abseil-cpp/absl/numeric/int128_benchmark.cc b/third_party/abseil-cpp/absl/numeric/int128_benchmark.cc index a5502d927c..eab1515c0a 100644 --- a/third_party/abseil-cpp/absl/numeric/int128_benchmark.cc +++ b/third_party/abseil-cpp/absl/numeric/int128_benchmark.cc @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/numeric/int128.h" - #include <algorithm> #include <cstdint> +#include <limits> #include <random> #include <vector> #include "benchmark/benchmark.h" #include "absl/base/config.h" +#include "absl/numeric/int128.h" namespace { @@ -32,57 +32,85 @@ std::mt19937 MakeRandomEngine() { return std::mt19937(seed); } -std::vector<std::pair<absl::uint128, absl::uint128>> -GetRandomClass128SampleUniformDivisor() { - std::vector<std::pair<absl::uint128, absl::uint128>> values; +template <typename T, + typename H = typename std::conditional< + std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type> +std::vector<std::pair<T, T>> GetRandomClass128SampleUniformDivisor() { + std::vector<std::pair<T, T>> values; std::mt19937 random = MakeRandomEngine(); - std::uniform_int_distribution<uint64_t> uniform_uint64; + std::uniform_int_distribution<H> uniform_h; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - absl::uint128 a = - absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); - absl::uint128 b = - absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); - values.emplace_back(std::max(a, b), - std::max(absl::uint128(2), std::min(a, b))); + T a{absl::MakeUint128(uniform_h(random), uniform_h(random))}; + T b{absl::MakeUint128(uniform_h(random), uniform_h(random))}; + values.emplace_back(std::max(a, b), std::max(T(2), std::min(a, b))); } return values; } +template <typename T> void BM_DivideClass128UniformDivisor(benchmark::State& state) { - auto values = GetRandomClass128SampleUniformDivisor(); + auto values = GetRandomClass128SampleUniformDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideClass128UniformDivisor); +BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_DivideClass128UniformDivisor, absl::int128); + +template <typename T> +void BM_RemainderClass128UniformDivisor(benchmark::State& state) { + auto values = GetRandomClass128SampleUniformDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_RemainderClass128UniformDivisor, absl::int128); -std::vector<std::pair<absl::uint128, uint64_t>> -GetRandomClass128SampleSmallDivisor() { - std::vector<std::pair<absl::uint128, uint64_t>> values; +template <typename T, + typename H = typename std::conditional< + std::numeric_limits<T>::is_signed, int64_t, uint64_t>::type> +std::vector<std::pair<T, H>> GetRandomClass128SampleSmallDivisor() { + std::vector<std::pair<T, H>> values; std::mt19937 random = MakeRandomEngine(); - std::uniform_int_distribution<uint64_t> uniform_uint64; + std::uniform_int_distribution<H> uniform_h; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - absl::uint128 a = - absl::MakeUint128(uniform_uint64(random), uniform_uint64(random)); - uint64_t b = std::max(uint64_t{2}, uniform_uint64(random)); - values.emplace_back(std::max(a, absl::uint128(b)), b); + T a{absl::MakeUint128(uniform_h(random), uniform_h(random))}; + H b{std::max(H{2}, uniform_h(random))}; + values.emplace_back(std::max(a, T(b)), b); } return values; } +template <typename T> void BM_DivideClass128SmallDivisor(benchmark::State& state) { - auto values = GetRandomClass128SampleSmallDivisor(); + auto values = GetRandomClass128SampleSmallDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideClass128SmallDivisor); +BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_DivideClass128SmallDivisor, absl::int128); + +template <typename T> +void BM_RemainderClass128SmallDivisor(benchmark::State& state) { + auto values = GetRandomClass128SampleSmallDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::uint128); +BENCHMARK_TEMPLATE(BM_RemainderClass128SmallDivisor, absl::int128); std::vector<std::pair<absl::uint128, absl::uint128>> GetRandomClass128Sample() { std::vector<std::pair<absl::uint128, absl::uint128>> values; @@ -121,74 +149,107 @@ BENCHMARK(BM_AddClass128); // Some implementations of <random> do not support __int128 when it is // available, so we make our own uniform_int_distribution-like type. +template <typename T, + typename H = typename std::conditional< + std::is_same<T, __int128>::value, int64_t, uint64_t>::type> class UniformIntDistribution128 { public: // NOLINTNEXTLINE: mimicking std::uniform_int_distribution API - unsigned __int128 operator()(std::mt19937& generator) { - return (static_cast<unsigned __int128>(dist64_(generator)) << 64) | - dist64_(generator); + T operator()(std::mt19937& generator) { + return (static_cast<T>(dist64_(generator)) << 64) | dist64_(generator); } private: - std::uniform_int_distribution<uint64_t> dist64_; + std::uniform_int_distribution<H> dist64_; }; -std::vector<std::pair<unsigned __int128, unsigned __int128>> -GetRandomIntrinsic128SampleUniformDivisor() { - std::vector<std::pair<unsigned __int128, unsigned __int128>> values; +template <typename T, + typename H = typename std::conditional< + std::is_same<T, __int128>::value, int64_t, uint64_t>::type> +std::vector<std::pair<T, T>> GetRandomIntrinsic128SampleUniformDivisor() { + std::vector<std::pair<T, T>> values; std::mt19937 random = MakeRandomEngine(); - UniformIntDistribution128 uniform_uint128; + UniformIntDistribution128<T> uniform_128; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - unsigned __int128 a = uniform_uint128(random); - unsigned __int128 b = uniform_uint128(random); - values.emplace_back( - std::max(a, b), - std::max(static_cast<unsigned __int128>(2), std::min(a, b))); + T a = uniform_128(random); + T b = uniform_128(random); + values.emplace_back(std::max(a, b), + std::max(static_cast<T>(2), std::min(a, b))); } return values; } +template <typename T> void BM_DivideIntrinsic128UniformDivisor(benchmark::State& state) { - auto values = GetRandomIntrinsic128SampleUniformDivisor(); + auto values = GetRandomIntrinsic128SampleUniformDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideIntrinsic128UniformDivisor); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128UniformDivisor, __int128); + +template <typename T> +void BM_RemainderIntrinsic128UniformDivisor(benchmark::State& state) { + auto values = GetRandomIntrinsic128SampleUniformDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128UniformDivisor, __int128); -std::vector<std::pair<unsigned __int128, uint64_t>> -GetRandomIntrinsic128SampleSmallDivisor() { - std::vector<std::pair<unsigned __int128, uint64_t>> values; +template <typename T, + typename H = typename std::conditional< + std::is_same<T, __int128>::value, int64_t, uint64_t>::type> +std::vector<std::pair<T, H>> GetRandomIntrinsic128SampleSmallDivisor() { + std::vector<std::pair<T, H>> values; std::mt19937 random = MakeRandomEngine(); - UniformIntDistribution128 uniform_uint128; - std::uniform_int_distribution<uint64_t> uniform_uint64; + UniformIntDistribution128<T> uniform_int128; + std::uniform_int_distribution<H> uniform_int64; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { - unsigned __int128 a = uniform_uint128(random); - uint64_t b = std::max(uint64_t{2}, uniform_uint64(random)); - values.emplace_back(std::max(a, static_cast<unsigned __int128>(b)), b); + T a = uniform_int128(random); + H b = std::max(H{2}, uniform_int64(random)); + values.emplace_back(std::max(a, static_cast<T>(b)), b); } return values; } +template <typename T> void BM_DivideIntrinsic128SmallDivisor(benchmark::State& state) { - auto values = GetRandomIntrinsic128SampleSmallDivisor(); + auto values = GetRandomIntrinsic128SampleSmallDivisor<T>(); while (state.KeepRunningBatch(values.size())) { for (const auto& pair : values) { benchmark::DoNotOptimize(pair.first / pair.second); } } } -BENCHMARK(BM_DivideIntrinsic128SmallDivisor); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_DivideIntrinsic128SmallDivisor, __int128); + +template <typename T> +void BM_RemainderIntrinsic128SmallDivisor(benchmark::State& state) { + auto values = GetRandomIntrinsic128SampleSmallDivisor<T>(); + while (state.KeepRunningBatch(values.size())) { + for (const auto& pair : values) { + benchmark::DoNotOptimize(pair.first % pair.second); + } + } +} +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, unsigned __int128); +BENCHMARK_TEMPLATE(BM_RemainderIntrinsic128SmallDivisor, __int128); std::vector<std::pair<unsigned __int128, unsigned __int128>> GetRandomIntrinsic128Sample() { std::vector<std::pair<unsigned __int128, unsigned __int128>> values; std::mt19937 random = MakeRandomEngine(); - UniformIntDistribution128 uniform_uint128; + UniformIntDistribution128<unsigned __int128> uniform_uint128; values.reserve(kSampleSize); for (size_t i = 0; i < kSampleSize; ++i) { values.emplace_back(uniform_uint128(random), uniform_uint128(random)); diff --git a/third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc b/third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc index d6c76dd320..3945fa2983 100644 --- a/third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc +++ b/third_party/abseil-cpp/absl/numeric/int128_have_intrinsic.inc @@ -155,7 +155,7 @@ constexpr int128::operator unsigned __int128() const { #if defined(__clang__) && !defined(__ppc64__) inline int128::operator float() const { return static_cast<float>(v_); } -inline int128::operator double () const { return static_cast<double>(v_); } +inline int128::operator double() const { return static_cast<double>(v_); } inline int128::operator long double() const { return static_cast<long double>(v_); @@ -163,8 +163,8 @@ inline int128::operator long double() const { #else // Clang on PowerPC // Forward declaration for conversion operators to floating point types. -int128 operator-(int128 v); -bool operator!=(int128 lhs, int128 rhs); +constexpr int128 operator-(int128 v); +constexpr bool operator!=(int128 lhs, int128 rhs); inline int128::operator float() const { // We must convert the absolute value and then negate as needed, because @@ -199,51 +199,45 @@ inline int128::operator long double() const { // Comparison operators. -inline bool operator==(int128 lhs, int128 rhs) { +constexpr bool operator==(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) == static_cast<__int128>(rhs); } -inline bool operator!=(int128 lhs, int128 rhs) { +constexpr bool operator!=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) != static_cast<__int128>(rhs); } -inline bool operator<(int128 lhs, int128 rhs) { +constexpr bool operator<(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) < static_cast<__int128>(rhs); } -inline bool operator>(int128 lhs, int128 rhs) { +constexpr bool operator>(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) > static_cast<__int128>(rhs); } -inline bool operator<=(int128 lhs, int128 rhs) { +constexpr bool operator<=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs); } -inline bool operator>=(int128 lhs, int128 rhs) { +constexpr bool operator>=(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs); } // Unary operators. -inline int128 operator-(int128 v) { - return -static_cast<__int128>(v); -} +constexpr int128 operator-(int128 v) { return -static_cast<__int128>(v); } -inline bool operator!(int128 v) { - return !static_cast<__int128>(v); -} +constexpr bool operator!(int128 v) { return !static_cast<__int128>(v); } -inline int128 operator~(int128 val) { - return ~static_cast<__int128>(val); -} +constexpr int128 operator~(int128 val) { return ~static_cast<__int128>(val); } // Arithmetic operators. -inline int128 operator+(int128 lhs, int128 rhs) { +constexpr int128 operator+(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) + static_cast<__int128>(rhs); } -inline int128 operator-(int128 lhs, int128 rhs) { +constexpr int128 operator-(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) - static_cast<__int128>(rhs); } @@ -281,22 +275,22 @@ inline int128& int128::operator--() { return *this; } -inline int128 operator|(int128 lhs, int128 rhs) { +constexpr int128 operator|(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) | static_cast<__int128>(rhs); } -inline int128 operator&(int128 lhs, int128 rhs) { +constexpr int128 operator&(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) & static_cast<__int128>(rhs); } -inline int128 operator^(int128 lhs, int128 rhs) { +constexpr int128 operator^(int128 lhs, int128 rhs) { return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs); } -inline int128 operator<<(int128 lhs, int amount) { +constexpr int128 operator<<(int128 lhs, int amount) { return static_cast<__int128>(lhs) << amount; } -inline int128 operator>>(int128 lhs, int amount) { +constexpr int128 operator>>(int128 lhs, int amount) { return static_cast<__int128>(lhs) >> amount; } diff --git a/third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc b/third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc index c753771ae7..8834804cec 100644 --- a/third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc +++ b/third_party/abseil-cpp/absl/numeric/int128_no_intrinsic.inc @@ -134,10 +134,6 @@ constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int) return static_cast<unsigned long long>(lo_); // NOLINT(runtime/int) } -// Forward declaration for conversion operators to floating point types. -int128 operator-(int128 v); -bool operator!=(int128 lhs, int128 rhs); - inline int128::operator float() const { // We must convert the absolute value and then negate as needed, because // floating point types are typically sign-magnitude. Otherwise, the @@ -169,76 +165,80 @@ inline int128::operator long double() const { // Comparison operators. -inline bool operator==(int128 lhs, int128 rhs) { +constexpr bool operator==(int128 lhs, int128 rhs) { return (Int128Low64(lhs) == Int128Low64(rhs) && Int128High64(lhs) == Int128High64(rhs)); } -inline bool operator!=(int128 lhs, int128 rhs) { - return !(lhs == rhs); -} +constexpr bool operator!=(int128 lhs, int128 rhs) { return !(lhs == rhs); } -inline bool operator<(int128 lhs, int128 rhs) { +constexpr bool operator<(int128 lhs, int128 rhs) { return (Int128High64(lhs) == Int128High64(rhs)) ? (Int128Low64(lhs) < Int128Low64(rhs)) : (Int128High64(lhs) < Int128High64(rhs)); } -inline bool operator>(int128 lhs, int128 rhs) { +constexpr bool operator>(int128 lhs, int128 rhs) { return (Int128High64(lhs) == Int128High64(rhs)) ? (Int128Low64(lhs) > Int128Low64(rhs)) : (Int128High64(lhs) > Int128High64(rhs)); } -inline bool operator<=(int128 lhs, int128 rhs) { - return !(lhs > rhs); -} +constexpr bool operator<=(int128 lhs, int128 rhs) { return !(lhs > rhs); } -inline bool operator>=(int128 lhs, int128 rhs) { - return !(lhs < rhs); -} +constexpr bool operator>=(int128 lhs, int128 rhs) { return !(lhs < rhs); } // Unary operators. -inline int128 operator-(int128 v) { - int64_t hi = ~Int128High64(v); - uint64_t lo = ~Int128Low64(v) + 1; - if (lo == 0) ++hi; // carry - return MakeInt128(hi, lo); +constexpr int128 operator-(int128 v) { + return MakeInt128(~Int128High64(v) + (Int128Low64(v) == 0), + ~Int128Low64(v) + 1); } -inline bool operator!(int128 v) { +constexpr bool operator!(int128 v) { return !Int128Low64(v) && !Int128High64(v); } -inline int128 operator~(int128 val) { +constexpr int128 operator~(int128 val) { return MakeInt128(~Int128High64(val), ~Int128Low64(val)); } // Arithmetic operators. -inline int128 operator+(int128 lhs, int128 rhs) { - int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs), - Int128Low64(lhs) + Int128Low64(rhs)); - if (Int128Low64(result) < Int128Low64(lhs)) { // check for carry - return MakeInt128(Int128High64(result) + 1, Int128Low64(result)); - } - return result; +namespace int128_internal { +constexpr int128 SignedAddResult(int128 result, int128 lhs) { + // check for carry + return (Int128Low64(result) < Int128Low64(lhs)) + ? MakeInt128(Int128High64(result) + 1, Int128Low64(result)) + : result; +} +} // namespace int128_internal +constexpr int128 operator+(int128 lhs, int128 rhs) { + return int128_internal::SignedAddResult( + MakeInt128(Int128High64(lhs) + Int128High64(rhs), + Int128Low64(lhs) + Int128Low64(rhs)), + lhs); } -inline int128 operator-(int128 lhs, int128 rhs) { - int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs), - Int128Low64(lhs) - Int128Low64(rhs)); - if (Int128Low64(lhs) < Int128Low64(rhs)) { // check for carry - return MakeInt128(Int128High64(result) - 1, Int128Low64(result)); - } - return result; +namespace int128_internal { +constexpr int128 SignedSubstructResult(int128 result, int128 lhs, int128 rhs) { + // check for carry + return (Int128Low64(lhs) < Int128Low64(rhs)) + ? MakeInt128(Int128High64(result) - 1, Int128Low64(result)) + : result; +} +} // namespace int128_internal +constexpr int128 operator-(int128 lhs, int128 rhs) { + return int128_internal::SignedSubstructResult( + MakeInt128(Int128High64(lhs) - Int128High64(rhs), + Int128Low64(lhs) - Int128Low64(rhs)), + lhs, rhs); } inline int128 operator*(int128 lhs, int128 rhs) { - uint128 result = uint128(lhs) * rhs; - return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)), - Uint128Low64(result)); + return MakeInt128( + int128_internal::BitCastToSigned(Uint128High64(uint128(lhs) * rhs)), + Uint128Low64(uint128(lhs) * rhs)); } inline int128 int128::operator++(int) { @@ -263,46 +263,49 @@ inline int128& int128::operator--() { return *this; } -inline int128 operator|(int128 lhs, int128 rhs) { +constexpr int128 operator|(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) | Int128High64(rhs), Int128Low64(lhs) | Int128Low64(rhs)); } -inline int128 operator&(int128 lhs, int128 rhs) { +constexpr int128 operator&(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) & Int128High64(rhs), Int128Low64(lhs) & Int128Low64(rhs)); } -inline int128 operator^(int128 lhs, int128 rhs) { +constexpr int128 operator^(int128 lhs, int128 rhs) { return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs), Int128Low64(lhs) ^ Int128Low64(rhs)); } -inline int128 operator<<(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeInt128( - (Int128High64(lhs) << amount) | - static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)), - Int128Low64(lhs) << amount); - } - return lhs; - } - return MakeInt128(static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0); -} - -inline int128 operator>>(int128 lhs, int amount) { - // uint64_t shifts of >= 64 are undefined, so we need some special-casing. - if (amount < 64) { - if (amount != 0) { - return MakeInt128( - Int128High64(lhs) >> amount, - (Int128Low64(lhs) >> amount) | - (static_cast<uint64_t>(Int128High64(lhs)) << (64 - amount))); - } - return lhs; - } - return MakeInt128(0, - static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64))); +constexpr int128 operator<<(int128 lhs, int amount) { + // int64_t shifts of >= 64 are undefined, so we need some special-casing. + return amount >= 64 + ? MakeInt128( + static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0) + : amount == 0 + ? lhs + : MakeInt128( + (Int128High64(lhs) << amount) | + static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)), + Int128Low64(lhs) << amount); +} + +constexpr int128 operator>>(int128 lhs, int amount) { + // int64_t shifts of >= 64 are undefined, so we need some special-casing. + // The (Int128High64(lhs) >> 32) >> 32 "trick" causes the the most significant + // int64 to be inititialized with all zeros or all ones correctly. It takes + // into account whether the number is negative or positive, and whether the + // current architecture does arithmetic or logical right shifts for negative + // numbers. + return amount >= 64 + ? MakeInt128( + (Int128High64(lhs) >> 32) >> 32, + static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64))) + : amount == 0 + ? lhs + : MakeInt128(Int128High64(lhs) >> amount, + (Int128Low64(lhs) >> amount) | + (static_cast<uint64_t>(Int128High64(lhs)) + << (64 - amount))); } diff --git a/third_party/abseil-cpp/absl/numeric/int128_test.cc b/third_party/abseil-cpp/absl/numeric/int128_test.cc index bc86c714ac..dd9425d77a 100644 --- a/third_party/abseil-cpp/absl/numeric/int128_test.cc +++ b/third_party/abseil-cpp/absl/numeric/int128_test.cc @@ -226,6 +226,11 @@ TEST(Uint128, AllTests) { EXPECT_EQ(test >>= 1, one); EXPECT_EQ(test <<= 1, two); + EXPECT_EQ(big, +big); + EXPECT_EQ(two, +two); + EXPECT_EQ(absl::Uint128Max(), +absl::Uint128Max()); + EXPECT_EQ(zero, +zero); + EXPECT_EQ(big, -(-big)); EXPECT_EQ(two, -((-one) - 1)); EXPECT_EQ(absl::Uint128Max(), -one); @@ -234,6 +239,24 @@ TEST(Uint128, AllTests) { EXPECT_EQ(absl::Uint128Max(), absl::kuint128max); } +TEST(Int128, RightShiftOfNegativeNumbers) { + absl::int128 minus_six = -6; + absl::int128 minus_three = -3; + absl::int128 minus_two = -2; + absl::int128 minus_one = -1; + if ((-6 >> 1) == -3) { + // Right shift is arithmetic (sign propagates) + EXPECT_EQ(minus_six >> 1, minus_three); + EXPECT_EQ(minus_six >> 2, minus_two); + EXPECT_EQ(minus_six >> 65, minus_one); + } else { + // Right shift is logical (zeros shifted in at MSB) + EXPECT_EQ(minus_six >> 1, absl::int128(absl::uint128(minus_six) >> 1)); + EXPECT_EQ(minus_six >> 2, absl::int128(absl::uint128(minus_six) >> 2)); + EXPECT_EQ(minus_six >> 65, absl::int128(absl::uint128(minus_six) >> 65)); + } +} + TEST(Uint128, ConversionTests) { EXPECT_TRUE(absl::MakeUint128(1, 0)); @@ -769,6 +792,19 @@ TEST(Int128, ComparisonTest) { } } +TEST(Int128, UnaryPlusTest) { + int64_t values64[] = {0, 1, 12345, 0x4000000000000000, + std::numeric_limits<int64_t>::max()}; + for (int64_t value : values64) { + SCOPED_TRACE(::testing::Message() << "value = " << value); + + EXPECT_EQ(absl::int128(value), +absl::int128(value)); + EXPECT_EQ(absl::int128(-value), +absl::int128(-value)); + EXPECT_EQ(absl::MakeInt128(value, 0), +absl::MakeInt128(value, 0)); + EXPECT_EQ(absl::MakeInt128(-value, 0), +absl::MakeInt128(-value, 0)); + } +} + TEST(Int128, UnaryNegationTest) { int64_t values64[] = {0, 1, 12345, 0x4000000000000000, std::numeric_limits<int64_t>::max()}; diff --git a/third_party/abseil-cpp/absl/numeric/internal/bits.h b/third_party/abseil-cpp/absl/numeric/internal/bits.h new file mode 100644 index 0000000000..bfef06bce1 --- /dev/null +++ b/third_party/abseil-cpp/absl/numeric/internal/bits.h @@ -0,0 +1,358 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_NUMERIC_INTERNAL_BITS_H_ +#define ABSL_NUMERIC_INTERNAL_BITS_H_ + +#include <cstdint> +#include <limits> +#include <type_traits> + +// Clang on Windows has __builtin_clzll; otherwise we need to use the +// windows intrinsic functions. +#if defined(_MSC_VER) && !defined(__clang__) +#include <intrin.h> +#endif + +#include "absl/base/attributes.h" +#include "absl/base/config.h" + +#if defined(__GNUC__) && !defined(__clang__) +// GCC +#define ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(x) 1 +#else +#define ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(x) ABSL_HAVE_BUILTIN(x) +#endif + +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcountl) && \ + ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcountll) +#define ABSL_INTERNAL_CONSTEXPR_POPCOUNT constexpr +#define ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT 1 +#else +#define ABSL_INTERNAL_CONSTEXPR_POPCOUNT +#define ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT 0 +#endif + +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clz) && \ + ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clzll) +#define ABSL_INTERNAL_CONSTEXPR_CLZ constexpr +#define ABSL_INTERNAL_HAS_CONSTEXPR_CLZ 1 +#else +#define ABSL_INTERNAL_CONSTEXPR_CLZ +#define ABSL_INTERNAL_HAS_CONSTEXPR_CLZ 0 +#endif + +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctz) && \ + ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctzll) +#define ABSL_INTERNAL_CONSTEXPR_CTZ constexpr +#define ABSL_INTERNAL_HAS_CONSTEXPR_CTZ 1 +#else +#define ABSL_INTERNAL_CONSTEXPR_CTZ +#define ABSL_INTERNAL_HAS_CONSTEXPR_CTZ 0 +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace numeric_internal { + +constexpr bool IsPowerOf2(unsigned int x) noexcept { + return x != 0 && (x & (x - 1)) == 0; +} + +template <class T> +ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_ALWAYS_INLINE constexpr T RotateRight( + T x, int s) noexcept { + static_assert(std::is_unsigned<T>::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits<T>::digits), + "T must have a power-of-2 size"); + + return static_cast<T>(x >> (s & (std::numeric_limits<T>::digits - 1))) | + static_cast<T>(x << ((-s) & (std::numeric_limits<T>::digits - 1))); +} + +template <class T> +ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_ALWAYS_INLINE constexpr T RotateLeft( + T x, int s) noexcept { + static_assert(std::is_unsigned<T>::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits<T>::digits), + "T must have a power-of-2 size"); + + return static_cast<T>(x << (s & (std::numeric_limits<T>::digits - 1))) | + static_cast<T>(x >> ((-s) & (std::numeric_limits<T>::digits - 1))); +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int +Popcount32(uint32_t x) noexcept { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcount) + static_assert(sizeof(unsigned int) == sizeof(x), + "__builtin_popcount does not take 32-bit arg"); + return __builtin_popcount(x); +#else + x -= ((x >> 1) & 0x55555555); + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + return static_cast<int>((((x + (x >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24); +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int +Popcount64(uint64_t x) noexcept { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_popcountll) + static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int) + "__builtin_popcount does not take 64-bit arg"); + return __builtin_popcountll(x); +#else + x -= (x >> 1) & 0x5555555555555555ULL; + x = ((x >> 2) & 0x3333333333333333ULL) + (x & 0x3333333333333333ULL); + return static_cast<int>( + (((x + (x >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56); +#endif +} + +template <class T> +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int +Popcount(T x) noexcept { + static_assert(std::is_unsigned<T>::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits<T>::digits), + "T must have a power-of-2 size"); + static_assert(sizeof(x) <= sizeof(uint64_t), "T is too large"); + return sizeof(x) <= sizeof(uint32_t) ? Popcount32(x) : Popcount64(x); +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes32(uint32_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clz) + // Use __builtin_clz, which uses the following instructions: + // x86: bsr, lzcnt + // ARM64: clz + // PPC: cntlzd + + static_assert(sizeof(unsigned int) == sizeof(x), + "__builtin_clz does not take 32-bit arg"); + // Handle 0 as a special case because __builtin_clz(0) is undefined. + return x == 0 ? 32 : __builtin_clz(x); +#elif defined(_MSC_VER) && !defined(__clang__) + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse(&result, x)) { + return 31 - result; + } + return 32; +#else + int zeroes = 28; + if (x >> 16) { + zeroes -= 16; + x >>= 16; + } + if (x >> 8) { + zeroes -= 8; + x >>= 8; + } + if (x >> 4) { + zeroes -= 4; + x >>= 4; + } + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[x] + zeroes; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes16(uint16_t x) { +#if ABSL_HAVE_BUILTIN(__builtin_clzs) + static_assert(sizeof(unsigned short) == sizeof(x), // NOLINT(runtime/int) + "__builtin_clzs does not take 16-bit arg"); + return x == 0 ? 16 : __builtin_clzs(x); +#else + return CountLeadingZeroes32(x) - 16; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes64(uint64_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_clzll) + // Use __builtin_clzll, which uses the following instructions: + // x86: bsr, lzcnt + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int) + "__builtin_clzll does not take 64-bit arg"); + + // Handle 0 as a special case because __builtin_clzll(0) is undefined. + return x == 0 ? 64 : __builtin_clzll(x); +#elif defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_X64) || defined(_M_ARM64)) + // MSVC does not have __buitin_clzll. Use _BitScanReverse64. + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse64(&result, x)) { + return 63 - result; + } + return 64; +#elif defined(_MSC_VER) && !defined(__clang__) + // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse + unsigned long result = 0; // NOLINT(runtime/int) + if ((x >> 32) && + _BitScanReverse(&result, static_cast<unsigned long>(x >> 32))) { + return 31 - result; + } + if (_BitScanReverse(&result, static_cast<unsigned long>(x))) { + return 63 - result; + } + return 64; +#else + int zeroes = 60; + if (x >> 32) { + zeroes -= 32; + x >>= 32; + } + if (x >> 16) { + zeroes -= 16; + x >>= 16; + } + if (x >> 8) { + zeroes -= 8; + x >>= 8; + } + if (x >> 4) { + zeroes -= 4; + x >>= 4; + } + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[x] + zeroes; +#endif +} + +template <typename T> +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int +CountLeadingZeroes(T x) { + static_assert(std::is_unsigned<T>::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits<T>::digits), + "T must have a power-of-2 size"); + static_assert(sizeof(T) <= sizeof(uint64_t), "T too large"); + return sizeof(T) <= sizeof(uint16_t) + ? CountLeadingZeroes16(static_cast<uint16_t>(x)) - + (std::numeric_limits<uint16_t>::digits - + std::numeric_limits<T>::digits) + : (sizeof(T) <= sizeof(uint32_t) + ? CountLeadingZeroes32(static_cast<uint32_t>(x)) - + (std::numeric_limits<uint32_t>::digits - + std::numeric_limits<T>::digits) + : CountLeadingZeroes64(x)); +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroesNonzero32(uint32_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctz) + static_assert(sizeof(unsigned int) == sizeof(x), + "__builtin_ctz does not take 32-bit arg"); + return __builtin_ctz(x); +#elif defined(_MSC_VER) && !defined(__clang__) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward(&result, x); + return result; +#else + int c = 31; + x &= ~x + 1; + if (x & 0x0000FFFF) c -= 16; + if (x & 0x00FF00FF) c -= 8; + if (x & 0x0F0F0F0F) c -= 4; + if (x & 0x33333333) c -= 2; + if (x & 0x55555555) c -= 1; + return c; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroesNonzero64(uint64_t x) { +#if ABSL_NUMERIC_INTERNAL_HAVE_BUILTIN_OR_GCC(__builtin_ctzll) + static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int) + "__builtin_ctzll does not take 64-bit arg"); + return __builtin_ctzll(x); +#elif defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_X64) || defined(_M_ARM64)) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward64(&result, x); + return result; +#elif defined(_MSC_VER) && !defined(__clang__) + unsigned long result = 0; // NOLINT(runtime/int) + if (static_cast<uint32_t>(x) == 0) { + _BitScanForward(&result, static_cast<unsigned long>(x >> 32)); + return result + 32; + } + _BitScanForward(&result, static_cast<unsigned long>(x)); + return result; +#else + int c = 63; + x &= ~x + 1; + if (x & 0x00000000FFFFFFFF) c -= 32; + if (x & 0x0000FFFF0000FFFF) c -= 16; + if (x & 0x00FF00FF00FF00FF) c -= 8; + if (x & 0x0F0F0F0F0F0F0F0F) c -= 4; + if (x & 0x3333333333333333) c -= 2; + if (x & 0x5555555555555555) c -= 1; + return c; +#endif +} + +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroesNonzero16(uint16_t x) { +#if ABSL_HAVE_BUILTIN(__builtin_ctzs) + static_assert(sizeof(unsigned short) == sizeof(x), // NOLINT(runtime/int) + "__builtin_ctzs does not take 16-bit arg"); + return __builtin_ctzs(x); +#else + return CountTrailingZeroesNonzero32(x); +#endif +} + +template <class T> +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int +CountTrailingZeroes(T x) noexcept { + static_assert(std::is_unsigned<T>::value, "T must be unsigned"); + static_assert(IsPowerOf2(std::numeric_limits<T>::digits), + "T must have a power-of-2 size"); + static_assert(sizeof(T) <= sizeof(uint64_t), "T too large"); + return x == 0 ? std::numeric_limits<T>::digits + : (sizeof(T) <= sizeof(uint16_t) + ? CountTrailingZeroesNonzero16(static_cast<uint16_t>(x)) + : (sizeof(T) <= sizeof(uint32_t) + ? CountTrailingZeroesNonzero32( + static_cast<uint32_t>(x)) + : CountTrailingZeroesNonzero64(x))); +} + +// If T is narrower than unsigned, T{1} << bit_width will be promoted. We +// want to force it to wraparound so that bit_ceil of an invalid value are not +// core constant expressions. +template <class T> +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if<std::is_unsigned<T>::value, T>::type + BitCeilPromotionHelper(T x, T promotion) { + return (T{1} << (x + promotion)) >> promotion; +} + +template <class T> +ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline + typename std::enable_if<std::is_unsigned<T>::value, T>::type + BitCeilNonPowerOf2(T x) { + // If T is narrower than unsigned, it undergoes promotion to unsigned when we + // shift. We calculate the number of bits added by the wider type. + return BitCeilPromotionHelper( + static_cast<T>(std::numeric_limits<T>::digits - CountLeadingZeroes(x)), + T{sizeof(T) >= sizeof(unsigned) ? 0 + : std::numeric_limits<unsigned>::digits - + std::numeric_limits<T>::digits}); +} + +} // namespace numeric_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_NUMERIC_INTERNAL_BITS_H_ diff --git a/third_party/abseil-cpp/absl/numeric/internal/representation.h b/third_party/abseil-cpp/absl/numeric/internal/representation.h new file mode 100644 index 0000000000..82d332fdde --- /dev/null +++ b/third_party/abseil-cpp/absl/numeric/internal/representation.h @@ -0,0 +1,55 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_NUMERIC_INTERNAL_REPRESENTATION_H_ +#define ABSL_NUMERIC_INTERNAL_REPRESENTATION_H_ + +#include <limits> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace numeric_internal { + +// Returns true iff long double is represented as a pair of doubles added +// together. +inline constexpr bool IsDoubleDouble() { + // A double-double value always has exactly twice the precision of a double + // value--one double carries the high digits and one double carries the low + // digits. This property is not shared with any other common floating-point + // representation, so this test won't trigger false positives. For reference, + // this table gives the number of bits of precision of each common + // floating-point representation: + // + // type precision + // IEEE single 24 b + // IEEE double 53 + // x86 long double 64 + // double-double 106 + // IEEE quadruple 113 + // + // Note in particular that a quadruple-precision float has greater precision + // than a double-double float despite taking up the same amount of memory; the + // quad has more of its bits allocated to the mantissa than the double-double + // has. + return std::numeric_limits<long double>::digits == + 2 * std::numeric_limits<double>::digits; +} + +} // namespace numeric_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_NUMERIC_INTERNAL_REPRESENTATION_H_ diff --git a/third_party/abseil-cpp/absl/profiling/BUILD.bazel b/third_party/abseil-cpp/absl/profiling/BUILD.bazel new file mode 100644 index 0000000000..496a06b276 --- /dev/null +++ b/third_party/abseil-cpp/absl/profiling/BUILD.bazel @@ -0,0 +1,126 @@ +# Copyright 2021 The Abseil Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:private"]) + +licenses(["notice"]) + +cc_library( + name = "sample_recorder", + hdrs = ["internal/sample_recorder.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/synchronization", + "//absl/time", + ], +) + +cc_test( + name = "sample_recorder_test", + srcs = ["internal/sample_recorder_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":sample_recorder", + "//absl/base:core_headers", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "exponential_biased", + srcs = ["internal/exponential_biased.cc"], + hdrs = ["internal/exponential_biased.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "exponential_biased_test", + size = "small", + srcs = ["internal/exponential_biased_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":exponential_biased", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "periodic_sampler", + srcs = ["internal/periodic_sampler.cc"], + hdrs = ["internal/periodic_sampler.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":exponential_biased", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "periodic_sampler_test", + size = "small", + srcs = ["internal/periodic_sampler_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":periodic_sampler", + "//absl/base:core_headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_binary( + name = "periodic_sampler_benchmark", + testonly = 1, + srcs = ["internal/periodic_sampler_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":periodic_sampler", + "//absl/base:core_headers", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/third_party/abseil-cpp/absl/profiling/CMakeLists.txt b/third_party/abseil-cpp/absl/profiling/CMakeLists.txt new file mode 100644 index 0000000000..9b3a71021c --- /dev/null +++ b/third_party/abseil-cpp/absl/profiling/CMakeLists.txt @@ -0,0 +1,93 @@ +# Copyright 2021 The Abseil Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +absl_cc_library( + NAME + sample_recorder + HDRS + "internal/sample_recorder.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::synchronization +) + +absl_cc_test( + NAME + sample_recorder_test + SRCS + "internal/sample_recorder_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::sample_recorder + absl::time + GTest::gmock_main +) + +absl_cc_library( + NAME + exponential_biased + SRCS + "internal/exponential_biased.cc" + HDRS + "internal/exponential_biased.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers +) + +absl_cc_test( + NAME + exponential_biased_test + SRCS + "internal/exponential_biased_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::exponential_biased + absl::strings + GTest::gmock_main +) + +absl_cc_library( + NAME + periodic_sampler + SRCS + "internal/periodic_sampler.cc" + HDRS + "internal/periodic_sampler.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::exponential_biased +) + +absl_cc_test( + NAME + periodic_sampler_test + SRCS + "internal/periodic_sampler_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::core_headers + absl::periodic_sampler + GTest::gmock_main +) + diff --git a/third_party/abseil-cpp/absl/base/internal/exponential_biased.cc b/third_party/abseil-cpp/absl/profiling/internal/exponential_biased.cc index 1b30c061e3..81d9a75765 100644 --- a/third_party/abseil-cpp/absl/base/internal/exponential_biased.cc +++ b/third_party/abseil-cpp/absl/profiling/internal/exponential_biased.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/base/internal/exponential_biased.h" +#include "absl/profiling/internal/exponential_biased.h" #include <stdint.h> @@ -26,7 +26,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN -namespace base_internal { +namespace profiling_internal { // The algorithm generates a random number between 0 and 1 and applies the // inverse cumulative distribution function for an exponential. Specifically: @@ -64,7 +64,7 @@ int64_t ExponentialBiased::GetSkipCount(int64_t mean) { // Assume huge values are bias neutral, retain bias for next call. return std::numeric_limits<int64_t>::max() / 2; } - double value = std::round(interval); + double value = std::rint(interval); bias_ = interval - value; return value; } @@ -88,6 +88,6 @@ void ExponentialBiased::Initialize() { initialized_ = true; } -} // namespace base_internal +} // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/exponential_biased.h b/third_party/abseil-cpp/absl/profiling/internal/exponential_biased.h index 94f79a3378..d31f7782e8 100644 --- a/third_party/abseil-cpp/absl/base/internal/exponential_biased.h +++ b/third_party/abseil-cpp/absl/profiling/internal/exponential_biased.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ -#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ +#ifndef ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_ +#define ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_ #include <stdint.h> @@ -22,7 +22,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN -namespace base_internal { +namespace profiling_internal { // ExponentialBiased provides a small and fast random number generator for a // rounded exponential distribution. This generator manages very little state, @@ -66,7 +66,7 @@ namespace base_internal { // Adjusting with rounding bias is relatively trivial: // // double value = bias_ + exponential_distribution(mean)(); -// double rounded_value = std::round(value); +// double rounded_value = std::rint(value); // bias_ = value - rounded_value; // return rounded_value; // @@ -123,8 +123,8 @@ inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { return (prng_mult * rnd + prng_add) & prng_mod_mask; } -} // namespace base_internal +} // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ +#endif // ABSL_PROFILING_INTERNAL_EXPONENTIAL_BIASED_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/exponential_biased_test.cc b/third_party/abseil-cpp/absl/profiling/internal/exponential_biased_test.cc index 90a482d2a9..5675001dd3 100644 --- a/third_party/abseil-cpp/absl/base/internal/exponential_biased_test.cc +++ b/third_party/abseil-cpp/absl/profiling/internal/exponential_biased_test.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/base/internal/exponential_biased.h" +#include "absl/profiling/internal/exponential_biased.h" #include <stddef.h> @@ -28,7 +28,7 @@ using ::testing::Ge; namespace absl { ABSL_NAMESPACE_BEGIN -namespace base_internal { +namespace profiling_internal { MATCHER_P2(IsBetween, a, b, absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a, @@ -185,7 +185,7 @@ TEST(ExponentialBiasedTest, InitializationModes) { ABSL_CONST_INIT static ExponentialBiased eb_static; EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0)); -#if ABSL_HAVE_THREAD_LOCAL +#ifdef ABSL_HAVE_THREAD_LOCAL thread_local ExponentialBiased eb_thread; EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0)); #endif @@ -194,6 +194,6 @@ TEST(ExponentialBiasedTest, InitializationModes) { EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0)); } -} // namespace base_internal +} // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/periodic_sampler.cc b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler.cc index 520dabbaa0..a738a82c86 100644 --- a/third_party/abseil-cpp/absl/base/internal/periodic_sampler.cc +++ b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler.cc @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/base/internal/periodic_sampler.h" +#include "absl/profiling/internal/periodic_sampler.h" #include <atomic> -#include "absl/base/internal/exponential_biased.h" +#include "absl/profiling/internal/exponential_biased.h" namespace absl { ABSL_NAMESPACE_BEGIN -namespace base_internal { +namespace profiling_internal { int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept { return rng_.GetStride(period); @@ -48,6 +48,6 @@ bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { return true; } -} // namespace base_internal +} // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/periodic_sampler.h b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler.h index f8a86796b1..54f0af452b 100644 --- a/third_party/abseil-cpp/absl/base/internal/periodic_sampler.h +++ b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler.h @@ -12,19 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ -#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ +#ifndef ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ +#define ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ #include <stdint.h> #include <atomic> -#include "absl/base/internal/exponential_biased.h" #include "absl/base/optimization.h" +#include "absl/profiling/internal/exponential_biased.h" namespace absl { ABSL_NAMESPACE_BEGIN -namespace base_internal { +namespace profiling_internal { // PeriodicSamplerBase provides the basic period sampler implementation. // @@ -149,7 +149,7 @@ class PeriodicSamplerBase { // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 uint64_t stride_ = 0; - ExponentialBiased rng_; + absl::profiling_internal::ExponentialBiased rng_; }; inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { @@ -204,8 +204,8 @@ class PeriodicSampler final : public PeriodicSamplerBase { template <typename Tag, int default_period> std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period); -} // namespace base_internal +} // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl -#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ +#endif // ABSL_PROFILING_INTERNAL_PERIODIC_SAMPLER_H_ diff --git a/third_party/abseil-cpp/absl/base/internal/periodic_sampler_benchmark.cc b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler_benchmark.cc index 5ad469ce79..8f0e5574c3 100644 --- a/third_party/abseil-cpp/absl/base/internal/periodic_sampler_benchmark.cc +++ b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler_benchmark.cc @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "absl/profiling/internal/periodic_sampler.h" #include "benchmark/benchmark.h" -#include "absl/base/internal/periodic_sampler.h" namespace absl { ABSL_NAMESPACE_BEGIN -namespace base_internal { +namespace profiling_internal { namespace { template <typename Sampler> @@ -74,6 +74,6 @@ void BM_PeriodicSampler_Disabled(benchmark::State& state) { BENCHMARK(BM_PeriodicSampler_Disabled); } // namespace -} // namespace base_internal +} // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/base/internal/periodic_sampler_test.cc b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler_test.cc index 3b301e37ab..ef986f3878 100644 --- a/third_party/abseil-cpp/absl/base/internal/periodic_sampler_test.cc +++ b/third_party/abseil-cpp/absl/profiling/internal/periodic_sampler_test.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/base/internal/periodic_sampler.h" +#include "absl/profiling/internal/periodic_sampler.h" #include <thread> // NOLINT(build/c++11) @@ -23,7 +23,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN -namespace base_internal { +namespace profiling_internal { namespace { using testing::Eq; @@ -172,6 +172,6 @@ TEST(PeriodicSamplerTest, SetGlobalPeriod) { } } // namespace -} // namespace base_internal +} // namespace profiling_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/profiling/internal/sample_recorder.h b/third_party/abseil-cpp/absl/profiling/internal/sample_recorder.h new file mode 100644 index 0000000000..5e04a9cd19 --- /dev/null +++ b/third_party/abseil-cpp/absl/profiling/internal/sample_recorder.h @@ -0,0 +1,230 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// File: sample_recorder.h +// ----------------------------------------------------------------------------- +// +// This header file defines a lock-free linked list for recording samples +// collected from a random/stochastic process. +// +// This utility is internal-only. Use at your own risk. + +#ifndef ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_ +#define ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_ + +#include <atomic> +#include <cstddef> +#include <functional> + +#include "absl/base/config.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +// Sample<T> that has members required for linking samples in the linked list of +// samples maintained by the SampleRecorder. Type T defines the sampled data. +template <typename T> +struct Sample { + // Guards the ability to restore the sample to a pristine state. This + // prevents races with sampling and resurrecting an object. + absl::Mutex init_mu; + T* next = nullptr; + T* dead ABSL_GUARDED_BY(init_mu) = nullptr; +}; + +// Holds samples and their associated stack traces with a soft limit of +// `SetHashtablezMaxSamples()`. +// +// Thread safe. +template <typename T> +class SampleRecorder { + public: + SampleRecorder(); + ~SampleRecorder(); + + // Registers for sampling. Returns an opaque registration info. + T* Register(); + + // Unregisters the sample. + void Unregister(T* sample); + + // The dispose callback will be called on all samples the moment they are + // being unregistered. Only affects samples that are unregistered after the + // callback has been set. + // Returns the previous callback. + using DisposeCallback = void (*)(const T&); + DisposeCallback SetDisposeCallback(DisposeCallback f); + + // Iterates over all the registered `StackInfo`s. Returning the number of + // samples that have been dropped. + int64_t Iterate(const std::function<void(const T& stack)>& f); + + void SetMaxSamples(int32_t max); + + private: + void PushNew(T* sample); + void PushDead(T* sample); + T* PopDead(); + + std::atomic<size_t> dropped_samples_; + std::atomic<size_t> size_estimate_; + std::atomic<int32_t> max_samples_{1 << 20}; + + // Intrusive lock free linked lists for tracking samples. + // + // `all_` records all samples (they are never removed from this list) and is + // terminated with a `nullptr`. + // + // `graveyard_.dead` is a circular linked list. When it is empty, + // `graveyard_.dead == &graveyard`. The list is circular so that + // every item on it (even the last) has a non-null dead pointer. This allows + // `Iterate` to determine if a given sample is live or dead using only + // information on the sample itself. + // + // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead + // looks like this (G is the Graveyard): + // + // +---+ +---+ +---+ +---+ +---+ + // all -->| A |--->| B |--->| C |--->| D |--->| E | + // | | | | | | | | | | + // +---+ | | +->| |-+ | | +->| |-+ | | + // | G | +---+ | +---+ | +---+ | +---+ | +---+ + // | | | | | | + // | | --------+ +--------+ | + // +---+ | + // ^ | + // +--------------------------------------+ + // + std::atomic<T*> all_; + T graveyard_; + + std::atomic<DisposeCallback> dispose_; +}; + +template <typename T> +typename SampleRecorder<T>::DisposeCallback +SampleRecorder<T>::SetDisposeCallback(DisposeCallback f) { + return dispose_.exchange(f, std::memory_order_relaxed); +} + +template <typename T> +SampleRecorder<T>::SampleRecorder() + : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) { + absl::MutexLock l(&graveyard_.init_mu); + graveyard_.dead = &graveyard_; +} + +template <typename T> +SampleRecorder<T>::~SampleRecorder() { + T* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + T* next = s->next; + delete s; + s = next; + } +} + +template <typename T> +void SampleRecorder<T>::PushNew(T* sample) { + sample->next = all_.load(std::memory_order_relaxed); + while (!all_.compare_exchange_weak(sample->next, sample, + std::memory_order_release, + std::memory_order_relaxed)) { + } +} + +template <typename T> +void SampleRecorder<T>::PushDead(T* sample) { + if (auto* dispose = dispose_.load(std::memory_order_relaxed)) { + dispose(*sample); + } + + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + absl::MutexLock sample_lock(&sample->init_mu); + sample->dead = graveyard_.dead; + graveyard_.dead = sample; +} + +template <typename T> +T* SampleRecorder<T>::PopDead() { + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + + // The list is circular, so eventually it collapses down to + // graveyard_.dead == &graveyard_ + // when it is empty. + T* sample = graveyard_.dead; + if (sample == &graveyard_) return nullptr; + + absl::MutexLock sample_lock(&sample->init_mu); + graveyard_.dead = sample->dead; + sample->dead = nullptr; + sample->PrepareForSampling(); + return sample; +} + +template <typename T> +T* SampleRecorder<T>::Register() { + int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); + if (size > max_samples_.load(std::memory_order_relaxed)) { + size_estimate_.fetch_sub(1, std::memory_order_relaxed); + dropped_samples_.fetch_add(1, std::memory_order_relaxed); + return nullptr; + } + + T* sample = PopDead(); + if (sample == nullptr) { + // Resurrection failed. Hire a new warlock. + sample = new T(); + PushNew(sample); + } + + return sample; +} + +template <typename T> +void SampleRecorder<T>::Unregister(T* sample) { + PushDead(sample); + size_estimate_.fetch_sub(1, std::memory_order_relaxed); +} + +template <typename T> +int64_t SampleRecorder<T>::Iterate( + const std::function<void(const T& stack)>& f) { + T* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + absl::MutexLock l(&s->init_mu); + if (s->dead == nullptr) { + f(*s); + } + s = s->next; + } + + return dropped_samples_.load(std::memory_order_relaxed); +} + +template <typename T> +void SampleRecorder<T>::SetMaxSamples(int32_t max) { + max_samples_.store(max, std::memory_order_release); +} + +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_PROFILING_INTERNAL_SAMPLE_RECORDER_H_ diff --git a/third_party/abseil-cpp/absl/profiling/internal/sample_recorder_test.cc b/third_party/abseil-cpp/absl/profiling/internal/sample_recorder_test.cc new file mode 100644 index 0000000000..ec6e0fa22d --- /dev/null +++ b/third_party/abseil-cpp/absl/profiling/internal/sample_recorder_test.cc @@ -0,0 +1,171 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/profiling/internal/sample_recorder.h" + +#include <atomic> +#include <random> +#include <vector> + +#include "gmock/gmock.h" +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace profiling_internal { + +namespace { +using ::absl::synchronization_internal::ThreadPool; +using ::testing::IsEmpty; +using ::testing::UnorderedElementsAre; + +struct Info : public Sample<Info> { + public: + void PrepareForSampling() {} + std::atomic<size_t> size; + absl::Time create_time; +}; + +std::vector<size_t> GetSizes(SampleRecorder<Info>* s) { + std::vector<size_t> res; + s->Iterate([&](const Info& info) { + res.push_back(info.size.load(std::memory_order_acquire)); + }); + return res; +} + +Info* Register(SampleRecorder<Info>* s, size_t size) { + auto* info = s->Register(); + assert(info != nullptr); + info->size.store(size); + return info; +} + +TEST(SampleRecorderTest, Registration) { + SampleRecorder<Info> sampler; + auto* info1 = Register(&sampler, 1); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1)); + + auto* info2 = Register(&sampler, 2); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2)); + info1->size.store(3); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2)); + + sampler.Unregister(info1); + sampler.Unregister(info2); +} + +TEST(SampleRecorderTest, Unregistration) { + SampleRecorder<Info> sampler; + std::vector<Info*> infos; + for (size_t i = 0; i < 3; ++i) { + infos.push_back(Register(&sampler, i)); + } + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2)); + + sampler.Unregister(infos[1]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2)); + + infos.push_back(Register(&sampler, 3)); + infos.push_back(Register(&sampler, 4)); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4)); + sampler.Unregister(infos[3]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4)); + + sampler.Unregister(infos[0]); + sampler.Unregister(infos[2]); + sampler.Unregister(infos[4]); + EXPECT_THAT(GetSizes(&sampler), IsEmpty()); +} + +TEST(SampleRecorderTest, MultiThreaded) { + SampleRecorder<Info> sampler; + Notification stop; + ThreadPool pool(10); + + for (int i = 0; i < 10; ++i) { + pool.Schedule([&sampler, &stop]() { + std::random_device rd; + std::mt19937 gen(rd()); + + std::vector<Info*> infoz; + while (!stop.HasBeenNotified()) { + if (infoz.empty()) { + infoz.push_back(sampler.Register()); + } + switch (std::uniform_int_distribution<>(0, 2)(gen)) { + case 0: { + infoz.push_back(sampler.Register()); + break; + } + case 1: { + size_t p = + std::uniform_int_distribution<>(0, infoz.size() - 1)(gen); + Info* info = infoz[p]; + infoz[p] = infoz.back(); + infoz.pop_back(); + sampler.Unregister(info); + break; + } + case 2: { + absl::Duration oldest = absl::ZeroDuration(); + sampler.Iterate([&](const Info& info) { + oldest = std::max(oldest, absl::Now() - info.create_time); + }); + ASSERT_GE(oldest, absl::ZeroDuration()); + break; + } + } + } + }); + } + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); +} + +TEST(SampleRecorderTest, Callback) { + SampleRecorder<Info> sampler; + + auto* info1 = Register(&sampler, 1); + auto* info2 = Register(&sampler, 2); + + static const Info* expected; + + auto callback = [](const Info& info) { + // We can't use `info` outside of this callback because the object will be + // disposed as soon as we return from here. + EXPECT_EQ(&info, expected); + }; + + // Set the callback. + EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr); + expected = info1; + sampler.Unregister(info1); + + // Unset the callback. + EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr)); + expected = nullptr; // no more calls. + sampler.Unregister(info2); +} + +} // namespace +} // namespace profiling_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/random/BUILD.bazel b/third_party/abseil-cpp/absl/random/BUILD.bazel index f78fbc7eed..fdde78b606 100644 --- a/third_party/abseil-cpp/absl/random/BUILD.bazel +++ b/third_party/abseil-cpp/absl/random/BUILD.bazel @@ -16,7 +16,6 @@ # ABSL random-number generation libraries. -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -53,7 +52,6 @@ cc_library( "bernoulli_distribution.h", "beta_distribution.h", "discrete_distribution.h", - "distribution_format_traits.h", "distributions.h", "exponential_distribution.h", "gaussian_distribution.h", @@ -70,7 +68,8 @@ cc_library( "//absl/base:config", "//absl/base:core_headers", "//absl/meta:type_traits", - "//absl/random/internal:distributions", + "//absl/numeric:bits", + "//absl/random/internal:distribution_caller", "//absl/random/internal:fast_uniform_bits", "//absl/random/internal:fastmath", "//absl/random/internal:generate_real", @@ -79,7 +78,6 @@ cc_library( "//absl/random/internal:uniform_helper", "//absl/random/internal:wide_multiply", "//absl/strings", - "//absl/types:span", ], ) @@ -117,11 +115,12 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":random", "//absl/base:core_headers", + "//absl/base:fast_type_id", "//absl/meta:type_traits", "//absl/random/internal:distribution_caller", "//absl/random/internal:fast_uniform_bits", - "//absl/random/internal:mocking_bit_gen_base", ], ) @@ -141,20 +140,17 @@ cc_library( cc_library( name = "mocking_bit_gen", testonly = 1, - srcs = [ - "mocking_bit_gen.cc", - ], hdrs = [ "mocking_bit_gen.h", ], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":distributions", - "//absl/base:raw_logging_internal", + ":random", + "//absl/base:fast_type_id", "//absl/container:flat_hash_map", "//absl/meta:type_traits", "//absl/random/internal:distribution_caller", - "//absl/random/internal:mocking_bit_gen_base", "//absl/strings", "//absl/types:span", "//absl/types:variant", @@ -173,6 +169,7 @@ cc_test( deps = [ ":distributions", ":random", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "@com_google_googletest//:gtest_main", ], @@ -190,7 +187,9 @@ cc_test( ":distributions", ":random", "//absl/base:raw_logging_internal", + "//absl/numeric:representation", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "//absl/strings:str_format", @@ -201,6 +200,7 @@ cc_test( cc_test( name = "distributions_test", size = "small", + timeout = "moderate", srcs = [ "distributions_test.cc", ], @@ -238,9 +238,9 @@ cc_test( deps = [ ":distributions", ":random", - "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "//absl/strings:str_format", @@ -261,6 +261,7 @@ cc_test( ":random", "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -288,6 +289,7 @@ cc_test( "//absl/base:raw_logging_internal", "//absl/container:flat_hash_map", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "//absl/strings:str_format", @@ -306,7 +308,9 @@ cc_test( ":random", "//absl/base:core_headers", "//absl/base:raw_logging_internal", + "//absl/numeric:representation", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "//absl/strings:str_format", @@ -328,6 +332,7 @@ cc_test( ":random", "//absl/base:core_headers", "//absl/base:raw_logging_internal", + "//absl/numeric:representation", "//absl/random/internal:distribution_test_util", "//absl/random/internal:sequence_urbg", "//absl/strings", @@ -350,6 +355,7 @@ cc_test( ":random", "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -373,7 +379,9 @@ cc_test( ":distributions", ":random", "//absl/base:raw_logging_internal", + "//absl/numeric:representation", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -393,6 +401,7 @@ cc_test( ":random", "//absl/base:raw_logging_internal", "//absl/random/internal:distribution_test_util", + "//absl/random/internal:pcg_engine", "//absl/random/internal:sequence_urbg", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -408,6 +417,7 @@ cc_test( deps = [ ":bit_gen_ref", ":random", + "//absl/base:fast_type_id", "//absl/random/internal:sequence_urbg", "@com_google_googletest//:gtest_main", ], @@ -417,6 +427,7 @@ cc_test( name = "mocking_bit_gen_test", size = "small", srcs = ["mocking_bit_gen_test.cc"], + copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bit_gen_ref", @@ -431,6 +442,8 @@ cc_test( name = "mock_distributions_test", size = "small", srcs = ["mock_distributions_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":mock_distributions", ":mocking_bit_gen", diff --git a/third_party/abseil-cpp/absl/random/CMakeLists.txt b/third_party/abseil-cpp/absl/random/CMakeLists.txt index efa55d8fa7..9d1c67fb33 100644 --- a/third_party/abseil-cpp/absl/random/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/random/CMakeLists.txt @@ -45,7 +45,6 @@ absl_cc_library( absl::core_headers absl::random_internal_distribution_caller absl::random_internal_fast_uniform_bits - absl::random_internal_mocking_bit_gen_base absl::type_traits ) @@ -62,23 +61,24 @@ absl_cc_test( absl::random_bit_gen_ref absl::random_random absl::random_internal_sequence_urbg - gmock - gtest_main + absl::fast_type_id + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. absl_cc_library( NAME - random_internal_mocking_bit_gen_base + random_internal_mock_helpers HDRS - "internal/mocking_bit_gen_base.h" + "internal/mock_helpers.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::random_random - absl::strings + absl::fast_type_id + absl::optional ) # Internal-only target, do not depend on directly. @@ -93,6 +93,7 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_mocking_bit_gen + absl::random_internal_mock_helpers TESTONLY ) @@ -102,8 +103,6 @@ absl_cc_library( HDRS "mock_distributions.h" "mocking_bit_gen.h" - SRCS - "mocking_bit_gen.cc" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS @@ -113,15 +112,15 @@ absl_cc_library( absl::raw_logging_internal absl::random_distributions absl::random_internal_distribution_caller - absl::random_internal_mocking_bit_gen_base absl::random_internal_mock_overload_set + absl::random_random absl::strings absl::span absl::type_traits absl::utility absl::variant - gmock - gtest + GTest::gmock + GTest::gtest TESTONLY ) @@ -137,8 +136,8 @@ absl_cc_test( DEPS absl::random_mocking_bit_gen absl::random_random - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -154,8 +153,8 @@ absl_cc_test( absl::random_bit_gen_ref absl::random_mocking_bit_gen absl::random_random - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_library( @@ -168,7 +167,6 @@ absl_cc_library( "bernoulli_distribution.h" "beta_distribution.h" "discrete_distribution.h" - "distribution_format_traits.h" "distributions.h" "exponential_distribution.h" "gaussian_distribution.h" @@ -186,7 +184,7 @@ absl_cc_library( absl::config absl::core_headers absl::random_internal_generate_real - absl::random_internal_distributions + absl::random_internal_distribution_caller absl::random_internal_fast_uniform_bits absl::random_internal_fastmath absl::random_internal_iostream_state_saver @@ -194,7 +192,6 @@ absl_cc_library( absl::random_internal_uniform_helper absl::random_internal_wide_multiply absl::strings - absl::span absl::type_traits ) @@ -247,8 +244,9 @@ absl_cc_test( absl::random_distributions absl::random_random absl::random_internal_sequence_urbg - gmock - gtest_main + absl::random_internal_pcg_engine + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -261,15 +259,17 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::numeric_representation absl::random_distributions absl::random_random absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg + absl::random_internal_pcg_engine absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -285,8 +285,8 @@ absl_cc_test( absl::random_distributions absl::random_random absl::random_internal_distribution_test_util - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -301,8 +301,8 @@ absl_cc_test( absl::random_distributions absl::random_random absl::raw_logging_internal - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -314,16 +314,16 @@ absl_cc_test( ${ABSL_TEST_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} - absl::core_headers absl::random_distributions absl::random_internal_distribution_test_util + absl::random_internal_pcg_engine absl::random_internal_sequence_urbg absl::random_random absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -338,12 +338,13 @@ absl_cc_test( DEPS absl::random_distributions absl::random_internal_distribution_test_util + absl::random_internal_pcg_engine absl::random_internal_sequence_urbg absl::random_random absl::raw_logging_internal absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -361,12 +362,13 @@ absl_cc_test( absl::core_headers absl::flat_hash_map absl::random_internal_distribution_test_util + absl::random_internal_pcg_engine absl::random_internal_sequence_urbg absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -380,15 +382,17 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::core_headers + absl::numeric_representation absl::random_distributions absl::random_internal_distribution_test_util + absl::random_internal_pcg_engine absl::random_internal_sequence_urbg absl::random_random absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -402,6 +406,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::core_headers + absl::numeric_representation absl::random_distributions absl::random_internal_distribution_test_util absl::random_internal_sequence_urbg @@ -409,8 +414,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::str_format - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -425,12 +430,13 @@ absl_cc_test( DEPS absl::random_distributions absl::random_internal_distribution_test_util + absl::random_internal_pcg_engine absl::random_internal_sequence_urbg absl::random_random absl::raw_logging_internal absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -443,13 +449,15 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::numeric_representation absl::random_distributions absl::random_internal_distribution_test_util + absl::random_internal_pcg_engine absl::random_internal_sequence_urbg absl::random_random absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -464,12 +472,13 @@ absl_cc_test( DEPS absl::random_distributions absl::random_internal_distribution_test_util + absl::random_internal_pcg_engine absl::random_internal_sequence_urbg absl::random_random absl::raw_logging_internal absl::strings - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) absl_cc_test( @@ -483,7 +492,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_random - gtest_main + GTest::gtest_main ) absl_cc_test( @@ -499,8 +508,8 @@ absl_cc_test( absl::random_seed_sequences absl::random_internal_nonsecure_base absl::random_random - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -529,27 +538,8 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config -) - -# Internal-only target, do not depend on directly. -absl_cc_library( - NAME - random_internal_distributions - HDRS - "internal/distributions.h" - COPTS - ${ABSL_DEFAULT_COPTS} - LINKOPTS - ${ABSL_DEFAULT_LINKOPTS} - DEPS - absl::random_internal_distribution_caller - absl::random_internal_fast_uniform_bits - absl::random_internal_fastmath - absl::random_internal_traits - absl::random_internal_uniform_helper - absl::span - absl::strings - absl::type_traits + absl::utility + absl::fast_type_id ) # Internal-only target, do not depend on directly. @@ -625,6 +615,7 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::config + absl::endian TESTONLY ) @@ -740,7 +731,6 @@ absl_cc_library( absl::random_internal_salted_seed_seq absl::random_internal_seed_material absl::span - absl::strings absl::type_traits ) @@ -773,6 +763,7 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::endian absl::random_internal_iostream_state_saver absl::random_internal_randen absl::raw_logging_internal @@ -785,8 +776,9 @@ absl_cc_library( random_internal_platform HDRS "internal/randen_traits.h" - "internal/randen-keys.inc" "internal/platform.h" + SRCS + "internal/randen_round_keys.cc" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS @@ -902,7 +894,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_traits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -919,7 +911,7 @@ absl_cc_test( absl::bits absl::flags absl::random_internal_generate_real - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -934,7 +926,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_distribution_test_util - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -949,7 +941,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_fastmath - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -965,8 +957,8 @@ absl_cc_test( DEPS absl::random_internal_explicit_seed_seq absl::random_seed_sequences - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -981,8 +973,8 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_salted_seed_seq - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -998,7 +990,7 @@ absl_cc_test( DEPS absl::core_headers absl::random_internal_distribution_test_util - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1013,7 +1005,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_fast_uniform_bits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1032,7 +1024,7 @@ absl_cc_test( absl::random_distributions absl::random_seed_sequences absl::strings - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1047,8 +1039,8 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_seed_material - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1065,7 +1057,7 @@ absl_cc_test( absl::random_internal_pool_urbg absl::span absl::type_traits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1082,8 +1074,8 @@ absl_cc_test( absl::random_internal_explicit_seed_seq absl::random_internal_pcg_engine absl::time - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1102,8 +1094,8 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::time - gmock - gtest_main + GTest::gmock + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1119,7 +1111,7 @@ absl_cc_test( DEPS absl::random_internal_randen absl::type_traits - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1133,8 +1125,9 @@ absl_cc_test( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::endian absl::random_internal_randen_slow - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1153,8 +1146,8 @@ absl_cc_test( absl::random_internal_randen_hwaes_impl absl::raw_logging_internal absl::str_format - gmock - gtest + GTest::gmock + GTest::gtest ) # Internal-only target, do not depend on directly. @@ -1168,9 +1161,7 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS - absl::core_headers - absl::random_internal_fast_uniform_bits - absl::random_internal_iostream_state_saver + absl::config absl::random_internal_traits absl::type_traits ) @@ -1178,6 +1169,21 @@ absl_cc_library( # Internal-only target, do not depend on directly. absl_cc_test( NAME + random_internal_uniform_helper_test + SRCS + "internal/uniform_helper_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_uniform_helper + GTest::gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME random_internal_iostream_state_saver_test SRCS "internal/iostream_state_saver_test.cc" @@ -1187,7 +1193,7 @@ absl_cc_test( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::random_internal_iostream_state_saver - gtest_main + GTest::gtest_main ) # Internal-only target, do not depend on directly. @@ -1204,5 +1210,5 @@ absl_cc_test( absl::random_internal_wide_multiply absl::bits absl::int128 - gtest_main + GTest::gtest_main ) diff --git a/third_party/abseil-cpp/absl/random/bernoulli_distribution_test.cc b/third_party/abseil-cpp/absl/random/bernoulli_distribution_test.cc index f2c3b99cd4..b250f8787c 100644 --- a/third_party/abseil-cpp/absl/random/bernoulli_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/bernoulli_distribution_test.cc @@ -21,6 +21,7 @@ #include <utility> #include "gtest/gtest.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" @@ -63,7 +64,10 @@ TEST_P(BernoulliTest, Accuracy) { size_t trials = para.second; double p = para.first; - absl::InsecureBitGen rng; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6); size_t yes = 0; absl::bernoulli_distribution dist(p); @@ -131,7 +135,7 @@ TEST(BernoulliTest, StabilityTest) { 0x275b0dc7e0a18acfull, 0x36cebe0d2653682eull, 0x0361e9b23861596bull, }); - // Generate a std::string of '0' and '1' for the distribution output. + // Generate a string of '0' and '1' for the distribution output. auto generate = [&urbg](absl::bernoulli_distribution& dist) { std::string output; output.reserve(36); @@ -176,7 +180,7 @@ TEST(BernoulliTest, StabilityTest2) { 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); - // Generate a std::string of '0' and '1' for the distribution output. + // Generate a string of '0' and '1' for the distribution output. auto generate = [&urbg](absl::bernoulli_distribution& dist) { std::string output; output.reserve(13); diff --git a/third_party/abseil-cpp/absl/random/beta_distribution_test.cc b/third_party/abseil-cpp/absl/random/beta_distribution_test.cc index d0111b3e3a..d980c969f7 100644 --- a/third_party/abseil-cpp/absl/random/beta_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/beta_distribution_test.cc @@ -15,20 +15,24 @@ #include "absl/random/beta_distribution.h" #include <algorithm> +#include <cfloat> #include <cstddef> #include <cstdint> #include <iterator> #include <random> #include <sstream> #include <string> +#include <type_traits> #include <unordered_map> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" +#include "absl/numeric/internal/representation.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -41,7 +45,15 @@ namespace { template <typename IntType> class BetaDistributionInterfaceTest : public ::testing::Test {}; -using RealTypes = ::testing::Types<float, double, long double>; +// double-double arithmetic is not supported well by either GCC or Clang; see +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048, +// https://bugs.llvm.org/show_bug.cgi?id=49131, and +// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests +// with double doubles until compiler support is better. +using RealTypes = + std::conditional<absl::numeric_internal::IsDoubleDouble(), + ::testing::Types<float, double>, + ::testing::Types<float, double, long double>>::type; TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes); TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { @@ -52,9 +64,6 @@ TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { const TypeParam kLargeA = std::exp(std::log((std::numeric_limits<TypeParam>::max)()) - std::log(std::log((std::numeric_limits<TypeParam>::max)()))); - const TypeParam kLargeAPPC = std::exp( - std::log((std::numeric_limits<TypeParam>::max)()) - - std::log(std::log((std::numeric_limits<TypeParam>::max)())) - 10.0f); using param_type = typename absl::beta_distribution<TypeParam>::param_type; constexpr int kCount = 1000; @@ -75,9 +84,6 @@ TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { kLargeA, // std::nextafter(kLargeA, TypeParam(0)), // std::nextafter(kLargeA, std::numeric_limits<TypeParam>::max()), - kLargeAPPC, // - std::nextafter(kLargeAPPC, TypeParam(0)), - std::nextafter(kLargeAPPC, std::numeric_limits<TypeParam>::max()), // Boundary cases. std::numeric_limits<TypeParam>::max(), std::numeric_limits<TypeParam>::epsilon(), @@ -124,28 +130,6 @@ TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { ss >> after; -#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ - defined(__ppc__) || defined(__PPC__) - if (std::is_same<TypeParam, long double>::value) { - // Roundtripping floating point values requires sufficient precision - // to reconstruct the exact value. It turns out that long double - // has some errors doing this on ppc. - if (alpha <= std::numeric_limits<double>::max() && - alpha >= std::numeric_limits<double>::lowest()) { - EXPECT_EQ(static_cast<double>(before.alpha()), - static_cast<double>(after.alpha())) - << ss.str(); - } - if (beta <= std::numeric_limits<double>::max() && - beta >= std::numeric_limits<double>::lowest()) { - EXPECT_EQ(static_cast<double>(before.beta()), - static_cast<double>(after.beta())) - << ss.str(); - } - continue; - } -#endif - EXPECT_EQ(before.alpha(), after.alpha()); EXPECT_EQ(before.beta(), after.beta()); EXPECT_EQ(before, after) // @@ -159,8 +143,12 @@ TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { } TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) { + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6); + // Extreme cases when the params are abnormal. - absl::InsecureBitGen gen; constexpr int kCount = 1000; const TypeParam kSmallValues[] = { std::numeric_limits<TypeParam>::min(), @@ -186,7 +174,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) { int ones = 0; absl::beta_distribution<TypeParam> d(alpha, beta); for (int i = 0; i < kCount; ++i) { - TypeParam x = d(gen); + TypeParam x = d(rng); if (x == 0.0) { zeros++; } else if (x == 1.0) { @@ -212,7 +200,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) { for (TypeParam beta : kLargeValues) { absl::beta_distribution<TypeParam> d(alpha, beta); for (int i = 0; i < kCount; ++i) { - EXPECT_EQ(d(gen), 0.0); + EXPECT_EQ(d(rng), 0.0); } } } @@ -227,7 +215,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) { for (TypeParam beta : kSmallValues) { absl::beta_distribution<TypeParam> d(alpha, beta); for (int i = 0; i < kCount; ++i) { - EXPECT_EQ(d(gen), 1.0); + EXPECT_EQ(d(rng), 1.0); } } } @@ -237,7 +225,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) { absl::beta_distribution<TypeParam> d(std::numeric_limits<TypeParam>::max(), std::numeric_limits<TypeParam>::max()); for (int i = 0; i < kCount; ++i) { - EXPECT_EQ(d(gen), 0.5); + EXPECT_EQ(d(rng), 0.5); } } { @@ -246,7 +234,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) { std::numeric_limits<TypeParam>::max(), std::numeric_limits<TypeParam>::max() * 0.9999); for (int i = 0; i < kCount; ++i) { - TypeParam x = d(gen); + TypeParam x = d(rng); EXPECT_NE(x, 0.5f); EXPECT_FLOAT_EQ(x, 0.500025f); } @@ -571,6 +559,14 @@ TEST(BetaDistributionTest, StabilityTest) { // dependencies of the distribution change, such as RandU64ToDouble, then this // is also likely to change. TEST(BetaDistributionTest, AlgorithmBounds) { +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations are + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + { absl::random_internal::sequence_urbg urbg( {0x7fbe76c8b4395800ull, 0x8000000000000000ull}); diff --git a/third_party/abseil-cpp/absl/random/bit_gen_ref.h b/third_party/abseil-cpp/absl/random/bit_gen_ref.h index e8771162e5..9555460fd4 100644 --- a/third_party/abseil-cpp/absl/random/bit_gen_ref.h +++ b/third_party/abseil-cpp/absl/random/bit_gen_ref.h @@ -24,11 +24,11 @@ #ifndef ABSL_RANDOM_BIT_GEN_REF_H_ #define ABSL_RANDOM_BIT_GEN_REF_H_ +#include "absl/base/internal/fast_type_id.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" #include "absl/random/internal/distribution_caller.h" #include "absl/random/internal/fast_uniform_bits.h" -#include "absl/random/internal/mocking_bit_gen_base.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -51,6 +51,10 @@ struct is_urbg< typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> : std::true_type {}; +template <typename> +struct DistributionCaller; +class MockHelpers; + } // namespace random_internal // ----------------------------------------------------------------------------- @@ -77,23 +81,50 @@ struct is_urbg< // } // class BitGenRef { - public: - using result_type = uint64_t; + // SFINAE to detect whether the URBG type includes a member matching + // bool InvokeMock(base_internal::FastTypeIdType, void*, void*). + // + // These live inside BitGenRef so that they have friend access + // to MockingBitGen. (see similar methods in DistributionCaller). + template <template <class...> class Trait, class AlwaysVoid, class... Args> + struct detector : std::false_type {}; + template <template <class...> class Trait, class... Args> + struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> + : std::true_type {}; + + template <class T> + using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( + std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(), + std::declval<void*>())); + + template <typename T> + using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type; - BitGenRef(const absl::BitGenRef&) = default; - BitGenRef(absl::BitGenRef&&) = default; - BitGenRef& operator=(const absl::BitGenRef&) = default; - BitGenRef& operator=(absl::BitGenRef&&) = default; + public: + BitGenRef(const BitGenRef&) = default; + BitGenRef(BitGenRef&&) = default; + BitGenRef& operator=(const BitGenRef&) = default; + BitGenRef& operator=(BitGenRef&&) = default; + + template <typename URBG, typename absl::enable_if_t< + (!std::is_same<URBG, BitGenRef>::value && + random_internal::is_urbg<URBG>::value && + !HasInvokeMock<URBG>::value)>* = nullptr> + BitGenRef(URBG& gen) // NOLINT + : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), + mock_call_(NotAMock), + generate_impl_fn_(ImplFn<URBG>) {} template <typename URBG, - typename absl::enable_if_t< - (!std::is_same<URBG, BitGenRef>::value && - random_internal::is_urbg<URBG>::value)>* = nullptr> + typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value && + random_internal::is_urbg<URBG>::value && + HasInvokeMock<URBG>::value)>* = nullptr> BitGenRef(URBG& gen) // NOLINT - : mocked_gen_ptr_(MakeMockPointer(&gen)), - t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), - generate_impl_fn_(ImplFn<URBG>) { - } + : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), + mock_call_(&MockCall<URBG>), + generate_impl_fn_(ImplFn<URBG>) {} + + using result_type = uint64_t; static constexpr result_type(min)() { return (std::numeric_limits<result_type>::min)(); @@ -106,14 +137,9 @@ class BitGenRef { result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } private: - friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>; using impl_fn = result_type (*)(uintptr_t); - using mocker_base_t = absl::random_internal::MockingBitGenBase; - - // Convert an arbitrary URBG pointer into either a valid mocker_base_t - // pointer or a nullptr. - static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; } - static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; } + using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*, + void*); template <typename URBG> static result_type ImplFn(uintptr_t ptr) { @@ -123,30 +149,32 @@ class BitGenRef { return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); } - mocker_base_t* mocked_gen_ptr_; + // Get a type-erased InvokeMock pointer. + template <typename URBG> + static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type, + void* result, void* arg_tuple) { + return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result, + arg_tuple); + } + static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*) { + return false; + } + + inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple, + void* result) { + if (mock_call_ == NotAMock) return false; // avoids an indirect call. + return mock_call_(t_erased_gen_ptr_, type, args_tuple, result); + } + uintptr_t t_erased_gen_ptr_; + mock_call_fn mock_call_; impl_fn generate_impl_fn_; -}; - -namespace random_internal { -template <> -struct DistributionCaller<absl::BitGenRef> { - template <typename DistrT, typename FormatT, typename... Args> - static typename DistrT::result_type Call(absl::BitGenRef* gen_ref, - Args&&... args) { - auto* mock_ptr = gen_ref->mocked_gen_ptr_; - if (mock_ptr == nullptr) { - DistrT dist(std::forward<Args>(args)...); - return dist(*gen_ref); - } else { - return mock_ptr->template Call<DistrT, FormatT>( - std::forward<Args>(args)...); - } - } + template <typename> + friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock + friend class ::absl::random_internal::MockHelpers; // for InvokeMock }; -} // namespace random_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/random/bit_gen_ref_test.cc b/third_party/abseil-cpp/absl/random/bit_gen_ref_test.cc index ca0e4d7072..1135cf2da0 100644 --- a/third_party/abseil-cpp/absl/random/bit_gen_ref_test.cc +++ b/third_party/abseil-cpp/absl/random/bit_gen_ref_test.cc @@ -17,30 +17,31 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/fast_type_id.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" namespace absl { ABSL_NAMESPACE_BEGIN -class ConstBitGen : public absl::random_internal::MockingBitGenBase { - bool CallImpl(const std::type_info&, void*, void* result) override { +class ConstBitGen { + public: + // URBG interface + using result_type = absl::BitGen::result_type; + + static constexpr result_type(min)() { return (absl::BitGen::min)(); } + static constexpr result_type(max)() { return (absl::BitGen::max)(); } + result_type operator()() { return 1; } + + // InvokeMock method + bool InvokeMock(base_internal::FastTypeIdType index, void*, void* result) { *static_cast<int*>(result) = 42; return true; } }; -namespace random_internal { -template <> -struct DistributionCaller<ConstBitGen> { - template <typename DistrT, typename FormatT, typename... Args> - static typename DistrT::result_type Call(ConstBitGen* gen, Args&&... args) { - return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...); - } -}; -} // namespace random_internal - namespace { + int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); } template <typename T> diff --git a/third_party/abseil-cpp/absl/random/discrete_distribution_test.cc b/third_party/abseil-cpp/absl/random/discrete_distribution_test.cc index 7296f0ac22..415b14cc76 100644 --- a/third_party/abseil-cpp/absl/random/discrete_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/discrete_distribution_test.cc @@ -29,6 +29,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -98,6 +99,7 @@ TYPED_TEST(DiscreteDistributionTypeTest, Constructor) { } TEST(DiscreteDistributionTest, InitDiscreteDistribution) { + using testing::_; using testing::Pair; { @@ -110,8 +112,8 @@ TEST(DiscreteDistributionTest, InitDiscreteDistribution) { // Each bucket is p=1/3, so bucket 0 will send half it's traffic // to bucket 2, while the rest will retain all of their traffic. EXPECT_THAT(q, testing::ElementsAre(Pair(0.5, 2), // - Pair(1.0, 1), // - Pair(1.0, 2))); + Pair(1.0, _), // + Pair(1.0, _))); } { @@ -134,7 +136,7 @@ TEST(DiscreteDistributionTest, InitDiscreteDistribution) { EXPECT_THAT(q, testing::ElementsAre(Pair(b0, 3), // Pair(b1, 3), // - Pair(1.0, 2), // + Pair(1.0, _), // Pair(b3, 2), // Pair(b1, 3))); } @@ -156,7 +158,10 @@ TEST(DiscreteDistributionTest, ChiSquaredTest50) { std::iota(std::begin(weights), std::end(weights), 1); absl::discrete_distribution<int> dist(std::begin(weights), std::end(weights)); - absl::InsecureBitGen rng; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng(0x2B7E151628AED2A6); std::vector<int32_t> counts(kBuckets, 0); for (size_t i = 0; i < kTrials; i++) { diff --git a/third_party/abseil-cpp/absl/random/distribution_format_traits.h b/third_party/abseil-cpp/absl/random/distribution_format_traits.h deleted file mode 100644 index 22b358cc8c..0000000000 --- a/third_party/abseil-cpp/absl/random/distribution_format_traits.h +++ /dev/null @@ -1,278 +0,0 @@ -// -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. -// -#ifndef ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ -#define ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ - -#include <string> -#include <tuple> -#include <typeinfo> - -#include "absl/meta/type_traits.h" -#include "absl/random/bernoulli_distribution.h" -#include "absl/random/beta_distribution.h" -#include "absl/random/exponential_distribution.h" -#include "absl/random/gaussian_distribution.h" -#include "absl/random/log_uniform_int_distribution.h" -#include "absl/random/poisson_distribution.h" -#include "absl/random/uniform_int_distribution.h" -#include "absl/random/uniform_real_distribution.h" -#include "absl/random/zipf_distribution.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" -#include "absl/strings/string_view.h" -#include "absl/types/span.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -struct IntervalClosedClosedTag; -struct IntervalClosedOpenTag; -struct IntervalOpenClosedTag; -struct IntervalOpenOpenTag; - -namespace random_internal { - -// ScalarTypeName defines a preferred hierarchy of preferred type names for -// scalars, and is evaluated at compile time for the specific type -// specialization. -template <typename T> -constexpr const char* ScalarTypeName() { - static_assert(std::is_integral<T>() || std::is_floating_point<T>(), ""); - // clang-format off - return - std::is_same<T, float>::value ? "float" : - std::is_same<T, double>::value ? "double" : - std::is_same<T, long double>::value ? "long double" : - std::is_same<T, bool>::value ? "bool" : - std::is_signed<T>::value && sizeof(T) == 1 ? "int8_t" : - std::is_signed<T>::value && sizeof(T) == 2 ? "int16_t" : - std::is_signed<T>::value && sizeof(T) == 4 ? "int32_t" : - std::is_signed<T>::value && sizeof(T) == 8 ? "int64_t" : - std::is_unsigned<T>::value && sizeof(T) == 1 ? "uint8_t" : - std::is_unsigned<T>::value && sizeof(T) == 2 ? "uint16_t" : - std::is_unsigned<T>::value && sizeof(T) == 4 ? "uint32_t" : - std::is_unsigned<T>::value && sizeof(T) == 8 ? "uint64_t" : - "undefined"; - // clang-format on - - // NOTE: It would be nice to use typeid(T).name(), but that's an - // implementation-defined attribute which does not necessarily - // correspond to a name. We could potentially demangle it - // using, e.g. abi::__cxa_demangle. -} - -// Distribution traits used by DistributionCaller and internal implementation -// details of the mocking framework. -/* -struct DistributionFormatTraits { - // Returns the parameterized name of the distribution function. - static constexpr const char* FunctionName() - // Format DistrT parameters. - static std::string FormatArgs(DistrT& dist); - // Format DistrT::result_type results. - static std::string FormatResults(DistrT& dist); -}; -*/ -template <typename DistrT> -struct DistributionFormatTraits; - -template <typename R> -struct DistributionFormatTraits<absl::uniform_int_distribution<R>> { - using distribution_t = absl::uniform_int_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Uniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat("absl::IntervalClosedClosed, ", (d.min)(), ", ", - (d.max)()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::uniform_real_distribution<R>> { - using distribution_t = absl::uniform_real_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Uniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat((d.min)(), ", ", (d.max)()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::exponential_distribution<R>> { - using distribution_t = absl::exponential_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Exponential"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.lambda()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::poisson_distribution<R>> { - using distribution_t = absl::poisson_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Poisson"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.mean()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <> -struct DistributionFormatTraits<absl::bernoulli_distribution> { - using distribution_t = absl::bernoulli_distribution; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Bernoulli"; } - - static constexpr const char* FunctionName() { return Name(); } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.p()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::beta_distribution<R>> { - using distribution_t = absl::beta_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Beta"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.alpha(), ", ", d.beta()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::zipf_distribution<R>> { - using distribution_t = absl::zipf_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Zipf"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat(d.k(), ", ", d.v(), ", ", d.q()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::gaussian_distribution<R>> { - using distribution_t = absl::gaussian_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "Gaussian"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrJoin(std::make_tuple(d.mean(), d.stddev()), ", "); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename R> -struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> { - using distribution_t = absl::log_uniform_int_distribution<R>; - using result_t = typename distribution_t::result_type; - - static constexpr const char* Name() { return "LogUniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrJoin(std::make_tuple((d.min)(), (d.max)(), d.base()), ", "); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -template <typename NumType> -struct UniformDistributionWrapper; - -template <typename NumType> -struct DistributionFormatTraits<UniformDistributionWrapper<NumType>> { - using distribution_t = UniformDistributionWrapper<NumType>; - using result_t = NumType; - - static constexpr const char* Name() { return "Uniform"; } - - static std::string FunctionName() { - return absl::StrCat(Name(), "<", ScalarTypeName<NumType>(), ">"); - } - static std::string FormatArgs(const distribution_t& d) { - return absl::StrCat((d.min)(), ", ", (d.max)()); - } - static std::string FormatResults(absl::Span<const result_t> results) { - return absl::StrJoin(results, ", "); - } -}; - -} // namespace random_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ diff --git a/third_party/abseil-cpp/absl/random/distributions.h b/third_party/abseil-cpp/absl/random/distributions.h index d026d92b90..31c79694e5 100644 --- a/third_party/abseil-cpp/absl/random/distributions.h +++ b/third_party/abseil-cpp/absl/random/distributions.h @@ -55,10 +55,9 @@ #include "absl/base/internal/inline_variable.h" #include "absl/random/bernoulli_distribution.h" #include "absl/random/beta_distribution.h" -#include "absl/random/distribution_format_traits.h" #include "absl/random/exponential_distribution.h" #include "absl/random/gaussian_distribution.h" -#include "absl/random/internal/distributions.h" // IWYU pragma: export +#include "absl/random/internal/distribution_caller.h" // IWYU pragma: export #include "absl/random/internal/uniform_helper.h" // IWYU pragma: export #include "absl/random/log_uniform_int_distribution.h" #include "absl/random/poisson_distribution.h" @@ -126,14 +125,13 @@ Uniform(TagType tag, R lo, R hi) { using gen_t = absl::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; auto a = random_internal::uniform_lower_bound(tag, lo, hi); auto b = random_internal::uniform_upper_bound(tag, lo, hi); - if (a > b) return a; + if (!random_internal::is_uniform_range_valid(a, b)) return lo; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, tag, lo, hi); + distribution_t>(&urbg, tag, lo, hi); } // absl::Uniform<T>(bitgen, lo, hi) @@ -146,15 +144,14 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { using gen_t = absl::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; - constexpr auto tag = absl::IntervalClosedOpen; + auto a = random_internal::uniform_lower_bound(tag, lo, hi); auto b = random_internal::uniform_upper_bound(tag, lo, hi); - if (a > b) return a; + if (!random_internal::is_uniform_range_valid(a, b)) return lo; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, lo, hi); + distribution_t>(&urbg, lo, hi); } // absl::Uniform(tag, bitgen, lo, hi) @@ -172,14 +169,13 @@ Uniform(TagType tag, using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; using distribution_t = random_internal::UniformDistributionWrapper<return_t>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); - if (a > b) return a; + if (!random_internal::is_uniform_range_valid(a, b)) return lo; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, tag, static_cast<return_t>(lo), + distribution_t>(&urbg, tag, static_cast<return_t>(lo), static_cast<return_t>(hi)); } @@ -196,15 +192,14 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using return_t = typename random_internal::uniform_inferred_return_t<A, B>; using distribution_t = random_internal::UniformDistributionWrapper<return_t>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; constexpr auto tag = absl::IntervalClosedOpen; auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi); auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi); - if (a > b) return a; + if (!random_internal::is_uniform_range_valid(a, b)) return lo; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, static_cast<return_t>(lo), + distribution_t>(&urbg, static_cast<return_t>(lo), static_cast<return_t>(hi)); } @@ -217,10 +212,9 @@ typename absl::enable_if_t<!std::is_signed<R>::value, R> // Uniform(URBG&& urbg) { // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = random_internal::UniformDistributionWrapper<R>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg); + distribution_t>(&urbg); } // ----------------------------------------------------------------------------- @@ -248,10 +242,9 @@ bool Bernoulli(URBG&& urbg, // NOLINT(runtime/references) double p) { using gen_t = absl::decay_t<URBG>; using distribution_t = absl::bernoulli_distribution; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, p); + distribution_t>(&urbg, p); } // ----------------------------------------------------------------------------- @@ -281,10 +274,9 @@ RealType Beta(URBG&& urbg, // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::beta_distribution<RealType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, alpha, beta); + distribution_t>(&urbg, alpha, beta); } // ----------------------------------------------------------------------------- @@ -314,10 +306,9 @@ RealType Exponential(URBG&& urbg, // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::exponential_distribution<RealType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, lambda); + distribution_t>(&urbg, lambda); } // ----------------------------------------------------------------------------- @@ -346,10 +337,9 @@ RealType Gaussian(URBG&& urbg, // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::gaussian_distribution<RealType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, mean, stddev); + distribution_t>(&urbg, mean, stddev); } // ----------------------------------------------------------------------------- @@ -389,10 +379,9 @@ IntType LogUniform(URBG&& urbg, // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::log_uniform_int_distribution<IntType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, lo, hi, base); + distribution_t>(&urbg, lo, hi, base); } // ----------------------------------------------------------------------------- @@ -420,10 +409,9 @@ IntType Poisson(URBG&& urbg, // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::poisson_distribution<IntType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, mean); + distribution_t>(&urbg, mean); } // ----------------------------------------------------------------------------- @@ -453,10 +441,9 @@ IntType Zipf(URBG&& urbg, // NOLINT(runtime/references) using gen_t = absl::decay_t<URBG>; using distribution_t = typename absl::zipf_distribution<IntType>; - using format_t = random_internal::DistributionFormatTraits<distribution_t>; return random_internal::DistributionCaller<gen_t>::template Call< - distribution_t, format_t>(&urbg, hi, q, v); + distribution_t>(&urbg, hi, q, v); } ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/random/distributions_test.cc b/third_party/abseil-cpp/absl/random/distributions_test.cc index 2d92723ad2..d3a5dd75e5 100644 --- a/third_party/abseil-cpp/absl/random/distributions_test.cc +++ b/third_party/abseil-cpp/absl/random/distributions_test.cc @@ -14,6 +14,7 @@ #include "absl/random/distributions.h" +#include <cfloat> #include <cmath> #include <cstdint> #include <random> @@ -29,94 +30,6 @@ constexpr int kSize = 400000; class RandomDistributionsTest : public testing::Test {}; -TEST_F(RandomDistributionsTest, UniformBoundFunctions) { - using absl::IntervalClosedClosed; - using absl::IntervalClosedOpen; - using absl::IntervalOpenClosed; - using absl::IntervalOpenOpen; - using absl::random_internal::uniform_lower_bound; - using absl::random_internal::uniform_upper_bound; - - // absl::uniform_int_distribution natively assumes IntervalClosedClosed - // absl::uniform_real_distribution natively assumes IntervalClosedOpen - - EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1); - EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1); - EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0); - EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0); - EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0); - EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0); - - EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0); - EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0); - EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0); - EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0); - EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0); - EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0); - - EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99); - EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99); - EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0); - EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0); - EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0); - EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0); - - EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100); - EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100); - EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0); - EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0); - EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0); - EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0); - - // Negative value tests - EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99); - EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99); - EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0); - EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0); - EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0); - EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0); - - EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100); - EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100); - EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0); - EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0); - EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0), - -2.0); - EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0); - - EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2); - EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2); - EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0); - EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0); - EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0); - EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0); - - EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1); - EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1); - EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0); - EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0); - EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0); - EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0), - -1.0); - - // Edge cases: the next value toward itself is itself. - const double d = 1.0; - const float f = 1.0; - EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, d, d), d); - EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, f, f), f); - - EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0); - EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0); - EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0); - EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0); - - EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0.0f, - std::numeric_limits<float>::max()), - std::numeric_limits<float>::max()); - EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0.0, - std::numeric_limits<double>::max()), - std::numeric_limits<double>::max()); -} struct Invalid {}; @@ -284,7 +197,9 @@ TEST_F(RandomDistributionsTest, UniformTypeInference) { // Properly promotes float. CheckArgsInferType<float, double, double>(); +} +TEST_F(RandomDistributionsTest, UniformExamples) { // Examples. absl::InsecureBitGen gen; EXPECT_NE(1, absl::Uniform(gen, static_cast<uint16_t>(0), 1.0f)); @@ -307,6 +222,67 @@ TEST_F(RandomDistributionsTest, UniformNoBounds) { absl::Uniform<uint64_t>(gen); } +TEST_F(RandomDistributionsTest, UniformNonsenseRanges) { + // The ranges used in this test are undefined behavior. + // The results are arbitrary and subject to future changes. + +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations can be + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + + absl::InsecureBitGen gen; + + // <uint> + EXPECT_EQ(0, absl::Uniform<uint64_t>(gen, 0, 0)); + EXPECT_EQ(1, absl::Uniform<uint64_t>(gen, 1, 0)); + EXPECT_EQ(0, absl::Uniform<uint64_t>(absl::IntervalOpenOpen, gen, 0, 0)); + EXPECT_EQ(1, absl::Uniform<uint64_t>(absl::IntervalOpenOpen, gen, 1, 0)); + + constexpr auto m = (std::numeric_limits<uint64_t>::max)(); + + EXPECT_EQ(m, absl::Uniform(gen, m, m)); + EXPECT_EQ(m, absl::Uniform(gen, m, m - 1)); + EXPECT_EQ(m - 1, absl::Uniform(gen, m - 1, m)); + EXPECT_EQ(m, absl::Uniform(absl::IntervalOpenOpen, gen, m, m)); + EXPECT_EQ(m, absl::Uniform(absl::IntervalOpenOpen, gen, m, m - 1)); + EXPECT_EQ(m - 1, absl::Uniform(absl::IntervalOpenOpen, gen, m - 1, m)); + + // <int> + EXPECT_EQ(0, absl::Uniform<int64_t>(gen, 0, 0)); + EXPECT_EQ(1, absl::Uniform<int64_t>(gen, 1, 0)); + EXPECT_EQ(0, absl::Uniform<int64_t>(absl::IntervalOpenOpen, gen, 0, 0)); + EXPECT_EQ(1, absl::Uniform<int64_t>(absl::IntervalOpenOpen, gen, 1, 0)); + + constexpr auto l = (std::numeric_limits<int64_t>::min)(); + constexpr auto r = (std::numeric_limits<int64_t>::max)(); + + EXPECT_EQ(l, absl::Uniform(gen, l, l)); + EXPECT_EQ(r, absl::Uniform(gen, r, r)); + EXPECT_EQ(r, absl::Uniform(gen, r, r - 1)); + EXPECT_EQ(r - 1, absl::Uniform(gen, r - 1, r)); + EXPECT_EQ(l, absl::Uniform(absl::IntervalOpenOpen, gen, l, l)); + EXPECT_EQ(r, absl::Uniform(absl::IntervalOpenOpen, gen, r, r)); + EXPECT_EQ(r, absl::Uniform(absl::IntervalOpenOpen, gen, r, r - 1)); + EXPECT_EQ(r - 1, absl::Uniform(absl::IntervalOpenOpen, gen, r - 1, r)); + + // <double> + const double e = std::nextafter(1.0, 2.0); // 1 + epsilon + const double f = std::nextafter(1.0, 0.0); // 1 - epsilon + const double g = std::numeric_limits<double>::denorm_min(); + + EXPECT_EQ(1.0, absl::Uniform(gen, 1.0, e)); + EXPECT_EQ(1.0, absl::Uniform(gen, 1.0, f)); + EXPECT_EQ(0.0, absl::Uniform(gen, 0.0, g)); + + EXPECT_EQ(e, absl::Uniform(absl::IntervalOpenOpen, gen, 1.0, e)); + EXPECT_EQ(f, absl::Uniform(absl::IntervalOpenOpen, gen, 1.0, f)); + EXPECT_EQ(g, absl::Uniform(absl::IntervalOpenOpen, gen, 0.0, g)); +} + // TODO(lar): Validate properties of non-default interval-semantics. TEST_F(RandomDistributionsTest, UniformReal) { std::vector<double> values(kSize); diff --git a/third_party/abseil-cpp/absl/random/exponential_distribution_test.cc b/third_party/abseil-cpp/absl/random/exponential_distribution_test.cc index f3cfd76442..81a5d17bac 100644 --- a/third_party/abseil-cpp/absl/random/exponential_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/exponential_distribution_test.cc @@ -15,6 +15,7 @@ #include "absl/random/exponential_distribution.h" #include <algorithm> +#include <cfloat> #include <cmath> #include <cstddef> #include <cstdint> @@ -30,8 +31,10 @@ #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" +#include "absl/numeric/internal/representation.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -46,11 +49,15 @@ using absl::random_internal::kChiSquared; template <typename RealType> class ExponentialDistributionTypedTest : public ::testing::Test {}; -#if defined(__EMSCRIPTEN__) -using RealTypes = ::testing::Types<float, double>; -#else -using RealTypes = ::testing::Types<float, double, long double>; -#endif // defined(__EMSCRIPTEN__) +// double-double arithmetic is not supported well by either GCC or Clang; see +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048, +// https://bugs.llvm.org/show_bug.cgi?id=49131, and +// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests +// with double doubles until compiler support is better. +using RealTypes = + std::conditional<absl::numeric_internal::IsDoubleDouble(), + ::testing::Types<float, double>, + ::testing::Types<float, double, long double>>::type; TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes); TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { @@ -129,23 +136,6 @@ TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { ss >> after; -#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ - defined(__ppc__) || defined(__PPC__) - if (std::is_same<TypeParam, long double>::value) { - // Roundtripping floating point values requires sufficient precision to - // reconstruct the exact value. It turns out that long double has some - // errors doing this on ppc, particularly for values - // near {1.0 +/- epsilon}. - if (lambda <= std::numeric_limits<double>::max() && - lambda >= std::numeric_limits<double>::lowest()) { - EXPECT_EQ(static_cast<double>(before.lambda()), - static_cast<double>(after.lambda())) - << ss.str(); - } - continue; - } -#endif - EXPECT_EQ(before.lambda(), after.lambda()) // << ss.str() << " " // << (ss.good() ? "good " : "") // @@ -205,7 +195,10 @@ class ExponentialDistributionTests : public testing::TestWithParam<Param>, template <typename D> double SingleChiSquaredTest(); - absl::InsecureBitGen rng_; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6}; }; template <typename D> @@ -392,6 +385,15 @@ TEST(ExponentialDistributionTest, StabilityTest) { TEST(ExponentialDistributionTest, AlgorithmBounds) { // Relies on absl::uniform_real_distribution, so some of these comments // reference that. + +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations can be + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + absl::exponential_distribution<double> dist; { diff --git a/third_party/abseil-cpp/absl/random/gaussian_distribution_test.cc b/third_party/abseil-cpp/absl/random/gaussian_distribution_test.cc index 49c07513bf..c0bac2b0db 100644 --- a/third_party/abseil-cpp/absl/random/gaussian_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/gaussian_distribution_test.cc @@ -21,12 +21,14 @@ #include <iterator> #include <random> #include <string> +#include <type_traits> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" +#include "absl/numeric/internal/representation.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" #include "absl/random/internal/sequence_urbg.h" @@ -43,7 +45,15 @@ using absl::random_internal::kChiSquared; template <typename RealType> class GaussianDistributionInterfaceTest : public ::testing::Test {}; -using RealTypes = ::testing::Types<float, double, long double>; +// double-double arithmetic is not supported well by either GCC or Clang; see +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048, +// https://bugs.llvm.org/show_bug.cgi?id=49131, and +// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests +// with double doubles until compiler support is better. +using RealTypes = + std::conditional<absl::numeric_internal::IsDoubleDouble(), + ::testing::Types<float, double>, + ::testing::Types<float, double, long double>>::type; TYPED_TEST_CASE(GaussianDistributionInterfaceTest, RealTypes); TYPED_TEST(GaussianDistributionInterfaceTest, SerializeTest) { @@ -129,29 +139,6 @@ TYPED_TEST(GaussianDistributionInterfaceTest, SerializeTest) { ss >> after; -#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ - defined(__ppc__) || defined(__PPC__) - if (std::is_same<TypeParam, long double>::value) { - // Roundtripping floating point values requires sufficient precision - // to reconstruct the exact value. It turns out that long double - // has some errors doing this on ppc, particularly for values - // near {1.0 +/- epsilon}. - if (mean <= std::numeric_limits<double>::max() && - mean >= std::numeric_limits<double>::lowest()) { - EXPECT_EQ(static_cast<double>(before.mean()), - static_cast<double>(after.mean())) - << ss.str(); - } - if (stddev <= std::numeric_limits<double>::max() && - stddev >= std::numeric_limits<double>::lowest()) { - EXPECT_EQ(static_cast<double>(before.stddev()), - static_cast<double>(after.stddev())) - << ss.str(); - } - continue; - } -#endif - EXPECT_EQ(before.mean(), after.mean()); EXPECT_EQ(before.stddev(), after.stddev()) // << ss.str() << " " // @@ -213,7 +200,10 @@ class GaussianDistributionTests : public testing::TestWithParam<Param>, template <typename D> double SingleChiSquaredTest(); - absl::InsecureBitGen rng_; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6}; }; template <typename D> diff --git a/third_party/abseil-cpp/absl/random/internal/BUILD.bazel b/third_party/abseil-cpp/absl/random/internal/BUILD.bazel index d7ad4efec9..e93eebb68d 100644 --- a/third_party/abseil-cpp/absl/random/internal/BUILD.bazel +++ b/third_party/abseil-cpp/absl/random/internal/BUILD.bazel @@ -14,8 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") - # Internal-only implementation classes for Abseil Random load( "//absl:copts/configure_copts.bzl", @@ -30,16 +28,13 @@ package(default_visibility = [ "//absl/random:__pkg__", ]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "traits", hdrs = ["traits.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/random:__pkg__", - ], deps = ["//absl/base:config"], ) @@ -48,24 +43,10 @@ cc_library( hdrs = ["distribution_caller.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/random:__pkg__", - ], - deps = ["//absl/base:config"], -) - -cc_library( - name = "distributions", - hdrs = ["distributions.h"], - copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":distribution_caller", - ":traits", - ":uniform_helper", - "//absl/base", - "//absl/meta:type_traits", - "//absl/strings", + "//absl/base:config", + "//absl/base:fast_type_id", + "//absl/utility", ], ) @@ -76,10 +57,10 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = [ - "//absl/random:__pkg__", + deps = [ + "//absl/base:config", + "//absl/meta:type_traits", ], - deps = ["//absl/base:config"], ) cc_library( @@ -92,12 +73,14 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS + select({ - "//absl:windows": ["-DEFAULTLIB:bcrypt.lib"], + "//absl:msvc_compiler": ["-DEFAULTLIB:bcrypt.lib"], + "//absl:clang-cl_compiler": ["-DEFAULTLIB:bcrypt.lib"], "//conditions:default": [], }), deps = [ ":fast_uniform_bits", "//absl/base:core_headers", + "//absl/base:dynamic_annotations", "//absl/base:raw_logging_internal", "//absl/strings", "//absl/types:optional", @@ -115,7 +98,9 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = select({ - "//absl:windows": [], + "//absl:msvc_compiler": [], + "//absl:clang-cl_compiler": [], + "//absl:wasm": [], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, deps = [ @@ -140,7 +125,10 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - deps = ["//absl/base:config"], + deps = [ + "//absl/base:config", + "//absl/base:endian", + ], ) cc_library( @@ -191,8 +179,8 @@ cc_library( deps = [ ":fastmath", ":traits", - "//absl/base:bits", "//absl/meta:type_traits", + "//absl/numeric:bits", ], ) @@ -203,7 +191,7 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, - deps = ["//absl/base:bits"], + deps = ["//absl/numeric:bits"], ) cc_library( @@ -213,8 +201,8 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":traits", - "//absl/base:bits", "//absl/base:config", + "//absl/numeric:bits", "//absl/numeric:int128", ], ) @@ -230,7 +218,6 @@ cc_library( ":seed_material", "//absl/base:core_headers", "//absl/meta:type_traits", - "//absl/strings", "//absl/types:optional", "//absl/types:span", ], @@ -246,6 +233,7 @@ cc_library( ":iostream_state_saver", "//absl/base:config", "//absl/meta:type_traits", + "//absl/numeric:bits", "//absl/numeric:int128", ], ) @@ -258,19 +246,22 @@ cc_library( deps = [ ":iostream_state_saver", ":randen", + "//absl/base:endian", "//absl/meta:type_traits", ], ) cc_library( name = "platform", + srcs = [ + "randen_round_keys.cc", + ], hdrs = [ "randen_traits.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, textual_hdrs = [ - "randen-keys.inc", "platform.h", ], deps = ["//absl/base:config"], @@ -304,6 +295,8 @@ cc_library( ":platform", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:endian", + "//absl/numeric:int128", ], ) @@ -335,18 +328,16 @@ cc_library( "randen_hwaes.h", ], copts = ABSL_DEFAULT_COPTS + ABSL_RANDOM_RANDEN_COPTS + select({ - "//absl:windows": [], + "//absl:msvc_compiler": [], + "//absl:clang-cl_compiler": [], "//conditions:default": ["-Wno-pass-failed"], }), - # copts in RANDEN_HWAES_COPTS can make this target unusable as a module - # leading to a Clang diagnostic. Furthermore, it only has a private header - # anyway and thus there wouldn't be any gain from using it as a module. - features = ["-header_modules"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":platform", "//absl/base:config", "//absl/base:core_headers", + "//absl/numeric:int128", ], ) @@ -419,8 +410,8 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":generate_real", - "//absl/base:bits", "//absl/flags:flag", + "//absl/numeric:bits", "@com_google_googletest//:gtest_main", ], ) @@ -504,12 +495,11 @@ cc_test( ) cc_library( - name = "mocking_bit_gen_base", - hdrs = ["mocking_bit_gen_base.h"], - linkopts = ABSL_DEFAULT_LINKOPTS, + name = "mock_helpers", + hdrs = ["mock_helpers.h"], deps = [ - "//absl/random", - "//absl/strings", + "//absl/base:fast_type_id", + "//absl/types:optional", ], ) @@ -517,10 +507,8 @@ cc_library( name = "mock_overload_set", testonly = 1, hdrs = ["mock_overload_set.h"], - visibility = [ - "//absl/random:__pkg__", - ], deps = [ + ":mock_helpers", "//absl/random:mocking_bit_gen", "@com_google_googletest//:gtest", ], @@ -625,7 +613,9 @@ cc_test( copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":platform", ":randen_slow", + "//absl/base:endian", "@com_google_googletest//:gtest_main", ], ) @@ -634,7 +624,7 @@ cc_test( name = "randen_hwaes_test", size = "small", srcs = ["randen_hwaes_test.cc"], - copts = ABSL_TEST_COPTS, + copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, tags = ABSL_RANDOM_NONPORTABLE_TAGS, deps = [ @@ -655,7 +645,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":wide_multiply", - "//absl/base:bits", + "//absl/numeric:bits", "//absl/numeric:int128", "@com_google_googletest//:gtest_main", ], @@ -669,6 +659,7 @@ cc_library( deps = [ ":platform", ":randen_engine", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", ], @@ -680,6 +671,8 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":traits", + "//absl/base:config", "//absl/meta:type_traits", ], ) @@ -705,6 +698,7 @@ cc_test( cc_test( name = "randen_benchmarks", size = "medium", + timeout = "long", srcs = ["randen_benchmarks.cc"], copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS, flaky = 1, @@ -725,7 +719,6 @@ cc_test( cc_test( name = "iostream_state_saver_test", - size = "small", srcs = ["iostream_state_saver_test.cc"], linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ @@ -733,3 +726,15 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "uniform_helper_test", + size = "small", + srcs = ["uniform_helper_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":uniform_helper", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/third_party/abseil-cpp/absl/random/internal/distribution_caller.h b/third_party/abseil-cpp/absl/random/internal/distribution_caller.h index 02603cf843..fc81b787eb 100644 --- a/third_party/abseil-cpp/absl/random/internal/distribution_caller.h +++ b/third_party/abseil-cpp/absl/random/internal/distribution_caller.h @@ -20,6 +20,8 @@ #include <utility> #include "absl/base/config.h" +#include "absl/base/internal/fast_type_id.h" +#include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -30,27 +32,57 @@ namespace random_internal { // to intercept such calls. template <typename URBG> struct DistributionCaller { - // Call the provided distribution type. The parameters are expected - // to be explicitly specified. - // DistrT is the distribution type. - // FormatT is the formatter type: + // SFINAE to detect whether the URBG type includes a member matching + // bool InvokeMock(base_internal::FastTypeIdType, void*, void*). // - // struct FormatT { - // using result_type = distribution_t::result_type; - // static std::string FormatCall( - // const distribution_t& distr, - // absl::Span<const result_type>); - // - // static std::string FormatExpectation( - // absl::string_view match_args, - // absl::Span<const result_t> results); - // } - // - template <typename DistrT, typename FormatT, typename... Args> - static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { + // These live inside BitGenRef so that they have friend access + // to MockingBitGen. (see similar methods in DistributionCaller). + template <template <class...> class Trait, class AlwaysVoid, class... Args> + struct detector : std::false_type {}; + template <template <class...> class Trait, class... Args> + struct detector<Trait, absl::void_t<Trait<Args...>>, Args...> + : std::true_type {}; + + template <class T> + using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( + std::declval<::absl::base_internal::FastTypeIdType>(), + std::declval<void*>(), std::declval<void*>())); + + using HasInvokeMock = typename detector<invoke_mock_t, void, URBG>::type; + + // Default implementation of distribution caller. + template <typename DistrT, typename... Args> + static typename DistrT::result_type Impl(std::false_type, URBG* urbg, + Args&&... args) { DistrT dist(std::forward<Args>(args)...); return dist(*urbg); } + + // Mock implementation of distribution caller. + // The underlying KeyT must match the KeyT constructed by MockOverloadSet. + template <typename DistrT, typename... Args> + static typename DistrT::result_type Impl(std::true_type, URBG* urbg, + Args&&... args) { + using ResultT = typename DistrT::result_type; + using ArgTupleT = std::tuple<absl::decay_t<Args>...>; + using KeyT = ResultT(DistrT, ArgTupleT); + + ArgTupleT arg_tuple(std::forward<Args>(args)...); + ResultT result; + if (!urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple, + &result)) { + auto dist = absl::make_from_tuple<DistrT>(arg_tuple); + result = dist(*urbg); + } + return result; + } + + // Default implementation of distribution caller. + template <typename DistrT, typename... Args> + static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { + return Impl<DistrT, Args...>(HasInvokeMock{}, urbg, + std::forward<Args>(args)...); + } }; } // namespace random_internal diff --git a/third_party/abseil-cpp/absl/random/internal/distributions.h b/third_party/abseil-cpp/absl/random/internal/distributions.h deleted file mode 100644 index d7e3c0161f..0000000000 --- a/third_party/abseil-cpp/absl/random/internal/distributions.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. - -#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_ -#define ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_ - -#include <type_traits> - -#include "absl/meta/type_traits.h" -#include "absl/random/internal/distribution_caller.h" -#include "absl/random/internal/traits.h" -#include "absl/random/internal/uniform_helper.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace random_internal { - -// In the absence of an explicitly provided return-type, the template -// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on -// the data-types of the endpoint-arguments {A lo, B hi}. -// -// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the -// return-type, if one type can be implicitly converted into the other, in a -// lossless way. The template "is_widening_convertible" implements the -// compile-time logic for deciding if such a conversion is possible. -// -// If no such conversion between {A, B} exists, then the overload for -// absl::Uniform() will be discarded, and the call will be ill-formed. -// Return-type for absl::Uniform() when the return-type is inferred. -template <typename A, typename B> -using uniform_inferred_return_t = - absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>, - is_widening_convertible<B, A>>::value, - typename std::conditional< - is_widening_convertible<A, B>::value, B, A>::type>; - -} // namespace random_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_ diff --git a/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq.h b/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq.h index 6a743eaf46..25f791535f 100644 --- a/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq.h +++ b/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq.h @@ -23,6 +23,7 @@ #include <vector> #include "absl/base/config.h" +#include "absl/base/internal/endian.h" namespace absl { ABSL_NAMESPACE_BEGIN diff --git a/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq_test.cc b/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq_test.cc index a55ad73948..f867f610d0 100644 --- a/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/explicit_seed_seq_test.cc @@ -24,6 +24,8 @@ namespace { +using ::absl::random_internal::ExplicitSeedSeq; + template <typename Sseq> bool ConformsToInterface() { // Check that the SeedSequence can be default-constructed. @@ -64,14 +66,14 @@ TEST(SeedSequences, CheckInterfaces) { EXPECT_TRUE(ConformsToInterface<std::seed_seq>()); // Abseil classes - EXPECT_TRUE(ConformsToInterface<absl::random_internal::ExplicitSeedSeq>()); + EXPECT_TRUE(ConformsToInterface<ExplicitSeedSeq>()); } TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) { const size_t kNumBlocks = 128; uint32_t outputs[kNumBlocks]; - absl::random_internal::ExplicitSeedSeq seq; + ExplicitSeedSeq seq; seq.generate(outputs, &outputs[kNumBlocks]); for (uint32_t& seed : outputs) { @@ -87,8 +89,7 @@ TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) { for (uint32_t& seed : seed_material) { seed = urandom(); } - absl::random_internal::ExplicitSeedSeq seq(seed_material, - &seed_material[kNumBlocks]); + ExplicitSeedSeq seq(seed_material, &seed_material[kNumBlocks]); // Check that output is same as seed-material provided to constructor. { @@ -133,11 +134,10 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { for (uint32_t& entry : entropy) { entry = urandom(); } - absl::random_internal::ExplicitSeedSeq seq_from_entropy(std::begin(entropy), - std::end(entropy)); + ExplicitSeedSeq seq_from_entropy(std::begin(entropy), std::end(entropy)); // Copy constructor. { - absl::random_internal::ExplicitSeedSeq seq_copy(seq_from_entropy); + ExplicitSeedSeq seq_copy(seq_from_entropy); EXPECT_EQ(seq_copy.size(), seq_from_entropy.size()); std::vector<uint32_t> seeds_1; @@ -155,8 +155,7 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { for (uint32_t& entry : entropy) { entry = urandom(); } - absl::random_internal::ExplicitSeedSeq another_seq(std::begin(entropy), - std::end(entropy)); + ExplicitSeedSeq another_seq(std::begin(entropy), std::end(entropy)); std::vector<uint32_t> seeds_1; seeds_1.resize(1000, 0); @@ -202,3 +201,35 @@ TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { EXPECT_THAT(seeds_1, Each(Eq(0))); } } + +TEST(ExplicitSeedSeq, StdURBGGoldenTests) { + // Verify that for std::- URBG instances the results are stable across + // platforms (these should have deterministic output). + { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + std::minstd_rand rng(seed_sequence); + + std::minstd_rand::result_type values[4] = {rng(), rng(), rng(), rng()}; + EXPECT_THAT(values, + testing::ElementsAre(579252, 43785881, 464353103, 1501811174)); + } + + { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + std::mt19937 rng(seed_sequence); + + std::mt19937::result_type values[4] = {rng(), rng(), rng(), rng()}; + EXPECT_THAT(values, testing::ElementsAre(138416803, 151130212, 33817739, + 138416803)); + } + + { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + std::mt19937_64 rng(seed_sequence); + + std::mt19937_64::result_type values[4] = {rng(), rng(), rng(), rng()}; + EXPECT_THAT(values, + testing::ElementsAre(19738651785169348, 1464811352364190456, + 18054685302720800, 19738651785169348)); + } +} diff --git a/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits.h b/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits.h index f13c8729f7..425aaf7d83 100644 --- a/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits.h +++ b/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits.h @@ -21,6 +21,7 @@ #include <type_traits> #include "absl/base/config.h" +#include "absl/meta/type_traits.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -38,28 +39,17 @@ constexpr bool IsPowerOfTwoOrZero(UIntType n) { template <typename URBG> constexpr typename URBG::result_type RangeSize() { using result_type = typename URBG::result_type; + static_assert((URBG::max)() != (URBG::min)(), "URBG range cannot be 0."); return ((URBG::max)() == (std::numeric_limits<result_type>::max)() && (URBG::min)() == std::numeric_limits<result_type>::lowest()) ? result_type{0} - : (URBG::max)() - (URBG::min)() + result_type{1}; -} - -template <typename UIntType> -constexpr UIntType LargestPowerOfTwoLessThanOrEqualTo(UIntType n) { - return n < 2 ? n : 2 * LargestPowerOfTwoLessThanOrEqualTo(n / 2); -} - -// Given a URBG generating values in the closed interval [Lo, Hi], returns the -// largest power of two less than or equal to `Hi - Lo + 1`. -template <typename URBG> -constexpr typename URBG::result_type PowerOfTwoSubRangeSize() { - return LargestPowerOfTwoLessThanOrEqualTo(RangeSize<URBG>()); + : ((URBG::max)() - (URBG::min)() + result_type{1}); } // Computes the floor of the log. (i.e., std::floor(std::log2(N)); template <typename UIntType> constexpr UIntType IntegerLog2(UIntType n) { - return (n <= 1) ? 0 : 1 + IntegerLog2(n / 2); + return (n <= 1) ? 0 : 1 + IntegerLog2(n >> 1); } // Returns the number of bits of randomness returned through @@ -68,18 +58,23 @@ template <typename URBG> constexpr size_t NumBits() { return RangeSize<URBG>() == 0 ? std::numeric_limits<typename URBG::result_type>::digits - : IntegerLog2(PowerOfTwoSubRangeSize<URBG>()); + : IntegerLog2(RangeSize<URBG>()); } // Given a shift value `n`, constructs a mask with exactly the low `n` bits set. // If `n == 0`, all bits are set. template <typename UIntType> -constexpr UIntType MaskFromShift(UIntType n) { +constexpr UIntType MaskFromShift(size_t n) { return ((n % std::numeric_limits<UIntType>::digits) == 0) ? ~UIntType{0} : (UIntType{1} << n) - UIntType{1}; } +// Tags used to dispatch FastUniformBits::generate to the simple or more complex +// entropy extraction algorithm. +struct SimplifiedLoopTag {}; +struct RejectionLoopTag {}; + // FastUniformBits implements a fast path to acquire uniform independent bits // from a type which conforms to the [rand.req.urbg] concept. // Parameterized by: @@ -107,50 +102,16 @@ class FastUniformBits { "Class-template FastUniformBits<> must be parameterized using " "an unsigned type."); - // PowerOfTwoVariate() generates a single random variate, always returning a - // value in the half-open interval `[0, PowerOfTwoSubRangeSize<URBG>())`. If - // the URBG already generates values in a power-of-two range, the generator - // itself is used. Otherwise, we use rejection sampling on the largest - // possible power-of-two-sized subrange. - struct PowerOfTwoTag {}; - struct RejectionSamplingTag {}; - template <typename URBG> - static typename URBG::result_type PowerOfTwoVariate( - URBG& g) { // NOLINT(runtime/references) - using tag = - typename std::conditional<IsPowerOfTwoOrZero(RangeSize<URBG>()), - PowerOfTwoTag, RejectionSamplingTag>::type; - return PowerOfTwoVariate(g, tag{}); - } - - template <typename URBG> - static typename URBG::result_type PowerOfTwoVariate( - URBG& g, // NOLINT(runtime/references) - PowerOfTwoTag) { - return g() - (URBG::min)(); - } - - template <typename URBG> - static typename URBG::result_type PowerOfTwoVariate( - URBG& g, // NOLINT(runtime/references) - RejectionSamplingTag) { - // Use rejection sampling to ensure uniformity across the range. - typename URBG::result_type u; - do { - u = g() - (URBG::min)(); - } while (u >= PowerOfTwoSubRangeSize<URBG>()); - return u; - } - // Generate() generates a random value, dispatched on whether - // the underlying URBG must loop over multiple calls or not. + // the underlying URBG must use rejection sampling to generate a value, + // or whether a simplified loop will suffice. template <typename URBG> result_type Generate(URBG& g, // NOLINT(runtime/references) - std::true_type /* avoid_looping */); + SimplifiedLoopTag); template <typename URBG> result_type Generate(URBG& g, // NOLINT(runtime/references) - std::false_type /* avoid_looping */); + RejectionLoopTag); }; template <typename UIntType> @@ -162,31 +123,47 @@ FastUniformBits<UIntType>::operator()(URBG& g) { // NOLINT(runtime/references) // Y = (2 ^ kRange) - 1 static_assert((URBG::max)() > (URBG::min)(), "URBG::max and URBG::min may not be equal."); - using urbg_result_type = typename URBG::result_type; - constexpr urbg_result_type kRangeMask = - RangeSize<URBG>() == 0 - ? (std::numeric_limits<urbg_result_type>::max)() - : static_cast<urbg_result_type>(PowerOfTwoSubRangeSize<URBG>() - 1); - return Generate(g, std::integral_constant<bool, (kRangeMask >= (max)())>{}); + + using tag = absl::conditional_t<IsPowerOfTwoOrZero(RangeSize<URBG>()), + SimplifiedLoopTag, RejectionLoopTag>; + return Generate(g, tag{}); } template <typename UIntType> template <typename URBG> typename FastUniformBits<UIntType>::result_type FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) - std::true_type /* avoid_looping */) { - // The width of the result_type is less than than the width of the random bits - // provided by URBG. Thus, generate a single value and then simply mask off - // the required bits. + SimplifiedLoopTag) { + // The simplified version of FastUniformBits works only on URBGs that have + // a range that is a power of 2. In this case we simply loop and shift without + // attempting to balance the bits across calls. + static_assert(IsPowerOfTwoOrZero(RangeSize<URBG>()), + "incorrect Generate tag for URBG instance"); + + static constexpr size_t kResultBits = + std::numeric_limits<result_type>::digits; + static constexpr size_t kUrbgBits = NumBits<URBG>(); + static constexpr size_t kIters = + (kResultBits / kUrbgBits) + (kResultBits % kUrbgBits != 0); + static constexpr size_t kShift = (kIters == 1) ? 0 : kUrbgBits; + static constexpr auto kMin = (URBG::min)(); - return PowerOfTwoVariate(g) & (max)(); + result_type r = static_cast<result_type>(g() - kMin); + for (size_t n = 1; n < kIters; ++n) { + r = (r << kShift) + static_cast<result_type>(g() - kMin); + } + return r; } template <typename UIntType> template <typename URBG> typename FastUniformBits<UIntType>::result_type FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) - std::false_type /* avoid_looping */) { + RejectionLoopTag) { + static_assert(!IsPowerOfTwoOrZero(RangeSize<URBG>()), + "incorrect Generate tag for URBG instance"); + using urbg_result_type = typename URBG::result_type; + // See [rand.adapt.ibits] for more details on the constants calculated below. // // It is preferable to use roughly the same number of bits from each generator @@ -199,21 +176,44 @@ FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) // `kSmallIters` and `kLargeIters` times respectively such // that // - // `kTotalWidth == kSmallIters * kSmallWidth - // + kLargeIters * kLargeWidth` + // `kResultBits == kSmallIters * kSmallBits + // + kLargeIters * kLargeBits` // - // where `kTotalWidth` is the total number of bits in `result_type`. + // where `kResultBits` is the total number of bits in `result_type`. // - constexpr size_t kTotalWidth = std::numeric_limits<result_type>::digits; - constexpr size_t kUrbgWidth = NumBits<URBG>(); - constexpr size_t kTotalIters = - kTotalWidth / kUrbgWidth + (kTotalWidth % kUrbgWidth != 0); - constexpr size_t kSmallWidth = kTotalWidth / kTotalIters; - constexpr size_t kLargeWidth = kSmallWidth + 1; + static constexpr size_t kResultBits = + std::numeric_limits<result_type>::digits; // w + static constexpr urbg_result_type kUrbgRange = RangeSize<URBG>(); // R + static constexpr size_t kUrbgBits = NumBits<URBG>(); // m + + // compute the initial estimate of the bits used. + // [rand.adapt.ibits] 2 (c) + static constexpr size_t kA = // ceil(w/m) + (kResultBits / kUrbgBits) + ((kResultBits % kUrbgBits) != 0); // n' + + static constexpr size_t kABits = kResultBits / kA; // w0' + static constexpr urbg_result_type kARejection = + ((kUrbgRange >> kABits) << kABits); // y0' + + // refine the selection to reduce the rejection frequency. + static constexpr size_t kTotalIters = + ((kUrbgRange - kARejection) <= (kARejection / kA)) ? kA : (kA + 1); // n + + // [rand.adapt.ibits] 2 (b) + static constexpr size_t kSmallIters = + kTotalIters - (kResultBits % kTotalIters); // n0 + static constexpr size_t kSmallBits = kResultBits / kTotalIters; // w0 + static constexpr urbg_result_type kSmallRejection = + ((kUrbgRange >> kSmallBits) << kSmallBits); // y0 + + static constexpr size_t kLargeBits = kSmallBits + 1; // w0+1 + static constexpr urbg_result_type kLargeRejection = + ((kUrbgRange >> kLargeBits) << kLargeBits); // y1 + // - // Because `kLargeWidth == kSmallWidth + 1`, it follows that + // Because `kLargeBits == kSmallBits + 1`, it follows that // - // `kTotalWidth == kTotalIters * kSmallWidth + kLargeIters` + // `kResultBits == kSmallIters * kSmallBits + kLargeIters` // // and therefore // @@ -224,36 +224,40 @@ FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) // mentioned above, if the URBG width is a divisor of `kTotalWidth`, then // there would be no need for any large iterations (i.e., one loop would // suffice), and indeed, in this case, `kLargeIters` would be zero. - constexpr size_t kLargeIters = kTotalWidth % kSmallWidth; - constexpr size_t kSmallIters = - (kTotalWidth - (kLargeWidth * kLargeIters)) / kSmallWidth; + static_assert(kResultBits == kSmallIters * kSmallBits + + (kTotalIters - kSmallIters) * kLargeBits, + "Error in looping constant calculations."); - static_assert( - kTotalWidth == kSmallIters * kSmallWidth + kLargeIters * kLargeWidth, - "Error in looping constant calculations."); + // The small shift is essentially small bits, but due to the potential + // of generating a smaller result_type from a larger urbg type, the actual + // shift might be 0. + static constexpr size_t kSmallShift = kSmallBits % kResultBits; + static constexpr auto kSmallMask = + MaskFromShift<urbg_result_type>(kSmallShift); + static constexpr size_t kLargeShift = kLargeBits % kResultBits; + static constexpr auto kLargeMask = + MaskFromShift<urbg_result_type>(kLargeShift); - result_type s = 0; + static constexpr auto kMin = (URBG::min)(); - constexpr size_t kSmallShift = kSmallWidth % kTotalWidth; - constexpr result_type kSmallMask = MaskFromShift(result_type{kSmallShift}); + result_type s = 0; for (size_t n = 0; n < kSmallIters; ++n) { - s = (s << kSmallShift) + - (static_cast<result_type>(PowerOfTwoVariate(g)) & kSmallMask); - } + urbg_result_type v; + do { + v = g() - kMin; + } while (v >= kSmallRejection); - constexpr size_t kLargeShift = kLargeWidth % kTotalWidth; - constexpr result_type kLargeMask = MaskFromShift(result_type{kLargeShift}); - for (size_t n = 0; n < kLargeIters; ++n) { - s = (s << kLargeShift) + - (static_cast<result_type>(PowerOfTwoVariate(g)) & kLargeMask); + s = (s << kSmallShift) + static_cast<result_type>(v & kSmallMask); } - static_assert( - kLargeShift == kSmallShift + 1 || - (kLargeShift == 0 && - kSmallShift == std::numeric_limits<result_type>::digits - 1), - "Error in looping constant calculations"); + for (size_t n = kSmallIters; n < kTotalIters; ++n) { + urbg_result_type v; + do { + v = g() - kMin; + } while (v >= kLargeRejection); + s = (s << kLargeShift) + static_cast<result_type>(v & kLargeMask); + } return s; } diff --git a/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits_test.cc b/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits_test.cc index f5b837e586..cee702df85 100644 --- a/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/fast_uniform_bits_test.cc @@ -34,8 +34,8 @@ TYPED_TEST(FastUniformBitsTypedTest, BasicTest) { using Limits = std::numeric_limits<TypeParam>; using FastBits = FastUniformBits<TypeParam>; - EXPECT_EQ(0, FastBits::min()); - EXPECT_EQ(Limits::max(), FastBits::max()); + EXPECT_EQ(0, (FastBits::min)()); + EXPECT_EQ((Limits::max)(), (FastBits::max)()); constexpr int kIters = 10000; std::random_device rd; @@ -43,8 +43,8 @@ TYPED_TEST(FastUniformBitsTypedTest, BasicTest) { FastBits fast; for (int i = 0; i < kIters; i++) { const auto v = fast(gen); - EXPECT_LE(v, FastBits::max()); - EXPECT_GE(v, FastBits::min()); + EXPECT_LE(v, (FastBits::max)()); + EXPECT_GE(v, (FastBits::min)()); } } @@ -52,21 +52,26 @@ template <typename UIntType, UIntType Lo, UIntType Hi, UIntType Val = Lo> struct FakeUrbg { using result_type = UIntType; + FakeUrbg() = default; + explicit FakeUrbg(bool r) : reject(r) {} + static constexpr result_type(max)() { return Hi; } static constexpr result_type(min)() { return Lo; } - result_type operator()() { return Val; } -}; + result_type operator()() { + // when reject is set, return Hi half the time. + return ((++calls % 2) == 1 && reject) ? Hi : Val; + } -using UrngOddbits = FakeUrbg<uint8_t, 1, 0xfe, 0x73>; -using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>; -using Urng31bits = FakeUrbg<uint32_t, 1, 0xfffffffe, 0x60070f03>; -using Urng32bits = FakeUrbg<uint32_t, 0, 0xffffffff, 0x74010f01>; + bool reject = false; + size_t calls = 0; +}; TEST(FastUniformBitsTest, IsPowerOfTwoOrZero) { EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{0})); EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{1})); EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{2})); EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{3})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{4})); EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{16})); EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{17})); EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint8_t>::max)())); @@ -75,6 +80,7 @@ TEST(FastUniformBitsTest, IsPowerOfTwoOrZero) { EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{1})); EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{2})); EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{3})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{4})); EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{16})); EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{17})); EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint16_t>::max)())); @@ -91,181 +97,237 @@ TEST(FastUniformBitsTest, IsPowerOfTwoOrZero) { EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{1})); EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{2})); EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{3})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{4})); EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{64})); EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{17})); EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint64_t>::max)())); } TEST(FastUniformBitsTest, IntegerLog2) { - EXPECT_EQ(IntegerLog2(uint16_t{0}), 0); - EXPECT_EQ(IntegerLog2(uint16_t{1}), 0); - EXPECT_EQ(IntegerLog2(uint16_t{2}), 1); - EXPECT_EQ(IntegerLog2(uint16_t{3}), 1); - EXPECT_EQ(IntegerLog2(uint16_t{4}), 2); - EXPECT_EQ(IntegerLog2(uint16_t{5}), 2); - EXPECT_EQ(IntegerLog2(std::numeric_limits<uint64_t>::max()), 63); + EXPECT_EQ(0, IntegerLog2(uint16_t{0})); + EXPECT_EQ(0, IntegerLog2(uint16_t{1})); + EXPECT_EQ(1, IntegerLog2(uint16_t{2})); + EXPECT_EQ(1, IntegerLog2(uint16_t{3})); + EXPECT_EQ(2, IntegerLog2(uint16_t{4})); + EXPECT_EQ(2, IntegerLog2(uint16_t{5})); + EXPECT_EQ(2, IntegerLog2(uint16_t{7})); + EXPECT_EQ(3, IntegerLog2(uint16_t{8})); + EXPECT_EQ(63, IntegerLog2((std::numeric_limits<uint64_t>::max)())); } TEST(FastUniformBitsTest, RangeSize) { - EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1); - EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 6>>()), 5); - EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 10>>()), 9); + EXPECT_EQ(2, (RangeSize<FakeUrbg<uint8_t, 0, 1>>())); + EXPECT_EQ(3, (RangeSize<FakeUrbg<uint8_t, 0, 2>>())); + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint8_t, 0, 3>>())); + // EXPECT_EQ(0, (RangeSize<FakeUrbg<uint8_t, 2, 2>>())); + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint8_t, 2, 5>>())); + EXPECT_EQ(5, (RangeSize<FakeUrbg<uint8_t, 2, 6>>())); + EXPECT_EQ(9, (RangeSize<FakeUrbg<uint8_t, 2, 10>>())); EXPECT_EQ( - (RangeSize<FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()), - 0); - - EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1); - EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 6>>()), 5); - EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 18); - EXPECT_EQ((RangeSize< - FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()), - 0); - - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 6>>()), 5); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 18); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()), 0xffffffff); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()), 0xfffffffe); - EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 0xfffffffe>>()), 0xfffffffd); - EXPECT_EQ((RangeSize< - FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()), - 0); - - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 6>>()), 5); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 18); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()), 0x100000000ull); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()), 0xffffffffull); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()), 0xfffffffeull); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffe>>()), 0xfffffffdull); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()), 0ull); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()), - 0xffffffffffffffffull); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()), - 0xfffffffffffffffeull); - EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffffffffffeull>>()), - 0xfffffffffffffffdull); - EXPECT_EQ((RangeSize< - FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()), - 0); -} + 0, (RangeSize< + FakeUrbg<uint8_t, 0, (std::numeric_limits<uint8_t>::max)()>>())); -TEST(FastUniformBitsTest, PowerOfTwoSubRangeSize) { - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 6>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 10>>()), 8); - EXPECT_EQ((PowerOfTwoSubRangeSize< - FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()), - 0); - - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 6>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 16); - EXPECT_EQ((PowerOfTwoSubRangeSize< - FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()), - 0); - - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 6>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 16); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()), - 0x80000000); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()), - 0x80000000); - EXPECT_EQ((PowerOfTwoSubRangeSize< - FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()), - 0); - - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 6>>()), 4); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 16); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()), - 0x100000000ull); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()), - 0x80000000ull); - EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()), - 0x80000000ull); + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint16_t, 0, 3>>())); + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint16_t, 2, 5>>())); + EXPECT_EQ(5, (RangeSize<FakeUrbg<uint16_t, 2, 6>>())); + EXPECT_EQ(18, (RangeSize<FakeUrbg<uint16_t, 1000, 1017>>())); EXPECT_EQ( - (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()), - 0); + 0, (RangeSize< + FakeUrbg<uint16_t, 0, (std::numeric_limits<uint16_t>::max)()>>())); + + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint32_t, 0, 3>>())); + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint32_t, 2, 5>>())); + EXPECT_EQ(5, (RangeSize<FakeUrbg<uint32_t, 2, 6>>())); + EXPECT_EQ(18, (RangeSize<FakeUrbg<uint32_t, 1000, 1017>>())); + EXPECT_EQ(0, (RangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>())); + EXPECT_EQ(0xffffffff, (RangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>())); + EXPECT_EQ(0xfffffffe, (RangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>())); + EXPECT_EQ(0xfffffffd, (RangeSize<FakeUrbg<uint32_t, 2, 0xfffffffe>>())); EXPECT_EQ( - (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()), - 0x8000000000000000ull); + 0, (RangeSize< + FakeUrbg<uint32_t, 0, (std::numeric_limits<uint32_t>::max)()>>())); + + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint64_t, 0, 3>>())); + EXPECT_EQ(4, (RangeSize<FakeUrbg<uint64_t, 2, 5>>())); + EXPECT_EQ(5, (RangeSize<FakeUrbg<uint64_t, 2, 6>>())); + EXPECT_EQ(18, (RangeSize<FakeUrbg<uint64_t, 1000, 1017>>())); + EXPECT_EQ(0x100000000, (RangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>())); + EXPECT_EQ(0xffffffff, (RangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>())); + EXPECT_EQ(0xfffffffe, (RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>())); + EXPECT_EQ(0xfffffffd, (RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffe>>())); + EXPECT_EQ(0, (RangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffff>>())); + EXPECT_EQ(0xffffffffffffffff, + (RangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffff>>())); + EXPECT_EQ(0xfffffffffffffffe, + (RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffe>>())); + EXPECT_EQ(0xfffffffffffffffd, + (RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffffffffffe>>())); EXPECT_EQ( - (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()), - 0x8000000000000000ull); - EXPECT_EQ((PowerOfTwoSubRangeSize< - FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()), - 0); + 0, (RangeSize< + FakeUrbg<uint64_t, 0, (std::numeric_limits<uint64_t>::max)()>>())); } -TEST(FastUniformBitsTest, Urng4_VariousOutputs) { +// The constants need to be choosen so that an infinite rejection loop doesn't +// happen... +using Urng1_5bit = FakeUrbg<uint8_t, 0, 2, 0>; // ~1.5 bits (range 3) +using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>; +using Urng22bits = FakeUrbg<uint32_t, 0, 0x3fffff, 0x301020>; +using Urng31bits = FakeUrbg<uint32_t, 1, 0xfffffffe, 0x60070f03>; // ~31.9 bits +using Urng32bits = FakeUrbg<uint32_t, 0, 0xffffffff, 0x74010f01>; +using Urng33bits = + FakeUrbg<uint64_t, 1, 0x1ffffffff, 0x013301033>; // ~32.9 bits +using Urng63bits = FakeUrbg<uint64_t, 1, 0xfffffffffffffffe, + 0xfedcba9012345678>; // ~63.9 bits +using Urng64bits = + FakeUrbg<uint64_t, 0, 0xffffffffffffffff, 0x123456780fedcba9>; + +TEST(FastUniformBitsTest, OutputsUpTo32Bits) { // Tests that how values are composed; the single-bit deltas should be spread // across each invocation. + Urng1_5bit urng1_5; Urng4bits urng4; + Urng22bits urng22; Urng31bits urng31; Urng32bits urng32; + Urng33bits urng33; + Urng63bits urng63; + Urng64bits urng64; // 8-bit types { FastUniformBits<uint8_t> fast8; + EXPECT_EQ(0x0, fast8(urng1_5)); EXPECT_EQ(0x11, fast8(urng4)); + EXPECT_EQ(0x20, fast8(urng22)); EXPECT_EQ(0x2, fast8(urng31)); EXPECT_EQ(0x1, fast8(urng32)); + EXPECT_EQ(0x32, fast8(urng33)); + EXPECT_EQ(0x77, fast8(urng63)); + EXPECT_EQ(0xa9, fast8(urng64)); } // 16-bit types { FastUniformBits<uint16_t> fast16; + EXPECT_EQ(0x0, fast16(urng1_5)); EXPECT_EQ(0x1111, fast16(urng4)); - EXPECT_EQ(0xf02, fast16(urng31)); - EXPECT_EQ(0xf01, fast16(urng32)); + EXPECT_EQ(0x1020, fast16(urng22)); + EXPECT_EQ(0x0f02, fast16(urng31)); + EXPECT_EQ(0x0f01, fast16(urng32)); + EXPECT_EQ(0x1032, fast16(urng33)); + EXPECT_EQ(0x5677, fast16(urng63)); + EXPECT_EQ(0xcba9, fast16(urng64)); } // 32-bit types { FastUniformBits<uint32_t> fast32; + EXPECT_EQ(0x0, fast32(urng1_5)); EXPECT_EQ(0x11111111, fast32(urng4)); + EXPECT_EQ(0x08301020, fast32(urng22)); EXPECT_EQ(0x0f020f02, fast32(urng31)); EXPECT_EQ(0x74010f01, fast32(urng32)); + EXPECT_EQ(0x13301032, fast32(urng33)); + EXPECT_EQ(0x12345677, fast32(urng63)); + EXPECT_EQ(0x0fedcba9, fast32(urng64)); } +} + +TEST(FastUniformBitsTest, Outputs64Bits) { + // Tests that how values are composed; the single-bit deltas should be spread + // across each invocation. + FastUniformBits<uint64_t> fast64; - // 64-bit types { - FastUniformBits<uint64_t> fast64; + FakeUrbg<uint8_t, 0, 1, 0> urng0; + FakeUrbg<uint8_t, 0, 1, 1> urng1; + Urng4bits urng4; + Urng22bits urng22; + Urng31bits urng31; + Urng32bits urng32; + Urng33bits urng33; + Urng63bits urng63; + Urng64bits urng64; + + // somewhat degenerate cases only create a single bit. + EXPECT_EQ(0x0, fast64(urng0)); + EXPECT_EQ(64, urng0.calls); + EXPECT_EQ(0xffffffffffffffff, fast64(urng1)); + EXPECT_EQ(64, urng1.calls); + + // less degenerate cases. EXPECT_EQ(0x1111111111111111, fast64(urng4)); + EXPECT_EQ(16, urng4.calls); + EXPECT_EQ(0x01020c0408301020, fast64(urng22)); + EXPECT_EQ(3, urng22.calls); EXPECT_EQ(0x387811c3c0870f02, fast64(urng31)); + EXPECT_EQ(3, urng31.calls); EXPECT_EQ(0x74010f0174010f01, fast64(urng32)); + EXPECT_EQ(2, urng32.calls); + EXPECT_EQ(0x808194040cb01032, fast64(urng33)); + EXPECT_EQ(3, urng33.calls); + EXPECT_EQ(0x1234567712345677, fast64(urng63)); + EXPECT_EQ(2, urng63.calls); + EXPECT_EQ(0x123456780fedcba9, fast64(urng64)); + EXPECT_EQ(1, urng64.calls); + } + + // The 1.5 bit case is somewhat interesting in that the algorithm refinement + // causes one extra small sample. Comments here reference the names used in + // [rand.adapt.ibits] that correspond to this case. + { + Urng1_5bit urng1_5; + + // w = 64 + // R = 3 + // m = 1 + // n' = 64 + // w0' = 1 + // y0' = 2 + // n = (1 <= 0) > 64 : 65 = 65 + // n0 = 65 - (64%65) = 1 + // n1 = 64 + // w0 = 0 + // y0 = 3 + // w1 = 1 + // y1 = 2 + EXPECT_EQ(0x0, fast64(urng1_5)); + EXPECT_EQ(65, urng1_5.calls); + } + + // Validate rejections for non-power-of-2 cases. + { + Urng1_5bit urng1_5(true); + Urng31bits urng31(true); + Urng33bits urng33(true); + Urng63bits urng63(true); + + // For 1.5 bits, there would be 1+2*64, except the first + // value was accepted and shifted off the end. + EXPECT_EQ(0, fast64(urng1_5)); + EXPECT_EQ(128, urng1_5.calls); + EXPECT_EQ(0x387811c3c0870f02, fast64(urng31)); + EXPECT_EQ(6, urng31.calls); + EXPECT_EQ(0x808194040cb01032, fast64(urng33)); + EXPECT_EQ(6, urng33.calls); + EXPECT_EQ(0x1234567712345677, fast64(urng63)); + EXPECT_EQ(4, urng63.calls); } } TEST(FastUniformBitsTest, URBG32bitRegression) { // Validate with deterministic 32-bit std::minstd_rand // to ensure that operator() performs as expected. + + EXPECT_EQ(2147483646, RangeSize<std::minstd_rand>()); + EXPECT_EQ(30, IntegerLog2(RangeSize<std::minstd_rand>())); + std::minstd_rand gen(1); FastUniformBits<uint64_t> fast64; - EXPECT_EQ(0x05e47095f847c122ull, fast64(gen)); - EXPECT_EQ(0x8f82c1ba30b64d22ull, fast64(gen)); - EXPECT_EQ(0x3b971a3558155039ull, fast64(gen)); + EXPECT_EQ(0x05e47095f8791f45, fast64(gen)); + EXPECT_EQ(0x028be17e3c07c122, fast64(gen)); + EXPECT_EQ(0x55d2847c1626e8c2, fast64(gen)); } } // namespace diff --git a/third_party/abseil-cpp/absl/random/internal/fastmath.h b/third_party/abseil-cpp/absl/random/internal/fastmath.h index 6baeb5a7c9..963b7690f1 100644 --- a/third_party/abseil-cpp/absl/random/internal/fastmath.h +++ b/third_party/abseil-cpp/absl/random/internal/fastmath.h @@ -22,27 +22,22 @@ #include <cmath> #include <cstdint> -#include "absl/base/internal/bits.h" +#include "absl/numeric/bits.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// Returns the position of the first bit set. -inline int LeadingSetBit(uint64_t n) { - return 64 - base_internal::CountLeadingZeros64(n); -} - // Compute log2(n) using integer operations. // While std::log2 is more accurate than std::log(n) / std::log(2), for // very large numbers--those close to std::numeric_limits<uint64_t>::max() - 2, // for instance--std::log2 rounds up rather than down, which introduces // definite skew in the results. inline int IntLog2Floor(uint64_t n) { - return (n <= 1) ? 0 : (63 - base_internal::CountLeadingZeros64(n)); + return (n <= 1) ? 0 : (63 - countl_zero(n)); } inline int IntLog2Ceil(uint64_t n) { - return (n <= 1) ? 0 : (64 - base_internal::CountLeadingZeros64(n - 1)); + return (n <= 1) ? 0 : (64 - countl_zero(n - 1)); } inline double StirlingLogFactorial(double n) { @@ -55,18 +50,6 @@ inline double StirlingLogFactorial(double n) { (1.0 / 360.0) * ninv * ninv * ninv; } -// Rotate value right. -// -// We only implement the uint32_t / uint64_t versions because -// 1) those are the only ones we use, and -// 2) those are the only ones where clang detects the rotate idiom correctly. -inline constexpr uint32_t rotr(uint32_t value, uint8_t bits) { - return (value >> (bits & 31)) | (value << ((-bits) & 31)); -} -inline constexpr uint64_t rotr(uint64_t value, uint8_t bits) { - return (value >> (bits & 63)) | (value << ((-bits) & 63)); -} - } // namespace random_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/random/internal/fastmath_test.cc b/third_party/abseil-cpp/absl/random/internal/fastmath_test.cc index 65859c25de..0d6f9dc157 100644 --- a/third_party/abseil-cpp/absl/random/internal/fastmath_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/fastmath_test.cc @@ -27,19 +27,6 @@ namespace { -TEST(DistributionImplTest, LeadingSetBit) { - using absl::random_internal::LeadingSetBit; - constexpr uint64_t kZero = 0; - EXPECT_EQ(0, LeadingSetBit(kZero)); - EXPECT_EQ(64, LeadingSetBit(~kZero)); - - for (int index = 0; index < 64; index++) { - uint64_t x = static_cast<uint64_t>(1) << index; - EXPECT_EQ(index + 1, LeadingSetBit(x)) << index; - EXPECT_EQ(index + 1, LeadingSetBit(x + x - 1)) << index; - } -} - TEST(FastMathTest, IntLog2FloorTest) { using absl::random_internal::IntLog2Floor; constexpr uint64_t kZero = 0; diff --git a/third_party/abseil-cpp/absl/random/internal/gaussian_distribution_gentables.cc b/third_party/abseil-cpp/absl/random/internal/gaussian_distribution_gentables.cc index a2bf03940f..a95333d55f 100644 --- a/third_party/abseil-cpp/absl/random/internal/gaussian_distribution_gentables.cc +++ b/third_party/abseil-cpp/absl/random/internal/gaussian_distribution_gentables.cc @@ -111,12 +111,9 @@ void TableGenerator::Print(std::ostream* os) { "\n" "#include \"absl/random/gaussian_distribution.h\"\n" "\n" - // "namespace " and "absl" are broken apart so as not to conflict with - // script that adds the LTS inline namespace. - "namespace " - "absl {\n" - "namespace " - "random_internal {\n" + "namespace absl {\n" + "ABSL_NAMESPACE_BEGIN\n" + "namespace random_internal {\n" "\n" "const gaussian_distribution_base::Tables\n" " gaussian_distribution_base::zg_ = {\n"; @@ -125,10 +122,9 @@ void TableGenerator::Print(std::ostream* os) { FormatArrayContents(os, tables_.f); *os << "};\n" "\n" - "} // namespace " - "random_internal\n" - "} // namespace " - "absl\n" + "} // namespace random_internal\n" + "ABSL_NAMESPACE_END\n" + "} // namespace absl\n" "\n" "// clang-format on\n" "// END GENERATED CODE"; diff --git a/third_party/abseil-cpp/absl/random/internal/generate_real.h b/third_party/abseil-cpp/absl/random/internal/generate_real.h index 20f6d20807..d5fbb44c24 100644 --- a/third_party/abseil-cpp/absl/random/internal/generate_real.h +++ b/third_party/abseil-cpp/absl/random/internal/generate_real.h @@ -23,8 +23,8 @@ #include <limits> #include <type_traits> -#include "absl/base/internal/bits.h" #include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" #include "absl/random/internal/fastmath.h" #include "absl/random/internal/traits.h" @@ -120,17 +120,15 @@ inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { // Number of leading zeros is mapped to the exponent: 2^-clz // bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0 - int clz = base_internal::CountLeadingZeros64(bits); + int clz = countl_zero(bits); bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits. exp -= clz; // set the exponent. bits >>= (63 - kExp); // Construct the 32-bit or 64-bit IEEE 754 floating-point value from // the individual fields: sign, exp, mantissa(bits). - uint_type val = - (std::is_same<SignedTag, GeneratePositiveTag>::value ? 0u : sign) | - (static_cast<uint_type>(exp) << kExp) | - (static_cast<uint_type>(bits) & kMask); + uint_type val = sign | (static_cast<uint_type>(exp) << kExp) | + (static_cast<uint_type>(bits) & kMask); // bit_cast to the output-type real_type result; diff --git a/third_party/abseil-cpp/absl/random/internal/generate_real_test.cc b/third_party/abseil-cpp/absl/random/internal/generate_real_test.cc index aa02f0c2c1..b099dbf35f 100644 --- a/third_party/abseil-cpp/absl/random/internal/generate_real_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/generate_real_test.cc @@ -20,8 +20,8 @@ #include <string> #include "gtest/gtest.h" -#include "absl/base/internal/bits.h" #include "absl/flags/flag.h" +#include "absl/numeric/bits.h" ABSL_FLAG(int64_t, absl_random_test_trials, 50000, "Number of trials for the probability tests."); @@ -413,14 +413,13 @@ TEST(GenerateRealTest, U64ToDoubleSignedTest) { } TEST(GenerateRealTest, ExhaustiveFloat) { - using absl::base_internal::CountLeadingZeros64; auto ToFloat = [](uint64_t a) { return GenerateRealFromBits<float, GeneratePositiveTag, true>(a); }; // Rely on RandU64ToFloat generating values from greatest to least when - // supplied with uint64_t values from greatest (0xfff...) to least (0x0). Thus, - // this algorithm stores the previous value, and if the new value is at + // supplied with uint64_t values from greatest (0xfff...) to least (0x0). + // Thus, this algorithm stores the previous value, and if the new value is at // greater than or equal to the previous value, then there is a collision in // the generation algorithm. // @@ -464,7 +463,7 @@ TEST(GenerateRealTest, ExhaustiveFloat) { // Adjust decrement and check value based on how many leading 0 // bits are set in the current value. - const int clz = CountLeadingZeros64(x); + const int clz = absl::countl_zero(x); if (clz < kDig) { dec <<= (kDig - clz); chk = (~uint64_t(0)) >> (clz + 1); diff --git a/third_party/abseil-cpp/absl/random/internal/iostream_state_saver.h b/third_party/abseil-cpp/absl/random/internal/iostream_state_saver.h index 7378829a42..e6e242ee1e 100644 --- a/third_party/abseil-cpp/absl/random/internal/iostream_state_saver.h +++ b/third_party/abseil-cpp/absl/random/internal/iostream_state_saver.h @@ -192,8 +192,8 @@ struct stream_u128_helper<absl::uint128> { template <typename OStream> inline void write(absl::uint128 val, OStream& out) { - uint64_t h = Uint128High64(val); - uint64_t l = Uint128Low64(val); + uint64_t h = absl::Uint128High64(val); + uint64_t l = absl::Uint128Low64(val); out << h << out.fill() << l; } }; diff --git a/third_party/abseil-cpp/absl/random/internal/iostream_state_saver_test.cc b/third_party/abseil-cpp/absl/random/internal/iostream_state_saver_test.cc index 7bb8ad959c..6e66266cc0 100644 --- a/third_party/abseil-cpp/absl/random/internal/iostream_state_saver_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/iostream_state_saver_test.cc @@ -14,6 +14,9 @@ #include "absl/random/internal/iostream_state_saver.h" +#include <errno.h> +#include <stdio.h> + #include <sstream> #include <string> @@ -272,7 +275,6 @@ TEST(IOStreamStateSaver, RoundTripDoubles) { } } -#if !defined(__EMSCRIPTEN__) TEST(IOStreamStateSaver, RoundTripLongDoubles) { // Technically, C++ only guarantees that long double is at least as large as a // double. Practically it varies from 64-bits to 128-bits. @@ -350,7 +352,6 @@ TEST(IOStreamStateSaver, RoundTripLongDoubles) { } } } -#endif // !defined(__EMSCRIPTEN__) TEST(StrToDTest, DoubleMin) { const char kV[] = "2.22507385850720138e-308"; diff --git a/third_party/abseil-cpp/absl/random/internal/mock_helpers.h b/third_party/abseil-cpp/absl/random/internal/mock_helpers.h new file mode 100644 index 0000000000..9d6ab21ef5 --- /dev/null +++ b/third_party/abseil-cpp/absl/random/internal/mock_helpers.h @@ -0,0 +1,134 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_ +#define ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_ + +#include <tuple> +#include <type_traits> + +#include "absl/base/internal/fast_type_id.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { + +// MockHelpers works in conjunction with MockOverloadSet, MockingBitGen, and +// BitGenRef to enable the mocking capability for absl distribution functions. +// +// MockingBitGen registers mocks based on the typeid of a mock signature, KeyT, +// which is used to generate a unique id. +// +// KeyT is a signature of the form: +// result_type(discriminator_type, std::tuple<args...>) +// The mocked function signature will be composed from KeyT as: +// result_type(args...) +// +class MockHelpers { + using IdType = ::absl::base_internal::FastTypeIdType; + + // Given a key signature type used to index the mock, extract the components. + // KeyT is expected to have the form: + // result_type(discriminator_type, arg_tuple_type) + template <typename KeyT> + struct KeySignature; + + template <typename ResultT, typename DiscriminatorT, typename ArgTupleT> + struct KeySignature<ResultT(DiscriminatorT, ArgTupleT)> { + using result_type = ResultT; + using discriminator_type = DiscriminatorT; + using arg_tuple_type = ArgTupleT; + }; + + // Detector for InvokeMock. + template <class T> + using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock( + std::declval<IdType>(), std::declval<void*>(), std::declval<void*>())); + + // Empty implementation of InvokeMock. + template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG, + typename... Args> + static absl::optional<ReturnT> InvokeMockImpl(char, URBG*, Args&&...) { + return absl::nullopt; + } + + // Non-empty implementation of InvokeMock. + template <typename KeyT, typename ReturnT, typename ArgTupleT, typename URBG, + typename = invoke_mock_t<URBG>, typename... Args> + static absl::optional<ReturnT> InvokeMockImpl(int, URBG* urbg, + Args&&... args) { + ArgTupleT arg_tuple(std::forward<Args>(args)...); + ReturnT result; + if (urbg->InvokeMock(::absl::base_internal::FastTypeId<KeyT>(), &arg_tuple, + &result)) { + return result; + } + return absl::nullopt; + } + + public: + // InvokeMock is private; this provides access for some specialized use cases. + template <typename URBG> + static inline bool PrivateInvokeMock(URBG* urbg, IdType type, + void* args_tuple, void* result) { + return urbg->InvokeMock(type, args_tuple, result); + } + + // Invoke a mock for the KeyT (may or may not be a signature). + // + // KeyT is used to generate a typeid-based lookup key for the mock. + // KeyT is a signature of the form: + // result_type(discriminator_type, std::tuple<args...>) + // The mocked function signature will be composed from KeyT as: + // result_type(args...) + // + // An instance of arg_tuple_type must be constructable from Args..., since + // the underlying mechanism requires a pointer to an argument tuple. + template <typename KeyT, typename URBG, typename... Args> + static auto MaybeInvokeMock(URBG* urbg, Args&&... args) + -> absl::optional<typename KeySignature<KeyT>::result_type> { + // Use function overloading to dispatch to the implemenation since + // more modern patterns (e.g. require + constexpr) are not supported in all + // compiler configurations. + return InvokeMockImpl<KeyT, typename KeySignature<KeyT>::result_type, + typename KeySignature<KeyT>::arg_tuple_type, URBG>( + 0, urbg, std::forward<Args>(args)...); + } + + // Acquire a mock for the KeyT (may or may not be a signature). + // + // KeyT is used to generate a typeid-based lookup for the mock. + // KeyT is a signature of the form: + // result_type(discriminator_type, std::tuple<args...>) + // The mocked function signature will be composed from KeyT as: + // result_type(args...) + template <typename KeyT, typename MockURBG> + static auto MockFor(MockURBG& m) + -> decltype(m.template RegisterMock< + typename KeySignature<KeyT>::result_type, + typename KeySignature<KeyT>::arg_tuple_type>( + m, std::declval<IdType>())) { + return m.template RegisterMock<typename KeySignature<KeyT>::result_type, + typename KeySignature<KeyT>::arg_tuple_type>( + m, ::absl::base_internal::FastTypeId<KeyT>()); + } +}; + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_MOCK_HELPERS_H_ diff --git a/third_party/abseil-cpp/absl/random/internal/mock_overload_set.h b/third_party/abseil-cpp/absl/random/internal/mock_overload_set.h index c2a30d89d5..0d9c6c120c 100644 --- a/third_party/abseil-cpp/absl/random/internal/mock_overload_set.h +++ b/third_party/abseil-cpp/absl/random/internal/mock_overload_set.h @@ -19,7 +19,7 @@ #include <type_traits> #include "gmock/gmock.h" -#include "gtest/gtest.h" +#include "absl/random/internal/mock_helpers.h" #include "absl/random/mocking_bit_gen.h" namespace absl { @@ -35,17 +35,22 @@ struct MockSingleOverload; // EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to // `mock_single_overload.gmock_Call(...)`. Because expectations are stored on // the MockingBitGen (an argument passed inside `Call(...)`), this forwards to -// arguments to Mocking::Register. +// arguments to MockingBitGen::Register. +// +// The underlying KeyT must match the KeyT constructed by DistributionCaller. template <typename DistrT, typename Ret, typename... Args> struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> { static_assert(std::is_same<typename DistrT::result_type, Ret>::value, "Overload signature must have return type matching the " - "distributions result type."); - auto gmock_Call( - absl::MockingBitGen& gen, // NOLINT(google-runtime-references) - const ::testing::Matcher<Args>&... args) - -> decltype(gen.Register<DistrT, Args...>(args...)) { - return gen.Register<DistrT, Args...>(args...); + "distribution result_type."); + using KeyT = Ret(DistrT, std::tuple<Args...>); + + template <typename MockURBG> + auto gmock_Call(MockURBG& gen, const ::testing::Matcher<Args>&... matchers) + -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...)) { + static_assert(std::is_base_of<MockingBitGen, MockURBG>::value, + "Mocking requires an absl::MockingBitGen"); + return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matchers...); } }; @@ -53,13 +58,17 @@ template <typename DistrT, typename Ret, typename Arg, typename... Args> struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> { static_assert(std::is_same<typename DistrT::result_type, Ret>::value, "Overload signature must have return type matching the " - "distributions result type."); - auto gmock_Call( - const ::testing::Matcher<Arg>& arg, - absl::MockingBitGen& gen, // NOLINT(google-runtime-references) - const ::testing::Matcher<Args>&... args) - -> decltype(gen.Register<DistrT, Arg, Args...>(arg, args...)) { - return gen.Register<DistrT, Arg, Args...>(arg, args...); + "distribution result_type."); + using KeyT = Ret(DistrT, std::tuple<Arg, Args...>); + + template <typename MockURBG> + auto gmock_Call(const ::testing::Matcher<Arg>& matcher, MockURBG& gen, + const ::testing::Matcher<Args>&... matchers) + -> decltype(MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher, + matchers...)) { + static_assert(std::is_base_of<MockingBitGen, MockURBG>::value, + "Mocking requires an absl::MockingBitGen"); + return MockHelpers::MockFor<KeyT>(gen).gmock_Call(matcher, matchers...); } }; diff --git a/third_party/abseil-cpp/absl/random/internal/mocking_bit_gen_base.h b/third_party/abseil-cpp/absl/random/internal/mocking_bit_gen_base.h deleted file mode 100644 index eeeae9d295..0000000000 --- a/third_party/abseil-cpp/absl/random/internal/mocking_bit_gen_base.h +++ /dev/null @@ -1,120 +0,0 @@ -// -// Copyright 2018 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. -// -#ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ -#define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ - -#include <atomic> -#include <deque> -#include <string> -#include <typeinfo> - -#include "absl/random/random.h" -#include "absl/strings/str_cat.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace random_internal { - -// MockingBitGenExpectationFormatter is invoked to format unsatisfied mocks -// and remaining results into a description string. -template <typename DistrT, typename FormatT> -struct MockingBitGenExpectationFormatter { - std::string operator()(absl::string_view args) { - return absl::StrCat(FormatT::FunctionName(), "(", args, ")"); - } -}; - -// MockingBitGenCallFormatter is invoked to format each distribution call -// into a description string for the mock log. -template <typename DistrT, typename FormatT> -struct MockingBitGenCallFormatter { - std::string operator()(const DistrT& dist, - const typename DistrT::result_type& result) { - return absl::StrCat( - FormatT::FunctionName(), "(", FormatT::FormatArgs(dist), ") => {", - FormatT::FormatResults(absl::MakeSpan(&result, 1)), "}"); - } -}; - -class MockingBitGenBase { - template <typename> - friend struct DistributionCaller; - using generator_type = absl::BitGen; - - public: - // URBG interface - using result_type = generator_type::result_type; - static constexpr result_type(min)() { return (generator_type::min)(); } - static constexpr result_type(max)() { return (generator_type::max)(); } - result_type operator()() { return gen_(); } - - MockingBitGenBase() : gen_(), observed_call_log_() {} - virtual ~MockingBitGenBase() = default; - - protected: - const std::deque<std::string>& observed_call_log() { - return observed_call_log_; - } - - // CallImpl is the type-erased virtual dispatch. - // The type of dist is always distribution<T>, - // The type of result is always distribution<T>::result_type. - virtual bool CallImpl(const std::type_info& distr_type, void* dist_args, - void* result) = 0; - - template <typename DistrT, typename ArgTupleT> - static const std::type_info& GetTypeId() { - return typeid(std::pair<absl::decay_t<DistrT>, absl::decay_t<ArgTupleT>>); - } - - // Call the generating distribution function. - // Invoked by DistributionCaller<>::Call<DistT, FormatT>. - // DistT is the distribution type. - // FormatT is the distribution formatter traits type. - template <typename DistrT, typename FormatT, typename... Args> - typename DistrT::result_type Call(Args&&... args) { - using distr_result_type = typename DistrT::result_type; - using ArgTupleT = std::tuple<absl::decay_t<Args>...>; - - ArgTupleT arg_tuple(std::forward<Args>(args)...); - auto dist = absl::make_from_tuple<DistrT>(arg_tuple); - - distr_result_type result{}; - bool found_match = - CallImpl(GetTypeId<DistrT, ArgTupleT>(), &arg_tuple, &result); - - if (!found_match) { - result = dist(gen_); - } - - // TODO(asoffer): Forwarding the args through means we no longer need to - // extract them from the from the distribution in formatter traits. We can - // just StrJoin them. - observed_call_log_.push_back( - MockingBitGenCallFormatter<DistrT, FormatT>{}(dist, result)); - return result; - } - - private: - generator_type gen_; - std::deque<std::string> observed_call_log_; -}; // namespace random_internal - -} // namespace random_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ diff --git a/third_party/abseil-cpp/absl/random/internal/nanobenchmark.cc b/third_party/abseil-cpp/absl/random/internal/nanobenchmark.cc index 8fee77fcc7..c9181813f7 100644 --- a/third_party/abseil-cpp/absl/random/internal/nanobenchmark.cc +++ b/third_party/abseil-cpp/absl/random/internal/nanobenchmark.cc @@ -101,7 +101,7 @@ std::string BrandString() { char brand_string[49]; uint32_t abcd[4]; - // Check if brand std::string is supported (it is on all reasonable Intel/AMD) + // Check if brand string is supported (it is on all reasonable Intel/AMD) Cpuid(0x80000000U, 0, abcd); if (abcd[0] < 0x80000004U) { return std::string(); diff --git a/third_party/abseil-cpp/absl/random/internal/nanobenchmark_test.cc b/third_party/abseil-cpp/absl/random/internal/nanobenchmark_test.cc index ab824ef55f..f1571e269f 100644 --- a/third_party/abseil-cpp/absl/random/internal/nanobenchmark_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/nanobenchmark_test.cc @@ -53,7 +53,7 @@ void RunAll(const int argc, char* argv[]) { // Avoid migrating between cores - important on multi-socket systems. int cpu = -1; if (argc == 2) { - if (!SimpleAtoi(argv[1], &cpu)) { + if (!absl::SimpleAtoi(argv[1], &cpu)) { ABSL_RAW_LOG(FATAL, "The optional argument must be a CPU number >= 0.\n"); } } diff --git a/third_party/abseil-cpp/absl/random/internal/pcg_engine.h b/third_party/abseil-cpp/absl/random/internal/pcg_engine.h index 53c23fe1b4..8efaf2e09a 100644 --- a/third_party/abseil-cpp/absl/random/internal/pcg_engine.h +++ b/third_party/abseil-cpp/absl/random/internal/pcg_engine.h @@ -19,6 +19,7 @@ #include "absl/base/config.h" #include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/random/internal/fastmath.h" #include "absl/random/internal/iostream_state_saver.h" @@ -261,7 +262,7 @@ struct pcg_xsl_rr_128_64 { uint64_t rotate = h >> 58u; uint64_t s = Uint128Low64(state) ^ h; #endif - return random_internal::rotr(s, rotate); + return rotr(s, rotate); } }; @@ -281,8 +282,8 @@ struct pcg_xsh_rr_64_32 { using state_type = uint64_t; using result_type = uint32_t; inline uint32_t operator()(uint64_t state) { - return random_internal::rotr( - static_cast<uint32_t>(((state >> 18) ^ state) >> 27), state >> 59); + return rotr(static_cast<uint32_t>(((state >> 18) ^ state) >> 27), + state >> 59); } }; diff --git a/third_party/abseil-cpp/absl/random/internal/pool_urbg.cc b/third_party/abseil-cpp/absl/random/internal/pool_urbg.cc index 5bee530770..725100a415 100644 --- a/third_party/abseil-cpp/absl/random/internal/pool_urbg.cc +++ b/third_party/abseil-cpp/absl/random/internal/pool_urbg.cc @@ -194,11 +194,10 @@ RandenPoolEntry* PoolAlignedAlloc() { // Not all the platforms that we build for have std::aligned_alloc, however // since we never free these objects, we can over allocate and munge the // pointers to the correct alignment. - void* memory = std::malloc(sizeof(RandenPoolEntry) + kAlignment); - auto x = reinterpret_cast<intptr_t>(memory); + intptr_t x = reinterpret_cast<intptr_t>( + new char[sizeof(RandenPoolEntry) + kAlignment]); auto y = x % kAlignment; - void* aligned = - (y == 0) ? memory : reinterpret_cast<void*>(x + kAlignment - y); + void* aligned = reinterpret_cast<void*>(y == 0 ? x : (x + kAlignment - y)); return new (aligned) RandenPoolEntry(); } diff --git a/third_party/abseil-cpp/absl/random/internal/randen-keys.inc b/third_party/abseil-cpp/absl/random/internal/randen-keys.inc deleted file mode 100644 index fa4b166835..0000000000 --- a/third_party/abseil-cpp/absl/random/internal/randen-keys.inc +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. - -#ifndef ABSL_RANDOM_INTERNAL_RANDEN_KEYS_INC_ -#define ABSL_RANDOM_INTERNAL_RANDEN_KEYS_INC_ - -// Textual header to include the randen_keys where necessary. -// REQUIRES: struct u64x2{} -// -// PROVIDES: kKeys -// PROVIDES: round_keys[] - -// "Nothing up my sleeve" numbers from the first hex digits of Pi, obtained -// from http://hexpi.sourceforge.net/. The array was generated by following -// Python script: -/* -python << EOF -"""Generates Randen round keys array from pi-hex.62500.txt file.""" -import binascii - -KEYS = 136 - -def chunks(l, n): - """Yield successive n-sized chunks from l.""" - for i in range(0, len(l), n): - yield l[i:i + n] - -def pairwise(t): - """Transforms sequence into sequence of pairs.""" - it = iter(t) - return zip(it,it) - -def digits_from_pi(): - """Reads digits from hexpi.sourceforge.net file.""" - with open("pi-hex.62500.txt") as file: - return file.read() - -def digits_from_urandom(): - """Reads digits from /dev/urandom.""" - with open("/dev/urandom") as file: - return binascii.hexlify(file.read(KEYS * 16)) - -digits = digits_from_pi() -print("static constexpr const size_t kRoundKeys = {0};\n".format(KEYS)) -print("alignas(16) constexpr const u64x2 round_keys[kRoundKeys] = {") - -for i, (hi, lo) in zip(range(KEYS), pairwise(chunks(digits, 16))): - hi = "0x{0}ull".format(hi) - lo = "0x{0}ull".format(lo) - print(" u64x2({0}, {1}){2}".format(hi, lo, ',' if i+1 < KEYS else '')) - -print("};") -EOF -*/ - -static constexpr const size_t kRoundKeys = 136; - -alignas(16) constexpr u64x2 round_keys[kRoundKeys] = { - u64x2(0x243F6A8885A308D3ull, 0x13198A2E03707344ull), - u64x2(0xA4093822299F31D0ull, 0x082EFA98EC4E6C89ull), - u64x2(0x452821E638D01377ull, 0xBE5466CF34E90C6Cull), - u64x2(0xC0AC29B7C97C50DDull, 0x3F84D5B5B5470917ull), - u64x2(0x9216D5D98979FB1Bull, 0xD1310BA698DFB5ACull), - u64x2(0x2FFD72DBD01ADFB7ull, 0xB8E1AFED6A267E96ull), - u64x2(0xBA7C9045F12C7F99ull, 0x24A19947B3916CF7ull), - u64x2(0x0801F2E2858EFC16ull, 0x636920D871574E69ull), - u64x2(0xA458FEA3F4933D7Eull, 0x0D95748F728EB658ull), - u64x2(0x718BCD5882154AEEull, 0x7B54A41DC25A59B5ull), - u64x2(0x9C30D5392AF26013ull, 0xC5D1B023286085F0ull), - u64x2(0xCA417918B8DB38EFull, 0x8E79DCB0603A180Eull), - u64x2(0x6C9E0E8BB01E8A3Eull, 0xD71577C1BD314B27ull), - u64x2(0x78AF2FDA55605C60ull, 0xE65525F3AA55AB94ull), - u64x2(0x5748986263E81440ull, 0x55CA396A2AAB10B6ull), - u64x2(0xB4CC5C341141E8CEull, 0xA15486AF7C72E993ull), - u64x2(0xB3EE1411636FBC2Aull, 0x2BA9C55D741831F6ull), - u64x2(0xCE5C3E169B87931Eull, 0xAFD6BA336C24CF5Cull), - u64x2(0x7A32538128958677ull, 0x3B8F48986B4BB9AFull), - u64x2(0xC4BFE81B66282193ull, 0x61D809CCFB21A991ull), - u64x2(0x487CAC605DEC8032ull, 0xEF845D5DE98575B1ull), - u64x2(0xDC262302EB651B88ull, 0x23893E81D396ACC5ull), - u64x2(0x0F6D6FF383F44239ull, 0x2E0B4482A4842004ull), - u64x2(0x69C8F04A9E1F9B5Eull, 0x21C66842F6E96C9Aull), - u64x2(0x670C9C61ABD388F0ull, 0x6A51A0D2D8542F68ull), - u64x2(0x960FA728AB5133A3ull, 0x6EEF0B6C137A3BE4ull), - u64x2(0xBA3BF0507EFB2A98ull, 0xA1F1651D39AF0176ull), - u64x2(0x66CA593E82430E88ull, 0x8CEE8619456F9FB4ull), - u64x2(0x7D84A5C33B8B5EBEull, 0xE06F75D885C12073ull), - u64x2(0x401A449F56C16AA6ull, 0x4ED3AA62363F7706ull), - u64x2(0x1BFEDF72429B023Dull, 0x37D0D724D00A1248ull), - u64x2(0xDB0FEAD349F1C09Bull, 0x075372C980991B7Bull), - u64x2(0x25D479D8F6E8DEF7ull, 0xE3FE501AB6794C3Bull), - u64x2(0x976CE0BD04C006BAull, 0xC1A94FB6409F60C4ull), - u64x2(0x5E5C9EC2196A2463ull, 0x68FB6FAF3E6C53B5ull), - u64x2(0x1339B2EB3B52EC6Full, 0x6DFC511F9B30952Cull), - u64x2(0xCC814544AF5EBD09ull, 0xBEE3D004DE334AFDull), - u64x2(0x660F2807192E4BB3ull, 0xC0CBA85745C8740Full), - u64x2(0xD20B5F39B9D3FBDBull, 0x5579C0BD1A60320Aull), - u64x2(0xD6A100C6402C7279ull, 0x679F25FEFB1FA3CCull), - u64x2(0x8EA5E9F8DB3222F8ull, 0x3C7516DFFD616B15ull), - u64x2(0x2F501EC8AD0552ABull, 0x323DB5FAFD238760ull), - u64x2(0x53317B483E00DF82ull, 0x9E5C57BBCA6F8CA0ull), - u64x2(0x1A87562EDF1769DBull, 0xD542A8F6287EFFC3ull), - u64x2(0xAC6732C68C4F5573ull, 0x695B27B0BBCA58C8ull), - u64x2(0xE1FFA35DB8F011A0ull, 0x10FA3D98FD2183B8ull), - u64x2(0x4AFCB56C2DD1D35Bull, 0x9A53E479B6F84565ull), - u64x2(0xD28E49BC4BFB9790ull, 0xE1DDF2DAA4CB7E33ull), - u64x2(0x62FB1341CEE4C6E8ull, 0xEF20CADA36774C01ull), - u64x2(0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull), - u64x2(0xEAAD8E716B93D5A0ull, 0xD08ED1D0AFC725E0ull), - u64x2(0x8E3C5B2F8E7594B7ull, 0x8FF6E2FBF2122B64ull), - u64x2(0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull), - u64x2(0xD1CFF191B3A8C1ADull, 0x2F2F2218BE0E1777ull), - u64x2(0xEA752DFE8B021FA1ull, 0xE5A0CC0FB56F74E8ull), - u64x2(0x18ACF3D6CE89E299ull, 0xB4A84FE0FD13E0B7ull), - u64x2(0x7CC43B81D2ADA8D9ull, 0x165FA26680957705ull), - u64x2(0x93CC7314211A1477ull, 0xE6AD206577B5FA86ull), - u64x2(0xC75442F5FB9D35CFull, 0xEBCDAF0C7B3E89A0ull), - u64x2(0xD6411BD3AE1E7E49ull, 0x00250E2D2071B35Eull), - u64x2(0x226800BB57B8E0AFull, 0x2464369BF009B91Eull), - u64x2(0x5563911D59DFA6AAull, 0x78C14389D95A537Full), - u64x2(0x207D5BA202E5B9C5ull, 0x832603766295CFA9ull), - u64x2(0x11C819684E734A41ull, 0xB3472DCA7B14A94Aull), - u64x2(0x1B5100529A532915ull, 0xD60F573FBC9BC6E4ull), - u64x2(0x2B60A47681E67400ull, 0x08BA6FB5571BE91Full), - u64x2(0xF296EC6B2A0DD915ull, 0xB6636521E7B9F9B6ull), - u64x2(0xFF34052EC5855664ull, 0x53B02D5DA99F8FA1ull), - u64x2(0x08BA47996E85076Aull, 0x4B7A70E9B5B32944ull), - u64x2(0xDB75092EC4192623ull, 0xAD6EA6B049A7DF7Dull), - u64x2(0x9CEE60B88FEDB266ull, 0xECAA8C71699A18FFull), - u64x2(0x5664526CC2B19EE1ull, 0x193602A575094C29ull), - u64x2(0xA0591340E4183A3Eull, 0x3F54989A5B429D65ull), - u64x2(0x6B8FE4D699F73FD6ull, 0xA1D29C07EFE830F5ull), - u64x2(0x4D2D38E6F0255DC1ull, 0x4CDD20868470EB26ull), - u64x2(0x6382E9C6021ECC5Eull, 0x09686B3F3EBAEFC9ull), - u64x2(0x3C9718146B6A70A1ull, 0x687F358452A0E286ull), - u64x2(0xB79C5305AA500737ull, 0x3E07841C7FDEAE5Cull), - u64x2(0x8E7D44EC5716F2B8ull, 0xB03ADA37F0500C0Dull), - u64x2(0xF01C1F040200B3FFull, 0xAE0CF51A3CB574B2ull), - u64x2(0x25837A58DC0921BDull, 0xD19113F97CA92FF6ull), - u64x2(0x9432477322F54701ull, 0x3AE5E58137C2DADCull), - u64x2(0xC8B576349AF3DDA7ull, 0xA94461460FD0030Eull), - u64x2(0xECC8C73EA4751E41ull, 0xE238CD993BEA0E2Full), - u64x2(0x3280BBA1183EB331ull, 0x4E548B384F6DB908ull), - u64x2(0x6F420D03F60A04BFull, 0x2CB8129024977C79ull), - u64x2(0x5679B072BCAF89AFull, 0xDE9A771FD9930810ull), - u64x2(0xB38BAE12DCCF3F2Eull, 0x5512721F2E6B7124ull), - u64x2(0x501ADDE69F84CD87ull, 0x7A5847187408DA17ull), - u64x2(0xBC9F9ABCE94B7D8Cull, 0xEC7AEC3ADB851DFAull), - u64x2(0x63094366C464C3D2ull, 0xEF1C18473215D808ull), - u64x2(0xDD433B3724C2BA16ull, 0x12A14D432A65C451ull), - u64x2(0x50940002133AE4DDull, 0x71DFF89E10314E55ull), - u64x2(0x81AC77D65F11199Bull, 0x043556F1D7A3C76Bull), - u64x2(0x3C11183B5924A509ull, 0xF28FE6ED97F1FBFAull), - u64x2(0x9EBABF2C1E153C6Eull, 0x86E34570EAE96FB1ull), - u64x2(0x860E5E0A5A3E2AB3ull, 0x771FE71C4E3D06FAull), - u64x2(0x2965DCB999E71D0Full, 0x803E89D65266C825ull), - u64x2(0x2E4CC9789C10B36Aull, 0xC6150EBA94E2EA78ull), - u64x2(0xA6FC3C531E0A2DF4ull, 0xF2F74EA7361D2B3Dull), - u64x2(0x1939260F19C27960ull, 0x5223A708F71312B6ull), - u64x2(0xEBADFE6EEAC31F66ull, 0xE3BC4595A67BC883ull), - u64x2(0xB17F37D1018CFF28ull, 0xC332DDEFBE6C5AA5ull), - u64x2(0x6558218568AB9702ull, 0xEECEA50FDB2F953Bull), - u64x2(0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull), - u64x2(0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull), - u64x2(0x0334FE1EAA0363CFull, 0xB5735C904C70A239ull), - u64x2(0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull), - u64x2(0x9CAB5CABB2F3846Eull, 0x648B1EAF19BDF0CAull), - u64x2(0xA02369B9655ABB50ull, 0x40685A323C2AB4B3ull), - u64x2(0x319EE9D5C021B8F7ull, 0x9B540B19875FA099ull), - u64x2(0x95F7997E623D7DA8ull, 0xF837889A97E32D77ull), - u64x2(0x11ED935F16681281ull, 0x0E358829C7E61FD6ull), - u64x2(0x96DEDFA17858BA99ull, 0x57F584A51B227263ull), - u64x2(0x9B83C3FF1AC24696ull, 0xCDB30AEB532E3054ull), - u64x2(0x8FD948E46DBC3128ull, 0x58EBF2EF34C6FFEAull), - u64x2(0xFE28ED61EE7C3C73ull, 0x5D4A14D9E864B7E3ull), - u64x2(0x42105D14203E13E0ull, 0x45EEE2B6A3AAABEAull), - u64x2(0xDB6C4F15FACB4FD0ull, 0xC742F442EF6ABBB5ull), - u64x2(0x654F3B1D41CD2105ull, 0xD81E799E86854DC7ull), - u64x2(0xE44B476A3D816250ull, 0xCF62A1F25B8D2646ull), - u64x2(0xFC8883A0C1C7B6A3ull, 0x7F1524C369CB7492ull), - u64x2(0x47848A0B5692B285ull, 0x095BBF00AD19489Dull), - u64x2(0x1462B17423820D00ull, 0x58428D2A0C55F5EAull), - u64x2(0x1DADF43E233F7061ull, 0x3372F0928D937E41ull), - u64x2(0xD65FECF16C223BDBull, 0x7CDE3759CBEE7460ull), - u64x2(0x4085F2A7CE77326Eull, 0xA607808419F8509Eull), - u64x2(0xE8EFD85561D99735ull, 0xA969A7AAC50C06C2ull), - u64x2(0x5A04ABFC800BCADCull, 0x9E447A2EC3453484ull), - u64x2(0xFDD567050E1E9EC9ull, 0xDB73DBD3105588CDull), - u64x2(0x675FDA79E3674340ull, 0xC5C43465713E38D8ull), - u64x2(0x3D28F89EF16DFF20ull, 0x153E21E78FB03D4Aull), - u64x2(0xE6E39F2BDB83ADF7ull, 0xE93D5A68948140F7ull), - u64x2(0xF64C261C94692934ull, 0x411520F77602D4F7ull), - u64x2(0xBCF46B2ED4A10068ull, 0xD40824713320F46Aull), - u64x2(0x43B7D4B7500061AFull, 0x1E39F62E97244546ull)}; - -#endif // ABSL_RANDOM_INTERNAL_RANDEN_KEYS_INC_ diff --git a/third_party/abseil-cpp/absl/random/internal/randen.cc b/third_party/abseil-cpp/absl/random/internal/randen.cc index 78a1e00c08..c1bc044435 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen.cc +++ b/third_party/abseil-cpp/absl/random/internal/randen.cc @@ -17,7 +17,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/random/internal/randen_detect.h" -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. // diff --git a/third_party/abseil-cpp/absl/random/internal/randen.h b/third_party/abseil-cpp/absl/random/internal/randen.h index c2834aaf3d..9a3840b8f1 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen.h +++ b/third_party/abseil-cpp/absl/random/internal/randen.h @@ -26,7 +26,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. // diff --git a/third_party/abseil-cpp/absl/random/internal/randen_detect.cc b/third_party/abseil-cpp/absl/random/internal/randen_detect.cc index d63230c255..bbe7b96532 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_detect.cc +++ b/third_party/abseil-cpp/absl/random/internal/randen_detect.cc @@ -1,13 +1,13 @@ // Copyright 2017 The Abseil Authors. // -// Licensed under the Apache License, Version 2.0 (the"License"); +// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an"AS IS" BASIS, +// distributed under the License is distributed on an "AS IS" BASIS, // 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. diff --git a/third_party/abseil-cpp/absl/random/internal/randen_engine.h b/third_party/abseil-cpp/absl/random/internal/randen_engine.h index 6b33731336..372c3ac2bd 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_engine.h +++ b/third_party/abseil-cpp/absl/random/internal/randen_engine.h @@ -23,6 +23,7 @@ #include <limits> #include <type_traits> +#include "absl/base/internal/endian.h" #include "absl/meta/type_traits.h" #include "absl/random/internal/iostream_state_saver.h" #include "absl/random/internal/randen.h" @@ -76,7 +77,7 @@ class alignas(16) randen_engine { impl_.Generate(state_); } - return state_[next_++]; + return little_endian::ToHost(state_[next_++]); } template <class SeedSequence> @@ -120,6 +121,13 @@ class alignas(16) randen_engine { const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size; std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0); seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy); +#ifdef ABSL_IS_BIG_ENDIAN + // Randen expects the seed buffer to be in Little Endian; reverse it on + // Big Endian platforms. + for (sequence_result_type& e : buffer) { + e = absl::little_endian::FromHost(e); + } +#endif // The Randen paper suggests preferentially initializing even-numbered // 128-bit vectors of the randen state (there are 16 such vectors). // The seed data is merged into the state offset by 128-bits, which @@ -181,7 +189,8 @@ class alignas(16) randen_engine { // In the case that `elem` is `uint8_t`, it must be cast to something // larger so that it prints as an integer rather than a character. For // simplicity, apply the cast all circumstances. - os << static_cast<numeric_type>(elem) << os.fill(); + os << static_cast<numeric_type>(little_endian::FromHost(elem)) + << os.fill(); } os << engine.next_; return os; @@ -200,7 +209,7 @@ class alignas(16) randen_engine { // necessary to read a wider type and then cast it to uint8_t. numeric_type value; is >> value; - elem = static_cast<result_type>(value); + elem = little_endian::ToHost(static_cast<result_type>(value)); } is >> next; if (is.fail()) { diff --git a/third_party/abseil-cpp/absl/random/internal/randen_hwaes.cc b/third_party/abseil-cpp/absl/random/internal/randen_hwaes.cc index e23844f12e..fee6677cb4 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_hwaes.cc +++ b/third_party/abseil-cpp/absl/random/internal/randen_hwaes.cc @@ -23,48 +23,20 @@ #include <cstring> #include "absl/base/attributes.h" +#include "absl/numeric/int128.h" #include "absl/random/internal/platform.h" +#include "absl/random/internal/randen_traits.h" // ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain // a hardware accelerated implementation of randen, or whether it // will contain stubs that exit the process. -#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) -// The platform.h directives are sufficient to indicate whether -// we should build accelerated implementations for x86. -#if (ABSL_HAVE_ACCELERATED_AES || ABSL_RANDOM_INTERNAL_AES_DISPATCH) -#define ABSL_RANDEN_HWAES_IMPL 1 -#endif -#elif defined(ABSL_ARCH_PPC) -// The platform.h directives are sufficient to indicate whether -// we should build accelerated implementations for PPC. -// -// NOTE: This has mostly been tested on 64-bit Power variants, -// and not embedded cpus such as powerpc32-8540 #if ABSL_HAVE_ACCELERATED_AES +// The following plaforms have implemented RandenHwAes. +#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \ + defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \ + defined(ABSL_ARCH_AARCH64) #define ABSL_RANDEN_HWAES_IMPL 1 #endif -#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) -// ARM is somewhat more complicated. We might support crypto natively... -#if ABSL_HAVE_ACCELERATED_AES || \ - (defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO)) -#define ABSL_RANDEN_HWAES_IMPL 1 - -#elif ABSL_RANDOM_INTERNAL_AES_DISPATCH && !defined(__APPLE__) && \ - (defined(__GNUC__) && __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 9) -// ...or, on GCC, we can use an ASM directive to -// instruct the assember to allow crypto instructions. -#define ABSL_RANDEN_HWAES_IMPL 1 -#define ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE 1 -#endif -#else -// HWAES is unsupported by these architectures / platforms: -// __myriad2__ -// __mips__ -// -// Other architectures / platforms are unknown. -// -// See the Abseil documentation on supported macros at: -// https://abseil.io/docs/cpp/platforms/macros #endif #if !defined(ABSL_RANDEN_HWAES_IMPL) @@ -115,8 +87,11 @@ ABSL_NAMESPACE_END // Accelerated implementations are supported. // We need the per-architecture includes and defines. // +namespace { -#include "absl/random/internal/randen_traits.h" +using absl::random_internal::RandenTraits; + +} // namespace // TARGET_CRYPTO defines a crypto attribute for each architecture. // @@ -141,6 +116,7 @@ ABSL_NAMESPACE_END #include <altivec.h> // <altivec.h> #defines vector __vector; in C++, this is bad form. #undef vector +#undef bool // Rely on the PowerPC AltiVec vector operations for accelerated AES // instructions. GCC support of the PPC vector types is described in: @@ -150,7 +126,6 @@ ABSL_NAMESPACE_END using Vector128 = __vector unsigned long long; // NOLINT(runtime/int) namespace { - inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) { // Reverses the bytes of the vector. const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8, @@ -177,14 +152,9 @@ inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, } // Enables native loads in the round loop by pre-swapping. -inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t* state) { - using absl::random_internal::RandenTraits; - constexpr size_t kLanes = 2; - constexpr size_t kFeistelBlocks = RandenTraits::kFeistelBlocks; - - for (uint32_t branch = 0; branch < kFeistelBlocks; ++branch) { - const Vector128 v = ReverseBytes(Vector128Load(state + kLanes * branch)); - Vector128Store(v, state + kLanes * branch); +inline ABSL_TARGET_CRYPTO void SwapEndian(absl::uint128* state) { + for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) { + Vector128Store(ReverseBytes(Vector128Load(state + block)), state + block); } } @@ -192,22 +162,6 @@ inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t* state) { #elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) -// This asm directive will cause the file to be compiled with crypto extensions -// whether or not the cpu-architecture supports it. -#if ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE -asm(".arch_extension crypto\n"); - -// Override missing defines. -#if !defined(__ARM_NEON) -#define __ARM_NEON 1 -#endif - -#if !defined(__ARM_FEATURE_CRYPTO) -#define __ARM_FEATURE_CRYPTO 1 -#endif - -#endif - // Rely on the ARM NEON+Crypto advanced simd types, defined in <arm_neon.h>. // uint8x16_t is the user alias for underlying __simd128_uint8_t type. // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf @@ -251,13 +205,13 @@ inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key; } -inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {} +inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {} } // namespace #elif defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) // On x86 we rely on the aesni instructions -#include <wmmintrin.h> +#include <immintrin.h> namespace { @@ -266,7 +220,7 @@ namespace { class Vector128 { public: // Convert from/to intrinsics. - inline explicit Vector128(const __m128i& Vector128) : data_(Vector128) {} + inline explicit Vector128(const __m128i& v) : data_(v) {} inline __m128i data() const { return data_; } @@ -297,39 +251,12 @@ inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, return Vector128(_mm_aesenc_si128(state.data(), round_key.data())); } -inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {} +inline ABSL_TARGET_CRYPTO void SwapEndian(void*) {} } // namespace #endif -namespace { - -// u64x2 is a 128-bit, (2 x uint64_t lanes) struct used to store -// the randen_keys. -struct alignas(16) u64x2 { - constexpr u64x2(uint64_t hi, uint64_t lo) -#if defined(ABSL_ARCH_PPC) - // This has been tested with PPC running in little-endian mode; - // We byte-swap the u64x2 structure from little-endian to big-endian - // because altivec always runs in big-endian mode. - : v{__builtin_bswap64(hi), __builtin_bswap64(lo)} { -#else - : v{lo, hi} { -#endif - } - - constexpr bool operator==(const u64x2& other) const { - return v[0] == other.v[0] && v[1] == other.v[1]; - } - - constexpr bool operator!=(const u64x2& other) const { - return !(*this == other); - } - - uint64_t v[2]; -}; // namespace - #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-pragmas" @@ -338,7 +265,6 @@ struct alignas(16) u64x2 { // At this point, all of the platform-specific features have been defined / // implemented. // -// REQUIRES: using u64x2 = ... // REQUIRES: using Vector128 = ... // REQUIRES: Vector128 Vector128Load(void*) {...} // REQUIRES: void Vector128Store(Vector128, void*) {...} @@ -347,94 +273,50 @@ struct alignas(16) u64x2 { // // PROVIDES: absl::random_internal::RandenHwAes::Absorb // PROVIDES: absl::random_internal::RandenHwAes::Generate - -// RANDen = RANDom generator or beetroots in Swiss German. -// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random -// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. -// -// High-level summary: -// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is -// a sponge-like random generator that requires a cryptographic permutation. -// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by -// achieving backtracking resistance with only one Permute() per buffer. -// -// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round -// Function" constructs up to 1024-bit permutations using an improved -// Generalized Feistel network with 2-round AES-128 functions. This Feistel -// block shuffle achieves diffusion faster and is less vulnerable to -// sliced-biclique attacks than the Type-2 cyclic shuffle. -// -// 3) "Improving the Generalized Feistel" and "New criterion for diffusion -// property" extends the same kind of improved Feistel block shuffle to 16 -// branches, which enables a 2048-bit permutation. -// -// We combine these three ideas and also change Simpira's subround keys from -// structured/low-entropy counters to digits of Pi. - -// Randen constants. -using absl::random_internal::RandenTraits; -constexpr size_t kStateBytes = RandenTraits::kStateBytes; -constexpr size_t kCapacityBytes = RandenTraits::kCapacityBytes; -constexpr size_t kFeistelBlocks = RandenTraits::kFeistelBlocks; -constexpr size_t kFeistelRounds = RandenTraits::kFeistelRounds; -constexpr size_t kFeistelFunctions = RandenTraits::kFeistelFunctions; - -// Independent keys (272 = 2.1 KiB) for the first AES subround of each function. -constexpr size_t kKeys = kFeistelRounds * kFeistelFunctions; - -// INCLUDE keys. -#include "absl/random/internal/randen-keys.inc" - -static_assert(kKeys == kRoundKeys, "kKeys and kRoundKeys must be equal"); -static_assert(round_keys[kKeys - 1] != u64x2(0, 0), - "Too few round_keys initializers"); - -// Number of uint64_t lanes per 128-bit vector; -constexpr size_t kLanes = 2; +namespace { // Block shuffles applies a shuffle to the entire state between AES rounds. // Improved odd-even shuffle from "New criterion for diffusion property". -inline ABSL_TARGET_CRYPTO void BlockShuffle(uint64_t* state) { - static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); - - constexpr size_t shuffle[kFeistelBlocks] = {7, 2, 13, 4, 11, 8, 3, 6, - 15, 0, 9, 10, 1, 14, 5, 12}; - - // The fully unrolled loop without the memcpy improves the speed by about - // 30% over the equivalent loop. - const Vector128 v0 = Vector128Load(state + kLanes * shuffle[0]); - const Vector128 v1 = Vector128Load(state + kLanes * shuffle[1]); - const Vector128 v2 = Vector128Load(state + kLanes * shuffle[2]); - const Vector128 v3 = Vector128Load(state + kLanes * shuffle[3]); - const Vector128 v4 = Vector128Load(state + kLanes * shuffle[4]); - const Vector128 v5 = Vector128Load(state + kLanes * shuffle[5]); - const Vector128 v6 = Vector128Load(state + kLanes * shuffle[6]); - const Vector128 v7 = Vector128Load(state + kLanes * shuffle[7]); - const Vector128 w0 = Vector128Load(state + kLanes * shuffle[8]); - const Vector128 w1 = Vector128Load(state + kLanes * shuffle[9]); - const Vector128 w2 = Vector128Load(state + kLanes * shuffle[10]); - const Vector128 w3 = Vector128Load(state + kLanes * shuffle[11]); - const Vector128 w4 = Vector128Load(state + kLanes * shuffle[12]); - const Vector128 w5 = Vector128Load(state + kLanes * shuffle[13]); - const Vector128 w6 = Vector128Load(state + kLanes * shuffle[14]); - const Vector128 w7 = Vector128Load(state + kLanes * shuffle[15]); - - Vector128Store(v0, state + kLanes * 0); - Vector128Store(v1, state + kLanes * 1); - Vector128Store(v2, state + kLanes * 2); - Vector128Store(v3, state + kLanes * 3); - Vector128Store(v4, state + kLanes * 4); - Vector128Store(v5, state + kLanes * 5); - Vector128Store(v6, state + kLanes * 6); - Vector128Store(v7, state + kLanes * 7); - Vector128Store(w0, state + kLanes * 8); - Vector128Store(w1, state + kLanes * 9); - Vector128Store(w2, state + kLanes * 10); - Vector128Store(w3, state + kLanes * 11); - Vector128Store(w4, state + kLanes * 12); - Vector128Store(w5, state + kLanes * 13); - Vector128Store(w6, state + kLanes * 14); - Vector128Store(w7, state + kLanes * 15); +inline ABSL_TARGET_CRYPTO void BlockShuffle(absl::uint128* state) { + static_assert(RandenTraits::kFeistelBlocks == 16, + "Expecting 16 FeistelBlocks."); + + constexpr size_t shuffle[RandenTraits::kFeistelBlocks] = { + 7, 2, 13, 4, 11, 8, 3, 6, 15, 0, 9, 10, 1, 14, 5, 12}; + + const Vector128 v0 = Vector128Load(state + shuffle[0]); + const Vector128 v1 = Vector128Load(state + shuffle[1]); + const Vector128 v2 = Vector128Load(state + shuffle[2]); + const Vector128 v3 = Vector128Load(state + shuffle[3]); + const Vector128 v4 = Vector128Load(state + shuffle[4]); + const Vector128 v5 = Vector128Load(state + shuffle[5]); + const Vector128 v6 = Vector128Load(state + shuffle[6]); + const Vector128 v7 = Vector128Load(state + shuffle[7]); + const Vector128 w0 = Vector128Load(state + shuffle[8]); + const Vector128 w1 = Vector128Load(state + shuffle[9]); + const Vector128 w2 = Vector128Load(state + shuffle[10]); + const Vector128 w3 = Vector128Load(state + shuffle[11]); + const Vector128 w4 = Vector128Load(state + shuffle[12]); + const Vector128 w5 = Vector128Load(state + shuffle[13]); + const Vector128 w6 = Vector128Load(state + shuffle[14]); + const Vector128 w7 = Vector128Load(state + shuffle[15]); + + Vector128Store(v0, state + 0); + Vector128Store(v1, state + 1); + Vector128Store(v2, state + 2); + Vector128Store(v3, state + 3); + Vector128Store(v4, state + 4); + Vector128Store(v5, state + 5); + Vector128Store(v6, state + 6); + Vector128Store(v7, state + 7); + Vector128Store(w0, state + 8); + Vector128Store(w1, state + 9); + Vector128Store(w2, state + 10); + Vector128Store(w3, state + 11); + Vector128Store(w4, state + 12); + Vector128Store(w5, state + 13); + Vector128Store(w6, state + 14); + Vector128Store(w7, state + 15); } // Feistel round function using two AES subrounds. Very similar to F() @@ -442,28 +324,30 @@ inline ABSL_TARGET_CRYPTO void BlockShuffle(uint64_t* state) { // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel // XORs are 'free' (included in the second AES instruction). -inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( - uint64_t* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { - static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); +inline ABSL_TARGET_CRYPTO const absl::uint128* FeistelRound( + absl::uint128* state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + static_assert(RandenTraits::kFeistelBlocks == 16, + "Expecting 16 FeistelBlocks."); // MSVC does a horrible job at unrolling loops. // So we unroll the loop by hand to improve the performance. - const Vector128 s0 = Vector128Load(state + kLanes * 0); - const Vector128 s1 = Vector128Load(state + kLanes * 1); - const Vector128 s2 = Vector128Load(state + kLanes * 2); - const Vector128 s3 = Vector128Load(state + kLanes * 3); - const Vector128 s4 = Vector128Load(state + kLanes * 4); - const Vector128 s5 = Vector128Load(state + kLanes * 5); - const Vector128 s6 = Vector128Load(state + kLanes * 6); - const Vector128 s7 = Vector128Load(state + kLanes * 7); - const Vector128 s8 = Vector128Load(state + kLanes * 8); - const Vector128 s9 = Vector128Load(state + kLanes * 9); - const Vector128 s10 = Vector128Load(state + kLanes * 10); - const Vector128 s11 = Vector128Load(state + kLanes * 11); - const Vector128 s12 = Vector128Load(state + kLanes * 12); - const Vector128 s13 = Vector128Load(state + kLanes * 13); - const Vector128 s14 = Vector128Load(state + kLanes * 14); - const Vector128 s15 = Vector128Load(state + kLanes * 15); + const Vector128 s0 = Vector128Load(state + 0); + const Vector128 s1 = Vector128Load(state + 1); + const Vector128 s2 = Vector128Load(state + 2); + const Vector128 s3 = Vector128Load(state + 3); + const Vector128 s4 = Vector128Load(state + 4); + const Vector128 s5 = Vector128Load(state + 5); + const Vector128 s6 = Vector128Load(state + 6); + const Vector128 s7 = Vector128Load(state + 7); + const Vector128 s8 = Vector128Load(state + 8); + const Vector128 s9 = Vector128Load(state + 9); + const Vector128 s10 = Vector128Load(state + 10); + const Vector128 s11 = Vector128Load(state + 11); + const Vector128 s12 = Vector128Load(state + 12); + const Vector128 s13 = Vector128Load(state + 13); + const Vector128 s14 = Vector128Load(state + 14); + const Vector128 s15 = Vector128Load(state + 15); // Encode even blocks with keys. const Vector128 e0 = AesRound(s0, Vector128Load(keys + 0)); @@ -486,14 +370,14 @@ inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( const Vector128 o15 = AesRound(e14, s15); // Store odd blocks. (These will be shuffled later). - Vector128Store(o1, state + kLanes * 1); - Vector128Store(o3, state + kLanes * 3); - Vector128Store(o5, state + kLanes * 5); - Vector128Store(o7, state + kLanes * 7); - Vector128Store(o9, state + kLanes * 9); - Vector128Store(o11, state + kLanes * 11); - Vector128Store(o13, state + kLanes * 13); - Vector128Store(o15, state + kLanes * 15); + Vector128Store(o1, state + 1); + Vector128Store(o3, state + 3); + Vector128Store(o5, state + 5); + Vector128Store(o7, state + 7); + Vector128Store(o9, state + 9); + Vector128Store(o11, state + 11); + Vector128Store(o13, state + 13); + Vector128Store(o15, state + 15); return keys + 8; } @@ -503,16 +387,14 @@ inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( // 2^64 queries if the round function is a PRF. This is similar to the b=8 case // of Simpira v2, but more efficient than its generic construction for b=16. inline ABSL_TARGET_CRYPTO void Permute( - const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, uint64_t* state) { - const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys128 = - static_cast<const u64x2*>(keys); - + absl::uint128* state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { // (Successfully unrolled; the first iteration jumps into the second half) #ifdef __clang__ #pragma clang loop unroll_count(2) #endif - for (size_t round = 0; round < kFeistelRounds; ++round) { - keys128 = FeistelRound(state, keys128); + for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) { + keys = FeistelRound(state, keys); BlockShuffle(state); } } @@ -528,96 +410,102 @@ bool HasRandenHwAesImplementation() { return true; } const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() { // Round keys for one AES per Feistel round and branch. // The canonical implementation uses first digits of Pi. - return round_keys; +#if defined(ABSL_ARCH_PPC) + return kRandenRoundKeysBE; +#else + return kRandenRoundKeys; +#endif } // NOLINTNEXTLINE void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void, void* state_void) { - auto* state = static_cast<uint64_t*>(state_void); - const auto* seed = static_cast<const uint64_t*>(seed_void); - - constexpr size_t kCapacityBlocks = kCapacityBytes / sizeof(Vector128); - constexpr size_t kStateBlocks = kStateBytes / sizeof(Vector128); - - static_assert(kCapacityBlocks * sizeof(Vector128) == kCapacityBytes, - "Not i*V"); - static_assert(kCapacityBlocks == 1, "Unexpected Randen kCapacityBlocks"); - static_assert(kStateBlocks == 16, "Unexpected Randen kStateBlocks"); - - Vector128 b1 = Vector128Load(state + kLanes * 1); - b1 ^= Vector128Load(seed + kLanes * 0); - Vector128Store(b1, state + kLanes * 1); - - Vector128 b2 = Vector128Load(state + kLanes * 2); - b2 ^= Vector128Load(seed + kLanes * 1); - Vector128Store(b2, state + kLanes * 2); - - Vector128 b3 = Vector128Load(state + kLanes * 3); - b3 ^= Vector128Load(seed + kLanes * 2); - Vector128Store(b3, state + kLanes * 3); - - Vector128 b4 = Vector128Load(state + kLanes * 4); - b4 ^= Vector128Load(seed + kLanes * 3); - Vector128Store(b4, state + kLanes * 4); - - Vector128 b5 = Vector128Load(state + kLanes * 5); - b5 ^= Vector128Load(seed + kLanes * 4); - Vector128Store(b5, state + kLanes * 5); - - Vector128 b6 = Vector128Load(state + kLanes * 6); - b6 ^= Vector128Load(seed + kLanes * 5); - Vector128Store(b6, state + kLanes * 6); - - Vector128 b7 = Vector128Load(state + kLanes * 7); - b7 ^= Vector128Load(seed + kLanes * 6); - Vector128Store(b7, state + kLanes * 7); - - Vector128 b8 = Vector128Load(state + kLanes * 8); - b8 ^= Vector128Load(seed + kLanes * 7); - Vector128Store(b8, state + kLanes * 8); - - Vector128 b9 = Vector128Load(state + kLanes * 9); - b9 ^= Vector128Load(seed + kLanes * 8); - Vector128Store(b9, state + kLanes * 9); - - Vector128 b10 = Vector128Load(state + kLanes * 10); - b10 ^= Vector128Load(seed + kLanes * 9); - Vector128Store(b10, state + kLanes * 10); - - Vector128 b11 = Vector128Load(state + kLanes * 11); - b11 ^= Vector128Load(seed + kLanes * 10); - Vector128Store(b11, state + kLanes * 11); - - Vector128 b12 = Vector128Load(state + kLanes * 12); - b12 ^= Vector128Load(seed + kLanes * 11); - Vector128Store(b12, state + kLanes * 12); - - Vector128 b13 = Vector128Load(state + kLanes * 13); - b13 ^= Vector128Load(seed + kLanes * 12); - Vector128Store(b13, state + kLanes * 13); - - Vector128 b14 = Vector128Load(state + kLanes * 14); - b14 ^= Vector128Load(seed + kLanes * 13); - Vector128Store(b14, state + kLanes * 14); - - Vector128 b15 = Vector128Load(state + kLanes * 15); - b15 ^= Vector128Load(seed + kLanes * 14); - Vector128Store(b15, state + kLanes * 15); + static_assert(RandenTraits::kCapacityBytes / sizeof(Vector128) == 1, + "Unexpected Randen kCapacityBlocks"); + static_assert(RandenTraits::kStateBytes / sizeof(Vector128) == 16, + "Unexpected Randen kStateBlocks"); + + auto* state = reinterpret_cast<absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>( + state_void); + const auto* seed = + reinterpret_cast<const absl::uint128 * ABSL_RANDOM_INTERNAL_RESTRICT>( + seed_void); + + Vector128 b1 = Vector128Load(state + 1); + b1 ^= Vector128Load(seed + 0); + Vector128Store(b1, state + 1); + + Vector128 b2 = Vector128Load(state + 2); + b2 ^= Vector128Load(seed + 1); + Vector128Store(b2, state + 2); + + Vector128 b3 = Vector128Load(state + 3); + b3 ^= Vector128Load(seed + 2); + Vector128Store(b3, state + 3); + + Vector128 b4 = Vector128Load(state + 4); + b4 ^= Vector128Load(seed + 3); + Vector128Store(b4, state + 4); + + Vector128 b5 = Vector128Load(state + 5); + b5 ^= Vector128Load(seed + 4); + Vector128Store(b5, state + 5); + + Vector128 b6 = Vector128Load(state + 6); + b6 ^= Vector128Load(seed + 5); + Vector128Store(b6, state + 6); + + Vector128 b7 = Vector128Load(state + 7); + b7 ^= Vector128Load(seed + 6); + Vector128Store(b7, state + 7); + + Vector128 b8 = Vector128Load(state + 8); + b8 ^= Vector128Load(seed + 7); + Vector128Store(b8, state + 8); + + Vector128 b9 = Vector128Load(state + 9); + b9 ^= Vector128Load(seed + 8); + Vector128Store(b9, state + 9); + + Vector128 b10 = Vector128Load(state + 10); + b10 ^= Vector128Load(seed + 9); + Vector128Store(b10, state + 10); + + Vector128 b11 = Vector128Load(state + 11); + b11 ^= Vector128Load(seed + 10); + Vector128Store(b11, state + 11); + + Vector128 b12 = Vector128Load(state + 12); + b12 ^= Vector128Load(seed + 11); + Vector128Store(b12, state + 12); + + Vector128 b13 = Vector128Load(state + 13); + b13 ^= Vector128Load(seed + 12); + Vector128Store(b13, state + 13); + + Vector128 b14 = Vector128Load(state + 14); + b14 ^= Vector128Load(seed + 13); + Vector128Store(b14, state + 14); + + Vector128 b15 = Vector128Load(state + 15); + b15 ^= Vector128Load(seed + 14); + Vector128Store(b15, state + 15); } // NOLINTNEXTLINE -void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys, +void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys_void, void* state_void) { - static_assert(kCapacityBytes == sizeof(Vector128), "Capacity mismatch"); + static_assert(RandenTraits::kCapacityBytes == sizeof(Vector128), + "Capacity mismatch"); - auto* state = static_cast<uint64_t*>(state_void); + auto* state = reinterpret_cast<absl::uint128*>(state_void); + const auto* keys = reinterpret_cast<const absl::uint128*>(keys_void); const Vector128 prev_inner = Vector128Load(state); SwapEndian(state); - Permute(keys, state); + Permute(state, keys); SwapEndian(state); diff --git a/third_party/abseil-cpp/absl/random/internal/randen_hwaes.h b/third_party/abseil-cpp/absl/random/internal/randen_hwaes.h index bce36b5226..71a7f69f25 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_hwaes.h +++ b/third_party/abseil-cpp/absl/random/internal/randen_hwaes.h @@ -26,7 +26,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. // diff --git a/third_party/abseil-cpp/absl/random/internal/randen_hwaes_test.cc b/third_party/abseil-cpp/absl/random/internal/randen_hwaes_test.cc index a7cbd46b2d..2348b55c35 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_hwaes_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/randen_hwaes_test.cc @@ -27,42 +27,39 @@ namespace { using absl::random_internal::RandenHwAes; using absl::random_internal::RandenTraits; -struct randen { - static constexpr size_t kStateSizeT = - RandenTraits::kStateBytes / sizeof(uint64_t); - uint64_t state[kStateSizeT]; - static constexpr size_t kSeedSizeT = - RandenTraits::kSeedBytes / sizeof(uint32_t); - uint32_t seed[kSeedSizeT]; -}; - TEST(RandenHwAesTest, Default) { EXPECT_TRUE(absl::random_internal::CPUSupportsRandenHwAes()); - constexpr uint64_t kGolden[] = { - 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, - 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, - 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, - 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, - 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, - 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, - 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, - 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, - 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, - 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, - 0x026ff374c101da7e, 0x811ef0821c3de851, + constexpr uint8_t kGolden[] = { + 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d, + 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3, + 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f, + 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0, + 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff, + 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2, + 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5, + 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c, + 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56, + 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4, + 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91, + 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5, + 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d, + 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58, + 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e, + 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49, + 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0, + 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6, + 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61, + 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35, + 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c, + 0x82, 0xf0, 0x1e, 0x81, }; - alignas(16) randen d; - memset(d.state, 0, sizeof(d.state)); - RandenHwAes::Generate(RandenHwAes::GetKeys(), d.state); + alignas(16) uint8_t state[RandenTraits::kStateBytes]; + std::memset(state, 0, sizeof(state)); - uint64_t* id = d.state; - for (const auto& elem : kGolden) { - auto a = absl::StrFormat("%#x", elem); - auto b = absl::StrFormat("%#x", *id++); - EXPECT_EQ(a, b); - } + RandenHwAes::Generate(RandenHwAes::GetKeys(), state); + EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state))); } } // namespace diff --git a/third_party/abseil-cpp/absl/random/internal/randen_round_keys.cc b/third_party/abseil-cpp/absl/random/internal/randen_round_keys.cc new file mode 100644 index 0000000000..5fb3ca556d --- /dev/null +++ b/third_party/abseil-cpp/absl/random/internal/randen_round_keys.cc @@ -0,0 +1,462 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/random/internal/randen_traits.h" + +// This file contains only the round keys for randen. +// +// "Nothing up my sleeve" numbers from the first hex digits of Pi, obtained +// from http://hexpi.sourceforge.net/. The array was generated by following +// Python script: + +/* +python >tmp.cc << EOF +"""Generates Randen round keys array from pi-hex.62500.txt file.""" +import binascii + +KEYS = 17 * 8 + +def chunks(l, n): + """Yield successive n-sized chunks from l.""" + for i in range(0, len(l), n): + yield l[i:i + n] + +def pairwise(t): + """Transforms sequence into sequence of pairs.""" + it = iter(t) + return zip(it,it) + +def digits_from_pi(): + """Reads digits from hexpi.sourceforge.net file.""" + with open("pi-hex.62500.txt") as file: + return file.read() + +def digits_from_urandom(): + """Reads digits from /dev/urandom.""" + with open("/dev/urandom") as file: + return binascii.hexlify(file.read(KEYS * 16)) + +def print_row(b) + print(" 0x{0}, 0x{1}, 0x{2}, 0x{3}, 0x{4}, 0x{5}, 0x{6}, 0x{7}, 0x{8}, 0x{9}, +0x{10}, 0x{11}, 0x{12}, 0x{13}, 0x{14}, 0x{15},".format(*b)) + + +digits = digits_from_pi() +#digits = digits_from_urandom() + +print("namespace {") +print("static constexpr size_t kKeyBytes = {0};\n".format(KEYS * 16)) +print("}") + +print("alignas(16) const unsigned char kRandenRoundKeysBE[kKeyBytes] = {") + +for i, u16 in zip(range(KEYS), chunks(digits, 32)): + b = list(chunks(u16, 2)) + print_row(b) + +print("};") + +print("alignas(16) const unsigned char kRandenRoundKeys[kKeyBytes] = {") + +for i, u16 in zip(range(KEYS), chunks(digits, 32)): + b = list(chunks(u16, 2)) + b.reverse() + print_row(b) + +print("};") + +EOF + +*/ + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace random_internal { +namespace { +static constexpr size_t kKeyBytes = 2176; +} + +alignas(16) const unsigned char kRandenRoundKeysBE[kKeyBytes] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, 0x13, 0x19, 0x8A, 0x2E, + 0x03, 0x70, 0x73, 0x44, 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89, 0x45, 0x28, 0x21, 0xE6, + 0x38, 0xD0, 0x13, 0x77, 0xBE, 0x54, 0x66, 0xCF, 0x34, 0xE9, 0x0C, 0x6C, + 0xC0, 0xAC, 0x29, 0xB7, 0xC9, 0x7C, 0x50, 0xDD, 0x3F, 0x84, 0xD5, 0xB5, + 0xB5, 0x47, 0x09, 0x17, 0x92, 0x16, 0xD5, 0xD9, 0x89, 0x79, 0xFB, 0x1B, + 0xD1, 0x31, 0x0B, 0xA6, 0x98, 0xDF, 0xB5, 0xAC, 0x2F, 0xFD, 0x72, 0xDB, + 0xD0, 0x1A, 0xDF, 0xB7, 0xB8, 0xE1, 0xAF, 0xED, 0x6A, 0x26, 0x7E, 0x96, + 0xBA, 0x7C, 0x90, 0x45, 0xF1, 0x2C, 0x7F, 0x99, 0x24, 0xA1, 0x99, 0x47, + 0xB3, 0x91, 0x6C, 0xF7, 0x08, 0x01, 0xF2, 0xE2, 0x85, 0x8E, 0xFC, 0x16, + 0x63, 0x69, 0x20, 0xD8, 0x71, 0x57, 0x4E, 0x69, 0xA4, 0x58, 0xFE, 0xA3, + 0xF4, 0x93, 0x3D, 0x7E, 0x0D, 0x95, 0x74, 0x8F, 0x72, 0x8E, 0xB6, 0x58, + 0x71, 0x8B, 0xCD, 0x58, 0x82, 0x15, 0x4A, 0xEE, 0x7B, 0x54, 0xA4, 0x1D, + 0xC2, 0x5A, 0x59, 0xB5, 0x9C, 0x30, 0xD5, 0x39, 0x2A, 0xF2, 0x60, 0x13, + 0xC5, 0xD1, 0xB0, 0x23, 0x28, 0x60, 0x85, 0xF0, 0xCA, 0x41, 0x79, 0x18, + 0xB8, 0xDB, 0x38, 0xEF, 0x8E, 0x79, 0xDC, 0xB0, 0x60, 0x3A, 0x18, 0x0E, + 0x6C, 0x9E, 0x0E, 0x8B, 0xB0, 0x1E, 0x8A, 0x3E, 0xD7, 0x15, 0x77, 0xC1, + 0xBD, 0x31, 0x4B, 0x27, 0x78, 0xAF, 0x2F, 0xDA, 0x55, 0x60, 0x5C, 0x60, + 0xE6, 0x55, 0x25, 0xF3, 0xAA, 0x55, 0xAB, 0x94, 0x57, 0x48, 0x98, 0x62, + 0x63, 0xE8, 0x14, 0x40, 0x55, 0xCA, 0x39, 0x6A, 0x2A, 0xAB, 0x10, 0xB6, + 0xB4, 0xCC, 0x5C, 0x34, 0x11, 0x41, 0xE8, 0xCE, 0xA1, 0x54, 0x86, 0xAF, + 0x7C, 0x72, 0xE9, 0x93, 0xB3, 0xEE, 0x14, 0x11, 0x63, 0x6F, 0xBC, 0x2A, + 0x2B, 0xA9, 0xC5, 0x5D, 0x74, 0x18, 0x31, 0xF6, 0xCE, 0x5C, 0x3E, 0x16, + 0x9B, 0x87, 0x93, 0x1E, 0xAF, 0xD6, 0xBA, 0x33, 0x6C, 0x24, 0xCF, 0x5C, + 0x7A, 0x32, 0x53, 0x81, 0x28, 0x95, 0x86, 0x77, 0x3B, 0x8F, 0x48, 0x98, + 0x6B, 0x4B, 0xB9, 0xAF, 0xC4, 0xBF, 0xE8, 0x1B, 0x66, 0x28, 0x21, 0x93, + 0x61, 0xD8, 0x09, 0xCC, 0xFB, 0x21, 0xA9, 0x91, 0x48, 0x7C, 0xAC, 0x60, + 0x5D, 0xEC, 0x80, 0x32, 0xEF, 0x84, 0x5D, 0x5D, 0xE9, 0x85, 0x75, 0xB1, + 0xDC, 0x26, 0x23, 0x02, 0xEB, 0x65, 0x1B, 0x88, 0x23, 0x89, 0x3E, 0x81, + 0xD3, 0x96, 0xAC, 0xC5, 0x0F, 0x6D, 0x6F, 0xF3, 0x83, 0xF4, 0x42, 0x39, + 0x2E, 0x0B, 0x44, 0x82, 0xA4, 0x84, 0x20, 0x04, 0x69, 0xC8, 0xF0, 0x4A, + 0x9E, 0x1F, 0x9B, 0x5E, 0x21, 0xC6, 0x68, 0x42, 0xF6, 0xE9, 0x6C, 0x9A, + 0x67, 0x0C, 0x9C, 0x61, 0xAB, 0xD3, 0x88, 0xF0, 0x6A, 0x51, 0xA0, 0xD2, + 0xD8, 0x54, 0x2F, 0x68, 0x96, 0x0F, 0xA7, 0x28, 0xAB, 0x51, 0x33, 0xA3, + 0x6E, 0xEF, 0x0B, 0x6C, 0x13, 0x7A, 0x3B, 0xE4, 0xBA, 0x3B, 0xF0, 0x50, + 0x7E, 0xFB, 0x2A, 0x98, 0xA1, 0xF1, 0x65, 0x1D, 0x39, 0xAF, 0x01, 0x76, + 0x66, 0xCA, 0x59, 0x3E, 0x82, 0x43, 0x0E, 0x88, 0x8C, 0xEE, 0x86, 0x19, + 0x45, 0x6F, 0x9F, 0xB4, 0x7D, 0x84, 0xA5, 0xC3, 0x3B, 0x8B, 0x5E, 0xBE, + 0xE0, 0x6F, 0x75, 0xD8, 0x85, 0xC1, 0x20, 0x73, 0x40, 0x1A, 0x44, 0x9F, + 0x56, 0xC1, 0x6A, 0xA6, 0x4E, 0xD3, 0xAA, 0x62, 0x36, 0x3F, 0x77, 0x06, + 0x1B, 0xFE, 0xDF, 0x72, 0x42, 0x9B, 0x02, 0x3D, 0x37, 0xD0, 0xD7, 0x24, + 0xD0, 0x0A, 0x12, 0x48, 0xDB, 0x0F, 0xEA, 0xD3, 0x49, 0xF1, 0xC0, 0x9B, + 0x07, 0x53, 0x72, 0xC9, 0x80, 0x99, 0x1B, 0x7B, 0x25, 0xD4, 0x79, 0xD8, + 0xF6, 0xE8, 0xDE, 0xF7, 0xE3, 0xFE, 0x50, 0x1A, 0xB6, 0x79, 0x4C, 0x3B, + 0x97, 0x6C, 0xE0, 0xBD, 0x04, 0xC0, 0x06, 0xBA, 0xC1, 0xA9, 0x4F, 0xB6, + 0x40, 0x9F, 0x60, 0xC4, 0x5E, 0x5C, 0x9E, 0xC2, 0x19, 0x6A, 0x24, 0x63, + 0x68, 0xFB, 0x6F, 0xAF, 0x3E, 0x6C, 0x53, 0xB5, 0x13, 0x39, 0xB2, 0xEB, + 0x3B, 0x52, 0xEC, 0x6F, 0x6D, 0xFC, 0x51, 0x1F, 0x9B, 0x30, 0x95, 0x2C, + 0xCC, 0x81, 0x45, 0x44, 0xAF, 0x5E, 0xBD, 0x09, 0xBE, 0xE3, 0xD0, 0x04, + 0xDE, 0x33, 0x4A, 0xFD, 0x66, 0x0F, 0x28, 0x07, 0x19, 0x2E, 0x4B, 0xB3, + 0xC0, 0xCB, 0xA8, 0x57, 0x45, 0xC8, 0x74, 0x0F, 0xD2, 0x0B, 0x5F, 0x39, + 0xB9, 0xD3, 0xFB, 0xDB, 0x55, 0x79, 0xC0, 0xBD, 0x1A, 0x60, 0x32, 0x0A, + 0xD6, 0xA1, 0x00, 0xC6, 0x40, 0x2C, 0x72, 0x79, 0x67, 0x9F, 0x25, 0xFE, + 0xFB, 0x1F, 0xA3, 0xCC, 0x8E, 0xA5, 0xE9, 0xF8, 0xDB, 0x32, 0x22, 0xF8, + 0x3C, 0x75, 0x16, 0xDF, 0xFD, 0x61, 0x6B, 0x15, 0x2F, 0x50, 0x1E, 0xC8, + 0xAD, 0x05, 0x52, 0xAB, 0x32, 0x3D, 0xB5, 0xFA, 0xFD, 0x23, 0x87, 0x60, + 0x53, 0x31, 0x7B, 0x48, 0x3E, 0x00, 0xDF, 0x82, 0x9E, 0x5C, 0x57, 0xBB, + 0xCA, 0x6F, 0x8C, 0xA0, 0x1A, 0x87, 0x56, 0x2E, 0xDF, 0x17, 0x69, 0xDB, + 0xD5, 0x42, 0xA8, 0xF6, 0x28, 0x7E, 0xFF, 0xC3, 0xAC, 0x67, 0x32, 0xC6, + 0x8C, 0x4F, 0x55, 0x73, 0x69, 0x5B, 0x27, 0xB0, 0xBB, 0xCA, 0x58, 0xC8, + 0xE1, 0xFF, 0xA3, 0x5D, 0xB8, 0xF0, 0x11, 0xA0, 0x10, 0xFA, 0x3D, 0x98, + 0xFD, 0x21, 0x83, 0xB8, 0x4A, 0xFC, 0xB5, 0x6C, 0x2D, 0xD1, 0xD3, 0x5B, + 0x9A, 0x53, 0xE4, 0x79, 0xB6, 0xF8, 0x45, 0x65, 0xD2, 0x8E, 0x49, 0xBC, + 0x4B, 0xFB, 0x97, 0x90, 0xE1, 0xDD, 0xF2, 0xDA, 0xA4, 0xCB, 0x7E, 0x33, + 0x62, 0xFB, 0x13, 0x41, 0xCE, 0xE4, 0xC6, 0xE8, 0xEF, 0x20, 0xCA, 0xDA, + 0x36, 0x77, 0x4C, 0x01, 0xD0, 0x7E, 0x9E, 0xFE, 0x2B, 0xF1, 0x1F, 0xB4, + 0x95, 0xDB, 0xDA, 0x4D, 0xAE, 0x90, 0x91, 0x98, 0xEA, 0xAD, 0x8E, 0x71, + 0x6B, 0x93, 0xD5, 0xA0, 0xD0, 0x8E, 0xD1, 0xD0, 0xAF, 0xC7, 0x25, 0xE0, + 0x8E, 0x3C, 0x5B, 0x2F, 0x8E, 0x75, 0x94, 0xB7, 0x8F, 0xF6, 0xE2, 0xFB, + 0xF2, 0x12, 0x2B, 0x64, 0x88, 0x88, 0xB8, 0x12, 0x90, 0x0D, 0xF0, 0x1C, + 0x4F, 0xAD, 0x5E, 0xA0, 0x68, 0x8F, 0xC3, 0x1C, 0xD1, 0xCF, 0xF1, 0x91, + 0xB3, 0xA8, 0xC1, 0xAD, 0x2F, 0x2F, 0x22, 0x18, 0xBE, 0x0E, 0x17, 0x77, + 0xEA, 0x75, 0x2D, 0xFE, 0x8B, 0x02, 0x1F, 0xA1, 0xE5, 0xA0, 0xCC, 0x0F, + 0xB5, 0x6F, 0x74, 0xE8, 0x18, 0xAC, 0xF3, 0xD6, 0xCE, 0x89, 0xE2, 0x99, + 0xB4, 0xA8, 0x4F, 0xE0, 0xFD, 0x13, 0xE0, 0xB7, 0x7C, 0xC4, 0x3B, 0x81, + 0xD2, 0xAD, 0xA8, 0xD9, 0x16, 0x5F, 0xA2, 0x66, 0x80, 0x95, 0x77, 0x05, + 0x93, 0xCC, 0x73, 0x14, 0x21, 0x1A, 0x14, 0x77, 0xE6, 0xAD, 0x20, 0x65, + 0x77, 0xB5, 0xFA, 0x86, 0xC7, 0x54, 0x42, 0xF5, 0xFB, 0x9D, 0x35, 0xCF, + 0xEB, 0xCD, 0xAF, 0x0C, 0x7B, 0x3E, 0x89, 0xA0, 0xD6, 0x41, 0x1B, 0xD3, + 0xAE, 0x1E, 0x7E, 0x49, 0x00, 0x25, 0x0E, 0x2D, 0x20, 0x71, 0xB3, 0x5E, + 0x22, 0x68, 0x00, 0xBB, 0x57, 0xB8, 0xE0, 0xAF, 0x24, 0x64, 0x36, 0x9B, + 0xF0, 0x09, 0xB9, 0x1E, 0x55, 0x63, 0x91, 0x1D, 0x59, 0xDF, 0xA6, 0xAA, + 0x78, 0xC1, 0x43, 0x89, 0xD9, 0x5A, 0x53, 0x7F, 0x20, 0x7D, 0x5B, 0xA2, + 0x02, 0xE5, 0xB9, 0xC5, 0x83, 0x26, 0x03, 0x76, 0x62, 0x95, 0xCF, 0xA9, + 0x11, 0xC8, 0x19, 0x68, 0x4E, 0x73, 0x4A, 0x41, 0xB3, 0x47, 0x2D, 0xCA, + 0x7B, 0x14, 0xA9, 0x4A, 0x1B, 0x51, 0x00, 0x52, 0x9A, 0x53, 0x29, 0x15, + 0xD6, 0x0F, 0x57, 0x3F, 0xBC, 0x9B, 0xC6, 0xE4, 0x2B, 0x60, 0xA4, 0x76, + 0x81, 0xE6, 0x74, 0x00, 0x08, 0xBA, 0x6F, 0xB5, 0x57, 0x1B, 0xE9, 0x1F, + 0xF2, 0x96, 0xEC, 0x6B, 0x2A, 0x0D, 0xD9, 0x15, 0xB6, 0x63, 0x65, 0x21, + 0xE7, 0xB9, 0xF9, 0xB6, 0xFF, 0x34, 0x05, 0x2E, 0xC5, 0x85, 0x56, 0x64, + 0x53, 0xB0, 0x2D, 0x5D, 0xA9, 0x9F, 0x8F, 0xA1, 0x08, 0xBA, 0x47, 0x99, + 0x6E, 0x85, 0x07, 0x6A, 0x4B, 0x7A, 0x70, 0xE9, 0xB5, 0xB3, 0x29, 0x44, + 0xDB, 0x75, 0x09, 0x2E, 0xC4, 0x19, 0x26, 0x23, 0xAD, 0x6E, 0xA6, 0xB0, + 0x49, 0xA7, 0xDF, 0x7D, 0x9C, 0xEE, 0x60, 0xB8, 0x8F, 0xED, 0xB2, 0x66, + 0xEC, 0xAA, 0x8C, 0x71, 0x69, 0x9A, 0x18, 0xFF, 0x56, 0x64, 0x52, 0x6C, + 0xC2, 0xB1, 0x9E, 0xE1, 0x19, 0x36, 0x02, 0xA5, 0x75, 0x09, 0x4C, 0x29, + 0xA0, 0x59, 0x13, 0x40, 0xE4, 0x18, 0x3A, 0x3E, 0x3F, 0x54, 0x98, 0x9A, + 0x5B, 0x42, 0x9D, 0x65, 0x6B, 0x8F, 0xE4, 0xD6, 0x99, 0xF7, 0x3F, 0xD6, + 0xA1, 0xD2, 0x9C, 0x07, 0xEF, 0xE8, 0x30, 0xF5, 0x4D, 0x2D, 0x38, 0xE6, + 0xF0, 0x25, 0x5D, 0xC1, 0x4C, 0xDD, 0x20, 0x86, 0x84, 0x70, 0xEB, 0x26, + 0x63, 0x82, 0xE9, 0xC6, 0x02, 0x1E, 0xCC, 0x5E, 0x09, 0x68, 0x6B, 0x3F, + 0x3E, 0xBA, 0xEF, 0xC9, 0x3C, 0x97, 0x18, 0x14, 0x6B, 0x6A, 0x70, 0xA1, + 0x68, 0x7F, 0x35, 0x84, 0x52, 0xA0, 0xE2, 0x86, 0xB7, 0x9C, 0x53, 0x05, + 0xAA, 0x50, 0x07, 0x37, 0x3E, 0x07, 0x84, 0x1C, 0x7F, 0xDE, 0xAE, 0x5C, + 0x8E, 0x7D, 0x44, 0xEC, 0x57, 0x16, 0xF2, 0xB8, 0xB0, 0x3A, 0xDA, 0x37, + 0xF0, 0x50, 0x0C, 0x0D, 0xF0, 0x1C, 0x1F, 0x04, 0x02, 0x00, 0xB3, 0xFF, + 0xAE, 0x0C, 0xF5, 0x1A, 0x3C, 0xB5, 0x74, 0xB2, 0x25, 0x83, 0x7A, 0x58, + 0xDC, 0x09, 0x21, 0xBD, 0xD1, 0x91, 0x13, 0xF9, 0x7C, 0xA9, 0x2F, 0xF6, + 0x94, 0x32, 0x47, 0x73, 0x22, 0xF5, 0x47, 0x01, 0x3A, 0xE5, 0xE5, 0x81, + 0x37, 0xC2, 0xDA, 0xDC, 0xC8, 0xB5, 0x76, 0x34, 0x9A, 0xF3, 0xDD, 0xA7, + 0xA9, 0x44, 0x61, 0x46, 0x0F, 0xD0, 0x03, 0x0E, 0xEC, 0xC8, 0xC7, 0x3E, + 0xA4, 0x75, 0x1E, 0x41, 0xE2, 0x38, 0xCD, 0x99, 0x3B, 0xEA, 0x0E, 0x2F, + 0x32, 0x80, 0xBB, 0xA1, 0x18, 0x3E, 0xB3, 0x31, 0x4E, 0x54, 0x8B, 0x38, + 0x4F, 0x6D, 0xB9, 0x08, 0x6F, 0x42, 0x0D, 0x03, 0xF6, 0x0A, 0x04, 0xBF, + 0x2C, 0xB8, 0x12, 0x90, 0x24, 0x97, 0x7C, 0x79, 0x56, 0x79, 0xB0, 0x72, + 0xBC, 0xAF, 0x89, 0xAF, 0xDE, 0x9A, 0x77, 0x1F, 0xD9, 0x93, 0x08, 0x10, + 0xB3, 0x8B, 0xAE, 0x12, 0xDC, 0xCF, 0x3F, 0x2E, 0x55, 0x12, 0x72, 0x1F, + 0x2E, 0x6B, 0x71, 0x24, 0x50, 0x1A, 0xDD, 0xE6, 0x9F, 0x84, 0xCD, 0x87, + 0x7A, 0x58, 0x47, 0x18, 0x74, 0x08, 0xDA, 0x17, 0xBC, 0x9F, 0x9A, 0xBC, + 0xE9, 0x4B, 0x7D, 0x8C, 0xEC, 0x7A, 0xEC, 0x3A, 0xDB, 0x85, 0x1D, 0xFA, + 0x63, 0x09, 0x43, 0x66, 0xC4, 0x64, 0xC3, 0xD2, 0xEF, 0x1C, 0x18, 0x47, + 0x32, 0x15, 0xD8, 0x08, 0xDD, 0x43, 0x3B, 0x37, 0x24, 0xC2, 0xBA, 0x16, + 0x12, 0xA1, 0x4D, 0x43, 0x2A, 0x65, 0xC4, 0x51, 0x50, 0x94, 0x00, 0x02, + 0x13, 0x3A, 0xE4, 0xDD, 0x71, 0xDF, 0xF8, 0x9E, 0x10, 0x31, 0x4E, 0x55, + 0x81, 0xAC, 0x77, 0xD6, 0x5F, 0x11, 0x19, 0x9B, 0x04, 0x35, 0x56, 0xF1, + 0xD7, 0xA3, 0xC7, 0x6B, 0x3C, 0x11, 0x18, 0x3B, 0x59, 0x24, 0xA5, 0x09, + 0xF2, 0x8F, 0xE6, 0xED, 0x97, 0xF1, 0xFB, 0xFA, 0x9E, 0xBA, 0xBF, 0x2C, + 0x1E, 0x15, 0x3C, 0x6E, 0x86, 0xE3, 0x45, 0x70, 0xEA, 0xE9, 0x6F, 0xB1, + 0x86, 0x0E, 0x5E, 0x0A, 0x5A, 0x3E, 0x2A, 0xB3, 0x77, 0x1F, 0xE7, 0x1C, + 0x4E, 0x3D, 0x06, 0xFA, 0x29, 0x65, 0xDC, 0xB9, 0x99, 0xE7, 0x1D, 0x0F, + 0x80, 0x3E, 0x89, 0xD6, 0x52, 0x66, 0xC8, 0x25, 0x2E, 0x4C, 0xC9, 0x78, + 0x9C, 0x10, 0xB3, 0x6A, 0xC6, 0x15, 0x0E, 0xBA, 0x94, 0xE2, 0xEA, 0x78, + 0xA6, 0xFC, 0x3C, 0x53, 0x1E, 0x0A, 0x2D, 0xF4, 0xF2, 0xF7, 0x4E, 0xA7, + 0x36, 0x1D, 0x2B, 0x3D, 0x19, 0x39, 0x26, 0x0F, 0x19, 0xC2, 0x79, 0x60, + 0x52, 0x23, 0xA7, 0x08, 0xF7, 0x13, 0x12, 0xB6, 0xEB, 0xAD, 0xFE, 0x6E, + 0xEA, 0xC3, 0x1F, 0x66, 0xE3, 0xBC, 0x45, 0x95, 0xA6, 0x7B, 0xC8, 0x83, + 0xB1, 0x7F, 0x37, 0xD1, 0x01, 0x8C, 0xFF, 0x28, 0xC3, 0x32, 0xDD, 0xEF, + 0xBE, 0x6C, 0x5A, 0xA5, 0x65, 0x58, 0x21, 0x85, 0x68, 0xAB, 0x97, 0x02, + 0xEE, 0xCE, 0xA5, 0x0F, 0xDB, 0x2F, 0x95, 0x3B, 0x2A, 0xEF, 0x7D, 0xAD, + 0x5B, 0x6E, 0x2F, 0x84, 0x15, 0x21, 0xB6, 0x28, 0x29, 0x07, 0x61, 0x70, + 0xEC, 0xDD, 0x47, 0x75, 0x61, 0x9F, 0x15, 0x10, 0x13, 0xCC, 0xA8, 0x30, + 0xEB, 0x61, 0xBD, 0x96, 0x03, 0x34, 0xFE, 0x1E, 0xAA, 0x03, 0x63, 0xCF, + 0xB5, 0x73, 0x5C, 0x90, 0x4C, 0x70, 0xA2, 0x39, 0xD5, 0x9E, 0x9E, 0x0B, + 0xCB, 0xAA, 0xDE, 0x14, 0xEE, 0xCC, 0x86, 0xBC, 0x60, 0x62, 0x2C, 0xA7, + 0x9C, 0xAB, 0x5C, 0xAB, 0xB2, 0xF3, 0x84, 0x6E, 0x64, 0x8B, 0x1E, 0xAF, + 0x19, 0xBD, 0xF0, 0xCA, 0xA0, 0x23, 0x69, 0xB9, 0x65, 0x5A, 0xBB, 0x50, + 0x40, 0x68, 0x5A, 0x32, 0x3C, 0x2A, 0xB4, 0xB3, 0x31, 0x9E, 0xE9, 0xD5, + 0xC0, 0x21, 0xB8, 0xF7, 0x9B, 0x54, 0x0B, 0x19, 0x87, 0x5F, 0xA0, 0x99, + 0x95, 0xF7, 0x99, 0x7E, 0x62, 0x3D, 0x7D, 0xA8, 0xF8, 0x37, 0x88, 0x9A, + 0x97, 0xE3, 0x2D, 0x77, 0x11, 0xED, 0x93, 0x5F, 0x16, 0x68, 0x12, 0x81, + 0x0E, 0x35, 0x88, 0x29, 0xC7, 0xE6, 0x1F, 0xD6, 0x96, 0xDE, 0xDF, 0xA1, + 0x78, 0x58, 0xBA, 0x99, 0x57, 0xF5, 0x84, 0xA5, 0x1B, 0x22, 0x72, 0x63, + 0x9B, 0x83, 0xC3, 0xFF, 0x1A, 0xC2, 0x46, 0x96, 0xCD, 0xB3, 0x0A, 0xEB, + 0x53, 0x2E, 0x30, 0x54, 0x8F, 0xD9, 0x48, 0xE4, 0x6D, 0xBC, 0x31, 0x28, + 0x58, 0xEB, 0xF2, 0xEF, 0x34, 0xC6, 0xFF, 0xEA, 0xFE, 0x28, 0xED, 0x61, + 0xEE, 0x7C, 0x3C, 0x73, 0x5D, 0x4A, 0x14, 0xD9, 0xE8, 0x64, 0xB7, 0xE3, + 0x42, 0x10, 0x5D, 0x14, 0x20, 0x3E, 0x13, 0xE0, 0x45, 0xEE, 0xE2, 0xB6, + 0xA3, 0xAA, 0xAB, 0xEA, 0xDB, 0x6C, 0x4F, 0x15, 0xFA, 0xCB, 0x4F, 0xD0, + 0xC7, 0x42, 0xF4, 0x42, 0xEF, 0x6A, 0xBB, 0xB5, 0x65, 0x4F, 0x3B, 0x1D, + 0x41, 0xCD, 0x21, 0x05, 0xD8, 0x1E, 0x79, 0x9E, 0x86, 0x85, 0x4D, 0xC7, + 0xE4, 0x4B, 0x47, 0x6A, 0x3D, 0x81, 0x62, 0x50, 0xCF, 0x62, 0xA1, 0xF2, + 0x5B, 0x8D, 0x26, 0x46, 0xFC, 0x88, 0x83, 0xA0, 0xC1, 0xC7, 0xB6, 0xA3, + 0x7F, 0x15, 0x24, 0xC3, 0x69, 0xCB, 0x74, 0x92, 0x47, 0x84, 0x8A, 0x0B, + 0x56, 0x92, 0xB2, 0x85, 0x09, 0x5B, 0xBF, 0x00, 0xAD, 0x19, 0x48, 0x9D, + 0x14, 0x62, 0xB1, 0x74, 0x23, 0x82, 0x0D, 0x00, 0x58, 0x42, 0x8D, 0x2A, + 0x0C, 0x55, 0xF5, 0xEA, 0x1D, 0xAD, 0xF4, 0x3E, 0x23, 0x3F, 0x70, 0x61, + 0x33, 0x72, 0xF0, 0x92, 0x8D, 0x93, 0x7E, 0x41, 0xD6, 0x5F, 0xEC, 0xF1, + 0x6C, 0x22, 0x3B, 0xDB, 0x7C, 0xDE, 0x37, 0x59, 0xCB, 0xEE, 0x74, 0x60, + 0x40, 0x85, 0xF2, 0xA7, 0xCE, 0x77, 0x32, 0x6E, 0xA6, 0x07, 0x80, 0x84, + 0x19, 0xF8, 0x50, 0x9E, 0xE8, 0xEF, 0xD8, 0x55, 0x61, 0xD9, 0x97, 0x35, + 0xA9, 0x69, 0xA7, 0xAA, 0xC5, 0x0C, 0x06, 0xC2, 0x5A, 0x04, 0xAB, 0xFC, + 0x80, 0x0B, 0xCA, 0xDC, 0x9E, 0x44, 0x7A, 0x2E, 0xC3, 0x45, 0x34, 0x84, + 0xFD, 0xD5, 0x67, 0x05, 0x0E, 0x1E, 0x9E, 0xC9, 0xDB, 0x73, 0xDB, 0xD3, + 0x10, 0x55, 0x88, 0xCD, 0x67, 0x5F, 0xDA, 0x79, 0xE3, 0x67, 0x43, 0x40, + 0xC5, 0xC4, 0x34, 0x65, 0x71, 0x3E, 0x38, 0xD8, 0x3D, 0x28, 0xF8, 0x9E, + 0xF1, 0x6D, 0xFF, 0x20, 0x15, 0x3E, 0x21, 0xE7, 0x8F, 0xB0, 0x3D, 0x4A, + 0xE6, 0xE3, 0x9F, 0x2B, 0xDB, 0x83, 0xAD, 0xF7, 0xE9, 0x3D, 0x5A, 0x68, + 0x94, 0x81, 0x40, 0xF7, 0xF6, 0x4C, 0x26, 0x1C, 0x94, 0x69, 0x29, 0x34, + 0x41, 0x15, 0x20, 0xF7, 0x76, 0x02, 0xD4, 0xF7, 0xBC, 0xF4, 0x6B, 0x2E, + 0xD4, 0xA1, 0x00, 0x68, 0xD4, 0x08, 0x24, 0x71, 0x33, 0x20, 0xF4, 0x6A, + 0x43, 0xB7, 0xD4, 0xB7, 0x50, 0x00, 0x61, 0xAF, 0x1E, 0x39, 0xF6, 0x2E, + 0x97, 0x24, 0x45, 0x46, +}; + +alignas(16) const unsigned char kRandenRoundKeys[kKeyBytes] = { + 0x44, 0x73, 0x70, 0x03, 0x2E, 0x8A, 0x19, 0x13, 0xD3, 0x08, 0xA3, 0x85, + 0x88, 0x6A, 0x3F, 0x24, 0x89, 0x6C, 0x4E, 0xEC, 0x98, 0xFA, 0x2E, 0x08, + 0xD0, 0x31, 0x9F, 0x29, 0x22, 0x38, 0x09, 0xA4, 0x6C, 0x0C, 0xE9, 0x34, + 0xCF, 0x66, 0x54, 0xBE, 0x77, 0x13, 0xD0, 0x38, 0xE6, 0x21, 0x28, 0x45, + 0x17, 0x09, 0x47, 0xB5, 0xB5, 0xD5, 0x84, 0x3F, 0xDD, 0x50, 0x7C, 0xC9, + 0xB7, 0x29, 0xAC, 0xC0, 0xAC, 0xB5, 0xDF, 0x98, 0xA6, 0x0B, 0x31, 0xD1, + 0x1B, 0xFB, 0x79, 0x89, 0xD9, 0xD5, 0x16, 0x92, 0x96, 0x7E, 0x26, 0x6A, + 0xED, 0xAF, 0xE1, 0xB8, 0xB7, 0xDF, 0x1A, 0xD0, 0xDB, 0x72, 0xFD, 0x2F, + 0xF7, 0x6C, 0x91, 0xB3, 0x47, 0x99, 0xA1, 0x24, 0x99, 0x7F, 0x2C, 0xF1, + 0x45, 0x90, 0x7C, 0xBA, 0x69, 0x4E, 0x57, 0x71, 0xD8, 0x20, 0x69, 0x63, + 0x16, 0xFC, 0x8E, 0x85, 0xE2, 0xF2, 0x01, 0x08, 0x58, 0xB6, 0x8E, 0x72, + 0x8F, 0x74, 0x95, 0x0D, 0x7E, 0x3D, 0x93, 0xF4, 0xA3, 0xFE, 0x58, 0xA4, + 0xB5, 0x59, 0x5A, 0xC2, 0x1D, 0xA4, 0x54, 0x7B, 0xEE, 0x4A, 0x15, 0x82, + 0x58, 0xCD, 0x8B, 0x71, 0xF0, 0x85, 0x60, 0x28, 0x23, 0xB0, 0xD1, 0xC5, + 0x13, 0x60, 0xF2, 0x2A, 0x39, 0xD5, 0x30, 0x9C, 0x0E, 0x18, 0x3A, 0x60, + 0xB0, 0xDC, 0x79, 0x8E, 0xEF, 0x38, 0xDB, 0xB8, 0x18, 0x79, 0x41, 0xCA, + 0x27, 0x4B, 0x31, 0xBD, 0xC1, 0x77, 0x15, 0xD7, 0x3E, 0x8A, 0x1E, 0xB0, + 0x8B, 0x0E, 0x9E, 0x6C, 0x94, 0xAB, 0x55, 0xAA, 0xF3, 0x25, 0x55, 0xE6, + 0x60, 0x5C, 0x60, 0x55, 0xDA, 0x2F, 0xAF, 0x78, 0xB6, 0x10, 0xAB, 0x2A, + 0x6A, 0x39, 0xCA, 0x55, 0x40, 0x14, 0xE8, 0x63, 0x62, 0x98, 0x48, 0x57, + 0x93, 0xE9, 0x72, 0x7C, 0xAF, 0x86, 0x54, 0xA1, 0xCE, 0xE8, 0x41, 0x11, + 0x34, 0x5C, 0xCC, 0xB4, 0xF6, 0x31, 0x18, 0x74, 0x5D, 0xC5, 0xA9, 0x2B, + 0x2A, 0xBC, 0x6F, 0x63, 0x11, 0x14, 0xEE, 0xB3, 0x5C, 0xCF, 0x24, 0x6C, + 0x33, 0xBA, 0xD6, 0xAF, 0x1E, 0x93, 0x87, 0x9B, 0x16, 0x3E, 0x5C, 0xCE, + 0xAF, 0xB9, 0x4B, 0x6B, 0x98, 0x48, 0x8F, 0x3B, 0x77, 0x86, 0x95, 0x28, + 0x81, 0x53, 0x32, 0x7A, 0x91, 0xA9, 0x21, 0xFB, 0xCC, 0x09, 0xD8, 0x61, + 0x93, 0x21, 0x28, 0x66, 0x1B, 0xE8, 0xBF, 0xC4, 0xB1, 0x75, 0x85, 0xE9, + 0x5D, 0x5D, 0x84, 0xEF, 0x32, 0x80, 0xEC, 0x5D, 0x60, 0xAC, 0x7C, 0x48, + 0xC5, 0xAC, 0x96, 0xD3, 0x81, 0x3E, 0x89, 0x23, 0x88, 0x1B, 0x65, 0xEB, + 0x02, 0x23, 0x26, 0xDC, 0x04, 0x20, 0x84, 0xA4, 0x82, 0x44, 0x0B, 0x2E, + 0x39, 0x42, 0xF4, 0x83, 0xF3, 0x6F, 0x6D, 0x0F, 0x9A, 0x6C, 0xE9, 0xF6, + 0x42, 0x68, 0xC6, 0x21, 0x5E, 0x9B, 0x1F, 0x9E, 0x4A, 0xF0, 0xC8, 0x69, + 0x68, 0x2F, 0x54, 0xD8, 0xD2, 0xA0, 0x51, 0x6A, 0xF0, 0x88, 0xD3, 0xAB, + 0x61, 0x9C, 0x0C, 0x67, 0xE4, 0x3B, 0x7A, 0x13, 0x6C, 0x0B, 0xEF, 0x6E, + 0xA3, 0x33, 0x51, 0xAB, 0x28, 0xA7, 0x0F, 0x96, 0x76, 0x01, 0xAF, 0x39, + 0x1D, 0x65, 0xF1, 0xA1, 0x98, 0x2A, 0xFB, 0x7E, 0x50, 0xF0, 0x3B, 0xBA, + 0xB4, 0x9F, 0x6F, 0x45, 0x19, 0x86, 0xEE, 0x8C, 0x88, 0x0E, 0x43, 0x82, + 0x3E, 0x59, 0xCA, 0x66, 0x73, 0x20, 0xC1, 0x85, 0xD8, 0x75, 0x6F, 0xE0, + 0xBE, 0x5E, 0x8B, 0x3B, 0xC3, 0xA5, 0x84, 0x7D, 0x06, 0x77, 0x3F, 0x36, + 0x62, 0xAA, 0xD3, 0x4E, 0xA6, 0x6A, 0xC1, 0x56, 0x9F, 0x44, 0x1A, 0x40, + 0x48, 0x12, 0x0A, 0xD0, 0x24, 0xD7, 0xD0, 0x37, 0x3D, 0x02, 0x9B, 0x42, + 0x72, 0xDF, 0xFE, 0x1B, 0x7B, 0x1B, 0x99, 0x80, 0xC9, 0x72, 0x53, 0x07, + 0x9B, 0xC0, 0xF1, 0x49, 0xD3, 0xEA, 0x0F, 0xDB, 0x3B, 0x4C, 0x79, 0xB6, + 0x1A, 0x50, 0xFE, 0xE3, 0xF7, 0xDE, 0xE8, 0xF6, 0xD8, 0x79, 0xD4, 0x25, + 0xC4, 0x60, 0x9F, 0x40, 0xB6, 0x4F, 0xA9, 0xC1, 0xBA, 0x06, 0xC0, 0x04, + 0xBD, 0xE0, 0x6C, 0x97, 0xB5, 0x53, 0x6C, 0x3E, 0xAF, 0x6F, 0xFB, 0x68, + 0x63, 0x24, 0x6A, 0x19, 0xC2, 0x9E, 0x5C, 0x5E, 0x2C, 0x95, 0x30, 0x9B, + 0x1F, 0x51, 0xFC, 0x6D, 0x6F, 0xEC, 0x52, 0x3B, 0xEB, 0xB2, 0x39, 0x13, + 0xFD, 0x4A, 0x33, 0xDE, 0x04, 0xD0, 0xE3, 0xBE, 0x09, 0xBD, 0x5E, 0xAF, + 0x44, 0x45, 0x81, 0xCC, 0x0F, 0x74, 0xC8, 0x45, 0x57, 0xA8, 0xCB, 0xC0, + 0xB3, 0x4B, 0x2E, 0x19, 0x07, 0x28, 0x0F, 0x66, 0x0A, 0x32, 0x60, 0x1A, + 0xBD, 0xC0, 0x79, 0x55, 0xDB, 0xFB, 0xD3, 0xB9, 0x39, 0x5F, 0x0B, 0xD2, + 0xCC, 0xA3, 0x1F, 0xFB, 0xFE, 0x25, 0x9F, 0x67, 0x79, 0x72, 0x2C, 0x40, + 0xC6, 0x00, 0xA1, 0xD6, 0x15, 0x6B, 0x61, 0xFD, 0xDF, 0x16, 0x75, 0x3C, + 0xF8, 0x22, 0x32, 0xDB, 0xF8, 0xE9, 0xA5, 0x8E, 0x60, 0x87, 0x23, 0xFD, + 0xFA, 0xB5, 0x3D, 0x32, 0xAB, 0x52, 0x05, 0xAD, 0xC8, 0x1E, 0x50, 0x2F, + 0xA0, 0x8C, 0x6F, 0xCA, 0xBB, 0x57, 0x5C, 0x9E, 0x82, 0xDF, 0x00, 0x3E, + 0x48, 0x7B, 0x31, 0x53, 0xC3, 0xFF, 0x7E, 0x28, 0xF6, 0xA8, 0x42, 0xD5, + 0xDB, 0x69, 0x17, 0xDF, 0x2E, 0x56, 0x87, 0x1A, 0xC8, 0x58, 0xCA, 0xBB, + 0xB0, 0x27, 0x5B, 0x69, 0x73, 0x55, 0x4F, 0x8C, 0xC6, 0x32, 0x67, 0xAC, + 0xB8, 0x83, 0x21, 0xFD, 0x98, 0x3D, 0xFA, 0x10, 0xA0, 0x11, 0xF0, 0xB8, + 0x5D, 0xA3, 0xFF, 0xE1, 0x65, 0x45, 0xF8, 0xB6, 0x79, 0xE4, 0x53, 0x9A, + 0x5B, 0xD3, 0xD1, 0x2D, 0x6C, 0xB5, 0xFC, 0x4A, 0x33, 0x7E, 0xCB, 0xA4, + 0xDA, 0xF2, 0xDD, 0xE1, 0x90, 0x97, 0xFB, 0x4B, 0xBC, 0x49, 0x8E, 0xD2, + 0x01, 0x4C, 0x77, 0x36, 0xDA, 0xCA, 0x20, 0xEF, 0xE8, 0xC6, 0xE4, 0xCE, + 0x41, 0x13, 0xFB, 0x62, 0x98, 0x91, 0x90, 0xAE, 0x4D, 0xDA, 0xDB, 0x95, + 0xB4, 0x1F, 0xF1, 0x2B, 0xFE, 0x9E, 0x7E, 0xD0, 0xE0, 0x25, 0xC7, 0xAF, + 0xD0, 0xD1, 0x8E, 0xD0, 0xA0, 0xD5, 0x93, 0x6B, 0x71, 0x8E, 0xAD, 0xEA, + 0x64, 0x2B, 0x12, 0xF2, 0xFB, 0xE2, 0xF6, 0x8F, 0xB7, 0x94, 0x75, 0x8E, + 0x2F, 0x5B, 0x3C, 0x8E, 0x1C, 0xC3, 0x8F, 0x68, 0xA0, 0x5E, 0xAD, 0x4F, + 0x1C, 0xF0, 0x0D, 0x90, 0x12, 0xB8, 0x88, 0x88, 0x77, 0x17, 0x0E, 0xBE, + 0x18, 0x22, 0x2F, 0x2F, 0xAD, 0xC1, 0xA8, 0xB3, 0x91, 0xF1, 0xCF, 0xD1, + 0xE8, 0x74, 0x6F, 0xB5, 0x0F, 0xCC, 0xA0, 0xE5, 0xA1, 0x1F, 0x02, 0x8B, + 0xFE, 0x2D, 0x75, 0xEA, 0xB7, 0xE0, 0x13, 0xFD, 0xE0, 0x4F, 0xA8, 0xB4, + 0x99, 0xE2, 0x89, 0xCE, 0xD6, 0xF3, 0xAC, 0x18, 0x05, 0x77, 0x95, 0x80, + 0x66, 0xA2, 0x5F, 0x16, 0xD9, 0xA8, 0xAD, 0xD2, 0x81, 0x3B, 0xC4, 0x7C, + 0x86, 0xFA, 0xB5, 0x77, 0x65, 0x20, 0xAD, 0xE6, 0x77, 0x14, 0x1A, 0x21, + 0x14, 0x73, 0xCC, 0x93, 0xA0, 0x89, 0x3E, 0x7B, 0x0C, 0xAF, 0xCD, 0xEB, + 0xCF, 0x35, 0x9D, 0xFB, 0xF5, 0x42, 0x54, 0xC7, 0x5E, 0xB3, 0x71, 0x20, + 0x2D, 0x0E, 0x25, 0x00, 0x49, 0x7E, 0x1E, 0xAE, 0xD3, 0x1B, 0x41, 0xD6, + 0x1E, 0xB9, 0x09, 0xF0, 0x9B, 0x36, 0x64, 0x24, 0xAF, 0xE0, 0xB8, 0x57, + 0xBB, 0x00, 0x68, 0x22, 0x7F, 0x53, 0x5A, 0xD9, 0x89, 0x43, 0xC1, 0x78, + 0xAA, 0xA6, 0xDF, 0x59, 0x1D, 0x91, 0x63, 0x55, 0xA9, 0xCF, 0x95, 0x62, + 0x76, 0x03, 0x26, 0x83, 0xC5, 0xB9, 0xE5, 0x02, 0xA2, 0x5B, 0x7D, 0x20, + 0x4A, 0xA9, 0x14, 0x7B, 0xCA, 0x2D, 0x47, 0xB3, 0x41, 0x4A, 0x73, 0x4E, + 0x68, 0x19, 0xC8, 0x11, 0xE4, 0xC6, 0x9B, 0xBC, 0x3F, 0x57, 0x0F, 0xD6, + 0x15, 0x29, 0x53, 0x9A, 0x52, 0x00, 0x51, 0x1B, 0x1F, 0xE9, 0x1B, 0x57, + 0xB5, 0x6F, 0xBA, 0x08, 0x00, 0x74, 0xE6, 0x81, 0x76, 0xA4, 0x60, 0x2B, + 0xB6, 0xF9, 0xB9, 0xE7, 0x21, 0x65, 0x63, 0xB6, 0x15, 0xD9, 0x0D, 0x2A, + 0x6B, 0xEC, 0x96, 0xF2, 0xA1, 0x8F, 0x9F, 0xA9, 0x5D, 0x2D, 0xB0, 0x53, + 0x64, 0x56, 0x85, 0xC5, 0x2E, 0x05, 0x34, 0xFF, 0x44, 0x29, 0xB3, 0xB5, + 0xE9, 0x70, 0x7A, 0x4B, 0x6A, 0x07, 0x85, 0x6E, 0x99, 0x47, 0xBA, 0x08, + 0x7D, 0xDF, 0xA7, 0x49, 0xB0, 0xA6, 0x6E, 0xAD, 0x23, 0x26, 0x19, 0xC4, + 0x2E, 0x09, 0x75, 0xDB, 0xFF, 0x18, 0x9A, 0x69, 0x71, 0x8C, 0xAA, 0xEC, + 0x66, 0xB2, 0xED, 0x8F, 0xB8, 0x60, 0xEE, 0x9C, 0x29, 0x4C, 0x09, 0x75, + 0xA5, 0x02, 0x36, 0x19, 0xE1, 0x9E, 0xB1, 0xC2, 0x6C, 0x52, 0x64, 0x56, + 0x65, 0x9D, 0x42, 0x5B, 0x9A, 0x98, 0x54, 0x3F, 0x3E, 0x3A, 0x18, 0xE4, + 0x40, 0x13, 0x59, 0xA0, 0xF5, 0x30, 0xE8, 0xEF, 0x07, 0x9C, 0xD2, 0xA1, + 0xD6, 0x3F, 0xF7, 0x99, 0xD6, 0xE4, 0x8F, 0x6B, 0x26, 0xEB, 0x70, 0x84, + 0x86, 0x20, 0xDD, 0x4C, 0xC1, 0x5D, 0x25, 0xF0, 0xE6, 0x38, 0x2D, 0x4D, + 0xC9, 0xEF, 0xBA, 0x3E, 0x3F, 0x6B, 0x68, 0x09, 0x5E, 0xCC, 0x1E, 0x02, + 0xC6, 0xE9, 0x82, 0x63, 0x86, 0xE2, 0xA0, 0x52, 0x84, 0x35, 0x7F, 0x68, + 0xA1, 0x70, 0x6A, 0x6B, 0x14, 0x18, 0x97, 0x3C, 0x5C, 0xAE, 0xDE, 0x7F, + 0x1C, 0x84, 0x07, 0x3E, 0x37, 0x07, 0x50, 0xAA, 0x05, 0x53, 0x9C, 0xB7, + 0x0D, 0x0C, 0x50, 0xF0, 0x37, 0xDA, 0x3A, 0xB0, 0xB8, 0xF2, 0x16, 0x57, + 0xEC, 0x44, 0x7D, 0x8E, 0xB2, 0x74, 0xB5, 0x3C, 0x1A, 0xF5, 0x0C, 0xAE, + 0xFF, 0xB3, 0x00, 0x02, 0x04, 0x1F, 0x1C, 0xF0, 0xF6, 0x2F, 0xA9, 0x7C, + 0xF9, 0x13, 0x91, 0xD1, 0xBD, 0x21, 0x09, 0xDC, 0x58, 0x7A, 0x83, 0x25, + 0xDC, 0xDA, 0xC2, 0x37, 0x81, 0xE5, 0xE5, 0x3A, 0x01, 0x47, 0xF5, 0x22, + 0x73, 0x47, 0x32, 0x94, 0x0E, 0x03, 0xD0, 0x0F, 0x46, 0x61, 0x44, 0xA9, + 0xA7, 0xDD, 0xF3, 0x9A, 0x34, 0x76, 0xB5, 0xC8, 0x2F, 0x0E, 0xEA, 0x3B, + 0x99, 0xCD, 0x38, 0xE2, 0x41, 0x1E, 0x75, 0xA4, 0x3E, 0xC7, 0xC8, 0xEC, + 0x08, 0xB9, 0x6D, 0x4F, 0x38, 0x8B, 0x54, 0x4E, 0x31, 0xB3, 0x3E, 0x18, + 0xA1, 0xBB, 0x80, 0x32, 0x79, 0x7C, 0x97, 0x24, 0x90, 0x12, 0xB8, 0x2C, + 0xBF, 0x04, 0x0A, 0xF6, 0x03, 0x0D, 0x42, 0x6F, 0x10, 0x08, 0x93, 0xD9, + 0x1F, 0x77, 0x9A, 0xDE, 0xAF, 0x89, 0xAF, 0xBC, 0x72, 0xB0, 0x79, 0x56, + 0x24, 0x71, 0x6B, 0x2E, 0x1F, 0x72, 0x12, 0x55, 0x2E, 0x3F, 0xCF, 0xDC, + 0x12, 0xAE, 0x8B, 0xB3, 0x17, 0xDA, 0x08, 0x74, 0x18, 0x47, 0x58, 0x7A, + 0x87, 0xCD, 0x84, 0x9F, 0xE6, 0xDD, 0x1A, 0x50, 0xFA, 0x1D, 0x85, 0xDB, + 0x3A, 0xEC, 0x7A, 0xEC, 0x8C, 0x7D, 0x4B, 0xE9, 0xBC, 0x9A, 0x9F, 0xBC, + 0x08, 0xD8, 0x15, 0x32, 0x47, 0x18, 0x1C, 0xEF, 0xD2, 0xC3, 0x64, 0xC4, + 0x66, 0x43, 0x09, 0x63, 0x51, 0xC4, 0x65, 0x2A, 0x43, 0x4D, 0xA1, 0x12, + 0x16, 0xBA, 0xC2, 0x24, 0x37, 0x3B, 0x43, 0xDD, 0x55, 0x4E, 0x31, 0x10, + 0x9E, 0xF8, 0xDF, 0x71, 0xDD, 0xE4, 0x3A, 0x13, 0x02, 0x00, 0x94, 0x50, + 0x6B, 0xC7, 0xA3, 0xD7, 0xF1, 0x56, 0x35, 0x04, 0x9B, 0x19, 0x11, 0x5F, + 0xD6, 0x77, 0xAC, 0x81, 0xFA, 0xFB, 0xF1, 0x97, 0xED, 0xE6, 0x8F, 0xF2, + 0x09, 0xA5, 0x24, 0x59, 0x3B, 0x18, 0x11, 0x3C, 0xB1, 0x6F, 0xE9, 0xEA, + 0x70, 0x45, 0xE3, 0x86, 0x6E, 0x3C, 0x15, 0x1E, 0x2C, 0xBF, 0xBA, 0x9E, + 0xFA, 0x06, 0x3D, 0x4E, 0x1C, 0xE7, 0x1F, 0x77, 0xB3, 0x2A, 0x3E, 0x5A, + 0x0A, 0x5E, 0x0E, 0x86, 0x25, 0xC8, 0x66, 0x52, 0xD6, 0x89, 0x3E, 0x80, + 0x0F, 0x1D, 0xE7, 0x99, 0xB9, 0xDC, 0x65, 0x29, 0x78, 0xEA, 0xE2, 0x94, + 0xBA, 0x0E, 0x15, 0xC6, 0x6A, 0xB3, 0x10, 0x9C, 0x78, 0xC9, 0x4C, 0x2E, + 0x3D, 0x2B, 0x1D, 0x36, 0xA7, 0x4E, 0xF7, 0xF2, 0xF4, 0x2D, 0x0A, 0x1E, + 0x53, 0x3C, 0xFC, 0xA6, 0xB6, 0x12, 0x13, 0xF7, 0x08, 0xA7, 0x23, 0x52, + 0x60, 0x79, 0xC2, 0x19, 0x0F, 0x26, 0x39, 0x19, 0x83, 0xC8, 0x7B, 0xA6, + 0x95, 0x45, 0xBC, 0xE3, 0x66, 0x1F, 0xC3, 0xEA, 0x6E, 0xFE, 0xAD, 0xEB, + 0xA5, 0x5A, 0x6C, 0xBE, 0xEF, 0xDD, 0x32, 0xC3, 0x28, 0xFF, 0x8C, 0x01, + 0xD1, 0x37, 0x7F, 0xB1, 0x3B, 0x95, 0x2F, 0xDB, 0x0F, 0xA5, 0xCE, 0xEE, + 0x02, 0x97, 0xAB, 0x68, 0x85, 0x21, 0x58, 0x65, 0x70, 0x61, 0x07, 0x29, + 0x28, 0xB6, 0x21, 0x15, 0x84, 0x2F, 0x6E, 0x5B, 0xAD, 0x7D, 0xEF, 0x2A, + 0x96, 0xBD, 0x61, 0xEB, 0x30, 0xA8, 0xCC, 0x13, 0x10, 0x15, 0x9F, 0x61, + 0x75, 0x47, 0xDD, 0xEC, 0x39, 0xA2, 0x70, 0x4C, 0x90, 0x5C, 0x73, 0xB5, + 0xCF, 0x63, 0x03, 0xAA, 0x1E, 0xFE, 0x34, 0x03, 0xA7, 0x2C, 0x62, 0x60, + 0xBC, 0x86, 0xCC, 0xEE, 0x14, 0xDE, 0xAA, 0xCB, 0x0B, 0x9E, 0x9E, 0xD5, + 0xCA, 0xF0, 0xBD, 0x19, 0xAF, 0x1E, 0x8B, 0x64, 0x6E, 0x84, 0xF3, 0xB2, + 0xAB, 0x5C, 0xAB, 0x9C, 0xB3, 0xB4, 0x2A, 0x3C, 0x32, 0x5A, 0x68, 0x40, + 0x50, 0xBB, 0x5A, 0x65, 0xB9, 0x69, 0x23, 0xA0, 0x99, 0xA0, 0x5F, 0x87, + 0x19, 0x0B, 0x54, 0x9B, 0xF7, 0xB8, 0x21, 0xC0, 0xD5, 0xE9, 0x9E, 0x31, + 0x77, 0x2D, 0xE3, 0x97, 0x9A, 0x88, 0x37, 0xF8, 0xA8, 0x7D, 0x3D, 0x62, + 0x7E, 0x99, 0xF7, 0x95, 0xD6, 0x1F, 0xE6, 0xC7, 0x29, 0x88, 0x35, 0x0E, + 0x81, 0x12, 0x68, 0x16, 0x5F, 0x93, 0xED, 0x11, 0x63, 0x72, 0x22, 0x1B, + 0xA5, 0x84, 0xF5, 0x57, 0x99, 0xBA, 0x58, 0x78, 0xA1, 0xDF, 0xDE, 0x96, + 0x54, 0x30, 0x2E, 0x53, 0xEB, 0x0A, 0xB3, 0xCD, 0x96, 0x46, 0xC2, 0x1A, + 0xFF, 0xC3, 0x83, 0x9B, 0xEA, 0xFF, 0xC6, 0x34, 0xEF, 0xF2, 0xEB, 0x58, + 0x28, 0x31, 0xBC, 0x6D, 0xE4, 0x48, 0xD9, 0x8F, 0xE3, 0xB7, 0x64, 0xE8, + 0xD9, 0x14, 0x4A, 0x5D, 0x73, 0x3C, 0x7C, 0xEE, 0x61, 0xED, 0x28, 0xFE, + 0xEA, 0xAB, 0xAA, 0xA3, 0xB6, 0xE2, 0xEE, 0x45, 0xE0, 0x13, 0x3E, 0x20, + 0x14, 0x5D, 0x10, 0x42, 0xB5, 0xBB, 0x6A, 0xEF, 0x42, 0xF4, 0x42, 0xC7, + 0xD0, 0x4F, 0xCB, 0xFA, 0x15, 0x4F, 0x6C, 0xDB, 0xC7, 0x4D, 0x85, 0x86, + 0x9E, 0x79, 0x1E, 0xD8, 0x05, 0x21, 0xCD, 0x41, 0x1D, 0x3B, 0x4F, 0x65, + 0x46, 0x26, 0x8D, 0x5B, 0xF2, 0xA1, 0x62, 0xCF, 0x50, 0x62, 0x81, 0x3D, + 0x6A, 0x47, 0x4B, 0xE4, 0x92, 0x74, 0xCB, 0x69, 0xC3, 0x24, 0x15, 0x7F, + 0xA3, 0xB6, 0xC7, 0xC1, 0xA0, 0x83, 0x88, 0xFC, 0x9D, 0x48, 0x19, 0xAD, + 0x00, 0xBF, 0x5B, 0x09, 0x85, 0xB2, 0x92, 0x56, 0x0B, 0x8A, 0x84, 0x47, + 0xEA, 0xF5, 0x55, 0x0C, 0x2A, 0x8D, 0x42, 0x58, 0x00, 0x0D, 0x82, 0x23, + 0x74, 0xB1, 0x62, 0x14, 0x41, 0x7E, 0x93, 0x8D, 0x92, 0xF0, 0x72, 0x33, + 0x61, 0x70, 0x3F, 0x23, 0x3E, 0xF4, 0xAD, 0x1D, 0x60, 0x74, 0xEE, 0xCB, + 0x59, 0x37, 0xDE, 0x7C, 0xDB, 0x3B, 0x22, 0x6C, 0xF1, 0xEC, 0x5F, 0xD6, + 0x9E, 0x50, 0xF8, 0x19, 0x84, 0x80, 0x07, 0xA6, 0x6E, 0x32, 0x77, 0xCE, + 0xA7, 0xF2, 0x85, 0x40, 0xC2, 0x06, 0x0C, 0xC5, 0xAA, 0xA7, 0x69, 0xA9, + 0x35, 0x97, 0xD9, 0x61, 0x55, 0xD8, 0xEF, 0xE8, 0x84, 0x34, 0x45, 0xC3, + 0x2E, 0x7A, 0x44, 0x9E, 0xDC, 0xCA, 0x0B, 0x80, 0xFC, 0xAB, 0x04, 0x5A, + 0xCD, 0x88, 0x55, 0x10, 0xD3, 0xDB, 0x73, 0xDB, 0xC9, 0x9E, 0x1E, 0x0E, + 0x05, 0x67, 0xD5, 0xFD, 0xD8, 0x38, 0x3E, 0x71, 0x65, 0x34, 0xC4, 0xC5, + 0x40, 0x43, 0x67, 0xE3, 0x79, 0xDA, 0x5F, 0x67, 0x4A, 0x3D, 0xB0, 0x8F, + 0xE7, 0x21, 0x3E, 0x15, 0x20, 0xFF, 0x6D, 0xF1, 0x9E, 0xF8, 0x28, 0x3D, + 0xF7, 0x40, 0x81, 0x94, 0x68, 0x5A, 0x3D, 0xE9, 0xF7, 0xAD, 0x83, 0xDB, + 0x2B, 0x9F, 0xE3, 0xE6, 0xF7, 0xD4, 0x02, 0x76, 0xF7, 0x20, 0x15, 0x41, + 0x34, 0x29, 0x69, 0x94, 0x1C, 0x26, 0x4C, 0xF6, 0x6A, 0xF4, 0x20, 0x33, + 0x71, 0x24, 0x08, 0xD4, 0x68, 0x00, 0xA1, 0xD4, 0x2E, 0x6B, 0xF4, 0xBC, + 0x46, 0x45, 0x24, 0x97, 0x2E, 0xF6, 0x39, 0x1E, 0xAF, 0x61, 0x00, 0x50, + 0xB7, 0xD4, 0xB7, 0x43, +}; + +} // namespace random_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/random/internal/randen_slow.cc b/third_party/abseil-cpp/absl/random/internal/randen_slow.cc index 8d07458254..9bfd2a4092 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_slow.cc +++ b/third_party/abseil-cpp/absl/random/internal/randen_slow.cc @@ -19,7 +19,10 @@ #include <cstring> #include "absl/base/attributes.h" +#include "absl/base/internal/endian.h" +#include "absl/numeric/int128.h" #include "absl/random/internal/platform.h" +#include "absl/random/internal/randen_traits.h" #if ABSL_HAVE_ATTRIBUTE(always_inline) || \ (defined(__GNUC__) && !defined(__clang__)) @@ -37,369 +40,303 @@ namespace { // AES portions based on rijndael-alg-fst.c, -// https://fastcrypto.org/front/misc/rijndael-alg-fst.c +// https://fastcrypto.org/front/misc/rijndael-alg-fst.c, and modified for +// platform-endianness. // // Implementation of // http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf constexpr uint32_t te0[256] = { - 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, - 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, - 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, - 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, - 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, - 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, - 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, - 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, - 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, - 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, - 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, - 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, - 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, - 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, - 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, - 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, - 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, - 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, - 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, - 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, - 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, - 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, - 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, - 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, - 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, - 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, - 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, - 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, - 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, - 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, - 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, - 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, - 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, - 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, - 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, - 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, - 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, - 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, - 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, - 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, - 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, - 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, - 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, + 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, + 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, + 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, + 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, + 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, + 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, + 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, + 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, + 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, + 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, + 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, + 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, + 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, + 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, + 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, + 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, + 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, + 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, + 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, + 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, + 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, + 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, + 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, + 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, + 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, + 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, + 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, + 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, + 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, + 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, + 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, + 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, + 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, + 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, }; constexpr uint32_t te1[256] = { - 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, - 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, - 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, - 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, - 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, - 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, - 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, - 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, - 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, - 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, - 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, - 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, - 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, - 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, - 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, - 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, - 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, - 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, - 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, - 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, - 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, - 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, - 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, - 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, - 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, - 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, - 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, - 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, - 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, - 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, - 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, - 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, - 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, - 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, - 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, - 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, - 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, - 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, - 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, - 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, - 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, - 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, - 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, + 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, + 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, + 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, + 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, + 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, + 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, + 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, + 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, + 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, + 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, + 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, + 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, + 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, + 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, + 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, + 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, + 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, + 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, + 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, + 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, + 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, + 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, + 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, + 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, + 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, + 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, + 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, + 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, + 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, + 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, + 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, + 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, + 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, + 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, + 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, + 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, + 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, + 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, + 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, + 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, + 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, + 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a, }; constexpr uint32_t te2[256] = { - 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, - 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, - 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, - 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, - 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, - 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, - 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, - 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, - 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, - 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, - 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, - 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, - 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, - 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, - 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, - 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, - 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, - 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, - 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, - 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, - 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, - 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, - 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, - 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, - 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, - 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, - 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, - 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, - 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, - 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, - 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, - 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, - 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, - 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, - 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, - 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, - 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, - 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, - 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, - 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, - 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, - 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, - 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, + 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, + 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, + 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, + 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, + 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, + 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, + 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, + 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, + 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, + 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, + 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, + 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, + 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, + 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, + 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, + 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, + 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, + 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, + 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, + 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, + 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, + 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, + 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, + 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, + 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, + 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, + 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, + 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, + 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, + 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, + 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, + 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, + 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, + 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, + 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, + 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, + 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, + 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, + 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, + 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, + 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, + 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16, }; constexpr uint32_t te3[256] = { - 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, - 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, - 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, - 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, - 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, - 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, - 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, - 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, - 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, - 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, - 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, - 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, - 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, - 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, - 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, - 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, - 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, - 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, - 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, - 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, - 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, - 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, - 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, - 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, - 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, - 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, - 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, - 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, - 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, - 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, - 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, - 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, - 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, - 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, - 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, - 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, - 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, - 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, - 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, - 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, - 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, - 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, - 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, -}; - -struct alignas(16) u64x2 { - constexpr u64x2() : v{0, 0} {}; - constexpr u64x2(uint64_t hi, uint64_t lo) : v{lo, hi} {} - - uint64_t v[2]; + 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, + 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, + 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, + 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, + 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, + 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, + 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, + 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, + 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, + 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, + 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, + 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, + 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, + 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, + 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, + 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, + 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, + 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, + 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, + 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, + 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, + 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, + 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, + 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, + 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, + 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, + 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, + 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, + 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, + 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, + 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, + 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, + 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, + 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, + 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, + 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, + 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, + 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, + 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, + 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, + 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, + 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616, }; // Software implementation of the Vector128 class, using uint32_t // as an underlying vector register. -// -struct Vector128 { - inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128& operator^=( - const Vector128& other) { - s[0] ^= other.s[0]; - s[1] ^= other.s[1]; - s[2] ^= other.s[2]; - s[3] ^= other.s[3]; - return *this; - } - +struct alignas(16) Vector128 { uint32_t s[4]; }; inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 -Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { +Vector128Load(const void* from) { Vector128 result; - const uint8_t* ABSL_RANDOM_INTERNAL_RESTRICT src = - reinterpret_cast<const uint8_t*>(from); - - result.s[0] = static_cast<uint32_t>(src[0]) << 24 | - static_cast<uint32_t>(src[1]) << 16 | - static_cast<uint32_t>(src[2]) << 8 | - static_cast<uint32_t>(src[3]); - result.s[1] = static_cast<uint32_t>(src[4]) << 24 | - static_cast<uint32_t>(src[5]) << 16 | - static_cast<uint32_t>(src[6]) << 8 | - static_cast<uint32_t>(src[7]); - result.s[2] = static_cast<uint32_t>(src[8]) << 24 | - static_cast<uint32_t>(src[9]) << 16 | - static_cast<uint32_t>(src[10]) << 8 | - static_cast<uint32_t>(src[11]); - result.s[3] = static_cast<uint32_t>(src[12]) << 24 | - static_cast<uint32_t>(src[13]) << 16 | - static_cast<uint32_t>(src[14]) << 8 | - static_cast<uint32_t>(src[15]); + std::memcpy(result.s, from, sizeof(Vector128)); return result; } inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store( - const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { - uint8_t* dst = reinterpret_cast<uint8_t*>(to); - dst[0] = static_cast<uint8_t>(v.s[0] >> 24); - dst[1] = static_cast<uint8_t>(v.s[0] >> 16); - dst[2] = static_cast<uint8_t>(v.s[0] >> 8); - dst[3] = static_cast<uint8_t>(v.s[0]); - dst[4] = static_cast<uint8_t>(v.s[1] >> 24); - dst[5] = static_cast<uint8_t>(v.s[1] >> 16); - dst[6] = static_cast<uint8_t>(v.s[1] >> 8); - dst[7] = static_cast<uint8_t>(v.s[1]); - dst[8] = static_cast<uint8_t>(v.s[2] >> 24); - dst[9] = static_cast<uint8_t>(v.s[2] >> 16); - dst[10] = static_cast<uint8_t>(v.s[2] >> 8); - dst[11] = static_cast<uint8_t>(v.s[2]); - dst[12] = static_cast<uint8_t>(v.s[3] >> 24); - dst[13] = static_cast<uint8_t>(v.s[3] >> 16); - dst[14] = static_cast<uint8_t>(v.s[3] >> 8); - dst[15] = static_cast<uint8_t>(v.s[3]); + const Vector128& v, void* to) { + std::memcpy(to, v.s, sizeof(Vector128)); } // One round of AES. "round_key" is a public constant for breaking the // symmetry of AES (ensures previously equal columns differ afterwards). inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 AesRound(const Vector128& state, const Vector128& round_key) { - // clang-format off Vector128 result; - result.s[0] = round_key.s[0] ^ - te0[uint8_t(state.s[0] >> 24)] ^ - te1[uint8_t(state.s[1] >> 16)] ^ - te2[uint8_t(state.s[2] >> 8)] ^ - te3[uint8_t(state.s[3])]; - result.s[1] = round_key.s[1] ^ - te0[uint8_t(state.s[1] >> 24)] ^ - te1[uint8_t(state.s[2] >> 16)] ^ - te2[uint8_t(state.s[3] >> 8)] ^ - te3[uint8_t(state.s[0])]; - result.s[2] = round_key.s[2] ^ - te0[uint8_t(state.s[2] >> 24)] ^ - te1[uint8_t(state.s[3] >> 16)] ^ - te2[uint8_t(state.s[0] >> 8)] ^ - te3[uint8_t(state.s[1])]; - result.s[3] = round_key.s[3] ^ - te0[uint8_t(state.s[3] >> 24)] ^ - te1[uint8_t(state.s[0] >> 16)] ^ - te2[uint8_t(state.s[1] >> 8)] ^ - te3[uint8_t(state.s[2])]; +#ifdef ABSL_IS_LITTLE_ENDIAN + result.s[0] = round_key.s[0] ^ // + te0[uint8_t(state.s[0])] ^ // + te1[uint8_t(state.s[1] >> 8)] ^ // + te2[uint8_t(state.s[2] >> 16)] ^ // + te3[uint8_t(state.s[3] >> 24)]; + result.s[1] = round_key.s[1] ^ // + te0[uint8_t(state.s[1])] ^ // + te1[uint8_t(state.s[2] >> 8)] ^ // + te2[uint8_t(state.s[3] >> 16)] ^ // + te3[uint8_t(state.s[0] >> 24)]; + result.s[2] = round_key.s[2] ^ // + te0[uint8_t(state.s[2])] ^ // + te1[uint8_t(state.s[3] >> 8)] ^ // + te2[uint8_t(state.s[0] >> 16)] ^ // + te3[uint8_t(state.s[1] >> 24)]; + result.s[3] = round_key.s[3] ^ // + te0[uint8_t(state.s[3])] ^ // + te1[uint8_t(state.s[0] >> 8)] ^ // + te2[uint8_t(state.s[1] >> 16)] ^ // + te3[uint8_t(state.s[2] >> 24)]; +#else + result.s[0] = round_key.s[0] ^ // + te0[uint8_t(state.s[0])] ^ // + te1[uint8_t(state.s[3] >> 8)] ^ // + te2[uint8_t(state.s[2] >> 16)] ^ // + te3[uint8_t(state.s[1] >> 24)]; + result.s[1] = round_key.s[1] ^ // + te0[uint8_t(state.s[1])] ^ // + te1[uint8_t(state.s[0] >> 8)] ^ // + te2[uint8_t(state.s[3] >> 16)] ^ // + te3[uint8_t(state.s[2] >> 24)]; + result.s[2] = round_key.s[2] ^ // + te0[uint8_t(state.s[2])] ^ // + te1[uint8_t(state.s[1] >> 8)] ^ // + te2[uint8_t(state.s[0] >> 16)] ^ // + te3[uint8_t(state.s[3] >> 24)]; + result.s[3] = round_key.s[3] ^ // + te0[uint8_t(state.s[3])] ^ // + te1[uint8_t(state.s[2] >> 8)] ^ // + te2[uint8_t(state.s[1] >> 16)] ^ // + te3[uint8_t(state.s[0] >> 24)]; +#endif return result; - // clang-format on } -// RANDen = RANDom generator or beetroots in Swiss German. -// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random -// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. -// -// High-level summary: -// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is -// a sponge-like random generator that requires a cryptographic permutation. -// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by -// achieving backtracking resistance with only one Permute() per buffer. -// -// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round -// Function" constructs up to 1024-bit permutations using an improved -// Generalized Feistel network with 2-round AES-128 functions. This Feistel -// block shuffle achieves diffusion faster and is less vulnerable to -// sliced-biclique attacks than the Type-2 cyclic shuffle. -// -// 3) "Improving the Generalized Feistel" and "New criterion for diffusion -// property" extends the same kind of improved Feistel block shuffle to 16 -// branches, which enables a 2048-bit permutation. -// -// Combine these three ideas and also change Simpira's subround keys from -// structured/low-entropy counters to digits of Pi. - -// Randen constants. -constexpr size_t kFeistelBlocks = 16; -constexpr size_t kFeistelFunctions = kFeistelBlocks / 2; // = 8 -constexpr size_t kFeistelRounds = 16 + 1; // > 4 * log2(kFeistelBlocks) -constexpr size_t kKeys = kFeistelRounds * kFeistelFunctions; - -// INCLUDE keys. -#include "absl/random/internal/randen-keys.inc" - -static_assert(kKeys == kRoundKeys, "kKeys and kRoundKeys must be equal"); - -// 2 uint64_t lanes per Vector128 -static constexpr size_t kLanes = 2; +using ::absl::random_internal::RandenTraits; // The improved Feistel block shuffle function for 16 blocks. inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state_u64) { - static_assert(kFeistelBlocks == 16, + absl::uint128* state) { + static_assert(RandenTraits::kFeistelBlocks == 16, "Feistel block shuffle only works for 16 blocks."); - constexpr size_t shuffle[kFeistelBlocks] = {7, 2, 13, 4, 11, 8, 3, 6, - 15, 0, 9, 10, 1, 14, 5, 12}; - - u64x2* ABSL_RANDOM_INTERNAL_RESTRICT state = - reinterpret_cast<u64x2*>(state_u64); + constexpr size_t shuffle[RandenTraits::kFeistelBlocks] = { + 7, 2, 13, 4, 11, 8, 3, 6, 15, 0, 9, 10, 1, 14, 5, 12}; // The fully unrolled loop without the memcpy improves the speed by about - // 30% over the equivalent (leaving code here as a comment): - if (false) { - u64x2 source[kFeistelBlocks]; - std::memcpy(source, state, sizeof(source)); - for (size_t i = 0; i < kFeistelBlocks; i++) { - const u64x2 v0 = source[shuffle[i]]; - state[i] = v0; - } + // 30% over the equivalent: +#if 0 + absl::uint128 source[RandenTraits::kFeistelBlocks]; + std::memcpy(source, state, sizeof(source)); + for (size_t i = 0; i < RandenTraits::kFeistelBlocks; i++) { + const absl::uint128 v0 = source[shuffle[i]]; + state[i] = v0; } + return; +#endif - const u64x2 v0 = state[shuffle[0]]; - const u64x2 v1 = state[shuffle[1]]; - const u64x2 v2 = state[shuffle[2]]; - const u64x2 v3 = state[shuffle[3]]; - const u64x2 v4 = state[shuffle[4]]; - const u64x2 v5 = state[shuffle[5]]; - const u64x2 v6 = state[shuffle[6]]; - const u64x2 v7 = state[shuffle[7]]; - const u64x2 w0 = state[shuffle[8]]; - const u64x2 w1 = state[shuffle[9]]; - const u64x2 w2 = state[shuffle[10]]; - const u64x2 w3 = state[shuffle[11]]; - const u64x2 w4 = state[shuffle[12]]; - const u64x2 w5 = state[shuffle[13]]; - const u64x2 w6 = state[shuffle[14]]; - const u64x2 w7 = state[shuffle[15]]; + const absl::uint128 v0 = state[shuffle[0]]; + const absl::uint128 v1 = state[shuffle[1]]; + const absl::uint128 v2 = state[shuffle[2]]; + const absl::uint128 v3 = state[shuffle[3]]; + const absl::uint128 v4 = state[shuffle[4]]; + const absl::uint128 v5 = state[shuffle[5]]; + const absl::uint128 v6 = state[shuffle[6]]; + const absl::uint128 v7 = state[shuffle[7]]; + const absl::uint128 w0 = state[shuffle[8]]; + const absl::uint128 w1 = state[shuffle[9]]; + const absl::uint128 w2 = state[shuffle[10]]; + const absl::uint128 w3 = state[shuffle[11]]; + const absl::uint128 w4 = state[shuffle[12]]; + const absl::uint128 w5 = state[shuffle[13]]; + const absl::uint128 w6 = state[shuffle[14]]; + const absl::uint128 w7 = state[shuffle[15]]; state[0] = v0; state[1] = v1; state[2] = v2; @@ -423,24 +360,24 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( // per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel // XORs are 'free' (included in the second AES instruction). -inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound( - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state, - const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { - for (size_t branch = 0; branch < kFeistelBlocks; branch += 4) { - const Vector128 s0 = Vector128Load(state + kLanes * branch); - const Vector128 s1 = Vector128Load(state + kLanes * (branch + 1)); +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const absl::uint128* +FeistelRound(absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + for (size_t branch = 0; branch < RandenTraits::kFeistelBlocks; branch += 4) { + const Vector128 s0 = Vector128Load(state + branch); + const Vector128 s1 = Vector128Load(state + branch + 1); const Vector128 f0 = AesRound(s0, Vector128Load(keys)); keys++; const Vector128 o1 = AesRound(f0, s1); - Vector128Store(o1, state + kLanes * (branch + 1)); + Vector128Store(o1, state + branch + 1); // Manually unroll this loop once. about 10% better than not unrolled. - const Vector128 s2 = Vector128Load(state + kLanes * (branch + 2)); - const Vector128 s3 = Vector128Load(state + kLanes * (branch + 3)); + const Vector128 s2 = Vector128Load(state + branch + 2); + const Vector128 s3 = Vector128Load(state + branch + 3); const Vector128 f2 = AesRound(s2, Vector128Load(keys)); keys++; const Vector128 o3 = AesRound(f2, s3); - Vector128Store(o3, state + kLanes * (branch + 3)); + Vector128Store(o3, state + branch + 3); } return keys; } @@ -450,15 +387,31 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound( // 2^64 queries if the round function is a PRF. This is similar to the b=8 case // of Simpira v2, but more efficient than its generic construction for b=16. inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute( - const void* keys, uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { - const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys128 = - static_cast<const u64x2*>(keys); - for (size_t round = 0; round < kFeistelRounds; ++round) { - keys128 = FeistelRound(state, keys128); + absl::uint128* state, + const absl::uint128* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + for (size_t round = 0; round < RandenTraits::kFeistelRounds; ++round) { + keys = FeistelRound(state, keys); BlockShuffle(state); } } +// Enables native loads in the round loop by pre-swapping. +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void SwapEndian( + absl::uint128* state) { +#ifdef ABSL_IS_BIG_ENDIAN + for (uint32_t block = 0; block < RandenTraits::kFeistelBlocks; ++block) { + uint64_t new_lo = absl::little_endian::ToHost64( + static_cast<uint64_t>(state[block] >> 64)); + uint64_t new_hi = absl::little_endian::ToHost64( + static_cast<uint64_t>((state[block] << 64) >> 64)); + state[block] = (static_cast<absl::uint128>(new_hi) << 64) | new_lo; + } +#else + // Avoid warning about unused variable. + (void)state; +#endif +} + } // namespace namespace absl { @@ -468,37 +421,49 @@ namespace random_internal { const void* RandenSlow::GetKeys() { // Round keys for one AES per Feistel round and branch. // The canonical implementation uses first digits of Pi. - return round_keys; +#ifdef ABSL_IS_LITTLE_ENDIAN + return kRandenRoundKeys; +#else + return kRandenRoundKeysBE; +#endif } void RandenSlow::Absorb(const void* seed_void, void* state_void) { - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = - reinterpret_cast<uint64_t*>(state_void); - const uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT seed = - reinterpret_cast<const uint64_t*>(seed_void); - - constexpr size_t kCapacityBlocks = kCapacityBytes / sizeof(uint64_t); - static_assert(kCapacityBlocks * sizeof(uint64_t) == kCapacityBytes, - "Not i*V"); - for (size_t i = kCapacityBlocks; i < kStateBytes / sizeof(uint64_t); ++i) { + auto* state = + reinterpret_cast<uint64_t * ABSL_RANDOM_INTERNAL_RESTRICT>(state_void); + const auto* seed = + reinterpret_cast<const uint64_t * ABSL_RANDOM_INTERNAL_RESTRICT>( + seed_void); + + constexpr size_t kCapacityBlocks = + RandenTraits::kCapacityBytes / sizeof(uint64_t); + static_assert( + kCapacityBlocks * sizeof(uint64_t) == RandenTraits::kCapacityBytes, + "Not i*V"); + + for (size_t i = kCapacityBlocks; + i < RandenTraits::kStateBytes / sizeof(uint64_t); ++i) { state[i] ^= seed[i - kCapacityBlocks]; } } -void RandenSlow::Generate(const void* keys, void* state_void) { - static_assert(kCapacityBytes == sizeof(Vector128), "Capacity mismatch"); +void RandenSlow::Generate(const void* keys_void, void* state_void) { + static_assert(RandenTraits::kCapacityBytes == sizeof(absl::uint128), + "Capacity mismatch"); + + auto* state = reinterpret_cast<absl::uint128*>(state_void); + const auto* keys = reinterpret_cast<const absl::uint128*>(keys_void); + + const absl::uint128 prev_inner = state[0]; - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = - reinterpret_cast<uint64_t*>(state_void); + SwapEndian(state); - const Vector128 prev_inner = Vector128Load(state); + Permute(state, keys); - Permute(keys, state); + SwapEndian(state); // Ensure backtracking resistance. - Vector128 inner = Vector128Load(state); - inner ^= prev_inner; - Vector128Store(inner, state); + *state ^= prev_inner; } } // namespace random_internal diff --git a/third_party/abseil-cpp/absl/random/internal/randen_slow.h b/third_party/abseil-cpp/absl/random/internal/randen_slow.h index 72f92b54ad..532c3a8991 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_slow.h +++ b/third_party/abseil-cpp/absl/random/internal/randen_slow.h @@ -23,18 +23,11 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // RandenSlow implements the basic state manipulation methods for // architectures lacking AES hardware acceleration intrinsics. class RandenSlow { public: - // Size of the entire sponge / state for the randen PRNG. - static constexpr size_t kStateBytes = 256; // 2048-bit - - // Size of the 'inner' (inaccessible) part of the sponge. Larger values would - // require more frequent calls to RandenGenerate. - static constexpr size_t kCapacityBytes = 16; // 128-bit - static void Generate(const void* keys, void* state_void); static void Absorb(const void* seed_void, void* state_void); static const void* GetKeys(); diff --git a/third_party/abseil-cpp/absl/random/internal/randen_slow_test.cc b/third_party/abseil-cpp/absl/random/internal/randen_slow_test.cc index c07155d8dd..ed6039586c 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_slow_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/randen_slow_test.cc @@ -17,45 +17,45 @@ #include <cstring> #include "gtest/gtest.h" +#include "absl/base/internal/endian.h" +#include "absl/random/internal/randen_traits.h" namespace { using absl::random_internal::RandenSlow; - -// Local state parameters. -constexpr size_t kSeedBytes = - RandenSlow::kStateBytes - RandenSlow::kCapacityBytes; -constexpr size_t kStateSizeT = RandenSlow::kStateBytes / sizeof(uint64_t); -constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t); - -struct randen { - uint64_t state[kStateSizeT]; - uint32_t seed[kSeedSizeT]; -}; +using absl::random_internal::RandenTraits; TEST(RandenSlowTest, Default) { - constexpr uint64_t kGolden[] = { - 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, - 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, - 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, - 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, - 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, - 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, - 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, - 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, - 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, - 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, - 0x026ff374c101da7e, 0x811ef0821c3de851, + constexpr uint8_t kGolden[] = { + 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d, + 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3, + 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f, + 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0, + 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff, + 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2, + 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5, + 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c, + 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56, + 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4, + 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91, + 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5, + 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d, + 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58, + 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e, + 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49, + 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0, + 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6, + 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61, + 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35, + 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c, + 0x82, 0xf0, 0x1e, 0x81, }; - alignas(16) randen d; - std::memset(d.state, 0, sizeof(d.state)); - RandenSlow::Generate(RandenSlow::GetKeys(), d.state); + alignas(16) uint8_t state[RandenTraits::kStateBytes]; + std::memset(state, 0, sizeof(state)); - uint64_t* id = d.state; - for (const auto& elem : kGolden) { - EXPECT_EQ(elem, *id++); - } + RandenSlow::Generate(RandenSlow::GetKeys(), state); + EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state))); } } // namespace diff --git a/third_party/abseil-cpp/absl/random/internal/randen_test.cc b/third_party/abseil-cpp/absl/random/internal/randen_test.cc index c186fe0d68..92773b8d9a 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/randen_test.cc @@ -23,9 +23,6 @@ namespace { using absl::random_internal::Randen; -// Local state parameters. -constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t); - TEST(RandenTest, CopyAndMove) { static_assert(std::is_copy_constructible<Randen>::value, "Randen must be copy constructible"); @@ -41,30 +38,38 @@ TEST(RandenTest, CopyAndMove) { } TEST(RandenTest, Default) { - constexpr uint64_t kGolden[] = { - 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, - 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, - 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, - 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, - 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, - 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, - 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, - 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, - 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, - 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, - 0x026ff374c101da7e, 0x811ef0821c3de851, + constexpr uint8_t kGolden[] = { + 0xee, 0xd3, 0xe6, 0x0e, 0x09, 0x34, 0x65, 0x6c, 0xc6, 0x33, 0x53, 0x9d, + 0x9b, 0x2b, 0x4e, 0x04, 0x77, 0x39, 0x43, 0x4e, 0x13, 0x4f, 0xc1, 0xc3, + 0xee, 0x10, 0x04, 0xd9, 0x7c, 0xf4, 0xa9, 0xdd, 0x10, 0xca, 0xd8, 0x7f, + 0x08, 0xf3, 0x7b, 0x88, 0x12, 0x29, 0xc7, 0x45, 0xf5, 0x80, 0xb7, 0xf0, + 0x9f, 0x59, 0x96, 0x76, 0xd3, 0xb1, 0xdb, 0x15, 0x59, 0x6d, 0x3c, 0xff, + 0xba, 0x63, 0xec, 0x30, 0xa6, 0x20, 0x7f, 0x6f, 0x60, 0x73, 0x9f, 0xb2, + 0x4c, 0xa5, 0x49, 0x6f, 0x31, 0x8a, 0x80, 0x02, 0x0e, 0xe5, 0xc8, 0xd5, + 0xf9, 0xea, 0x8f, 0x3b, 0x8a, 0xde, 0xd9, 0x3f, 0x5e, 0x60, 0xbf, 0x9c, + 0xbb, 0x3b, 0x18, 0x78, 0x1a, 0xae, 0x70, 0xc9, 0xd5, 0x1e, 0x30, 0x56, + 0xd3, 0xff, 0xb2, 0xd8, 0x37, 0x3c, 0xc7, 0x0f, 0xfe, 0x27, 0xb3, 0xf4, + 0x19, 0x9a, 0x8f, 0xeb, 0x76, 0x8d, 0xfd, 0xcd, 0x9d, 0x0c, 0x42, 0x91, + 0xeb, 0x06, 0xa5, 0xc3, 0x56, 0x95, 0xff, 0x3e, 0xdd, 0x05, 0xaf, 0xd5, + 0xa1, 0xc4, 0x83, 0x8f, 0xb7, 0x1b, 0xdb, 0x48, 0x8c, 0xfe, 0x6b, 0x0d, + 0x0e, 0x92, 0x23, 0x70, 0x42, 0x6d, 0x95, 0x34, 0x58, 0x57, 0xd3, 0x58, + 0x40, 0xb8, 0x87, 0x6b, 0xc2, 0xf4, 0x1e, 0xed, 0xf3, 0x2d, 0x0b, 0x3e, + 0xa2, 0x32, 0xef, 0x8e, 0xfc, 0x54, 0x11, 0x43, 0xf3, 0xab, 0x7c, 0x49, + 0x8b, 0x9a, 0x02, 0x70, 0x05, 0x37, 0x24, 0x4e, 0xea, 0xe5, 0x90, 0xf0, + 0x49, 0x57, 0x8b, 0xd8, 0x2f, 0x69, 0x70, 0xa9, 0x82, 0xa5, 0x51, 0xc6, + 0xf5, 0x42, 0x63, 0xbb, 0x2c, 0xec, 0xfc, 0x78, 0xdb, 0x55, 0x2f, 0x61, + 0x45, 0xb7, 0x3c, 0x46, 0xe3, 0xaf, 0x16, 0x18, 0xad, 0xe4, 0x2e, 0x35, + 0x7e, 0xda, 0x01, 0xc1, 0x74, 0xf3, 0x6f, 0x02, 0x51, 0xe8, 0x3d, 0x1c, + 0x82, 0xf0, 0x1e, 0x81, }; - alignas(16) uint64_t state[kStateSizeT]; + alignas(16) uint8_t state[Randen::kStateBytes]; std::memset(state, 0, sizeof(state)); Randen r; r.Generate(state); - auto id = std::begin(state); - for (const auto& elem : kGolden) { - EXPECT_EQ(elem, *id++); - } + EXPECT_EQ(0, std::memcmp(state, kGolden, sizeof(state))); } } // namespace diff --git a/third_party/abseil-cpp/absl/random/internal/randen_traits.h b/third_party/abseil-cpp/absl/random/internal/randen_traits.h index 2b8bbe7383..120022c9fb 100644 --- a/third_party/abseil-cpp/absl/random/internal/randen_traits.h +++ b/third_party/abseil-cpp/absl/random/internal/randen_traits.h @@ -28,10 +28,29 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { -// RANDen = RANDom generator or beetroots in Swiss German. +// RANDen = RANDom generator or beetroots in Swiss High German. // 'Strong' (well-distributed, unpredictable, backtracking-resistant) random // generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. // +// High-level summary: +// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is +// a sponge-like random generator that requires a cryptographic permutation. +// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by +// achieving backtracking resistance with only one Permute() per buffer. +// +// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round +// Function" constructs up to 1024-bit permutations using an improved +// Generalized Feistel network with 2-round AES-128 functions. This Feistel +// block shuffle achieves diffusion faster and is less vulnerable to +// sliced-biclique attacks than the Type-2 cyclic shuffle. +// +// 3) "Improving the Generalized Feistel" and "New criterion for diffusion +// property" extends the same kind of improved Feistel block shuffle to 16 +// branches, which enables a 2048-bit permutation. +// +// Combine these three ideas and also change Simpira's subround keys from +// structured/low-entropy counters to digits of Pi (or other random source). + // RandenTraits contains the basic algorithm traits, such as the size of the // state, seed, sponge, etc. struct RandenTraits { @@ -45,17 +64,23 @@ struct RandenTraits { // Size of the default seed consumed by the sponge. static constexpr size_t kSeedBytes = kStateBytes - kCapacityBytes; + // Assuming 128-bit blocks, the number of blocks in the state. // Largest size for which security proofs are known. static constexpr size_t kFeistelBlocks = 16; - // Type-2 generalized Feistel => one round function for every two blocks. - static constexpr size_t kFeistelFunctions = kFeistelBlocks / 2; // = 8 - // Ensures SPRP security and two full subblock diffusions. // Must be > 4 * log2(kFeistelBlocks). static constexpr size_t kFeistelRounds = 16 + 1; + + // Size of the key. A 128-bit key block is used for every-other + // feistel block (Type-2 generalized Feistel network) in each round. + static constexpr size_t kKeyBytes = 16 * kFeistelRounds * kFeistelBlocks / 2; }; +// Randen key arrays. In randen_round_keys.cc +extern const unsigned char kRandenRoundKeys[RandenTraits::kKeyBytes]; +extern const unsigned char kRandenRoundKeysBE[RandenTraits::kKeyBytes]; + } // namespace random_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/random/internal/seed_material.cc b/third_party/abseil-cpp/absl/random/internal/seed_material.cc index 4d38a57419..c03cad8502 100644 --- a/third_party/abseil-cpp/absl/random/internal/seed_material.cc +++ b/third_party/abseil-cpp/absl/random/internal/seed_material.cc @@ -28,6 +28,7 @@ #include <cstdlib> #include <cstring> +#include "absl/base/dynamic_annotations.h" #include "absl/base/internal/raw_logging.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" @@ -50,6 +51,18 @@ #endif +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +// glibc >= 2.25 has getentropy() +#define ABSL_RANDOM_USE_GET_ENTROPY 1 +#endif + +#if defined(__EMSCRIPTEN__) +#include <sys/random.h> +// Emscripten has getentropy, but it resides in a different header. +#define ABSL_RANDOM_USE_GET_ENTROPY 1 +#endif + #if defined(ABSL_RANDOM_USE_BCRYPT) #include <bcrypt.h> @@ -122,8 +135,32 @@ bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { #else +#if defined(ABSL_RANDOM_USE_GET_ENTROPY) +// On *nix, use getentropy() if supported. Note that libc may support +// getentropy(), but the kernel may not, in which case this function will return +// false. +bool ReadSeedMaterialFromGetEntropy(absl::Span<uint32_t> values) { + auto buffer = reinterpret_cast<uint8_t*>(values.data()); + size_t buffer_size = sizeof(uint32_t) * values.size(); + while (buffer_size > 0) { + // getentropy() has a maximum permitted length of 256. + size_t to_read = std::min<size_t>(buffer_size, 256); + int result = getentropy(buffer, to_read); + if (result < 0) { + return false; + } + // https://github.com/google/sanitizers/issues/1173 + // MemorySanitizer can't see through getentropy(). + ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buffer, to_read); + buffer += to_read; + buffer_size -= to_read; + } + return true; +} +#endif // defined(ABSL_RANDOM_GETENTROPY) + // On *nix, read entropy from /dev/urandom. -bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { +bool ReadSeedMaterialFromDevURandom(absl::Span<uint32_t> values) { const char kEntropyFile[] = "/dev/urandom"; auto buffer = reinterpret_cast<uint8_t*>(values.data()); @@ -150,6 +187,17 @@ bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { return success; } +bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { +#if defined(ABSL_RANDOM_USE_GET_ENTROPY) + if (ReadSeedMaterialFromGetEntropy(values)) { + return true; + } +#endif + // Libc may support getentropy, but the kernel may not, so we still have + // to fallback to ReadSeedMaterialFromDevURandom(). + return ReadSeedMaterialFromDevURandom(values); +} + #endif } // namespace diff --git a/third_party/abseil-cpp/absl/random/internal/uniform_helper.h b/third_party/abseil-cpp/absl/random/internal/uniform_helper.h index 663107cb3a..1243bc1c62 100644 --- a/third_party/abseil-cpp/absl/random/internal/uniform_helper.h +++ b/third_party/abseil-cpp/absl/random/internal/uniform_helper.h @@ -19,10 +19,13 @@ #include <limits> #include <type_traits> +#include "absl/base/config.h" #include "absl/meta/type_traits.h" +#include "absl/random/internal/traits.h" namespace absl { ABSL_NAMESPACE_BEGIN + template <typename IntType> class uniform_int_distribution; @@ -58,6 +61,26 @@ struct IntervalOpenOpenTag : public random_internal::TagTypeCompare<IntervalOpenOpenTag> {}; namespace random_internal { + +// In the absence of an explicitly provided return-type, the template +// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on +// the data-types of the endpoint-arguments {A lo, B hi}. +// +// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the +// return-type, if one type can be implicitly converted into the other, in a +// lossless way. The template "is_widening_convertible" implements the +// compile-time logic for deciding if such a conversion is possible. +// +// If no such conversion between {A, B} exists, then the overload for +// absl::Uniform() will be discarded, and the call will be ill-formed. +// Return-type for absl::Uniform() when the return-type is inferred. +template <typename A, typename B> +using uniform_inferred_return_t = + absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>, + is_widening_convertible<B, A>>::value, + typename std::conditional< + is_widening_convertible<A, B>::value, B, A>::type>; + // The functions // uniform_lower_bound(tag, a, b) // and @@ -82,7 +105,7 @@ typename absl::enable_if_t< std::is_same<Tag, IntervalOpenOpenTag>>>::value, IntType> uniform_lower_bound(Tag, IntType a, IntType) { - return a + 1; + return a < (std::numeric_limits<IntType>::max)() ? (a + 1) : a; } template <typename FloatType, typename Tag> @@ -113,7 +136,7 @@ typename absl::enable_if_t< std::is_same<Tag, IntervalOpenOpenTag>>>::value, IntType> uniform_upper_bound(Tag, IntType, IntType b) { - return b - 1; + return b > (std::numeric_limits<IntType>::min)() ? (b - 1) : b; } template <typename FloatType, typename Tag> @@ -149,12 +172,53 @@ uniform_upper_bound(Tag, FloatType, FloatType b) { return std::nextafter(b, (std::numeric_limits<FloatType>::max)()); } +// Returns whether the bounds are valid for the underlying distribution. +// Inputs must have already been resolved via uniform_*_bound calls. +// +// The c++ standard constraints in [rand.dist.uni.int] are listed as: +// requires: lo <= hi. +// +// In the uniform_int_distrubtion, {lo, hi} are closed, closed. Thus: +// [0, 0] is legal. +// [0, 0) is not legal, but [0, 1) is, which translates to [0, 0]. +// (0, 1) is not legal, but (0, 2) is, which translates to [1, 1]. +// (0, 0] is not legal, but (0, 1] is, which translates to [1, 1]. +// +// The c++ standard constraints in [rand.dist.uni.real] are listed as: +// requires: lo <= hi. +// requires: (hi - lo) <= numeric_limits<T>::max() +// +// In the uniform_real_distribution, {lo, hi} are closed, open, Thus: +// [0, 0] is legal, which is [0, 0+epsilon). +// [0, 0) is legal. +// (0, 0) is not legal, but (0-epsilon, 0+epsilon) is. +// (0, 0] is not legal, but (0, 0+epsilon] is. +// +template <typename FloatType> +absl::enable_if_t<std::is_floating_point<FloatType>::value, bool> +is_uniform_range_valid(FloatType a, FloatType b) { + return a <= b && std::isfinite(b - a); +} + +template <typename IntType> +absl::enable_if_t<std::is_integral<IntType>::value, bool> +is_uniform_range_valid(IntType a, IntType b) { + return a <= b; +} + +// UniformDistribution selects either absl::uniform_int_distribution +// or absl::uniform_real_distribution depending on the NumType parameter. template <typename NumType> using UniformDistribution = typename std::conditional<std::is_integral<NumType>::value, absl::uniform_int_distribution<NumType>, absl::uniform_real_distribution<NumType>>::type; +// UniformDistributionWrapper is used as the underlying distribution type +// by the absl::Uniform template function. It selects the proper Abseil +// uniform distribution and provides constructor overloads that match the +// expected parameter order as well as adjusting distribtuion bounds based +// on the tag. template <typename NumType> struct UniformDistributionWrapper : public UniformDistribution<NumType> { template <typename TagType> diff --git a/third_party/abseil-cpp/absl/random/internal/uniform_helper_test.cc b/third_party/abseil-cpp/absl/random/internal/uniform_helper_test.cc new file mode 100644 index 0000000000..173c49b0b7 --- /dev/null +++ b/third_party/abseil-cpp/absl/random/internal/uniform_helper_test.cc @@ -0,0 +1,279 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/random/internal/uniform_helper.h" + +#include <cmath> +#include <cstdint> +#include <random> + +#include "gtest/gtest.h" + +namespace { + +using absl::IntervalClosedClosedTag; +using absl::IntervalClosedOpenTag; +using absl::IntervalOpenClosedTag; +using absl::IntervalOpenOpenTag; +using absl::random_internal::uniform_inferred_return_t; +using absl::random_internal::uniform_lower_bound; +using absl::random_internal::uniform_upper_bound; + +class UniformHelperTest : public testing::Test {}; + +TEST_F(UniformHelperTest, UniformBoundFunctionsGeneral) { + constexpr IntervalClosedClosedTag IntervalClosedClosed; + constexpr IntervalClosedOpenTag IntervalClosedOpen; + constexpr IntervalOpenClosedTag IntervalOpenClosed; + constexpr IntervalOpenOpenTag IntervalOpenOpen; + + // absl::uniform_int_distribution natively assumes IntervalClosedClosed + // absl::uniform_real_distribution natively assumes IntervalClosedOpen + + EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1); + EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0); + + EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0); + EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99); + EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99); + EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0); + EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100); + EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100); + EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0); + EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0); + + // Negative value tests + EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99); + EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0); + + EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100); + EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0), + -2.0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2); + EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2); + EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0); + EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1); + EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1); + EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0); + EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0), + -1.0); + + EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0); + EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0); + EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0); + EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0); +} + +TEST_F(UniformHelperTest, UniformBoundFunctionsIntBounds) { + // Verifies the saturating nature of uniform_lower_bound and + // uniform_upper_bound + constexpr IntervalOpenOpenTag IntervalOpenOpen; + + // uint max. + constexpr auto m = (std::numeric_limits<uint64_t>::max)(); + + EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0u, 0u)); + EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m, m)); + EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m - 1, m - 1)); + EXPECT_EQ(0, uniform_upper_bound(IntervalOpenOpen, 0u, 0u)); + EXPECT_EQ(m - 1, uniform_upper_bound(IntervalOpenOpen, m, m)); + + // int min/max + constexpr auto l = (std::numeric_limits<int64_t>::min)(); + constexpr auto r = (std::numeric_limits<int64_t>::max)(); + EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0, 0)); + EXPECT_EQ(l + 1, uniform_lower_bound(IntervalOpenOpen, l, l)); + EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r - 1, r - 1)); + EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r, r)); + EXPECT_EQ(-1, uniform_upper_bound(IntervalOpenOpen, 0, 0)); + EXPECT_EQ(l, uniform_upper_bound(IntervalOpenOpen, l, l)); + EXPECT_EQ(r - 1, uniform_upper_bound(IntervalOpenOpen, r, r)); +} + +TEST_F(UniformHelperTest, UniformBoundFunctionsRealBounds) { + // absl::uniform_real_distribution natively assumes IntervalClosedOpen; + // use the inverse here so each bound has to change. + constexpr IntervalOpenClosedTag IntervalOpenClosed; + + // Edge cases: the next value toward itself is itself. + EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, 1.0, 1.0)); + EXPECT_EQ(1.0f, uniform_lower_bound(IntervalOpenClosed, 1.0f, 1.0f)); + + // rightmost and leftmost finite values. + constexpr auto r = (std::numeric_limits<double>::max)(); + const auto re = std::nexttoward(r, 0.0); + constexpr auto l = -r; + const auto le = std::nexttoward(l, 0.0); + + EXPECT_EQ(l, uniform_lower_bound(IntervalOpenClosed, l, l)); // (l,l) + EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, r, r)); // (r,r) + EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, r)); // (l,r) + EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, 0.0)); // (l, 0) + EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, le)); // (l, le) + EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, re, r)); // (re, r) + + EXPECT_EQ(le, uniform_upper_bound(IntervalOpenClosed, l, l)); // (l,l) + EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, r, r)); // (r,r) + EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, r)); // (l,r) + EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, re)); // (l,re) + EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, 0.0, r)); // (0, r) + EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, re, r)); // (re, r) + EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, le, re)); // (le, re) + + const double e = std::nextafter(1.0, 2.0); // 1 + epsilon + const double f = std::nextafter(1.0, 0.0); // 1 - epsilon + + // (1.0, 1.0 + epsilon) + EXPECT_EQ(e, uniform_lower_bound(IntervalOpenClosed, 1.0, e)); + EXPECT_EQ(std::nextafter(e, 2.0), + uniform_upper_bound(IntervalOpenClosed, 1.0, e)); + + // (1.0-epsilon, 1.0) + EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, f, 1.0)); + EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, f, 1.0)); + + // denorm cases. + const double g = std::numeric_limits<double>::denorm_min(); + const double h = std::nextafter(g, 1.0); + + // (0, denorm_min) + EXPECT_EQ(g, uniform_lower_bound(IntervalOpenClosed, 0.0, g)); + EXPECT_EQ(h, uniform_upper_bound(IntervalOpenClosed, 0.0, g)); + + // (denorm_min, 1.0) + EXPECT_EQ(h, uniform_lower_bound(IntervalOpenClosed, g, 1.0)); + EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, g, 1.0)); + + // Edge cases: invalid bounds. + EXPECT_EQ(f, uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0)); +} + +struct Invalid {}; + +template <typename A, typename B> +auto InferredUniformReturnT(int) -> uniform_inferred_return_t<A, B>; + +template <typename, typename> +Invalid InferredUniformReturnT(...); + +// Given types <A, B, Expect>, CheckArgsInferType() verifies that +// +// uniform_inferred_return_t<A, B> and +// uniform_inferred_return_t<B, A> +// +// returns the type "Expect". +// +// This interface can also be used to assert that a given inferred return types +// are invalid. Writing: +// +// CheckArgsInferType<float, int, Invalid>() +// +// will assert that this overload does not exist. +template <typename A, typename B, typename Expect> +void CheckArgsInferType() { + static_assert( + absl::conjunction< + std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>, + std::is_same<Expect, + decltype(InferredUniformReturnT<B, A>(0))>>::value, + ""); +} + +TEST_F(UniformHelperTest, UniformTypeInference) { + // Infers common types. + CheckArgsInferType<uint16_t, uint16_t, uint16_t>(); + CheckArgsInferType<uint32_t, uint32_t, uint32_t>(); + CheckArgsInferType<uint64_t, uint64_t, uint64_t>(); + CheckArgsInferType<int16_t, int16_t, int16_t>(); + CheckArgsInferType<int32_t, int32_t, int32_t>(); + CheckArgsInferType<int64_t, int64_t, int64_t>(); + CheckArgsInferType<float, float, float>(); + CheckArgsInferType<double, double, double>(); + + // Properly promotes uint16_t. + CheckArgsInferType<uint16_t, uint32_t, uint32_t>(); + CheckArgsInferType<uint16_t, uint64_t, uint64_t>(); + CheckArgsInferType<uint16_t, int32_t, int32_t>(); + CheckArgsInferType<uint16_t, int64_t, int64_t>(); + CheckArgsInferType<uint16_t, float, float>(); + CheckArgsInferType<uint16_t, double, double>(); + + // Properly promotes int16_t. + CheckArgsInferType<int16_t, int32_t, int32_t>(); + CheckArgsInferType<int16_t, int64_t, int64_t>(); + CheckArgsInferType<int16_t, float, float>(); + CheckArgsInferType<int16_t, double, double>(); + + // Invalid (u)int16_t-pairings do not compile. + // See "CheckArgsInferType" comments above, for how this is achieved. + CheckArgsInferType<uint16_t, int16_t, Invalid>(); + CheckArgsInferType<int16_t, uint32_t, Invalid>(); + CheckArgsInferType<int16_t, uint64_t, Invalid>(); + + // Properly promotes uint32_t. + CheckArgsInferType<uint32_t, uint64_t, uint64_t>(); + CheckArgsInferType<uint32_t, int64_t, int64_t>(); + CheckArgsInferType<uint32_t, double, double>(); + + // Properly promotes int32_t. + CheckArgsInferType<int32_t, int64_t, int64_t>(); + CheckArgsInferType<int32_t, double, double>(); + + // Invalid (u)int32_t-pairings do not compile. + CheckArgsInferType<uint32_t, int32_t, Invalid>(); + CheckArgsInferType<int32_t, uint64_t, Invalid>(); + CheckArgsInferType<int32_t, float, Invalid>(); + CheckArgsInferType<uint32_t, float, Invalid>(); + + // Invalid (u)int64_t-pairings do not compile. + CheckArgsInferType<uint64_t, int64_t, Invalid>(); + CheckArgsInferType<int64_t, float, Invalid>(); + CheckArgsInferType<int64_t, double, Invalid>(); + + // Properly promotes float. + CheckArgsInferType<float, double, double>(); +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/random/internal/wide_multiply.h b/third_party/abseil-cpp/absl/random/internal/wide_multiply.h index 6e4cf1be0a..b6e6c4b6aa 100644 --- a/third_party/abseil-cpp/absl/random/internal/wide_multiply.h +++ b/third_party/abseil-cpp/absl/random/internal/wide_multiply.h @@ -26,7 +26,7 @@ #endif #include "absl/base/config.h" -#include "absl/base/internal/bits.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/random/internal/traits.h" @@ -38,9 +38,9 @@ namespace random_internal { // MultiplyU64ToU128 multiplies two 64-bit values to a 128-bit value. // If an intrinsic is available, it is used, otherwise use native 32-bit // multiplies to construct the result. -inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { +inline absl::uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { #if defined(ABSL_HAVE_INTRINSIC_INT128) - return uint128(static_cast<__uint128_t>(a) * b); + return absl::uint128(static_cast<__uint128_t>(a) * b); #elif defined(ABSL_INTERNAL_USE_UMUL128) // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC. uint64_t high = 0; @@ -93,14 +93,14 @@ struct wide_multiply { template <> struct wide_multiply<uint64_t> { using input_type = uint64_t; - using result_type = uint128; + using result_type = absl::uint128; static result_type multiply(uint64_t a, uint64_t b) { return MultiplyU64ToU128(a, b); } - static uint64_t hi(result_type r) { return Uint128High64(r); } - static uint64_t lo(result_type r) { return Uint128Low64(r); } + static uint64_t hi(result_type r) { return absl::Uint128High64(r); } + static uint64_t lo(result_type r) { return absl::Uint128Low64(r); } }; #endif diff --git a/third_party/abseil-cpp/absl/random/internal/wide_multiply_test.cc b/third_party/abseil-cpp/absl/random/internal/wide_multiply_test.cc index 922603f226..e276cb51cf 100644 --- a/third_party/abseil-cpp/absl/random/internal/wide_multiply_test.cc +++ b/third_party/abseil-cpp/absl/random/internal/wide_multiply_test.cc @@ -15,7 +15,6 @@ #include "absl/random/internal/wide_multiply.h" #include "gtest/gtest.h" -#include "absl/base/internal/bits.h" #include "absl/numeric/int128.h" using absl::random_internal::MultiplyU64ToU128; @@ -28,7 +27,7 @@ TEST(WideMultiplyTest, MultiplyU64ToU128Test) { EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0)); - // Max uint64 + // Max uint64_t EXPECT_EQ(MultiplyU64ToU128(kMax, kMax), absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001)); EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1)); diff --git a/third_party/abseil-cpp/absl/random/log_uniform_int_distribution.h b/third_party/abseil-cpp/absl/random/log_uniform_int_distribution.h index 960816e2f8..43e101169c 100644 --- a/third_party/abseil-cpp/absl/random/log_uniform_int_distribution.h +++ b/third_party/abseil-cpp/absl/random/log_uniform_int_distribution.h @@ -23,6 +23,7 @@ #include <ostream> #include <type_traits> +#include "absl/numeric/bits.h" #include "absl/random/internal/fastmath.h" #include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" @@ -68,8 +69,10 @@ class log_uniform_int_distribution { if (base_ == 2) { // Determine where the first set bit is on range(), giving a log2(range) // value which can be used to construct bounds. - log_range_ = (std::min)(random_internal::LeadingSetBit(range()), - std::numeric_limits<unsigned_type>::digits); + log_range_ = + (std::min)(bit_width(range()), + static_cast<unsigned_type>( + std::numeric_limits<unsigned_type>::digits)); } else { // NOTE: Computing the logN(x) introduces error from 2 sources: // 1. Conversion of int to double loses precision for values >= diff --git a/third_party/abseil-cpp/absl/random/log_uniform_int_distribution_test.cc b/third_party/abseil-cpp/absl/random/log_uniform_int_distribution_test.cc index 5270531d20..5e780d96d3 100644 --- a/third_party/abseil-cpp/absl/random/log_uniform_int_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/log_uniform_int_distribution_test.cc @@ -27,6 +27,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -121,7 +122,10 @@ class LogUniformIntChiSquaredTest // data generated by the log-uniform-int distribution. double ChiSquaredTestImpl(); - absl::InsecureBitGen rng_; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6}; }; double LogUniformIntChiSquaredTest::ChiSquaredTestImpl() { @@ -194,7 +198,6 @@ double LogUniformIntChiSquaredTest::ChiSquaredTestImpl() { TEST_P(LogUniformIntChiSquaredTest, MultiTest) { const int kTrials = 5; - int failures = 0; for (int i = 0; i < kTrials; i++) { double p_value = ChiSquaredTestImpl(); diff --git a/third_party/abseil-cpp/absl/random/mock_distributions.h b/third_party/abseil-cpp/absl/random/mock_distributions.h index d36d5ba03c..764ab370ab 100644 --- a/third_party/abseil-cpp/absl/random/mock_distributions.h +++ b/third_party/abseil-cpp/absl/random/mock_distributions.h @@ -27,6 +27,11 @@ // More information about the Googletest testing framework is available at // https://github.com/google/googletest // +// EXPECT_CALL and ON_CALL need to be made within the same DLL component as +// the call to absl::Uniform and related methods, otherwise mocking will fail +// since the underlying implementation creates a type-specific pointer which +// will be distinct across different DLL boundaries. +// // Example: // // absl::MockingBitGen mock; diff --git a/third_party/abseil-cpp/absl/random/mocking_bit_gen.h b/third_party/abseil-cpp/absl/random/mocking_bit_gen.h index 36cef91113..7b2b80eb35 100644 --- a/third_party/abseil-cpp/absl/random/mocking_bit_gen.h +++ b/third_party/abseil-cpp/absl/random/mocking_bit_gen.h @@ -33,17 +33,16 @@ #include <memory> #include <tuple> #include <type_traits> -#include <typeindex> -#include <typeinfo> #include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/fast_type_id.h" #include "absl/container/flat_hash_map.h" #include "absl/meta/type_traits.h" #include "absl/random/distributions.h" #include "absl/random/internal/distribution_caller.h" -#include "absl/random/internal/mocking_bit_gen_base.h" +#include "absl/random/random.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/types/span.h" @@ -54,11 +53,12 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { - -template <typename, typename> -struct MockSingleOverload; +template <typename> +struct DistributionCaller; +class MockHelpers; } // namespace random_internal +class BitGenRef; // MockingBitGen // @@ -96,100 +96,144 @@ struct MockSingleOverload; // At this time, only mock distributions supplied within the Abseil random // library are officially supported. // -class MockingBitGen : public absl::random_internal::MockingBitGenBase { +// EXPECT_CALL and ON_CALL need to be made within the same DLL component as +// the call to absl::Uniform and related methods, otherwise mocking will fail +// since the underlying implementation creates a type-specific pointer which +// will be distinct across different DLL boundaries. +// +class MockingBitGen { public: - MockingBitGen() {} + MockingBitGen() = default; + ~MockingBitGen() = default; - ~MockingBitGen() override; + // URBG interface + using result_type = absl::BitGen::result_type; - private: - template <typename DistrT, typename... Args> - using MockFnType = - ::testing::MockFunction<typename DistrT::result_type(Args...)>; + static constexpr result_type(min)() { return (absl::BitGen::min)(); } + static constexpr result_type(max)() { return (absl::BitGen::max)(); } + result_type operator()() { return gen_(); } - // MockingBitGen::Register - // - // Register<DistrT, FormatT, ArgTupleT> is the main extension point for - // extending the MockingBitGen framework. It provides a mechanism to install a - // mock expectation for the distribution `distr_t` onto the MockingBitGen - // context. - // - // The returned MockFunction<...> type can be used to setup additional - // distribution parameters of the expectation. - template <typename DistrT, typename... Args, typename... Ms> - decltype(std::declval<MockFnType<DistrT, Args...>>().gmock_Call( - std::declval<Ms>()...)) - Register(Ms&&... matchers) { - auto& mock = - mocks_[std::type_index(GetTypeId<DistrT, std::tuple<Args...>>())]; - - if (!mock.mock_fn) { - auto* mock_fn = new MockFnType<DistrT, Args...>; - mock.mock_fn = mock_fn; - mock.match_impl = &MatchImpl<DistrT, Args...>; - deleters_.emplace_back([mock_fn] { delete mock_fn; }); + private: + // GetMockFnType returns the testing::MockFunction for a result and tuple. + // This method only exists for type deduction and is otherwise unimplemented. + template <typename ResultT, typename... Args> + static auto GetMockFnType(ResultT, std::tuple<Args...>) + -> ::testing::MockFunction<ResultT(Args...)>; + + // MockFnCaller is a helper method for use with absl::apply to + // apply an ArgTupleT to a compatible MockFunction. + // NOTE: MockFnCaller is essentially equivalent to the lambda: + // [fn](auto... args) { return fn->Call(std::move(args)...)} + // however that fails to build on some supported platforms. + template <typename MockFnType, typename ResultT, typename Tuple> + struct MockFnCaller; + + // specialization for std::tuple. + template <typename MockFnType, typename ResultT, typename... Args> + struct MockFnCaller<MockFnType, ResultT, std::tuple<Args...>> { + MockFnType* fn; + inline ResultT operator()(Args... args) { + return fn->Call(std::move(args)...); } + }; - return static_cast<MockFnType<DistrT, Args...>*>(mock.mock_fn) - ->gmock_Call(std::forward<Ms>(matchers)...); - } + // FunctionHolder owns a particular ::testing::MockFunction associated with + // a mocked type signature, and implement the type-erased Apply call, which + // applies type-erased arguments to the mock. + class FunctionHolder { + public: + virtual ~FunctionHolder() = default; + + // Call is a dispatch function which converts the + // generic type-erased parameters into a specific mock invocation call. + virtual void Apply(/*ArgTupleT*/ void* args_tuple, + /*ResultT*/ void* result) = 0; + }; - mutable std::vector<std::function<void()>> deleters_; + template <typename MockFnType, typename ResultT, typename ArgTupleT> + class FunctionHolderImpl final : public FunctionHolder { + public: + void Apply(void* args_tuple, void* result) override { + // Requires tuple_args to point to a ArgTupleT, which is a + // std::tuple<Args...> used to invoke the mock function. Requires result + // to point to a ResultT, which is the result of the call. + *static_cast<ResultT*>(result) = + absl::apply(MockFnCaller<MockFnType, ResultT, ArgTupleT>{&mock_fn_}, + *static_cast<ArgTupleT*>(args_tuple)); + } - using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args, - void* t_erased_result); - struct MockData { - void* mock_fn = nullptr; - match_impl_fn match_impl = nullptr; + MockFnType mock_fn_; }; - mutable absl::flat_hash_map<std::type_index, MockData> mocks_; - - template <typename DistrT, typename... Args> - static void MatchImpl(void* mock_fn, void* dist_args, void* result) { - using result_type = typename DistrT::result_type; - *static_cast<result_type*>(result) = absl::apply( - [mock_fn](Args... args) -> result_type { - return (*static_cast<MockFnType<DistrT, Args...>*>(mock_fn)) - .Call(std::move(args)...); - }, - *static_cast<std::tuple<Args...>*>(dist_args)); + // MockingBitGen::RegisterMock + // + // RegisterMock<ResultT, ArgTupleT>(FastTypeIdType) is the main extension + // point for extending the MockingBitGen framework. It provides a mechanism to + // install a mock expectation for a function like ResultT(Args...) keyed by + // type_idex onto the MockingBitGen context. The key is that the type_index + // used to register must match the type index used to call the mock. + // + // The returned MockFunction<...> type can be used to setup additional + // distribution parameters of the expectation. + template <typename ResultT, typename ArgTupleT, typename SelfT> + auto RegisterMock(SelfT&, base_internal::FastTypeIdType type) + -> decltype(GetMockFnType(std::declval<ResultT>(), + std::declval<ArgTupleT>()))& { + using MockFnType = decltype(GetMockFnType(std::declval<ResultT>(), + std::declval<ArgTupleT>())); + + using WrappedFnType = absl::conditional_t< + std::is_same<SelfT, ::testing::NiceMock<absl::MockingBitGen>>::value, + ::testing::NiceMock<MockFnType>, + absl::conditional_t< + std::is_same<SelfT, + ::testing::NaggyMock<absl::MockingBitGen>>::value, + ::testing::NaggyMock<MockFnType>, + absl::conditional_t< + std::is_same<SelfT, + ::testing::StrictMock<absl::MockingBitGen>>::value, + ::testing::StrictMock<MockFnType>, MockFnType>>>; + + using ImplT = FunctionHolderImpl<WrappedFnType, ResultT, ArgTupleT>; + auto& mock = mocks_[type]; + if (!mock) { + mock = absl::make_unique<ImplT>(); + } + return static_cast<ImplT*>(mock.get())->mock_fn_; } - // Looks for an appropriate mock - Returns the mocked result if one is found. - // Otherwise, returns a random value generated by the underlying URBG. - bool CallImpl(const std::type_info& key_type, void* dist_args, - void* result) override { + // MockingBitGen::InvokeMock + // + // InvokeMock(FastTypeIdType, args, result) is the entrypoint for invoking + // mocks registered on MockingBitGen. + // + // When no mocks are registered on the provided FastTypeIdType, returns false. + // Otherwise attempts to invoke the mock function ResultT(Args...) that + // was previously registered via the type_index. + // Requires tuple_args to point to a ArgTupleT, which is a std::tuple<Args...> + // used to invoke the mock function. + // Requires result to point to a ResultT, which is the result of the call. + inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple, + void* result) { // Trigger a mock, if there exists one that matches `param`. - auto it = mocks_.find(std::type_index(key_type)); + auto it = mocks_.find(type); if (it == mocks_.end()) return false; - auto* mock_data = static_cast<MockData*>(&it->second); - mock_data->match_impl(mock_data->mock_fn, dist_args, result); + it->second->Apply(args_tuple, result); return true; } - template <typename, typename> - friend struct ::absl::random_internal::MockSingleOverload; - friend struct ::absl::random_internal::DistributionCaller< - absl::MockingBitGen>; -}; + absl::flat_hash_map<base_internal::FastTypeIdType, + std::unique_ptr<FunctionHolder>> + mocks_; + absl::BitGen gen_; -// ----------------------------------------------------------------------------- -// Implementation Details Only Below -// ----------------------------------------------------------------------------- - -namespace random_internal { - -template <> -struct DistributionCaller<absl::MockingBitGen> { - template <typename DistrT, typename FormatT, typename... Args> - static typename DistrT::result_type Call(absl::MockingBitGen* gen, - Args&&... args) { - return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...); - } + template <typename> + friend struct ::absl::random_internal::DistributionCaller; // for InvokeMock + friend class ::absl::BitGenRef; // for InvokeMock + friend class ::absl::random_internal::MockHelpers; // for RegisterMock, + // InvokeMock }; -} // namespace random_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/random/mocking_bit_gen_test.cc b/third_party/abseil-cpp/absl/random/mocking_bit_gen_test.cc index f0ffc9ac92..c713ceafd2 100644 --- a/third_party/abseil-cpp/absl/random/mocking_bit_gen_test.cc +++ b/third_party/abseil-cpp/absl/random/mocking_bit_gen_test.cc @@ -15,6 +15,7 @@ // #include "absl/random/mocking_bit_gen.h" +#include <cmath> #include <numeric> #include <random> @@ -26,6 +27,8 @@ #include "absl/random/random.h" namespace { + +using ::testing::_; using ::testing::Ne; using ::testing::Return; @@ -326,8 +329,9 @@ TEST(BasicMocking, WillByDefaultWithArgs) { absl::MockingBitGen gen; ON_CALL(absl::MockPoisson<int>(), Call(gen, _)) - .WillByDefault( - [](double lambda) { return static_cast<int>(lambda * 10); }); + .WillByDefault([](double lambda) { + return static_cast<int>(std::rint(lambda * 10)); + }); EXPECT_EQ(absl::Poisson<int>(gen, 1.7), 17); EXPECT_EQ(absl::Poisson<int>(gen, 0.03), 0); } @@ -344,4 +348,47 @@ TEST(MockingBitGen, InSequenceSucceedsInOrder) { EXPECT_EQ(absl::Poisson<int>(gen, 2.0), 4); } +TEST(MockingBitGen, NiceMock) { + ::testing::NiceMock<absl::MockingBitGen> gen; + ON_CALL(absl::MockUniform<int>(), Call(gen, _, _)).WillByDefault(Return(145)); + + ON_CALL(absl::MockPoisson<int>(), Call(gen, _)).WillByDefault(Return(3)); + + EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 10, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 100, 1000), 145); +} + +TEST(MockingBitGen, NaggyMock) { + // This is difficult to test, as only the output matters, so just verify + // that ON_CALL can be installed. Anything else requires log inspection. + ::testing::NaggyMock<absl::MockingBitGen> gen; + + ON_CALL(absl::MockUniform<int>(), Call(gen, _, _)).WillByDefault(Return(145)); + ON_CALL(absl::MockPoisson<int>(), Call(gen, _)).WillByDefault(Return(3)); + + EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145); +} + +TEST(MockingBitGen, StrictMock_NotEnough) { + EXPECT_NONFATAL_FAILURE( + []() { + ::testing::StrictMock<absl::MockingBitGen> gen; + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, _, _)) + .WillOnce(Return(145)); + }(), + "unsatisfied and active"); +} + +TEST(MockingBitGen, StrictMock_TooMany) { + ::testing::StrictMock<absl::MockingBitGen> gen; + + EXPECT_CALL(absl::MockUniform<int>(), Call(gen, _, _)).WillOnce(Return(145)); + EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145); + + EXPECT_NONFATAL_FAILURE( + [&]() { EXPECT_EQ(absl::Uniform(gen, 10, 1000), 0); }(), + "over-saturated and active"); +} + } // namespace diff --git a/third_party/abseil-cpp/absl/random/poisson_distribution_test.cc b/third_party/abseil-cpp/absl/random/poisson_distribution_test.cc index 9d215fbc70..8baabd1118 100644 --- a/third_party/abseil-cpp/absl/random/poisson_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/poisson_distribution_test.cc @@ -30,6 +30,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -257,7 +258,10 @@ class PoissonDistributionZTest : public testing::TestWithParam<ZParam>, template <typename D> bool SingleZTest(const double p, const size_t samples); - absl::InsecureBitGen rng_; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6}; }; template <typename D> @@ -357,9 +361,13 @@ class PoissonDistributionChiSquaredTest : public testing::TestWithParam<double>, private: void InitChiSquaredTest(const double buckets); - absl::InsecureBitGen rng_; std::vector<size_t> cutoffs_; std::vector<double> expected_; + + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6}; }; void PoissonDistributionChiSquaredTest::InitChiSquaredTest( diff --git a/third_party/abseil-cpp/absl/random/random.h b/third_party/abseil-cpp/absl/random/random.h index c8f326e642..71b6309288 100644 --- a/third_party/abseil-cpp/absl/random/random.h +++ b/third_party/abseil-cpp/absl/random/random.h @@ -109,7 +109,7 @@ ABSL_NAMESPACE_BEGIN // absl::BitGen::max() // -// Returns the largest possible value from this bit generator., and +// Returns the largest possible value from this bit generator. // absl::BitGen::discard(num) // diff --git a/third_party/abseil-cpp/absl/random/seed_sequences_test.cc b/third_party/abseil-cpp/absl/random/seed_sequences_test.cc index 2cc8b0e6f2..fe1100bda0 100644 --- a/third_party/abseil-cpp/absl/random/seed_sequences_test.cc +++ b/third_party/abseil-cpp/absl/random/seed_sequences_test.cc @@ -96,7 +96,6 @@ template <typename URBG> void TestReproducibleVariateSequencesForNonsecureURBG() { const size_t kNumVariates = 1000; - // Master RNG instance. URBG rng; // Reused for both RNG instances. auto reusable_seed = absl::CreateSeedSeqFrom(&rng); diff --git a/third_party/abseil-cpp/absl/random/uniform_int_distribution.h b/third_party/abseil-cpp/absl/random/uniform_int_distribution.h index da66564a6b..c1f54ccebc 100644 --- a/third_party/abseil-cpp/absl/random/uniform_int_distribution.h +++ b/third_party/abseil-cpp/absl/random/uniform_int_distribution.h @@ -196,7 +196,7 @@ typename random_internal::make_unsigned_bits<IntType>::type uniform_int_distribution<IntType>::Generate( URBG& g, // NOLINT(runtime/references) typename random_internal::make_unsigned_bits<IntType>::type R) { - random_internal::FastUniformBits<unsigned_type> fast_bits; + random_internal::FastUniformBits<unsigned_type> fast_bits; unsigned_type bits = fast_bits(g); const unsigned_type Lim = R + 1; if ((R & Lim) == 0) { diff --git a/third_party/abseil-cpp/absl/random/uniform_int_distribution_test.cc b/third_party/abseil-cpp/absl/random/uniform_int_distribution_test.cc index aacff88d38..276d72ad20 100644 --- a/third_party/abseil-cpp/absl/random/uniform_int_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/uniform_int_distribution_test.cc @@ -26,6 +26,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -123,7 +124,7 @@ TYPED_TEST(UniformIntDistributionTest, ViolatesPreconditionsDeathTest) { absl::uniform_int_distribution<TypeParam> dist(10, 1); auto x = dist(gen); - // Any value will generate a non-empty std::string. + // Any value will generate a non-empty string. EXPECT_FALSE(absl::StrCat(+x).empty()) << x; #endif // NDEBUG } @@ -134,7 +135,11 @@ TYPED_TEST(UniformIntDistributionTest, TestMoments) { using param_type = typename absl::uniform_int_distribution<TypeParam>::param_type; - absl::InsecureBitGen rng; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6}; + std::vector<double> values(kSize); for (const auto& param : {param_type(0, Limits::max()), param_type(13, 127)}) { @@ -178,7 +183,11 @@ TYPED_TEST(UniformIntDistributionTest, ChiSquaredTest50) { const TypeParam min = std::is_unsigned<TypeParam>::value ? 37 : -37; const TypeParam max = min + kBuckets; - absl::InsecureBitGen rng; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6}; + absl::uniform_int_distribution<TypeParam> dist(min, max); std::vector<int32_t> counts(kBuckets + 1, 0); diff --git a/third_party/abseil-cpp/absl/random/uniform_real_distribution_test.cc b/third_party/abseil-cpp/absl/random/uniform_real_distribution_test.cc index a56374a65b..035bd284d1 100644 --- a/third_party/abseil-cpp/absl/random/uniform_real_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/uniform_real_distribution_test.cc @@ -14,19 +14,23 @@ #include "absl/random/uniform_real_distribution.h" +#include <cfloat> #include <cmath> #include <cstdint> #include <iterator> #include <random> #include <sstream> #include <string> +#include <type_traits> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" +#include "absl/numeric/internal/representation.h" #include "absl/random/internal/chi_square.h" #include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -54,15 +58,27 @@ namespace { template <typename RealType> class UniformRealDistributionTest : public ::testing::Test {}; -#if defined(__EMSCRIPTEN__) -using RealTypes = ::testing::Types<float, double>; -#else -using RealTypes = ::testing::Types<float, double, long double>; -#endif // defined(__EMSCRIPTEN__) +// double-double arithmetic is not supported well by either GCC or Clang; see +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99048, +// https://bugs.llvm.org/show_bug.cgi?id=49131, and +// https://bugs.llvm.org/show_bug.cgi?id=49132. Don't bother running these tests +// with double doubles until compiler support is better. +using RealTypes = + std::conditional<absl::numeric_internal::IsDoubleDouble(), + ::testing::Types<float, double>, + ::testing::Types<float, double, long double>>::type; TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations are + // performed with 80-bit floats. This produces slightly different results from + // what we expect below. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + using param_type = typename absl::uniform_real_distribution<TypeParam>::param_type; @@ -207,7 +223,11 @@ TYPED_TEST(UniformRealDistributionTest, TestMoments) { constexpr int kSize = 1000000; std::vector<double> values(kSize); - absl::InsecureBitGen rng; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6}; + absl::uniform_real_distribution<TypeParam> dist; for (int i = 0; i < kSize; i++) { values[i] = dist(rng); @@ -237,7 +257,11 @@ TYPED_TEST(UniformRealDistributionTest, ChiSquaredTest50) { const int kThreshold = absl::random_internal::ChiSquareValue(kBuckets - 1, 0.999999); - absl::InsecureBitGen rng; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng{0x2B7E151628AED2A6}; + for (const auto& param : {param_type(0, 1), param_type(5, 12), param_type(-5, 13), param_type(-5, -2)}) { const double min_val = param.a(); diff --git a/third_party/abseil-cpp/absl/random/zipf_distribution_test.cc b/third_party/abseil-cpp/absl/random/zipf_distribution_test.cc index 4d4a0fcf79..f8cf70e0dd 100644 --- a/third_party/abseil-cpp/absl/random/zipf_distribution_test.cc +++ b/third_party/abseil-cpp/absl/random/zipf_distribution_test.cc @@ -27,6 +27,7 @@ #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/random/internal/chi_square.h" +#include "absl/random/internal/pcg_engine.h" #include "absl/random/internal/sequence_urbg.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -213,7 +214,10 @@ class ZipfTest : public testing::TestWithParam<zipf_u64::param_type>, public: ZipfTest() : ZipfModel(GetParam().k(), GetParam().q(), GetParam().v()) {} - absl::InsecureBitGen rng_; + // We use a fixed bit generator for distribution accuracy tests. This allows + // these tests to be deterministic, while still testing the qualify of the + // implementation. + absl::random_internal::pcg64_2018_engine rng_{0x2B7E151628AED2A6}; }; TEST_P(ZipfTest, ChiSquaredTest) { diff --git a/third_party/abseil-cpp/absl/status/BUILD.bazel b/third_party/abseil-cpp/absl/status/BUILD.bazel index 2b83077def..bae5156f4a 100644 --- a/third_party/abseil-cpp/absl/status/BUILD.bazel +++ b/third_party/abseil-cpp/absl/status/BUILD.bazel @@ -17,7 +17,6 @@ # It will expand later to have utilities around `Status` like `StatusOr`, # `StatusBuilder` and macros. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -26,11 +25,12 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "status", srcs = [ + "internal/status_internal.h", "status.cc", "status_payload_printer.cc", ], @@ -40,12 +40,14 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, deps = [ + "//absl/base:atomic_hook", "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/container:inlined_vector", "//absl/debugging:stacktrace", "//absl/debugging:symbolize", + "//absl/functional:function_ref", "//absl/strings", "//absl/strings:cord", "//absl/strings:str_format", @@ -63,3 +65,41 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "statusor", + srcs = [ + "internal/statusor_internal.h", + "statusor.cc", + ], + hdrs = [ + "statusor.h", + ], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":status", + "//absl/base", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/types:variant", + "//absl/utility", + ], +) + +cc_test( + name = "statusor_test", + size = "small", + srcs = ["statusor_test.cc"], + deps = [ + ":status", + ":statusor", + "//absl/base", + "//absl/memory", + "//absl/strings", + "//absl/types:any", + "//absl/utility", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/third_party/abseil-cpp/absl/status/CMakeLists.txt b/third_party/abseil-cpp/absl/status/CMakeLists.txt index f05cee5e95..f107c85b1c 100644 --- a/third_party/abseil-cpp/absl/status/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/status/CMakeLists.txt @@ -19,14 +19,17 @@ absl_cc_library( HDRS "status.h" SRCS + "internal/status_internal.h" "status.cc" "status_payload_printer.h" "status_payload_printer.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::atomic_hook absl::config absl::core_headers + absl::function_ref absl::raw_logging_internal absl::inlined_vector absl::stacktrace @@ -48,5 +51,40 @@ absl_cc_test( DEPS absl::status absl::strings - gmock_main + GTest::gmock_main +) + +absl_cc_library( + NAME + statusor + HDRS + "statusor.h" + SRCS + "statusor.cc" + "internal/statusor_internal.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::status + absl::core_headers + absl::raw_logging_internal + absl::type_traits + absl::strings + absl::utility + absl::variant + PUBLIC +) + +absl_cc_test( + NAME + statusor_test + SRCS + "statusor_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::status + absl::statusor + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/status/internal/status_internal.h b/third_party/abseil-cpp/absl/status/internal/status_internal.h new file mode 100644 index 0000000000..ac12940a6d --- /dev/null +++ b/third_party/abseil-cpp/absl/status/internal/status_internal.h @@ -0,0 +1,69 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +#ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ +#define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ + +#include <string> + +#include "absl/container/inlined_vector.h" +#include "absl/strings/cord.h" + +#ifndef SWIG +// Disabled for SWIG as it doesn't parse attributes correctly. +namespace absl { +ABSL_NAMESPACE_BEGIN +// Returned Status objects may not be ignored. Codesearch doesn't handle ifdefs +// as part of a class definitions (b/6995610), so we use a forward declaration. +class ABSL_MUST_USE_RESULT Status; +ABSL_NAMESPACE_END +} // namespace absl +#endif // !SWIG + +namespace absl { +ABSL_NAMESPACE_BEGIN + +enum class StatusCode : int; + +namespace status_internal { + +// Container for status payloads. +struct Payload { + std::string type_url; + absl::Cord payload; +}; + +using Payloads = absl::InlinedVector<Payload, 1>; + +// Reference-counted representation of Status data. +struct StatusRep { + StatusRep(absl::StatusCode code_arg, absl::string_view message_arg, + std::unique_ptr<status_internal::Payloads> payloads_arg) + : ref(int32_t{1}), + code(code_arg), + message(message_arg), + payloads(std::move(payloads_arg)) {} + + std::atomic<int32_t> ref; + absl::StatusCode code; + std::string message; + std::unique_ptr<status_internal::Payloads> payloads; +}; + +absl::StatusCode MapToLocalCode(int value); +} // namespace status_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_ diff --git a/third_party/abseil-cpp/absl/status/internal/statusor_internal.h b/third_party/abseil-cpp/absl/status/internal/statusor_internal.h new file mode 100644 index 0000000000..eaac2c0b14 --- /dev/null +++ b/third_party/abseil-cpp/absl/status/internal/statusor_internal.h @@ -0,0 +1,396 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ +#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ + +#include <type_traits> +#include <utility> + +#include "absl/base/attributes.h" +#include "absl/meta/type_traits.h" +#include "absl/status/status.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +template <typename T> +class ABSL_MUST_USE_RESULT StatusOr; + +namespace internal_statusor { + +// Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator +// StatusOr<T>()`. +template <typename T, typename U, typename = void> +struct HasConversionOperatorToStatusOr : std::false_type {}; + +template <typename T, typename U> +void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]); + +template <typename T, typename U> +struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))> + : std::true_type {}; + +// Detects whether `T` is constructible or convertible from `StatusOr<U>`. +template <typename T, typename U> +using IsConstructibleOrConvertibleFromStatusOr = + absl::disjunction<std::is_constructible<T, StatusOr<U>&>, + std::is_constructible<T, const StatusOr<U>&>, + std::is_constructible<T, StatusOr<U>&&>, + std::is_constructible<T, const StatusOr<U>&&>, + std::is_convertible<StatusOr<U>&, T>, + std::is_convertible<const StatusOr<U>&, T>, + std::is_convertible<StatusOr<U>&&, T>, + std::is_convertible<const StatusOr<U>&&, T>>; + +// Detects whether `T` is constructible or convertible or assignable from +// `StatusOr<U>`. +template <typename T, typename U> +using IsConstructibleOrConvertibleOrAssignableFromStatusOr = + absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>, + std::is_assignable<T&, StatusOr<U>&>, + std::is_assignable<T&, const StatusOr<U>&>, + std::is_assignable<T&, StatusOr<U>&&>, + std::is_assignable<T&, const StatusOr<U>&&>>; + +// Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e. +// when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`. +template <typename T, typename U> +struct IsDirectInitializationAmbiguous + : public absl::conditional_t< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + U>::value, + std::false_type, + IsDirectInitializationAmbiguous< + T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + +template <typename T, typename V> +struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> + : public IsConstructibleOrConvertibleFromStatusOr<T, V> {}; + +// Checks against the constraints of the direction initialization, i.e. when +// `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution. +template <typename T, typename U> +using IsDirectInitializationValid = absl::disjunction< + // Short circuits if T is basically U. + std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + absl::negation<absl::disjunction< + std::is_same<absl::StatusOr<T>, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::Status, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::in_place_t, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + IsDirectInitializationAmbiguous<T, U>>>>; + +// This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which +// is equivalent to whether all the following conditions are met: +// 1. `U` is `StatusOr<V>`. +// 2. `T` is constructible and assignable from `V`. +// 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`). +// For example, the following code is considered ambiguous: +// (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`) +// StatusOr<bool> s1 = true; // s1.ok() && s1.ValueOrDie() == true +// StatusOr<bool> s2 = false; // s2.ok() && s2.ValueOrDie() == false +// s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`? +template <typename T, typename U> +struct IsForwardingAssignmentAmbiguous + : public absl::conditional_t< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + U>::value, + std::false_type, + IsForwardingAssignmentAmbiguous< + T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; + +template <typename T, typename U> +struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> + : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {}; + +// Checks against the constraints of the forwarding assignment, i.e. whether +// `StatusOr<T>::operator(U&&)` should participate in overload resolution. +template <typename T, typename U> +using IsForwardingAssignmentValid = absl::disjunction< + // Short circuits if T is basically U. + std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, + absl::negation<absl::disjunction< + std::is_same<absl::StatusOr<T>, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::Status, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + std::is_same<absl::in_place_t, + absl::remove_cv_t<absl::remove_reference_t<U>>>, + IsForwardingAssignmentAmbiguous<T, U>>>>; + +class Helper { + public: + // Move type-agnostic error handling to the .cc. + static void HandleInvalidStatusCtorArg(Status*); + ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status); +}; + +// Construct an instance of T in `p` through placement new, passing Args... to +// the constructor. +// This abstraction is here mostly for the gcc performance fix. +template <typename T, typename... Args> +ABSL_ATTRIBUTE_NONNULL(1) void PlacementNew(void* p, Args&&... args) { + new (p) T(std::forward<Args>(args)...); +} + +// Helper base class to hold the data and all operations. +// We move all this to a base class to allow mixing with the appropriate +// TraitsBase specialization. +template <typename T> +class StatusOrData { + template <typename U> + friend class StatusOrData; + + public: + StatusOrData() = delete; + + StatusOrData(const StatusOrData& other) { + if (other.ok()) { + MakeValue(other.data_); + MakeStatus(); + } else { + MakeStatus(other.status_); + } + } + + StatusOrData(StatusOrData&& other) noexcept { + if (other.ok()) { + MakeValue(std::move(other.data_)); + MakeStatus(); + } else { + MakeStatus(std::move(other.status_)); + } + } + + template <typename U> + explicit StatusOrData(const StatusOrData<U>& other) { + if (other.ok()) { + MakeValue(other.data_); + MakeStatus(); + } else { + MakeStatus(other.status_); + } + } + + template <typename U> + explicit StatusOrData(StatusOrData<U>&& other) { + if (other.ok()) { + MakeValue(std::move(other.data_)); + MakeStatus(); + } else { + MakeStatus(std::move(other.status_)); + } + } + + template <typename... Args> + explicit StatusOrData(absl::in_place_t, Args&&... args) + : data_(std::forward<Args>(args)...) { + MakeStatus(); + } + + explicit StatusOrData(const T& value) : data_(value) { + MakeStatus(); + } + explicit StatusOrData(T&& value) : data_(std::move(value)) { + MakeStatus(); + } + + template <typename U, + absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value, + int> = 0> + explicit StatusOrData(U&& v) : status_(std::forward<U>(v)) { + EnsureNotOk(); + } + + StatusOrData& operator=(const StatusOrData& other) { + if (this == &other) return *this; + if (other.ok()) + Assign(other.data_); + else + AssignStatus(other.status_); + return *this; + } + + StatusOrData& operator=(StatusOrData&& other) { + if (this == &other) return *this; + if (other.ok()) + Assign(std::move(other.data_)); + else + AssignStatus(std::move(other.status_)); + return *this; + } + + ~StatusOrData() { + if (ok()) { + status_.~Status(); + data_.~T(); + } else { + status_.~Status(); + } + } + + template <typename U> + void Assign(U&& value) { + if (ok()) { + data_ = std::forward<U>(value); + } else { + MakeValue(std::forward<U>(value)); + status_ = OkStatus(); + } + } + + template <typename U> + void AssignStatus(U&& v) { + Clear(); + status_ = static_cast<absl::Status>(std::forward<U>(v)); + EnsureNotOk(); + } + + bool ok() const { return status_.ok(); } + + protected: + // status_ will always be active after the constructor. + // We make it a union to be able to initialize exactly how we need without + // waste. + // Eg. in the copy constructor we use the default constructor of Status in + // the ok() path to avoid an extra Ref call. + union { + Status status_; + }; + + // data_ is active iff status_.ok()==true + struct Dummy {}; + union { + // When T is const, we need some non-const object we can cast to void* for + // the placement new. dummy_ is that object. + Dummy dummy_; + T data_; + }; + + void Clear() { + if (ok()) data_.~T(); + } + + void EnsureOk() const { + if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_); + } + + void EnsureNotOk() { + if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_); + } + + // Construct the value (ie. data_) through placement new with the passed + // argument. + template <typename... Arg> + void MakeValue(Arg&&... arg) { + internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...); + } + + // Construct the status (ie. status_) through placement new with the passed + // argument. + template <typename... Args> + void MakeStatus(Args&&... args) { + internal_statusor::PlacementNew<Status>(&status_, + std::forward<Args>(args)...); + } +}; + +// Helper base classes to allow implicitly deleted constructors and assignment +// operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete +// the copy constructor when T is not copy constructible and `StatusOr` will +// inherit that behavior implicitly. +template <typename T, bool = std::is_copy_constructible<T>::value> +struct CopyCtorBase { + CopyCtorBase() = default; + CopyCtorBase(const CopyCtorBase&) = default; + CopyCtorBase(CopyCtorBase&&) = default; + CopyCtorBase& operator=(const CopyCtorBase&) = default; + CopyCtorBase& operator=(CopyCtorBase&&) = default; +}; + +template <typename T> +struct CopyCtorBase<T, false> { + CopyCtorBase() = default; + CopyCtorBase(const CopyCtorBase&) = delete; + CopyCtorBase(CopyCtorBase&&) = default; + CopyCtorBase& operator=(const CopyCtorBase&) = default; + CopyCtorBase& operator=(CopyCtorBase&&) = default; +}; + +template <typename T, bool = std::is_move_constructible<T>::value> +struct MoveCtorBase { + MoveCtorBase() = default; + MoveCtorBase(const MoveCtorBase&) = default; + MoveCtorBase(MoveCtorBase&&) = default; + MoveCtorBase& operator=(const MoveCtorBase&) = default; + MoveCtorBase& operator=(MoveCtorBase&&) = default; +}; + +template <typename T> +struct MoveCtorBase<T, false> { + MoveCtorBase() = default; + MoveCtorBase(const MoveCtorBase&) = default; + MoveCtorBase(MoveCtorBase&&) = delete; + MoveCtorBase& operator=(const MoveCtorBase&) = default; + MoveCtorBase& operator=(MoveCtorBase&&) = default; +}; + +template <typename T, bool = std::is_copy_constructible<T>::value&& + std::is_copy_assignable<T>::value> +struct CopyAssignBase { + CopyAssignBase() = default; + CopyAssignBase(const CopyAssignBase&) = default; + CopyAssignBase(CopyAssignBase&&) = default; + CopyAssignBase& operator=(const CopyAssignBase&) = default; + CopyAssignBase& operator=(CopyAssignBase&&) = default; +}; + +template <typename T> +struct CopyAssignBase<T, false> { + CopyAssignBase() = default; + CopyAssignBase(const CopyAssignBase&) = default; + CopyAssignBase(CopyAssignBase&&) = default; + CopyAssignBase& operator=(const CopyAssignBase&) = delete; + CopyAssignBase& operator=(CopyAssignBase&&) = default; +}; + +template <typename T, bool = std::is_move_constructible<T>::value&& + std::is_move_assignable<T>::value> +struct MoveAssignBase { + MoveAssignBase() = default; + MoveAssignBase(const MoveAssignBase&) = default; + MoveAssignBase(MoveAssignBase&&) = default; + MoveAssignBase& operator=(const MoveAssignBase&) = default; + MoveAssignBase& operator=(MoveAssignBase&&) = default; +}; + +template <typename T> +struct MoveAssignBase<T, false> { + MoveAssignBase() = default; + MoveAssignBase(const MoveAssignBase&) = default; + MoveAssignBase(MoveAssignBase&&) = default; + MoveAssignBase& operator=(const MoveAssignBase&) = default; + MoveAssignBase& operator=(MoveAssignBase&&) = delete; +}; + +ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status); + +} // namespace internal_statusor +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ diff --git a/third_party/abseil-cpp/absl/status/status.cc b/third_party/abseil-cpp/absl/status/status.cc index df3b740f77..bcf3413e5f 100644 --- a/third_party/abseil-cpp/absl/status/status.cc +++ b/third_party/abseil-cpp/absl/status/status.cc @@ -27,8 +27,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// The implementation was intentionally kept same as util::error::Code_Name() -// to ease the migration. std::string StatusCodeToString(StatusCode code) { switch (code) { case StatusCode::kOk: @@ -80,7 +78,7 @@ static int FindPayloadIndexByUrl(const Payloads* payloads, absl::string_view type_url) { if (payloads == nullptr) return -1; - for (int i = 0; i < payloads->size(); ++i) { + for (size_t i = 0; i < payloads->size(); ++i) { if ((*payloads)[i].type_url == type_url) return i; } @@ -163,21 +161,21 @@ bool Status::ErasePayload(absl::string_view type_url) { } void Status::ForEachPayload( - const std::function<void(absl::string_view, const absl::Cord&)>& visitor) + absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor) const { if (auto* payloads = GetPayloads()) { bool in_reverse = payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6; - for (int index = 0; index < payloads->size(); ++index) { + for (size_t index = 0; index < payloads->size(); ++index) { const auto& elem = (*payloads)[in_reverse ? payloads->size() - 1 - index : index]; #ifdef NDEBUG visitor(elem.type_url, elem.payload); #else - // In debug mode invaldiate the type url to prevent users from relying on - // this std::string lifetime. + // In debug mode invalidate the type url to prevent users from relying on + // this string lifetime. // NOLINTNEXTLINE intentional extra conversion to force temporary. visitor(std::string(elem.type_url), elem.payload); @@ -209,20 +207,10 @@ void Status::UnrefNonInlined(uintptr_t rep) { } } -uintptr_t Status::NewRep(absl::StatusCode code, absl::string_view msg, - std::unique_ptr<status_internal::Payloads> payloads) { - status_internal::StatusRep* rep = new status_internal::StatusRep; - rep->ref.store(1, std::memory_order_relaxed); - rep->code = code; - rep->message.assign(msg.data(), msg.size()); - rep->payloads = std::move(payloads); - return PointerToRep(rep); -} - Status::Status(absl::StatusCode code, absl::string_view msg) : rep_(CodeToInlinedRep(code)) { if (code != absl::StatusCode::kOk && !msg.empty()) { - rep_ = NewRep(code, msg, nullptr); + rep_ = PointerToRep(new status_internal::StatusRep(code, msg, nullptr)); } } @@ -241,8 +229,9 @@ absl::StatusCode Status::code() const { void Status::PrepareToModify() { ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status."); if (IsInlined(rep_)) { - rep_ = NewRep(static_cast<absl::StatusCode>(raw_code()), - absl::string_view(), nullptr); + rep_ = PointerToRep(new status_internal::StatusRep( + static_cast<absl::StatusCode>(raw_code()), absl::string_view(), + nullptr)); return; } @@ -253,7 +242,9 @@ void Status::PrepareToModify() { if (rep->payloads) { payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads); } - rep_ = NewRep(rep->code, message(), std::move(payloads)); + status_internal::StatusRep* const new_rep = new status_internal::StatusRep( + rep->code, message(), std::move(payloads)); + rep_ = PointerToRep(new_rep); UnrefNonInlined(rep_i); } } @@ -292,26 +283,32 @@ bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) { return true; } -std::string Status::ToStringSlow() const { +std::string Status::ToStringSlow(StatusToStringMode mode) const { std::string text; absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message()); - status_internal::StatusPayloadPrinter printer = - status_internal::GetStatusPayloadPrinter(); - this->ForEachPayload([&](absl::string_view type_url, - const absl::Cord& payload) { - absl::optional<std::string> result; - if (printer) result = printer(type_url, payload); - absl::StrAppend( - &text, " [", type_url, "='", - result.has_value() ? *result : absl::CHexEscape(std::string(payload)), - "']"); - }); + + const bool with_payload = (mode & StatusToStringMode::kWithPayload) == + StatusToStringMode::kWithPayload; + + if (with_payload) { + status_internal::StatusPayloadPrinter printer = + status_internal::GetStatusPayloadPrinter(); + this->ForEachPayload([&](absl::string_view type_url, + const absl::Cord& payload) { + absl::optional<std::string> result; + if (printer) result = printer(type_url, payload); + absl::StrAppend( + &text, " [", type_url, "='", + result.has_value() ? *result : absl::CHexEscape(std::string(payload)), + "']"); + }); + } return text; } std::ostream& operator<<(std::ostream& os, const Status& x) { - os << x.ToString(); + os << x.ToString(StatusToStringMode::kWithEverything); return os; } diff --git a/third_party/abseil-cpp/absl/status/status.h b/third_party/abseil-cpp/absl/status/status.h index 9706d4bace..39071e5f4a 100644 --- a/third_party/abseil-cpp/absl/status/status.h +++ b/third_party/abseil-cpp/absl/status/status.h @@ -11,6 +11,43 @@ // 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. +// +// ----------------------------------------------------------------------------- +// File: status.h +// ----------------------------------------------------------------------------- +// +// This header file defines the Abseil `status` library, consisting of: +// +// * An `absl::Status` class for holding error handling information +// * A set of canonical `absl::StatusCode` error codes, and associated +// utilities for generating and propagating status codes. +// * A set of helper functions for creating status codes and checking their +// values +// +// Within Google, `absl::Status` is the primary mechanism for gracefully +// handling errors across API boundaries (and in particular across RPC +// boundaries). Some of these errors may be recoverable, but others may not. +// Most functions that can produce a recoverable error should be designed to +// return an `absl::Status` (or `absl::StatusOr`). +// +// Example: +// +// absl::Status myFunction(absl::string_view fname, ...) { +// ... +// // encounter error +// if (error condition) { +// return absl::InvalidArgumentError("bad mode"); +// } +// // else, return OK +// return absl::OkStatus(); +// } +// +// An `absl::Status` is designed to either return "OK" or one of a number of +// different error codes, corresponding to typical error conditions. +// In almost all cases, when using `absl::Status` you should use the canonical +// error codes (of type `absl::StatusCode`) enumerated in this header file. +// These canonical codes are understood across the codebase and will be +// accepted across all API and RPC boundaries. #ifndef ABSL_STATUS_STATUS_H_ #define ABSL_STATUS_STATUS_H_ @@ -18,168 +55,543 @@ #include <string> #include "absl/container/inlined_vector.h" +#include "absl/functional/function_ref.h" +#include "absl/status/internal/status_internal.h" #include "absl/strings/cord.h" +#include "absl/strings/string_view.h" #include "absl/types/optional.h" namespace absl { ABSL_NAMESPACE_BEGIN +// absl::StatusCode +// +// An `absl::StatusCode` is an enumerated type indicating either no error ("OK") +// or an error condition. In most cases, an `absl::Status` indicates a +// recoverable error, and the purpose of signalling an error is to indicate what +// action to take in response to that error. These error codes map to the proto +// RPC error codes indicated in https://cloud.google.com/apis/design/errors. +// +// The errors listed below are the canonical errors associated with +// `absl::Status` and are used throughout the codebase. As a result, these +// error codes are somewhat generic. +// +// In general, try to return the most specific error that applies if more than +// one error may pertain. For example, prefer `kOutOfRange` over +// `kFailedPrecondition` if both codes apply. Similarly prefer `kNotFound` or +// `kAlreadyExists` over `kFailedPrecondition`. +// +// Because these errors may cross RPC boundaries, these codes are tied to the +// `google.rpc.Code` definitions within +// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto +// The string value of these RPC codes is denoted within each enum below. +// +// If your error handling code requires more context, you can attach payloads +// to your status. See `absl::Status::SetPayload()` and +// `absl::Status::GetPayload()` below. enum class StatusCode : int { + // StatusCode::kOk + // + // kOK (gRPC code "OK") does not indicate an error; this value is returned on + // success. It is typical to check for this value before proceeding on any + // given call across an API or RPC boundary. To check this value, use the + // `absl::Status::ok()` member function rather than inspecting the raw code. kOk = 0, + + // StatusCode::kCancelled + // + // kCancelled (gRPC code "CANCELLED") indicates the operation was cancelled, + // typically by the caller. kCancelled = 1, + + // StatusCode::kUnknown + // + // kUnknown (gRPC code "UNKNOWN") indicates an unknown error occurred. In + // general, more specific errors should be raised, if possible. Errors raised + // by APIs that do not return enough error information may be converted to + // this error. kUnknown = 2, + + // StatusCode::kInvalidArgument + // + // kInvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller + // specified an invalid argument, such as a malformed filename. Note that use + // of such errors should be narrowly limited to indicate the invalid nature of + // the arguments themselves. Errors with validly formed arguments that may + // cause errors with the state of the receiving system should be denoted with + // `kFailedPrecondition` instead. kInvalidArgument = 3, + + // StatusCode::kDeadlineExceeded + // + // kDeadlineExceeded (gRPC code "DEADLINE_EXCEEDED") indicates a deadline + // expired before the operation could complete. For operations that may change + // state within a system, this error may be returned even if the operation has + // completed successfully. For example, a successful response from a server + // could have been delayed long enough for the deadline to expire. kDeadlineExceeded = 4, + + // StatusCode::kNotFound + // + // kNotFound (gRPC code "NOT_FOUND") indicates some requested entity (such as + // a file or directory) was not found. + // + // `kNotFound` is useful if a request should be denied for an entire class of + // users, such as during a gradual feature rollout or undocumented allow list. + // If a request should be denied for specific sets of users, such as through + // user-based access control, use `kPermissionDenied` instead. kNotFound = 5, + + // StatusCode::kAlreadyExists + // + // kAlreadyExists (gRPC code "ALREADY_EXISTS") indicates that the entity a + // caller attempted to create (such as a file or directory) is already + // present. kAlreadyExists = 6, + + // StatusCode::kPermissionDenied + // + // kPermissionDenied (gRPC code "PERMISSION_DENIED") indicates that the caller + // does not have permission to execute the specified operation. Note that this + // error is different than an error due to an *un*authenticated user. This + // error code does not imply the request is valid or the requested entity + // exists or satisfies any other pre-conditions. + // + // `kPermissionDenied` must not be used for rejections caused by exhausting + // some resource. Instead, use `kResourceExhausted` for those errors. + // `kPermissionDenied` must not be used if the caller cannot be identified. + // Instead, use `kUnauthenticated` for those errors. kPermissionDenied = 7, + + // StatusCode::kResourceExhausted + // + // kResourceExhausted (gRPC code "RESOURCE_EXHAUSTED") indicates some resource + // has been exhausted, perhaps a per-user quota, or perhaps the entire file + // system is out of space. kResourceExhausted = 8, + + // StatusCode::kFailedPrecondition + // + // kFailedPrecondition (gRPC code "FAILED_PRECONDITION") indicates that the + // operation was rejected because the system is not in a state required for + // the operation's execution. For example, a directory to be deleted may be + // non-empty, an "rmdir" operation is applied to a non-directory, etc. + // + // Some guidelines that may help a service implementer in deciding between + // `kFailedPrecondition`, `kAborted`, and `kUnavailable`: + // + // (a) Use `kUnavailable` if the client can retry just the failing call. + // (b) Use `kAborted` if the client should retry at a higher transaction + // level (such as when a client-specified test-and-set fails, indicating + // the client should restart a read-modify-write sequence). + // (c) Use `kFailedPrecondition` if the client should not retry until + // the system state has been explicitly fixed. For example, if a "rmdir" + // fails because the directory is non-empty, `kFailedPrecondition` + // should be returned since the client should not retry unless + // the files are deleted from the directory. kFailedPrecondition = 9, + + // StatusCode::kAborted + // + // kAborted (gRPC code "ABORTED") indicates the operation was aborted, + // typically due to a concurrency issue such as a sequencer check failure or a + // failed transaction. + // + // See the guidelines above for deciding between `kFailedPrecondition`, + // `kAborted`, and `kUnavailable`. kAborted = 10, + + // StatusCode::kOutOfRange + // + // kOutOfRange (gRPC code "OUT_OF_RANGE") indicates the operation was + // attempted past the valid range, such as seeking or reading past an + // end-of-file. + // + // Unlike `kInvalidArgument`, this error indicates a problem that may + // be fixed if the system state changes. For example, a 32-bit file + // system will generate `kInvalidArgument` if asked to read at an + // offset that is not in the range [0,2^32-1], but it will generate + // `kOutOfRange` if asked to read from an offset past the current + // file size. + // + // There is a fair bit of overlap between `kFailedPrecondition` and + // `kOutOfRange`. We recommend using `kOutOfRange` (the more specific + // error) when it applies so that callers who are iterating through + // a space can easily look for an `kOutOfRange` error to detect when + // they are done. kOutOfRange = 11, + + // StatusCode::kUnimplemented + // + // kUnimplemented (gRPC code "UNIMPLEMENTED") indicates the operation is not + // implemented or supported in this service. In this case, the operation + // should not be re-attempted. kUnimplemented = 12, + + // StatusCode::kInternal + // + // kInternal (gRPC code "INTERNAL") indicates an internal error has occurred + // and some invariants expected by the underlying system have not been + // satisfied. This error code is reserved for serious errors. kInternal = 13, + + // StatusCode::kUnavailable + // + // kUnavailable (gRPC code "UNAVAILABLE") indicates the service is currently + // unavailable and that this is most likely a transient condition. An error + // such as this can be corrected by retrying with a backoff scheme. Note that + // it is not always safe to retry non-idempotent operations. + // + // See the guidelines above for deciding between `kFailedPrecondition`, + // `kAborted`, and `kUnavailable`. kUnavailable = 14, + + // StatusCode::kDataLoss + // + // kDataLoss (gRPC code "DATA_LOSS") indicates that unrecoverable data loss or + // corruption has occurred. As this error is serious, proper alerting should + // be attached to errors such as this. kDataLoss = 15, + + // StatusCode::kUnauthenticated + // + // kUnauthenticated (gRPC code "UNAUTHENTICATED") indicates that the request + // does not have valid authentication credentials for the operation. Correct + // the authentication and try again. kUnauthenticated = 16, + + // StatusCode::DoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ + // + // NOTE: this error code entry should not be used and you should not rely on + // its value, which may change. + // + // The purpose of this enumerated value is to force people who handle status + // codes with `switch()` statements to *not* simply enumerate all possible + // values, but instead provide a "default:" case. Providing such a default + // case ensures that code will compile when new codes are added. kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20 }; +// StatusCodeToString() +// // Returns the name for the status code, or "" if it is an unknown value. std::string StatusCodeToString(StatusCode code); +// operator<< +// // Streams StatusCodeToString(code) to `os`. std::ostream& operator<<(std::ostream& os, StatusCode code); -namespace status_internal { - -// Container for status payloads. -struct Payload { - std::string type_url; - absl::Cord payload; -}; - -using Payloads = absl::InlinedVector<Payload, 1>; - -// Reference-counted representation of Status data. -struct StatusRep { - std::atomic<int32_t> ref; - absl::StatusCode code; - std::string message; - std::unique_ptr<status_internal::Payloads> payloads; +// absl::StatusToStringMode +// +// An `absl::StatusToStringMode` is an enumerated type indicating how +// `absl::Status::ToString()` should construct the output string for a non-ok +// status. +enum class StatusToStringMode : int { + // ToString will not contain any extra data (such as payloads). It will only + // contain the error code and message, if any. + kWithNoExtraData = 0, + // ToString will contain the payloads. + kWithPayload = 1 << 0, + // ToString will include all the extra data this Status has. + kWithEverything = ~kWithNoExtraData, + // Default mode used by ToString. Its exact value might change in the future. + kDefault = kWithPayload, }; -absl::StatusCode MapToLocalCode(int value); -} // namespace status_internal +// absl::StatusToStringMode is specified as a bitmask type, which means the +// following operations must be provided: +inline constexpr StatusToStringMode operator&(StatusToStringMode lhs, + StatusToStringMode rhs) { + return static_cast<StatusToStringMode>(static_cast<int>(lhs) & + static_cast<int>(rhs)); +} +inline constexpr StatusToStringMode operator|(StatusToStringMode lhs, + StatusToStringMode rhs) { + return static_cast<StatusToStringMode>(static_cast<int>(lhs) | + static_cast<int>(rhs)); +} +inline constexpr StatusToStringMode operator^(StatusToStringMode lhs, + StatusToStringMode rhs) { + return static_cast<StatusToStringMode>(static_cast<int>(lhs) ^ + static_cast<int>(rhs)); +} +inline constexpr StatusToStringMode operator~(StatusToStringMode arg) { + return static_cast<StatusToStringMode>(~static_cast<int>(arg)); +} +inline StatusToStringMode& operator&=(StatusToStringMode& lhs, + StatusToStringMode rhs) { + lhs = lhs & rhs; + return lhs; +} +inline StatusToStringMode& operator|=(StatusToStringMode& lhs, + StatusToStringMode rhs) { + lhs = lhs | rhs; + return lhs; +} +inline StatusToStringMode& operator^=(StatusToStringMode& lhs, + StatusToStringMode rhs) { + lhs = lhs ^ rhs; + return lhs; +} -class ABSL_MUST_USE_RESULT Status final { +// absl::Status +// +// The `absl::Status` class is generally used to gracefully handle errors +// across API boundaries (and in particular across RPC boundaries). Some of +// these errors may be recoverable, but others may not. Most +// functions which can produce a recoverable error should be designed to return +// either an `absl::Status` (or the similar `absl::StatusOr<T>`, which holds +// either an object of type `T` or an error). +// +// API developers should construct their functions to return `absl::OkStatus()` +// upon success, or an `absl::StatusCode` upon another type of error (e.g +// an `absl::StatusCode::kInvalidArgument` error). The API provides convenience +// functions to construct each status code. +// +// Example: +// +// absl::Status myFunction(absl::string_view fname, ...) { +// ... +// // encounter error +// if (error condition) { +// // Construct an absl::StatusCode::kInvalidArgument error +// return absl::InvalidArgumentError("bad mode"); +// } +// // else, return OK +// return absl::OkStatus(); +// } +// +// Users handling status error codes should prefer checking for an OK status +// using the `ok()` member function. Handling multiple error codes may justify +// use of switch statement, but only check for error codes you know how to +// handle; do not try to exhaustively match against all canonical error codes. +// Errors that cannot be handled should be logged and/or propagated for higher +// levels to deal with. If you do use a switch statement, make sure that you +// also provide a `default:` switch case, so that code does not break as other +// canonical codes are added to the API. +// +// Example: +// +// absl::Status result = DoSomething(); +// if (!result.ok()) { +// LOG(ERROR) << result; +// } +// +// // Provide a default if switching on multiple error codes +// switch (result.code()) { +// // The user hasn't authenticated. Ask them to reauth +// case absl::StatusCode::kUnauthenticated: +// DoReAuth(); +// break; +// // The user does not have permission. Log an error. +// case absl::StatusCode::kPermissionDenied: +// LOG(ERROR) << result; +// break; +// // Propagate the error otherwise. +// default: +// return true; +// } +// +// An `absl::Status` can optionally include a payload with more information +// about the error. Typically, this payload serves one of several purposes: +// +// * It may provide more fine-grained semantic information about the error to +// facilitate actionable remedies. +// * It may provide human-readable contexual information that is more +// appropriate to display to an end user. +// +// Example: +// +// absl::Status result = DoSomething(); +// // Inform user to retry after 30 seconds +// // See more error details in googleapis/google/rpc/error_details.proto +// if (absl::IsResourceExhausted(result)) { +// google::rpc::RetryInfo info; +// info.retry_delay().seconds() = 30; +// // Payloads require a unique key (a URL to ensure no collisions with +// // other payloads), and an `absl::Cord` to hold the encoded data. +// absl::string_view url = "type.googleapis.com/google.rpc.RetryInfo"; +// result.SetPayload(url, info.SerializeAsCord()); +// return result; +// } +// +// For documentation see https://abseil.io/docs/cpp/guides/status. +// +// Returned Status objects may not be ignored. status_internal.h has a forward +// declaration of the form +// class ABSL_MUST_USE_RESULT Status; +class Status final { public: - // Creates an OK status with no message or payload. + // Constructors + + // This default constructor creates an OK status with no message or payload. + // Avoid this constructor and prefer explicit construction of an OK status + // with `absl::OkStatus()`. Status(); - // Create a status in the canonical error space with the specified code and - // error message. If `code == util::error::OK`, `msg` is ignored and an - // object identical to an OK status is constructed. + // Creates a status in the canonical error space with the specified + // `absl::StatusCode` and error message. If `code == absl::StatusCode::kOk`, // NOLINT + // `msg` is ignored and an object identical to an OK status is constructed. // - // `msg` must be in UTF-8. The implementation may complain (e.g., + // The `msg` string must be in UTF-8. The implementation may complain (e.g., // NOLINT // by printing a warning) if it is not. Status(absl::StatusCode code, absl::string_view msg); Status(const Status&); Status& operator=(const Status& x); - // Move operations. + // Move operators + // The moved-from state is valid but unspecified. Status(Status&&) noexcept; Status& operator=(Status&&); ~Status(); - // If `this->ok()`, stores `new_status` into *this. If `!this->ok()`, - // preserves the current data. May, in the future, augment the current status - // with additional information about `new_status`. + // Status::Update() + // + // Updates the existing status with `new_status` provided that `this->ok()`. + // If the existing status already contains a non-OK error, this update has no + // effect and preserves the current data. Note that this behavior may change + // in the future to augment a current non-ok status with additional + // information about `new_status`. // - // Convenient way of keeping track of the first error encountered. - // Instead of: - // if (overall_status.ok()) overall_status = new_status - // Use: + // `Update()` provides a convenient way of keeping track of the first error + // encountered. + // + // Example: + // // Instead of "if (overall_status.ok()) overall_status = new_status" // overall_status.Update(new_status); // - // Style guide exception for rvalue reference granted in CL 153567220. void Update(const Status& new_status); void Update(Status&& new_status); - // Returns true if the Status is OK. + // Status::ok() + // + // Returns `true` if `this->ok()`. Prefer checking for an OK status using this + // member function. ABSL_MUST_USE_RESULT bool ok() const; - // Returns the (canonical) error code. + // Status::code() + // + // Returns the canonical error code of type `absl::StatusCode` of this status. absl::StatusCode code() const; - // Returns the raw (canonical) error code which could be out of the range of - // the local `absl::StatusCode` enum. NOTE: This should only be called when - // converting to wire format. Use `code` for error handling. + // Status::raw_code() + // + // Returns a raw (canonical) error code corresponding to the enum value of + // `google.rpc.Code` definitions within + // https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto. + // These values could be out of the range of canonical `absl::StatusCode` + // enum values. + // + // NOTE: This function should only be called when converting to an associated + // wire format. Use `Status::code()` for error handling. int raw_code() const; - // Returns the error message. Note: prefer ToString() for debug logging. - // This message rarely describes the error code. It is not unusual for the - // error message to be the empty std::string. + // Status::message() + // + // Returns the error message associated with this error code, if available. + // Note that this message rarely describes the error code. It is not unusual + // for the error message to be the empty string. As a result, prefer + // `operator<<` or `Status::ToString()` for debug logging. absl::string_view message() const; friend bool operator==(const Status&, const Status&); friend bool operator!=(const Status&, const Status&); - // Returns a combination of the error code name, the message and the payloads. - // You can expect the code name and the message to be substrings of the - // result, and the payloads to be printed by the registered printer extensions - // if they are recognized. - // WARNING: Do not depend on the exact format of the result of `ToString()` - // which is subject to change. - std::string ToString() const; + // Status::ToString() + // + // Returns a string based on the `mode`. By default, it returns combination of + // the error code name, the message and any associated payload messages. This + // string is designed simply to be human readable and its exact format should + // not be load bearing. Do not depend on the exact format of the result of + // `ToString()` which is subject to change. + // + // The printed code name and the message are generally substrings of the + // result, and the payloads to be printed use the status payload printer + // mechanism (which is internal). + std::string ToString( + StatusToStringMode mode = StatusToStringMode::kDefault) const; + // Status::IgnoreError() + // // Ignores any errors. This method does nothing except potentially suppress // complaints from any tools that are checking that errors are not dropped on // the floor. void IgnoreError() const; - // Swap the contents of `a` with `b` + // swap() + // + // Swap the contents of one status with another. friend void swap(Status& a, Status& b); - // Payload management APIs - - // Type URL should be unique and follow the naming convention below: - // The idea of type URL comes from `google.protobuf.Any` - // (https://developers.google.com/protocol-buffers/docs/proto3#any). The - // type URL should be globally unique and follow the format of URL - // (https://en.wikipedia.org/wiki/URL). The default type URL for a given - // protobuf message type is "type.googleapis.com/packagename.messagename". For - // other custom wire formats, users should define the format of type URL in a - // similar practice so as to minimize the chance of conflict between type - // URLs. Users should make sure that the type URL can be mapped to a concrete - // C++ type if they want to deserialize the payload and read it effectively. + //---------------------------------------------------------------------------- + // Payload Management APIs + //---------------------------------------------------------------------------- - // Gets the payload based for `type_url` key, if it is present. + // A payload may be attached to a status to provide additional context to an + // error that may not be satisifed by an existing `absl::StatusCode`. + // Typically, this payload serves one of several purposes: + // + // * It may provide more fine-grained semantic information about the error + // to facilitate actionable remedies. + // * It may provide human-readable contexual information that is more + // appropriate to display to an end user. + // + // A payload consists of a [key,value] pair, where the key is a string + // referring to a unique "type URL" and the value is an object of type + // `absl::Cord` to hold the contextual data. + // + // The "type URL" should be unique and follow the format of a URL + // (https://en.wikipedia.org/wiki/URL) and, ideally, provide some + // documentation or schema on how to interpret its associated data. For + // example, the default type URL for a protobuf message type is + // "type.googleapis.com/packagename.messagename". Other custom wire formats + // should define the format of type URL in a similar practice so as to + // minimize the chance of conflict between type URLs. + // Users should ensure that the type URL can be mapped to a concrete + // C++ type if they want to deserialize the payload and read it effectively. + // + // To attach a payload to a status object, call `Status::SetPayload()`, + // passing it the type URL and an `absl::Cord` of associated data. Similarly, + // to extract the payload from a status, call `Status::GetPayload()`. You + // may attach multiple payloads (with differing type URLs) to any given + // status object, provided that the status is currently exhibiting an error + // code (i.e. is not OK). + + // Status::GetPayload() + // + // Gets the payload of a status given its unique `type_url` key, if present. absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const; - // Sets the payload for `type_url` key for a non-ok status, overwriting any - // existing payload for `type_url`. + // Status::SetPayload() // - // NOTE: Does nothing if the Status is ok. + // Sets the payload for a non-ok status using a `type_url` key, overwriting + // any existing payload for that `type_url`. + // + // NOTE: This function does nothing if the Status is ok. void SetPayload(absl::string_view type_url, absl::Cord payload); - // Erases the payload corresponding to the `type_url` key. Returns true if + // Status::ErasePayload() + // + // Erases the payload corresponding to the `type_url` key. Returns `true` if // the payload was present. bool ErasePayload(absl::string_view type_url); - // Iterates over the stored payloads and calls `visitor(type_key, payload)` - // for each one. + // Status::ForEachPayload() + // + // Iterates over the stored payloads and calls the + // `visitor(type_key, payload)` callable for each one. // - // NOTE: The order of calls to `visitor` is not specified and may change at + // NOTE: The order of calls to `visitor()` is not specified and may change at // any time. // - // NOTE: Any mutation on the same 'Status' object during visitation is + // NOTE: Any mutation on the same 'absl::Status' object during visitation is // forbidden and could result in undefined behavior. void ForEachPayload( - const std::function<void(absl::string_view, const absl::Cord&)>& visitor) + absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor) const; private: @@ -201,8 +613,9 @@ class ABSL_MUST_USE_RESULT Status final { status_internal::Payloads* GetPayloads(); // Takes ownership of payload. - static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg, - std::unique_ptr<status_internal::Payloads> payload); + static uintptr_t NewRep( + absl::StatusCode code, absl::string_view msg, + std::unique_ptr<status_internal::Payloads> payload); static bool EqualsSlow(const absl::Status& a, const absl::Status& b); // MSVC 14.0 limitation requires the const. @@ -231,8 +644,7 @@ class ABSL_MUST_USE_RESULT Status final { static uintptr_t PointerToRep(status_internal::StatusRep* r); static status_internal::StatusRep* RepToPointer(uintptr_t r); - // Returns std::string for non-ok Status. - std::string ToStringSlow() const; + std::string ToStringSlow(StatusToStringMode mode) const; // Status supports two different representations. // - When the low bit is off it is an inlined representation. @@ -245,14 +657,93 @@ class ABSL_MUST_USE_RESULT Status final { uintptr_t rep_; }; -// Returns an OK status, equivalent to a default constructed instance. +// OkStatus() +// +// Returns an OK status, equivalent to a default constructed instance. Prefer +// usage of `absl::OkStatus()` when constructing such an OK status. Status OkStatus(); +// operator<<() +// // Prints a human-readable representation of `x` to `os`. std::ostream& operator<<(std::ostream& os, const Status& x); -// ----------------------------------------------------------------- +// IsAborted() +// IsAlreadyExists() +// IsCancelled() +// IsDataLoss() +// IsDeadlineExceeded() +// IsFailedPrecondition() +// IsInternal() +// IsInvalidArgument() +// IsNotFound() +// IsOutOfRange() +// IsPermissionDenied() +// IsResourceExhausted() +// IsUnauthenticated() +// IsUnavailable() +// IsUnimplemented() +// IsUnknown() +// +// These convenience functions return `true` if a given status matches the +// `absl::StatusCode` error code of its associated function. +ABSL_MUST_USE_RESULT bool IsAborted(const Status& status); +ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status); +ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status); +ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status); +ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status); +ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status); +ABSL_MUST_USE_RESULT bool IsInternal(const Status& status); +ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status); +ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status); +ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status); +ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status); +ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status); +ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status); + +// AbortedError() +// AlreadyExistsError() +// CancelledError() +// DataLossError() +// DeadlineExceededError() +// FailedPreconditionError() +// InternalError() +// InvalidArgumentError() +// NotFoundError() +// OutOfRangeError() +// PermissionDeniedError() +// ResourceExhaustedError() +// UnauthenticatedError() +// UnavailableError() +// UnimplementedError() +// UnknownError() +// +// These convenience functions create an `absl::Status` object with an error +// code as indicated by the associated function name, using the error message +// passed in `message`. +Status AbortedError(absl::string_view message); +Status AlreadyExistsError(absl::string_view message); +Status CancelledError(absl::string_view message); +Status DataLossError(absl::string_view message); +Status DeadlineExceededError(absl::string_view message); +Status FailedPreconditionError(absl::string_view message); +Status InternalError(absl::string_view message); +Status InvalidArgumentError(absl::string_view message); +Status NotFoundError(absl::string_view message); +Status OutOfRangeError(absl::string_view message); +Status PermissionDeniedError(absl::string_view message); +Status ResourceExhaustedError(absl::string_view message); +Status UnauthenticatedError(absl::string_view message); +Status UnavailableError(absl::string_view message); +Status UnimplementedError(absl::string_view message); +Status UnknownError(absl::string_view message); + +//------------------------------------------------------------------------------ // Implementation details follow +//------------------------------------------------------------------------------ inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {} @@ -276,9 +767,11 @@ inline Status::Status(Status&& x) noexcept : rep_(x.rep_) { inline Status& Status::operator=(Status&& x) { uintptr_t old_rep = rep_; - rep_ = x.rep_; - x.rep_ = MovedFromRep(); - Unref(old_rep); + if (x.rep_ != old_rep) { + rep_ = x.rep_; + x.rep_ = MovedFromRep(); + Unref(old_rep); + } return *this; } @@ -315,8 +808,8 @@ inline bool operator!=(const Status& lhs, const Status& rhs) { return !(lhs == rhs); } -inline std::string Status::ToString() const { - return ok() ? "OK" : ToStringSlow(); +inline std::string Status::ToString(StatusToStringMode mode) const { + return ok() ? "OK" : ToStringSlow(mode); } inline void Status::IgnoreError() const { @@ -378,50 +871,11 @@ inline void Status::Unref(uintptr_t rep) { inline Status OkStatus() { return Status(); } -// Each of the functions below creates a Status object with a particular error -// code and the given message. The error code of the returned status object -// matches the name of the function. -Status AbortedError(absl::string_view message); -Status AlreadyExistsError(absl::string_view message); -Status CancelledError(absl::string_view message); -Status DataLossError(absl::string_view message); -Status DeadlineExceededError(absl::string_view message); -Status FailedPreconditionError(absl::string_view message); -Status InternalError(absl::string_view message); -Status InvalidArgumentError(absl::string_view message); -Status NotFoundError(absl::string_view message); -Status OutOfRangeError(absl::string_view message); -Status PermissionDeniedError(absl::string_view message); -Status ResourceExhaustedError(absl::string_view message); -Status UnauthenticatedError(absl::string_view message); -Status UnavailableError(absl::string_view message); -Status UnimplementedError(absl::string_view message); -Status UnknownError(absl::string_view message); - // Creates a `Status` object with the `absl::StatusCode::kCancelled` error code // and an empty message. It is provided only for efficiency, given that // message-less kCancelled errors are common in the infrastructure. inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); } -// Each of the functions below returns true if the given status matches the -// error code implied by the function's name. -ABSL_MUST_USE_RESULT bool IsAborted(const Status& status); -ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status); -ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status); -ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status); -ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status); -ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status); -ABSL_MUST_USE_RESULT bool IsInternal(const Status& status); -ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status); -ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status); -ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status); -ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status); -ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status); -ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status); - ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/status/status_payload_printer.cc b/third_party/abseil-cpp/absl/status/status_payload_printer.cc index ad96d76acf..a47aea11c2 100644 --- a/third_party/abseil-cpp/absl/status/status_payload_printer.cc +++ b/third_party/abseil-cpp/absl/status/status_payload_printer.cc @@ -16,26 +16,21 @@ #include <atomic> #include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace status_internal { -namespace { -// Tried constant initialized global variable but it doesn't work with Lexan -// (MSVC's `std::atomic` has trouble constant initializing). -std::atomic<StatusPayloadPrinter>& GetStatusPayloadPrinterStorage() { - ABSL_CONST_INIT static std::atomic<StatusPayloadPrinter> instance{nullptr}; - return instance; -} -} // namespace +ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES +static absl::base_internal::AtomicHook<StatusPayloadPrinter> storage; void SetStatusPayloadPrinter(StatusPayloadPrinter printer) { - GetStatusPayloadPrinterStorage().store(printer, std::memory_order_relaxed); + storage.Store(printer); } StatusPayloadPrinter GetStatusPayloadPrinter() { - return GetStatusPayloadPrinterStorage().load(std::memory_order_relaxed); + return storage.Load(); } } // namespace status_internal diff --git a/third_party/abseil-cpp/absl/status/status_test.cc b/third_party/abseil-cpp/absl/status/status_test.cc index ca9488ad22..1b038f6d98 100644 --- a/third_party/abseil-cpp/absl/status/status_test.cc +++ b/third_party/abseil-cpp/absl/status/status_test.cc @@ -36,7 +36,9 @@ TEST(StatusCode, InsertionOperator) { // its creator, and its classifier. struct ErrorTest { absl::StatusCode code; - using Creator = absl::Status (*)(absl::string_view); + using Creator = absl::Status (*)( + absl::string_view + ); using Classifier = bool (*)(const absl::Status&); Creator creator; Classifier classifier; @@ -78,7 +80,9 @@ TEST(Status, CreateAndClassify) { // expected error code and message. std::string message = absl::StrCat("error code ", test.code, " test message"); - absl::Status status = test.creator(message); + absl::Status status = test.creator( + message + ); EXPECT_EQ(test.code, status.code()); EXPECT_EQ(message, status.message()); @@ -280,6 +284,27 @@ TEST(Status, ToString) { HasSubstr("[bar='\\xff']"))); } +TEST(Status, ToStringMode) { + absl::Status s(absl::StatusCode::kInternal, "fail"); + s.SetPayload("foo", absl::Cord("bar")); + s.SetPayload("bar", absl::Cord("\377")); + + EXPECT_EQ("INTERNAL: fail", + s.ToString(absl::StatusToStringMode::kWithNoExtraData)); + + EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithPayload), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("[bar='\\xff']"))); + + EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithEverything), + AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), + HasSubstr("[bar='\\xff']"))); + + EXPECT_THAT(s.ToString(~absl::StatusToStringMode::kWithPayload), + AllOf(HasSubstr("INTERNAL: fail"), Not(HasSubstr("[foo='bar']")), + Not(HasSubstr("[bar='\\xff']")))); +} + absl::Status EraseAndReturn(const absl::Status& base) { absl::Status copy = base; EXPECT_TRUE(copy.ErasePayload(kUrl1)); @@ -397,6 +422,12 @@ TEST(Status, MoveAssignment) { assignee = std::move(status); EXPECT_EQ(assignee, copy); } + { + absl::Status status(absl::StatusCode::kInvalidArgument, "message"); + absl::Status copy(status); + status = static_cast<absl::Status&&>(status); + EXPECT_EQ(status, copy); + } } TEST(Status, Update) { @@ -454,5 +485,4 @@ TEST(Status, Swap) { test_swap(no_payload, with_payload); test_swap(with_payload, no_payload); } - } // namespace diff --git a/third_party/abseil-cpp/absl/status/statusor.cc b/third_party/abseil-cpp/absl/status/statusor.cc new file mode 100644 index 0000000000..96642b340f --- /dev/null +++ b/third_party/abseil-cpp/absl/status/statusor.cc @@ -0,0 +1,103 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/status/statusor.h" + +#include <cstdlib> +#include <utility> + +#include "absl/base/call_once.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +BadStatusOrAccess::BadStatusOrAccess(absl::Status status) + : status_(std::move(status)) {} + +BadStatusOrAccess::BadStatusOrAccess(const BadStatusOrAccess& other) + : status_(other.status_) {} + +BadStatusOrAccess& BadStatusOrAccess::operator=( + const BadStatusOrAccess& other) { + // Ensure assignment is correct regardless of whether this->InitWhat() has + // already been called. + other.InitWhat(); + status_ = other.status_; + what_ = other.what_; + return *this; +} + +BadStatusOrAccess& BadStatusOrAccess::operator=(BadStatusOrAccess&& other) { + // Ensure assignment is correct regardless of whether this->InitWhat() has + // already been called. + other.InitWhat(); + status_ = std::move(other.status_); + what_ = std::move(other.what_); + return *this; +} + +BadStatusOrAccess::BadStatusOrAccess(BadStatusOrAccess&& other) + : status_(std::move(other.status_)) {} + +const char* BadStatusOrAccess::what() const noexcept { + InitWhat(); + return what_.c_str(); +} + +const absl::Status& BadStatusOrAccess::status() const { return status_; } + +void BadStatusOrAccess::InitWhat() const { + absl::call_once(init_what_, [this] { + what_ = absl::StrCat("Bad StatusOr access: ", status_.ToString()); + }); +} + +namespace internal_statusor { + +void Helper::HandleInvalidStatusCtorArg(absl::Status* status) { + const char* kMessage = + "An OK status is not a valid constructor argument to StatusOr<T>"; +#ifdef NDEBUG + ABSL_INTERNAL_LOG(ERROR, kMessage); +#else + ABSL_INTERNAL_LOG(FATAL, kMessage); +#endif + // In optimized builds, we will fall back to InternalError. + *status = absl::InternalError(kMessage); +} + +void Helper::Crash(const absl::Status& status) { + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Attempting to fetch value instead of handling error ", + status.ToString())); +} + +void ThrowBadStatusOrAccess(absl::Status status) { +#ifdef ABSL_HAVE_EXCEPTIONS + throw absl::BadStatusOrAccess(std::move(status)); +#else + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Attempting to fetch value instead of handling error ", + status.ToString())); + std::abort(); +#endif +} + +} // namespace internal_statusor +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/status/statusor.h b/third_party/abseil-cpp/absl/status/statusor.h new file mode 100644 index 0000000000..c051fbb3aa --- /dev/null +++ b/third_party/abseil-cpp/absl/status/statusor.h @@ -0,0 +1,770 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// File: statusor.h +// ----------------------------------------------------------------------------- +// +// An `absl::StatusOr<T>` represents a union of an `absl::Status` object +// and an object of type `T`. The `absl::StatusOr<T>` will either contain an +// object of type `T` (indicating a successful operation), or an error (of type +// `absl::Status`) explaining why such a value is not present. +// +// In general, check the success of an operation returning an +// `absl::StatusOr<T>` like you would an `absl::Status` by using the `ok()` +// member function. +// +// Example: +// +// StatusOr<Foo> result = Calculation(); +// if (result.ok()) { +// result->DoSomethingCool(); +// } else { +// LOG(ERROR) << result.status(); +// } +#ifndef ABSL_STATUS_STATUSOR_H_ +#define ABSL_STATUS_STATUSOR_H_ + +#include <exception> +#include <initializer_list> +#include <new> +#include <string> +#include <type_traits> +#include <utility> + +#include "absl/base/attributes.h" +#include "absl/base/call_once.h" +#include "absl/meta/type_traits.h" +#include "absl/status/internal/statusor_internal.h" +#include "absl/status/status.h" +#include "absl/types/variant.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// BadStatusOrAccess +// +// This class defines the type of object to throw (if exceptions are enabled), +// when accessing the value of an `absl::StatusOr<T>` object that does not +// contain a value. This behavior is analogous to that of +// `std::bad_optional_access` in the case of accessing an invalid +// `std::optional` value. +// +// Example: +// +// try { +// absl::StatusOr<int> v = FetchInt(); +// DoWork(v.value()); // Accessing value() when not "OK" may throw +// } catch (absl::BadStatusOrAccess& ex) { +// LOG(ERROR) << ex.status(); +// } +class BadStatusOrAccess : public std::exception { + public: + explicit BadStatusOrAccess(absl::Status status); + ~BadStatusOrAccess() override = default; + + BadStatusOrAccess(const BadStatusOrAccess& other); + BadStatusOrAccess& operator=(const BadStatusOrAccess& other); + BadStatusOrAccess(BadStatusOrAccess&& other); + BadStatusOrAccess& operator=(BadStatusOrAccess&& other); + + // BadStatusOrAccess::what() + // + // Returns the associated explanatory string of the `absl::StatusOr<T>` + // object's error code. This function contains information about the failing + // status, but its exact formatting may change and should not be depended on. + // + // The pointer of this string is guaranteed to be valid until any non-const + // function is invoked on the exception object. + const char* what() const noexcept override; + + // BadStatusOrAccess::status() + // + // Returns the associated `absl::Status` of the `absl::StatusOr<T>` object's + // error. + const absl::Status& status() const; + + private: + void InitWhat() const; + + absl::Status status_; + mutable absl::once_flag init_what_; + mutable std::string what_; +}; + +// Returned StatusOr objects may not be ignored. +template <typename T> +class ABSL_MUST_USE_RESULT StatusOr; + +// absl::StatusOr<T> +// +// The `absl::StatusOr<T>` class template is a union of an `absl::Status` object +// and an object of type `T`. The `absl::StatusOr<T>` models an object that is +// either a usable object, or an error (of type `absl::Status`) explaining why +// such an object is not present. An `absl::StatusOr<T>` is typically the return +// value of a function which may fail. +// +// An `absl::StatusOr<T>` can never hold an "OK" status (an +// `absl::StatusCode::kOk` value); instead, the presence of an object of type +// `T` indicates success. Instead of checking for a `kOk` value, use the +// `absl::StatusOr<T>::ok()` member function. (It is for this reason, and code +// readability, that using the `ok()` function is preferred for `absl::Status` +// as well.) +// +// Example: +// +// StatusOr<Foo> result = DoBigCalculationThatCouldFail(); +// if (result.ok()) { +// result->DoSomethingCool(); +// } else { +// LOG(ERROR) << result.status(); +// } +// +// Accessing the object held by an `absl::StatusOr<T>` should be performed via +// `operator*` or `operator->`, after a call to `ok()` confirms that the +// `absl::StatusOr<T>` holds an object of type `T`: +// +// Example: +// +// absl::StatusOr<int> i = GetCount(); +// if (i.ok()) { +// updated_total += *i +// } +// +// NOTE: using `absl::StatusOr<T>::value()` when no valid value is present will +// throw an exception if exceptions are enabled or terminate the process when +// exceptions are not enabled. +// +// Example: +// +// StatusOr<Foo> result = DoBigCalculationThatCouldFail(); +// const Foo& foo = result.value(); // Crash/exception if no value present +// foo.DoSomethingCool(); +// +// A `absl::StatusOr<T*>` can be constructed from a null pointer like any other +// pointer value, and the result will be that `ok()` returns `true` and +// `value()` returns `nullptr`. Checking the value of pointer in an +// `absl::StatusOr<T>` generally requires a bit more care, to ensure both that a +// value is present and that value is not null: +// +// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg); +// if (!result.ok()) { +// LOG(ERROR) << result.status(); +// } else if (*result == nullptr) { +// LOG(ERROR) << "Unexpected null pointer"; +// } else { +// (*result)->DoSomethingCool(); +// } +// +// Example factory implementation returning StatusOr<T>: +// +// StatusOr<Foo> FooFactory::MakeFoo(int arg) { +// if (arg <= 0) { +// return absl::Status(absl::StatusCode::kInvalidArgument, +// "Arg must be positive"); +// } +// return Foo(arg); +// } +template <typename T> +class StatusOr : private internal_statusor::StatusOrData<T>, + private internal_statusor::CopyCtorBase<T>, + private internal_statusor::MoveCtorBase<T>, + private internal_statusor::CopyAssignBase<T>, + private internal_statusor::MoveAssignBase<T> { + template <typename U> + friend class StatusOr; + + typedef internal_statusor::StatusOrData<T> Base; + + public: + // StatusOr<T>::value_type + // + // This instance data provides a generic `value_type` member for use within + // generic programming. This usage is analogous to that of + // `optional::value_type` in the case of `std::optional`. + typedef T value_type; + + // Constructors + + // Constructs a new `absl::StatusOr` with an `absl::StatusCode::kUnknown` + // status. This constructor is marked 'explicit' to prevent usages in return + // values such as 'return {};', under the misconception that + // `absl::StatusOr<std::vector<int>>` will be initialized with an empty + // vector, instead of an `absl::StatusCode::kUnknown` error code. + explicit StatusOr(); + + // `StatusOr<T>` is copy constructible if `T` is copy constructible. + StatusOr(const StatusOr&) = default; + // `StatusOr<T>` is copy assignable if `T` is copy constructible and copy + // assignable. + StatusOr& operator=(const StatusOr&) = default; + + // `StatusOr<T>` is move constructible if `T` is move constructible. + StatusOr(StatusOr&&) = default; + // `StatusOr<T>` is moveAssignable if `T` is move constructible and move + // assignable. + StatusOr& operator=(StatusOr&&) = default; + + // Converting Constructors + + // Constructs a new `absl::StatusOr<T>` from an `absl::StatusOr<U>`, when `T` + // is constructible from `U`. To avoid ambiguity, these constructors are + // disabled if `T` is also constructible from `StatusOr<U>.`. This constructor + // is explicit if and only if the corresponding construction of `T` from `U` + // is explicit. (This constructor inherits its explicitness from the + // underlying constructor.) + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, + std::is_constructible<T, const U&>, + std::is_convertible<const U&, T>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(const StatusOr<U>& other) // NOLINT + : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, + std::is_constructible<T, const U&>, + absl::negation<std::is_convertible<const U&, T>>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(const StatusOr<U>& other) + : Base(static_cast<const typename StatusOr<U>::Base&>(other)) {} + + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, + std::is_convertible<U&&, T>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr(StatusOr<U>&& other) // NOLINT + : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, + absl::negation<std::is_convertible<U&&, T>>, + absl::negation< + internal_statusor::IsConstructibleOrConvertibleFromStatusOr< + T, U>>>::value, + int> = 0> + explicit StatusOr(StatusOr<U>&& other) + : Base(static_cast<typename StatusOr<U>::Base&&>(other)) {} + + // Converting Assignment Operators + + // Creates an `absl::StatusOr<T>` through assignment from an + // `absl::StatusOr<U>` when: + // + // * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` are OK by assigning + // `U` to `T` directly. + // * `absl::StatusOr<T>` is OK and `absl::StatusOr<U>` contains an error + // code by destroying `absl::StatusOr<T>`'s value and assigning from + // `absl::StatusOr<U>' + // * `absl::StatusOr<T>` contains an error code and `absl::StatusOr<U>` is + // OK by directly initializing `T` from `U`. + // * Both `absl::StatusOr<T>` and `absl::StatusOr<U>` contain an error + // code by assigning the `Status` in `absl::StatusOr<U>` to + // `absl::StatusOr<T>` + // + // These overloads only apply if `absl::StatusOr<T>` is constructible and + // assignable from `absl::StatusOr<U>` and `StatusOr<T>` cannot be directly + // assigned from `StatusOr<U>`. + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, + std::is_constructible<T, const U&>, + std::is_assignable<T, const U&>, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr& operator=(const StatusOr<U>& other) { + this->Assign(other); + return *this; + } + template < + typename U, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, + std::is_assignable<T, U&&>, + absl::negation< + internal_statusor:: + IsConstructibleOrConvertibleOrAssignableFromStatusOr< + T, U>>>::value, + int> = 0> + StatusOr& operator=(StatusOr<U>&& other) { + this->Assign(std::move(other)); + return *this; + } + + // Constructs a new `absl::StatusOr<T>` with a non-ok status. After calling + // this constructor, `this->ok()` will be `false` and calls to `value()` will + // crash, or produce an exception if exceptions are enabled. + // + // The constructor also takes any type `U` that is convertible to + // `absl::Status`. This constructor is explicit if an only if `U` is not of + // type `absl::Status` and the conversion from `U` to `Status` is explicit. + // + // REQUIRES: !Status(std::forward<U>(v)).ok(). This requirement is DCHECKed. + // In optimized builds, passing absl::OkStatus() here will have the effect + // of passing absl::StatusCode::kInternal as a fallback. + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + std::is_convertible<U&&, absl::Status>, + std::is_constructible<absl::Status, U&&>, + absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>, + absl::negation<std::is_same<absl::decay_t<U>, T>>, + absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>, + absl::negation<internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>::value, + int> = 0> + StatusOr(U&& v) : Base(std::forward<U>(v)) {} + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + absl::negation<std::is_convertible<U&&, absl::Status>>, + std::is_constructible<absl::Status, U&&>, + absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>, + absl::negation<std::is_same<absl::decay_t<U>, T>>, + absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>, + absl::negation<internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>::value, + int> = 0> + explicit StatusOr(U&& v) : Base(std::forward<U>(v)) {} + + template < + typename U = absl::Status, + absl::enable_if_t< + absl::conjunction< + std::is_convertible<U&&, absl::Status>, + std::is_constructible<absl::Status, U&&>, + absl::negation<std::is_same<absl::decay_t<U>, absl::StatusOr<T>>>, + absl::negation<std::is_same<absl::decay_t<U>, T>>, + absl::negation<std::is_same<absl::decay_t<U>, absl::in_place_t>>, + absl::negation<internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>::value, + int> = 0> + StatusOr& operator=(U&& v) { + this->AssignStatus(std::forward<U>(v)); + return *this; + } + + // Perfect-forwarding value assignment operator. + + // If `*this` contains a `T` value before the call, the contained value is + // assigned from `std::forward<U>(v)`; Otherwise, it is directly-initialized + // from `std::forward<U>(v)`. + // This function does not participate in overload unless: + // 1. `std::is_constructible_v<T, U>` is true, + // 2. `std::is_assignable_v<T&, U>` is true. + // 3. `std::is_same_v<StatusOr<T>, std::remove_cvref_t<U>>` is false. + // 4. Assigning `U` to `T` is not ambiguous: + // If `U` is `StatusOr<V>` and `T` is constructible and assignable from + // both `StatusOr<V>` and `V`, the assignment is considered bug-prone and + // ambiguous thus will fail to compile. For example: + // StatusOr<bool> s1 = true; // s1.ok() && *s1 == true + // StatusOr<bool> s2 = false; // s2.ok() && *s2 == false + // s1 = s2; // ambiguous, `s1 = *s2` or `s1 = bool(s2)`? + template < + typename U = T, + typename = typename std::enable_if<absl::conjunction< + std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>, + absl::disjunction< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, T>, + absl::conjunction< + absl::negation<std::is_convertible<U&&, absl::Status>>, + absl::negation<internal_statusor:: + HasConversionOperatorToStatusOr<T, U&&>>>>, + internal_statusor::IsForwardingAssignmentValid<T, U&&>>::value>::type> + StatusOr& operator=(U&& v) { + this->Assign(std::forward<U>(v)); + return *this; + } + + // Constructs the inner value `T` in-place using the provided args, using the + // `T(args...)` constructor. + template <typename... Args> + explicit StatusOr(absl::in_place_t, Args&&... args); + template <typename U, typename... Args> + explicit StatusOr(absl::in_place_t, std::initializer_list<U> ilist, + Args&&... args); + + // Constructs the inner value `T` in-place using the provided args, using the + // `T(U)` (direct-initialization) constructor. This constructor is only valid + // if `T` can be constructed from a `U`. Can accept move or copy constructors. + // + // This constructor is explicit if `U` is not convertible to `T`. To avoid + // ambiguity, this constructor is disabled if `U` is a `StatusOr<J>`, where + // `J` is convertible to `T`. + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid<T, U&&>, + std::is_constructible<T, U&&>, std::is_convertible<U&&, T>, + absl::disjunction< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + T>, + absl::conjunction< + absl::negation<std::is_convertible<U&&, absl::Status>>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>>>::value, + int> = 0> + StatusOr(U&& u) // NOLINT + : StatusOr(absl::in_place, std::forward<U>(u)) {} + + template < + typename U = T, + absl::enable_if_t< + absl::conjunction< + internal_statusor::IsDirectInitializationValid<T, U&&>, + absl::disjunction< + std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, + T>, + absl::conjunction< + absl::negation<std::is_constructible<absl::Status, U&&>>, + absl::negation< + internal_statusor::HasConversionOperatorToStatusOr< + T, U&&>>>>, + std::is_constructible<T, U&&>, + absl::negation<std::is_convertible<U&&, T>>>::value, + int> = 0> + explicit StatusOr(U&& u) // NOLINT + : StatusOr(absl::in_place, std::forward<U>(u)) {} + + // StatusOr<T>::ok() + // + // Returns whether or not this `absl::StatusOr<T>` holds a `T` value. This + // member function is analagous to `absl::Status::ok()` and should be used + // similarly to check the status of return values. + // + // Example: + // + // StatusOr<Foo> result = DoBigCalculationThatCouldFail(); + // if (result.ok()) { + // // Handle result + // else { + // // Handle error + // } + ABSL_MUST_USE_RESULT bool ok() const { return this->status_.ok(); } + + // StatusOr<T>::status() + // + // Returns a reference to the current `absl::Status` contained within the + // `absl::StatusOr<T>`. If `absl::StatusOr<T>` contains a `T`, then this + // function returns `absl::OkStatus()`. + const Status& status() const&; + Status status() &&; + + // StatusOr<T>::value() + // + // Returns a reference to the held value if `this->ok()`. Otherwise, throws + // `absl::BadStatusOrAccess` if exceptions are enabled, or is guaranteed to + // terminate the process if exceptions are disabled. + // + // If you have already checked the status using `this->ok()`, you probably + // want to use `operator*()` or `operator->()` to access the value instead of + // `value`. + // + // Note: for value types that are cheap to copy, prefer simple code: + // + // T value = statusor.value(); + // + // Otherwise, if the value type is expensive to copy, but can be left + // in the StatusOr, simply assign to a reference: + // + // T& value = statusor.value(); // or `const T&` + // + // Otherwise, if the value type supports an efficient move, it can be + // used as follows: + // + // T value = std::move(statusor).value(); + // + // The `std::move` on statusor instead of on the whole expression enables + // warnings about possible uses of the statusor object after the move. + const T& value() const& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T& value() & ABSL_ATTRIBUTE_LIFETIME_BOUND; + const T&& value() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T&& value() && ABSL_ATTRIBUTE_LIFETIME_BOUND; + + // StatusOr<T>:: operator*() + // + // Returns a reference to the current value. + // + // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined. + // + // Use `this->ok()` to verify that there is a current value within the + // `absl::StatusOr<T>`. Alternatively, see the `value()` member function for a + // similar API that guarantees crashing or throwing an exception if there is + // no current value. + const T& operator*() const& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T& operator*() & ABSL_ATTRIBUTE_LIFETIME_BOUND; + const T&& operator*() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND; + T&& operator*() && ABSL_ATTRIBUTE_LIFETIME_BOUND; + + // StatusOr<T>::operator->() + // + // Returns a pointer to the current value. + // + // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined. + // + // Use `this->ok()` to verify that there is a current value. + const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND; + T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND; + + // StatusOr<T>::value_or() + // + // Returns the current value if `this->ok() == true`. Otherwise constructs a + // value using the provided `default_value`. + // + // Unlike `value`, this function returns by value, copying the current value + // if necessary. If the value type supports an efficient move, it can be used + // as follows: + // + // T value = std::move(statusor).value_or(def); + // + // Unlike with `value`, calling `std::move()` on the result of `value_or` will + // still trigger a copy. + template <typename U> + T value_or(U&& default_value) const&; + template <typename U> + T value_or(U&& default_value) &&; + + // StatusOr<T>::IgnoreError() + // + // Ignores any errors. This method does nothing except potentially suppress + // complaints from any tools that are checking that errors are not dropped on + // the floor. + void IgnoreError() const; + + // StatusOr<T>::emplace() + // + // Reconstructs the inner value T in-place using the provided args, using the + // T(args...) constructor. Returns reference to the reconstructed `T`. + template <typename... Args> + T& emplace(Args&&... args) { + if (ok()) { + this->Clear(); + this->MakeValue(std::forward<Args>(args)...); + } else { + this->MakeValue(std::forward<Args>(args)...); + this->status_ = absl::OkStatus(); + } + return this->data_; + } + + template < + typename U, typename... Args, + absl::enable_if_t< + std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, + int> = 0> + T& emplace(std::initializer_list<U> ilist, Args&&... args) { + if (ok()) { + this->Clear(); + this->MakeValue(ilist, std::forward<Args>(args)...); + } else { + this->MakeValue(ilist, std::forward<Args>(args)...); + this->status_ = absl::OkStatus(); + } + return this->data_; + } + + private: + using internal_statusor::StatusOrData<T>::Assign; + template <typename U> + void Assign(const absl::StatusOr<U>& other); + template <typename U> + void Assign(absl::StatusOr<U>&& other); +}; + +// operator==() +// +// This operator checks the equality of two `absl::StatusOr<T>` objects. +template <typename T> +bool operator==(const StatusOr<T>& lhs, const StatusOr<T>& rhs) { + if (lhs.ok() && rhs.ok()) return *lhs == *rhs; + return lhs.status() == rhs.status(); +} + +// operator!=() +// +// This operator checks the inequality of two `absl::StatusOr<T>` objects. +template <typename T> +bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) { + return !(lhs == rhs); +} + +//------------------------------------------------------------------------------ +// Implementation details for StatusOr<T> +//------------------------------------------------------------------------------ + +// TODO(sbenza): avoid the string here completely. +template <typename T> +StatusOr<T>::StatusOr() : Base(Status(absl::StatusCode::kUnknown, "")) {} + +template <typename T> +template <typename U> +inline void StatusOr<T>::Assign(const StatusOr<U>& other) { + if (other.ok()) { + this->Assign(*other); + } else { + this->AssignStatus(other.status()); + } +} + +template <typename T> +template <typename U> +inline void StatusOr<T>::Assign(StatusOr<U>&& other) { + if (other.ok()) { + this->Assign(*std::move(other)); + } else { + this->AssignStatus(std::move(other).status()); + } +} +template <typename T> +template <typename... Args> +StatusOr<T>::StatusOr(absl::in_place_t, Args&&... args) + : Base(absl::in_place, std::forward<Args>(args)...) {} + +template <typename T> +template <typename U, typename... Args> +StatusOr<T>::StatusOr(absl::in_place_t, std::initializer_list<U> ilist, + Args&&... args) + : Base(absl::in_place, ilist, std::forward<Args>(args)...) {} + +template <typename T> +const Status& StatusOr<T>::status() const& { + return this->status_; +} +template <typename T> +Status StatusOr<T>::status() && { + return ok() ? OkStatus() : std::move(this->status_); +} + +template <typename T> +const T& StatusOr<T>::value() const& { + if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_); + return this->data_; +} + +template <typename T> +T& StatusOr<T>::value() & { + if (!this->ok()) internal_statusor::ThrowBadStatusOrAccess(this->status_); + return this->data_; +} + +template <typename T> +const T&& StatusOr<T>::value() const&& { + if (!this->ok()) { + internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_)); + } + return std::move(this->data_); +} + +template <typename T> +T&& StatusOr<T>::value() && { + if (!this->ok()) { + internal_statusor::ThrowBadStatusOrAccess(std::move(this->status_)); + } + return std::move(this->data_); +} + +template <typename T> +const T& StatusOr<T>::operator*() const& { + this->EnsureOk(); + return this->data_; +} + +template <typename T> +T& StatusOr<T>::operator*() & { + this->EnsureOk(); + return this->data_; +} + +template <typename T> +const T&& StatusOr<T>::operator*() const&& { + this->EnsureOk(); + return std::move(this->data_); +} + +template <typename T> +T&& StatusOr<T>::operator*() && { + this->EnsureOk(); + return std::move(this->data_); +} + +template <typename T> +const T* StatusOr<T>::operator->() const { + this->EnsureOk(); + return &this->data_; +} + +template <typename T> +T* StatusOr<T>::operator->() { + this->EnsureOk(); + return &this->data_; +} + +template <typename T> +template <typename U> +T StatusOr<T>::value_or(U&& default_value) const& { + if (ok()) { + return this->data_; + } + return std::forward<U>(default_value); +} + +template <typename T> +template <typename U> +T StatusOr<T>::value_or(U&& default_value) && { + if (ok()) { + return std::move(this->data_); + } + return std::forward<U>(default_value); +} + +template <typename T> +void StatusOr<T>::IgnoreError() const { + // no-op +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STATUS_STATUSOR_H_ diff --git a/third_party/abseil-cpp/absl/status/statusor_test.cc b/third_party/abseil-cpp/absl/status/statusor_test.cc new file mode 100644 index 0000000000..7cae90e185 --- /dev/null +++ b/third_party/abseil-cpp/absl/status/statusor_test.cc @@ -0,0 +1,1847 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/status/statusor.h" + +#include <array> +#include <initializer_list> +#include <memory> +#include <string> +#include <type_traits> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/casts.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "absl/types/any.h" +#include "absl/utility/utility.h" + +namespace { + +using ::testing::AllOf; +using ::testing::AnyWith; +using ::testing::ElementsAre; +using ::testing::Field; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::Not; +using ::testing::Pointee; +using ::testing::VariantWith; + +#ifdef GTEST_HAS_STATUS_MATCHERS +using ::testing::status::IsOk; +using ::testing::status::IsOkAndHolds; +#else // GTEST_HAS_STATUS_MATCHERS +inline const ::absl::Status& GetStatus(const ::absl::Status& status) { + return status; +} + +template <typename T> +inline const ::absl::Status& GetStatus(const ::absl::StatusOr<T>& status) { + return status.status(); +} + +// Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a +// reference to StatusOr<T>. +template <typename StatusOrType> +class IsOkAndHoldsMatcherImpl + : public ::testing::MatcherInterface<StatusOrType> { + public: + typedef + typename std::remove_reference<StatusOrType>::type::value_type value_type; + + template <typename InnerMatcher> + explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) + : inner_matcher_(::testing::SafeMatcherCast<const value_type&>( + std::forward<InnerMatcher>(inner_matcher))) {} + + void DescribeTo(std::ostream* os) const override { + *os << "is OK and has a value that "; + inner_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const override { + *os << "isn't OK or has a value that "; + inner_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain( + StatusOrType actual_value, + ::testing::MatchResultListener* result_listener) const override { + if (!actual_value.ok()) { + *result_listener << "which has status " << actual_value.status(); + return false; + } + + ::testing::StringMatchResultListener inner_listener; + const bool matches = + inner_matcher_.MatchAndExplain(*actual_value, &inner_listener); + const std::string inner_explanation = inner_listener.str(); + if (!inner_explanation.empty()) { + *result_listener << "which contains value " + << ::testing::PrintToString(*actual_value) << ", " + << inner_explanation; + } + return matches; + } + + private: + const ::testing::Matcher<const value_type&> inner_matcher_; +}; + +// Implements IsOkAndHolds(m) as a polymorphic matcher. +template <typename InnerMatcher> +class IsOkAndHoldsMatcher { + public: + explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher) + : inner_matcher_(std::move(inner_matcher)) {} + + // Converts this polymorphic matcher to a monomorphic matcher of the + // given type. StatusOrType can be either StatusOr<T> or a + // reference to StatusOr<T>. + template <typename StatusOrType> + operator ::testing::Matcher<StatusOrType>() const { // NOLINT + return ::testing::Matcher<StatusOrType>( + new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_)); + } + + private: + const InnerMatcher inner_matcher_; +}; + +// Monomorphic implementation of matcher IsOk() for a given type T. +// T can be Status, StatusOr<>, or a reference to either of them. +template <typename T> +class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> { + public: + void DescribeTo(std::ostream* os) const override { *os << "is OK"; } + void DescribeNegationTo(std::ostream* os) const override { + *os << "is not OK"; + } + bool MatchAndExplain(T actual_value, + ::testing::MatchResultListener*) const override { + return GetStatus(actual_value).ok(); + } +}; + +// Implements IsOk() as a polymorphic matcher. +class IsOkMatcher { + public: + template <typename T> + operator ::testing::Matcher<T>() const { // NOLINT + return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<T>()); + } +}; + +// Macros for testing the results of functions that return absl::Status or +// absl::StatusOr<T> (for any type T). +#define EXPECT_OK(expression) EXPECT_THAT(expression, IsOk()) + +// Returns a gMock matcher that matches a StatusOr<> whose status is +// OK and whose value matches the inner matcher. +template <typename InnerMatcher> +IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> IsOkAndHolds( + InnerMatcher&& inner_matcher) { + return IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>( + std::forward<InnerMatcher>(inner_matcher)); +} + +// Returns a gMock matcher that matches a Status or StatusOr<> which is OK. +inline IsOkMatcher IsOk() { return IsOkMatcher(); } +#endif // GTEST_HAS_STATUS_MATCHERS + +struct CopyDetector { + CopyDetector() = default; + explicit CopyDetector(int xx) : x(xx) {} + CopyDetector(CopyDetector&& d) noexcept + : x(d.x), copied(false), moved(true) {} + CopyDetector(const CopyDetector& d) : x(d.x), copied(true), moved(false) {} + CopyDetector& operator=(const CopyDetector& c) { + x = c.x; + copied = true; + moved = false; + return *this; + } + CopyDetector& operator=(CopyDetector&& c) noexcept { + x = c.x; + copied = false; + moved = true; + return *this; + } + int x = 0; + bool copied = false; + bool moved = false; +}; + +testing::Matcher<const CopyDetector&> CopyDetectorHas(int a, bool b, bool c) { + return AllOf(Field(&CopyDetector::x, a), Field(&CopyDetector::moved, b), + Field(&CopyDetector::copied, c)); +} + +class Base1 { + public: + virtual ~Base1() {} + int pad; +}; + +class Base2 { + public: + virtual ~Base2() {} + int yetotherpad; +}; + +class Derived : public Base1, public Base2 { + public: + virtual ~Derived() {} + int evenmorepad; +}; + +class CopyNoAssign { + public: + explicit CopyNoAssign(int value) : foo(value) {} + CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {} + int foo; + + private: + const CopyNoAssign& operator=(const CopyNoAssign&); +}; + +absl::StatusOr<std::unique_ptr<int>> ReturnUniquePtr() { + // Uses implicit constructor from T&& + return absl::make_unique<int>(0); +} + +TEST(StatusOr, ElementType) { + static_assert(std::is_same<absl::StatusOr<int>::value_type, int>(), ""); + static_assert(std::is_same<absl::StatusOr<char>::value_type, char>(), ""); +} + +TEST(StatusOr, TestMoveOnlyInitialization) { + absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); + ASSERT_TRUE(thing.ok()); + EXPECT_EQ(0, **thing); + int* previous = thing->get(); + + thing = ReturnUniquePtr(); + EXPECT_TRUE(thing.ok()); + EXPECT_EQ(0, **thing); + EXPECT_NE(previous, thing->get()); +} + +TEST(StatusOr, TestMoveOnlyValueExtraction) { + absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); + ASSERT_TRUE(thing.ok()); + std::unique_ptr<int> ptr = *std::move(thing); + EXPECT_EQ(0, *ptr); + + thing = std::move(ptr); + ptr = std::move(*thing); + EXPECT_EQ(0, *ptr); +} + +TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) { + std::unique_ptr<int> ptr(*ReturnUniquePtr()); + EXPECT_EQ(0, *ptr); +} + +TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) { + static_assert( + std::is_same< + const int&&, + decltype(std::declval<const absl::StatusOr<int>&&>().value())>(), + "value() for const temporaries should return const T&&"); +} + +TEST(StatusOr, TestMoveOnlyConversion) { + absl::StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr()); + EXPECT_TRUE(const_thing.ok()); + EXPECT_EQ(0, **const_thing); + + // Test rvalue converting assignment + const int* const_previous = const_thing->get(); + const_thing = ReturnUniquePtr(); + EXPECT_TRUE(const_thing.ok()); + EXPECT_EQ(0, **const_thing); + EXPECT_NE(const_previous, const_thing->get()); +} + +TEST(StatusOr, TestMoveOnlyVector) { + // Sanity check that absl::StatusOr<MoveOnly> works in vector. + std::vector<absl::StatusOr<std::unique_ptr<int>>> vec; + vec.push_back(ReturnUniquePtr()); + vec.resize(2); + auto another_vec = std::move(vec); + EXPECT_EQ(0, **another_vec[0]); + EXPECT_EQ(absl::UnknownError(""), another_vec[1].status()); +} + +TEST(StatusOr, TestDefaultCtor) { + absl::StatusOr<int> thing; + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); +} + +TEST(StatusOr, StatusCtorForwards) { + absl::Status status(absl::StatusCode::kInternal, "Some error"); + + EXPECT_EQ(absl::StatusOr<int>(status).status().message(), "Some error"); + EXPECT_EQ(status.message(), "Some error"); + + EXPECT_EQ(absl::StatusOr<int>(std::move(status)).status().message(), + "Some error"); + EXPECT_NE(status.message(), "Some error"); +} + +TEST(BadStatusOrAccessTest, CopyConstructionWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{e1}; + EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, CopyAssignmentWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{absl::InternalError("other")}; + e2 = e1; + EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, MoveConstructionWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{std::move(e1)}; + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +TEST(BadStatusOrAccessTest, MoveAssignmentWhatOk) { + absl::Status error = + absl::InternalError("some arbitrary message too big for the sso buffer"); + absl::BadStatusOrAccess e1{error}; + absl::BadStatusOrAccess e2{absl::InternalError("other")}; + e2 = std::move(e1); + EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); +} + +// Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`, +// which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether +// exceptions are enabled. +#ifdef ABSL_HAVE_EXCEPTIONS +#define EXPECT_DEATH_OR_THROW(statement, status_) \ + EXPECT_THROW( \ + { \ + try { \ + statement; \ + } catch (const absl::BadStatusOrAccess& e) { \ + EXPECT_EQ(e.status(), status_); \ + EXPECT_THAT(e.what(), HasSubstr(e.status().ToString())); \ + throw; \ + } \ + }, \ + absl::BadStatusOrAccess); +#else // ABSL_HAVE_EXCEPTIONS +#define EXPECT_DEATH_OR_THROW(statement, status) \ + EXPECT_DEATH_IF_SUPPORTED(statement, status.ToString()); +#endif // ABSL_HAVE_EXCEPTIONS + +TEST(StatusOrDeathTest, TestDefaultCtorValue) { + absl::StatusOr<int> thing; + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); + const absl::StatusOr<int> thing2; + EXPECT_DEATH_OR_THROW(thing2.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestValueNotOk) { + absl::StatusOr<int> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +TEST(StatusOrDeathTest, TestValueNotOkConst) { + const absl::StatusOr<int> thing(absl::UnknownError("")); + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) { + absl::StatusOr<int*> thing; + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestPointerValueNotOk) { + absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +TEST(StatusOrDeathTest, TestPointerValueNotOkConst) { + const absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +#if GTEST_HAS_DEATH_TEST +TEST(StatusOrDeathTest, TestStatusCtorStatusOk) { + EXPECT_DEBUG_DEATH( + { + // This will DCHECK + absl::StatusOr<int> thing(absl::OkStatus()); + // In optimized mode, we are actually going to get error::INTERNAL for + // status here, rather than crashing, so check that. + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); + }, + "An OK status is not a valid constructor argument"); +} + +TEST(StatusOrDeathTest, TestPointerStatusCtorStatusOk) { + EXPECT_DEBUG_DEATH( + { + absl::StatusOr<int*> thing(absl::OkStatus()); + // In optimized mode, we are actually going to get error::INTERNAL for + // status here, rather than crashing, so check that. + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); + }, + "An OK status is not a valid constructor argument"); +} +#endif + +TEST(StatusOr, ValueAccessor) { + const int kIntValue = 110; + { + absl::StatusOr<int> status_or(kIntValue); + EXPECT_EQ(kIntValue, status_or.value()); + EXPECT_EQ(kIntValue, std::move(status_or).value()); + } + { + absl::StatusOr<CopyDetector> status_or(kIntValue); + EXPECT_THAT(status_or, + IsOkAndHolds(CopyDetectorHas(kIntValue, false, false))); + CopyDetector copy_detector = status_or.value(); + EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, false, true)); + copy_detector = std::move(status_or).value(); + EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, true, false)); + } +} + +TEST(StatusOr, BadValueAccess) { + const absl::Status kError = absl::CancelledError("message"); + absl::StatusOr<int> status_or(kError); + EXPECT_DEATH_OR_THROW(status_or.value(), kError); +} + +TEST(StatusOr, TestStatusCtor) { + absl::StatusOr<int> thing(absl::CancelledError()); + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestValueCtor) { + const int kI = 4; + const absl::StatusOr<int> thing(kI); + EXPECT_TRUE(thing.ok()); + EXPECT_EQ(kI, *thing); +} + +struct Foo { + const int x; + explicit Foo(int y) : x(y) {} +}; + +TEST(StatusOr, InPlaceConstruction) { + EXPECT_THAT(absl::StatusOr<Foo>(absl::in_place, 10), + IsOkAndHolds(Field(&Foo::x, 10))); +} + +struct InPlaceHelper { + InPlaceHelper(std::initializer_list<int> xs, std::unique_ptr<int> yy) + : x(xs), y(std::move(yy)) {} + const std::vector<int> x; + std::unique_ptr<int> y; +}; + +TEST(StatusOr, InPlaceInitListConstruction) { + absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12}, + absl::make_unique<int>(13)); + EXPECT_THAT(status_or, IsOkAndHolds(AllOf( + Field(&InPlaceHelper::x, ElementsAre(10, 11, 12)), + Field(&InPlaceHelper::y, Pointee(13))))); +} + +TEST(StatusOr, Emplace) { + absl::StatusOr<Foo> status_or_foo(10); + status_or_foo.emplace(20); + EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); + status_or_foo = absl::InvalidArgumentError("msg"); + EXPECT_FALSE(status_or_foo.ok()); + EXPECT_EQ(status_or_foo.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status_or_foo.status().message(), "msg"); + status_or_foo.emplace(20); + EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); +} + +TEST(StatusOr, EmplaceInitializerList) { + absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12}, + absl::make_unique<int>(13)); + status_or.emplace({1, 2, 3}, absl::make_unique<int>(4)); + EXPECT_THAT(status_or, + IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), + Field(&InPlaceHelper::y, Pointee(4))))); + status_or = absl::InvalidArgumentError("msg"); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status_or.status().message(), "msg"); + status_or.emplace({1, 2, 3}, absl::make_unique<int>(4)); + EXPECT_THAT(status_or, + IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), + Field(&InPlaceHelper::y, Pointee(4))))); +} + +TEST(StatusOr, TestCopyCtorStatusOk) { + const int kI = 4; + const absl::StatusOr<int> original(kI); + const absl::StatusOr<int> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(*original, *copy); +} + +TEST(StatusOr, TestCopyCtorStatusNotOk) { + absl::StatusOr<int> original(absl::CancelledError()); + absl::StatusOr<int> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestCopyCtorNonAssignable) { + const int kI = 4; + CopyNoAssign value(kI); + absl::StatusOr<CopyNoAssign> original(value); + absl::StatusOr<CopyNoAssign> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(original->foo, copy->foo); +} + +TEST(StatusOr, TestCopyCtorStatusOKConverting) { + const int kI = 4; + absl::StatusOr<int> original(kI); + absl::StatusOr<double> copy(original); + EXPECT_OK(copy.status()); + EXPECT_DOUBLE_EQ(*original, *copy); +} + +TEST(StatusOr, TestCopyCtorStatusNotOkConverting) { + absl::StatusOr<int> original(absl::CancelledError()); + absl::StatusOr<double> copy(original); + EXPECT_EQ(copy.status(), original.status()); +} + +TEST(StatusOr, TestAssignmentStatusOk) { + // Copy assignmment + { + const auto p = std::make_shared<int>(17); + absl::StatusOr<std::shared_ptr<int>> source(p); + + absl::StatusOr<std::shared_ptr<int>> target; + target = source; + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_EQ(p, *target); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_EQ(p, *source); + } + + // Move asssignment + { + const auto p = std::make_shared<int>(17); + absl::StatusOr<std::shared_ptr<int>> source(p); + + absl::StatusOr<std::shared_ptr<int>> target; + target = std::move(source); + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_EQ(p, *target); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_EQ(nullptr, *source); + } +} + +TEST(StatusOr, TestAssignmentStatusNotOk) { + // Copy assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<int> target; + target = source; + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(expected, source.status()); + } + + // Move assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<int> target; + target = std::move(source); + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); + } +} + +TEST(StatusOr, TestAssignmentStatusOKConverting) { + // Copy assignment + { + const int kI = 4; + absl::StatusOr<int> source(kI); + + absl::StatusOr<double> target; + target = source; + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_DOUBLE_EQ(kI, *target); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_DOUBLE_EQ(kI, *source); + } + + // Move assignment + { + const auto p = new int(17); + absl::StatusOr<std::unique_ptr<int>> source(absl::WrapUnique(p)); + + absl::StatusOr<std::shared_ptr<int>> target; + target = std::move(source); + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_EQ(p, target->get()); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_EQ(nullptr, source->get()); + } +} + +struct A { + int x; +}; + +struct ImplicitConstructibleFromA { + int x; + bool moved; + ImplicitConstructibleFromA(const A& a) // NOLINT + : x(a.x), moved(false) {} + ImplicitConstructibleFromA(A&& a) // NOLINT + : x(a.x), moved(true) {} +}; + +TEST(StatusOr, ImplicitConvertingConstructor) { + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>( + absl::StatusOr<A>(A{11})), + IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 11), + Field(&ImplicitConstructibleFromA::moved, true)))); + absl::StatusOr<A> a(A{12}); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(a), + IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 12), + Field(&ImplicitConstructibleFromA::moved, false)))); +} + +struct ExplicitConstructibleFromA { + int x; + bool moved; + explicit ExplicitConstructibleFromA(const A& a) : x(a.x), moved(false) {} + explicit ExplicitConstructibleFromA(A&& a) : x(a.x), moved(true) {} +}; + +TEST(StatusOr, ExplicitConvertingConstructor) { + EXPECT_FALSE( + (std::is_convertible<const absl::StatusOr<A>&, + absl::StatusOr<ExplicitConstructibleFromA>>::value)); + EXPECT_FALSE( + (std::is_convertible<absl::StatusOr<A>&&, + absl::StatusOr<ExplicitConstructibleFromA>>::value)); + EXPECT_THAT( + absl::StatusOr<ExplicitConstructibleFromA>(absl::StatusOr<A>(A{11})), + IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 11), + Field(&ExplicitConstructibleFromA::moved, true)))); + absl::StatusOr<A> a(A{12}); + EXPECT_THAT( + absl::StatusOr<ExplicitConstructibleFromA>(a), + IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 12), + Field(&ExplicitConstructibleFromA::moved, false)))); +} + +struct ImplicitConstructibleFromBool { + ImplicitConstructibleFromBool(bool y) : x(y) {} // NOLINT + bool x = false; +}; + +struct ConvertibleToBool { + explicit ConvertibleToBool(bool y) : x(y) {} + operator bool() const { return x; } // NOLINT + bool x = false; +}; + +TEST(StatusOr, ImplicitBooleanConstructionWithImplicitCasts) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromBool>>( + absl::StatusOr<bool>(false)), + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_FALSE((std::is_convertible< + absl::StatusOr<ConvertibleToBool>, + absl::StatusOr<ImplicitConstructibleFromBool>>::value)); +} + +TEST(StatusOr, BooleanConstructionWithImplicitCasts) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<bool>(false)}, + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<bool>(absl::InvalidArgumentError(""))}, + Not(IsOk())); + + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<ConvertibleToBool>(ConvertibleToBool{false})}, + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<ConvertibleToBool>(absl::InvalidArgumentError(""))}, + Not(IsOk())); +} + +TEST(StatusOr, ConstImplicitCast) { + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( + absl::StatusOr<const bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( + absl::StatusOr<const bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( + absl::StatusOr<bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( + absl::StatusOr<bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const std::string>>( + absl::StatusOr<std::string>("foo")), + IsOkAndHolds("foo")); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<std::string>>( + absl::StatusOr<const std::string>("foo")), + IsOkAndHolds("foo")); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<std::shared_ptr<const std::string>>>( + absl::StatusOr<std::shared_ptr<std::string>>( + std::make_shared<std::string>("foo"))), + IsOkAndHolds(Pointee(std::string("foo")))); +} + +TEST(StatusOr, ConstExplicitConstruction) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(false)), + IsOkAndHolds(false)); +} + +struct ExplicitConstructibleFromInt { + int x; + explicit ExplicitConstructibleFromInt(int y) : x(y) {} +}; + +TEST(StatusOr, ExplicitConstruction) { + EXPECT_THAT(absl::StatusOr<ExplicitConstructibleFromInt>(10), + IsOkAndHolds(Field(&ExplicitConstructibleFromInt::x, 10))); +} + +TEST(StatusOr, ImplicitConstruction) { + // Check implicit casting works. + auto status_or = + absl::implicit_cast<absl::StatusOr<absl::variant<int, std::string>>>(10); + EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); +} + +TEST(StatusOr, ImplicitConstructionFromInitliazerList) { + // Note: dropping the explicit std::initializer_list<int> is not supported + // by absl::StatusOr or absl::optional. + auto status_or = + absl::implicit_cast<absl::StatusOr<std::vector<int>>>({{10, 20, 30}}); + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, UniquePtrImplicitConstruction) { + auto status_or = absl::implicit_cast<absl::StatusOr<std::unique_ptr<Base1>>>( + absl::make_unique<Derived>()); + EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); +} + +TEST(StatusOr, NestedStatusOrCopyAndMoveConstructorTests) { + absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); + absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = + absl::InvalidArgumentError("foo"); + EXPECT_THAT(status_or, + IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> a = status_or; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> a_err = status_error; + EXPECT_THAT(a_err, Not(IsOk())); + + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; + absl::StatusOr<absl::StatusOr<CopyDetector>> b = cref; // NOLINT + EXPECT_THAT(b, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; + absl::StatusOr<absl::StatusOr<CopyDetector>> b_err = cref_err; // NOLINT + EXPECT_THAT(b_err, Not(IsOk())); + + absl::StatusOr<absl::StatusOr<CopyDetector>> c = std::move(status_or); + EXPECT_THAT(c, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> c_err = std::move(status_error); + EXPECT_THAT(c_err, Not(IsOk())); +} + +TEST(StatusOr, NestedStatusOrCopyAndMoveAssignment) { + absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); + absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = + absl::InvalidArgumentError("foo"); + absl::StatusOr<absl::StatusOr<CopyDetector>> a; + a = status_or; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + a = status_error; + EXPECT_THAT(a, Not(IsOk())); + + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; + a = cref; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; + a = cref_err; + EXPECT_THAT(a, Not(IsOk())); + a = std::move(status_or); + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + a = std::move(status_error); + EXPECT_THAT(a, Not(IsOk())); +} + +struct Copyable { + Copyable() {} + Copyable(const Copyable&) {} + Copyable& operator=(const Copyable&) { return *this; } +}; + +struct MoveOnly { + MoveOnly() {} + MoveOnly(MoveOnly&&) {} + MoveOnly& operator=(MoveOnly&&) { return *this; } +}; + +struct NonMovable { + NonMovable() {} + NonMovable(const NonMovable&) = delete; + NonMovable(NonMovable&&) = delete; + NonMovable& operator=(const NonMovable&) = delete; + NonMovable& operator=(NonMovable&&) = delete; +}; + +TEST(StatusOr, CopyAndMoveAbility) { + EXPECT_TRUE(std::is_copy_constructible<Copyable>::value); + EXPECT_TRUE(std::is_copy_assignable<Copyable>::value); + EXPECT_TRUE(std::is_move_constructible<Copyable>::value); + EXPECT_TRUE(std::is_move_assignable<Copyable>::value); + EXPECT_FALSE(std::is_copy_constructible<MoveOnly>::value); + EXPECT_FALSE(std::is_copy_assignable<MoveOnly>::value); + EXPECT_TRUE(std::is_move_constructible<MoveOnly>::value); + EXPECT_TRUE(std::is_move_assignable<MoveOnly>::value); + EXPECT_FALSE(std::is_copy_constructible<NonMovable>::value); + EXPECT_FALSE(std::is_copy_assignable<NonMovable>::value); + EXPECT_FALSE(std::is_move_constructible<NonMovable>::value); + EXPECT_FALSE(std::is_move_assignable<NonMovable>::value); +} + +TEST(StatusOr, StatusOrAnyCopyAndMoveConstructorTests) { + absl::StatusOr<absl::any> status_or = CopyDetector(10); + absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo"); + EXPECT_THAT( + status_or, + IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::any> a = status_or; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + absl::StatusOr<absl::any> a_err = status_error; + EXPECT_THAT(a_err, Not(IsOk())); + + const absl::StatusOr<absl::any>& cref = status_or; + // No lint for no-change copy. + absl::StatusOr<absl::any> b = cref; // NOLINT + EXPECT_THAT( + b, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::any>& cref_err = status_error; + // No lint for no-change copy. + absl::StatusOr<absl::any> b_err = cref_err; // NOLINT + EXPECT_THAT(b_err, Not(IsOk())); + + absl::StatusOr<absl::any> c = std::move(status_or); + EXPECT_THAT( + c, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::any> c_err = std::move(status_error); + EXPECT_THAT(c_err, Not(IsOk())); +} + +TEST(StatusOr, StatusOrAnyCopyAndMoveAssignment) { + absl::StatusOr<absl::any> status_or = CopyDetector(10); + absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo"); + absl::StatusOr<absl::any> a; + a = status_or; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + a = status_error; + EXPECT_THAT(a, Not(IsOk())); + + const absl::StatusOr<absl::any>& cref = status_or; + a = cref; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::any>& cref_err = status_error; + a = cref_err; + EXPECT_THAT(a, Not(IsOk())); + a = std::move(status_or); + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + a = std::move(status_error); + EXPECT_THAT(a, Not(IsOk())); +} + +TEST(StatusOr, StatusOrCopyAndMoveTestsConstructor) { + absl::StatusOr<CopyDetector> status_or(10); + ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); + absl::StatusOr<CopyDetector> a(status_or); + EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); + const absl::StatusOr<CopyDetector>& cref = status_or; + absl::StatusOr<CopyDetector> b(cref); // NOLINT + EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); + absl::StatusOr<CopyDetector> c(std::move(status_or)); + EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); +} + +TEST(StatusOr, StatusOrCopyAndMoveTestsAssignment) { + absl::StatusOr<CopyDetector> status_or(10); + ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); + absl::StatusOr<CopyDetector> a; + a = status_or; + EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); + const absl::StatusOr<CopyDetector>& cref = status_or; + absl::StatusOr<CopyDetector> b; + b = cref; + EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); + absl::StatusOr<CopyDetector> c; + c = std::move(status_or); + EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); +} + +TEST(StatusOr, AbslAnyAssignment) { + EXPECT_FALSE((std::is_assignable<absl::StatusOr<absl::any>, + absl::StatusOr<int>>::value)); + absl::StatusOr<absl::any> status_or; + status_or = absl::InvalidArgumentError("foo"); + EXPECT_THAT(status_or, Not(IsOk())); +} + +TEST(StatusOr, ImplicitAssignment) { + absl::StatusOr<absl::variant<int, std::string>> status_or; + status_or = 10; + EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); +} + +TEST(StatusOr, SelfDirectInitAssignment) { + absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; + status_or = *status_or; + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, ImplicitCastFromInitializerList) { + absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, UniquePtrImplicitAssignment) { + absl::StatusOr<std::unique_ptr<Base1>> status_or; + status_or = absl::make_unique<Derived>(); + EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); +} + +TEST(StatusOr, Pointer) { + struct A {}; + struct B : public A {}; + struct C : private A {}; + + EXPECT_TRUE((std::is_constructible<absl::StatusOr<A*>, B*>::value)); + EXPECT_TRUE((std::is_convertible<B*, absl::StatusOr<A*>>::value)); + EXPECT_FALSE((std::is_constructible<absl::StatusOr<A*>, C*>::value)); + EXPECT_FALSE((std::is_convertible<C*, absl::StatusOr<A*>>::value)); +} + +TEST(StatusOr, TestAssignmentStatusNotOkConverting) { + // Copy assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<double> target; + target = source; + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(expected, source.status()); + } + + // Move assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<double> target; + target = std::move(source); + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); + } +} + +TEST(StatusOr, SelfAssignment) { + // Copy-assignment, status OK + { + // A string long enough that it's likely to defeat any inline representation + // optimization. + const std::string long_str(128, 'a'); + + absl::StatusOr<std::string> so = long_str; + so = *&so; + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(long_str, *so); + } + + // Copy-assignment, error status + { + absl::StatusOr<int> so = absl::NotFoundError("taco"); + so = *&so; + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } + + // Move-assignment with copyable type, status OK + { + absl::StatusOr<int> so = 17; + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(17, *so); + } + + // Move-assignment with copyable type, error status + { + absl::StatusOr<int> so = absl::NotFoundError("taco"); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } + + // Move-assignment with non-copyable type, status OK + { + const auto raw = new int(17); + absl::StatusOr<std::unique_ptr<int>> so = absl::WrapUnique(raw); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(raw, so->get()); + } + + // Move-assignment with non-copyable type, error status + { + absl::StatusOr<std::unique_ptr<int>> so = absl::NotFoundError("taco"); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } +} + +// These types form the overload sets of the constructors and the assignment +// operators of `MockValue`. They distinguish construction from assignment, +// lvalue from rvalue. +struct FromConstructibleAssignableLvalue {}; +struct FromConstructibleAssignableRvalue {}; +struct FromImplicitConstructibleOnly {}; +struct FromAssignableOnly {}; + +// This class is for testing the forwarding value assignments of `StatusOr`. +// `from_rvalue` indicates whether the constructor or the assignment taking +// rvalue reference is called. `from_assignment` indicates whether any +// assignment is called. +struct MockValue { + // Constructs `MockValue` from `FromConstructibleAssignableLvalue`. + MockValue(const FromConstructibleAssignableLvalue&) // NOLINT + : from_rvalue(false), assigned(false) {} + // Constructs `MockValue` from `FromConstructibleAssignableRvalue`. + MockValue(FromConstructibleAssignableRvalue&&) // NOLINT + : from_rvalue(true), assigned(false) {} + // Constructs `MockValue` from `FromImplicitConstructibleOnly`. + // `MockValue` is not assignable from `FromImplicitConstructibleOnly`. + MockValue(const FromImplicitConstructibleOnly&) // NOLINT + : from_rvalue(false), assigned(false) {} + // Assigns `FromConstructibleAssignableLvalue`. + MockValue& operator=(const FromConstructibleAssignableLvalue&) { + from_rvalue = false; + assigned = true; + return *this; + } + // Assigns `FromConstructibleAssignableRvalue` (rvalue only). + MockValue& operator=(FromConstructibleAssignableRvalue&&) { + from_rvalue = true; + assigned = true; + return *this; + } + // Assigns `FromAssignableOnly`, but not constructible from + // `FromAssignableOnly`. + MockValue& operator=(const FromAssignableOnly&) { + from_rvalue = false; + assigned = true; + return *this; + } + bool from_rvalue; + bool assigned; +}; + +// operator=(U&&) +TEST(StatusOr, PerfectForwardingAssignment) { + // U == T + constexpr int kValue1 = 10, kValue2 = 20; + absl::StatusOr<CopyDetector> status_or; + CopyDetector lvalue(kValue1); + status_or = lvalue; + EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue1, false, true))); + status_or = CopyDetector(kValue2); + EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue2, true, false))); + + // U != T + EXPECT_TRUE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromConstructibleAssignableLvalue&>::value)); + EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&, + FromConstructibleAssignableLvalue&&>::value)); + EXPECT_FALSE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromConstructibleAssignableRvalue&>::value)); + EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&, + FromConstructibleAssignableRvalue&&>::value)); + EXPECT_TRUE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromImplicitConstructibleOnly&>::value)); + EXPECT_FALSE((std::is_assignable<absl::StatusOr<MockValue>&, + const FromAssignableOnly&>::value)); + + absl::StatusOr<MockValue> from_lvalue(FromConstructibleAssignableLvalue{}); + EXPECT_FALSE(from_lvalue->from_rvalue); + EXPECT_FALSE(from_lvalue->assigned); + from_lvalue = FromConstructibleAssignableLvalue{}; + EXPECT_FALSE(from_lvalue->from_rvalue); + EXPECT_TRUE(from_lvalue->assigned); + + absl::StatusOr<MockValue> from_rvalue(FromConstructibleAssignableRvalue{}); + EXPECT_TRUE(from_rvalue->from_rvalue); + EXPECT_FALSE(from_rvalue->assigned); + from_rvalue = FromConstructibleAssignableRvalue{}; + EXPECT_TRUE(from_rvalue->from_rvalue); + EXPECT_TRUE(from_rvalue->assigned); + + absl::StatusOr<MockValue> from_implicit_constructible( + FromImplicitConstructibleOnly{}); + EXPECT_FALSE(from_implicit_constructible->from_rvalue); + EXPECT_FALSE(from_implicit_constructible->assigned); + // construct a temporary `StatusOr` object and invoke the `StatusOr` move + // assignment operator. + from_implicit_constructible = FromImplicitConstructibleOnly{}; + EXPECT_FALSE(from_implicit_constructible->from_rvalue); + EXPECT_FALSE(from_implicit_constructible->assigned); +} + +TEST(StatusOr, TestStatus) { + absl::StatusOr<int> good(4); + EXPECT_TRUE(good.ok()); + absl::StatusOr<int> bad(absl::CancelledError()); + EXPECT_FALSE(bad.ok()); + EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, OperatorStarRefQualifiers) { + static_assert( + std::is_same<const int&, + decltype(*std::declval<const absl::StatusOr<int>&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<int&, decltype(*std::declval<absl::StatusOr<int>&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<const int&&, + decltype(*std::declval<const absl::StatusOr<int>&&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<int&&, decltype(*std::declval<absl::StatusOr<int>&&>())>(), + "Unexpected ref-qualifiers"); +} + +TEST(StatusOr, OperatorStar) { + const absl::StatusOr<std::string> const_lvalue("hello"); + EXPECT_EQ("hello", *const_lvalue); + + absl::StatusOr<std::string> lvalue("hello"); + EXPECT_EQ("hello", *lvalue); + + // Note: Recall that std::move() is equivalent to a static_cast to an rvalue + // reference type. + const absl::StatusOr<std::string> const_rvalue("hello"); + EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT + + absl::StatusOr<std::string> rvalue("hello"); + EXPECT_EQ("hello", *std::move(rvalue)); +} + +TEST(StatusOr, OperatorArrowQualifiers) { + static_assert( + std::is_same< + const int*, + decltype(std::declval<const absl::StatusOr<int>&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + int*, decltype(std::declval<absl::StatusOr<int>&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + const int*, + decltype(std::declval<const absl::StatusOr<int>&&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + int*, decltype(std::declval<absl::StatusOr<int>&&>().operator->())>(), + "Unexpected qualifiers"); +} + +TEST(StatusOr, OperatorArrow) { + const absl::StatusOr<std::string> const_lvalue("hello"); + EXPECT_EQ(std::string("hello"), const_lvalue->c_str()); + + absl::StatusOr<std::string> lvalue("hello"); + EXPECT_EQ(std::string("hello"), lvalue->c_str()); +} + +TEST(StatusOr, RValueStatus) { + absl::StatusOr<int> so(absl::NotFoundError("taco")); + const absl::Status s = std::move(so).status(); + + EXPECT_EQ(s.code(), absl::StatusCode::kNotFound); + EXPECT_EQ(s.message(), "taco"); + + // Check that !ok() still implies !status().ok(), even after moving out of the + // object. See the note on the rvalue ref-qualified status method. + EXPECT_FALSE(so.ok()); // NOLINT + EXPECT_FALSE(so.status().ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kInternal); + EXPECT_EQ(so.status().message(), "Status accessed after move."); +} + +TEST(StatusOr, TestValue) { + const int kI = 4; + absl::StatusOr<int> thing(kI); + EXPECT_EQ(kI, *thing); +} + +TEST(StatusOr, TestValueConst) { + const int kI = 4; + const absl::StatusOr<int> thing(kI); + EXPECT_EQ(kI, *thing); +} + +TEST(StatusOr, TestPointerDefaultCtor) { + absl::StatusOr<int*> thing; + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); +} + +TEST(StatusOr, TestPointerStatusCtor) { + absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerValueCtor) { + const int kI = 4; + + // Construction from a non-null pointer + { + absl::StatusOr<const int*> so(&kI); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(&kI, *so); + } + + // Construction from a null pointer constant + { + absl::StatusOr<const int*> so(nullptr); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(nullptr, *so); + } + + // Construction from a non-literal null pointer + { + const int* const p = nullptr; + + absl::StatusOr<const int*> so(p); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(nullptr, *so); + } +} + +TEST(StatusOr, TestPointerCopyCtorStatusOk) { + const int kI = 0; + absl::StatusOr<const int*> original(&kI); + absl::StatusOr<const int*> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(*original, *copy); +} + +TEST(StatusOr, TestPointerCopyCtorStatusNotOk) { + absl::StatusOr<int*> original(absl::CancelledError()); + absl::StatusOr<int*> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) { + Derived derived; + absl::StatusOr<Derived*> original(&derived); + absl::StatusOr<Base2*> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(static_cast<const Base2*>(*original), *copy); +} + +TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) { + absl::StatusOr<Derived*> original(absl::CancelledError()); + absl::StatusOr<Base2*> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerAssignmentStatusOk) { + const int kI = 0; + absl::StatusOr<const int*> source(&kI); + absl::StatusOr<const int*> target; + target = source; + EXPECT_OK(target.status()); + EXPECT_EQ(*source, *target); +} + +TEST(StatusOr, TestPointerAssignmentStatusNotOk) { + absl::StatusOr<int*> source(absl::CancelledError()); + absl::StatusOr<int*> target; + target = source; + EXPECT_EQ(target.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerAssignmentStatusOKConverting) { + Derived derived; + absl::StatusOr<Derived*> source(&derived); + absl::StatusOr<Base2*> target; + target = source; + EXPECT_OK(target.status()); + EXPECT_EQ(static_cast<const Base2*>(*source), *target); +} + +TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) { + absl::StatusOr<Derived*> source(absl::CancelledError()); + absl::StatusOr<Base2*> target; + target = source; + EXPECT_EQ(target.status(), source.status()); +} + +TEST(StatusOr, TestPointerStatus) { + const int kI = 0; + absl::StatusOr<const int*> good(&kI); + EXPECT_TRUE(good.ok()); + absl::StatusOr<const int*> bad(absl::CancelledError()); + EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerValue) { + const int kI = 0; + absl::StatusOr<const int*> thing(&kI); + EXPECT_EQ(&kI, *thing); +} + +TEST(StatusOr, TestPointerValueConst) { + const int kI = 0; + const absl::StatusOr<const int*> thing(&kI); + EXPECT_EQ(&kI, *thing); +} + +TEST(StatusOr, StatusOrVectorOfUniquePointerCanReserveAndResize) { + using EvilType = std::vector<std::unique_ptr<int>>; + static_assert(std::is_copy_constructible<EvilType>::value, ""); + std::vector<::absl::StatusOr<EvilType>> v(5); + v.reserve(v.capacity() + 10); + v.resize(v.capacity() + 10); +} + +TEST(StatusOr, ConstPayload) { + // A reduced version of a problematic type found in the wild. All of the + // operations below should compile. + absl::StatusOr<const int> a; + + // Copy-construction + absl::StatusOr<const int> b(a); + + // Copy-assignment + EXPECT_FALSE(std::is_copy_assignable<absl::StatusOr<const int>>::value); + + // Move-construction + absl::StatusOr<const int> c(std::move(a)); + + // Move-assignment + EXPECT_FALSE(std::is_move_assignable<absl::StatusOr<const int>>::value); +} + +TEST(StatusOr, MapToStatusOrUniquePtr) { + // A reduced version of a problematic type found in the wild. All of the + // operations below should compile. + using MapType = std::map<std::string, absl::StatusOr<std::unique_ptr<int>>>; + + MapType a; + + // Move-construction + MapType b(std::move(a)); + + // Move-assignment + a = std::move(b); +} + +TEST(StatusOr, ValueOrOk) { + const absl::StatusOr<int> status_or = 0; + EXPECT_EQ(status_or.value_or(-1), 0); +} + +TEST(StatusOr, ValueOrDefault) { + const absl::StatusOr<int> status_or = absl::CancelledError(); + EXPECT_EQ(status_or.value_or(-1), -1); +} + +TEST(StatusOr, MoveOnlyValueOrOk) { + EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(0)) + .value_or(absl::make_unique<int>(-1)), + Pointee(0)); +} + +TEST(StatusOr, MoveOnlyValueOrDefault) { + EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::CancelledError()) + .value_or(absl::make_unique<int>(-1)), + Pointee(-1)); +} + +static absl::StatusOr<int> MakeStatus() { return 100; } + +TEST(StatusOr, TestIgnoreError) { MakeStatus().IgnoreError(); } + +TEST(StatusOr, EqualityOperator) { + constexpr int kNumCases = 4; + std::array<absl::StatusOr<int>, kNumCases> group1 = { + absl::StatusOr<int>(1), absl::StatusOr<int>(2), + absl::StatusOr<int>(absl::InvalidArgumentError("msg")), + absl::StatusOr<int>(absl::InternalError("msg"))}; + std::array<absl::StatusOr<int>, kNumCases> group2 = { + absl::StatusOr<int>(1), absl::StatusOr<int>(2), + absl::StatusOr<int>(absl::InvalidArgumentError("msg")), + absl::StatusOr<int>(absl::InternalError("msg"))}; + for (int i = 0; i < kNumCases; ++i) { + for (int j = 0; j < kNumCases; ++j) { + if (i == j) { + EXPECT_TRUE(group1[i] == group2[j]); + EXPECT_FALSE(group1[i] != group2[j]); + } else { + EXPECT_FALSE(group1[i] == group2[j]); + EXPECT_TRUE(group1[i] != group2[j]); + } + } + } +} + +struct MyType { + bool operator==(const MyType&) const { return true; } +}; + +enum class ConvTraits { kNone = 0, kImplicit = 1, kExplicit = 2 }; + +// This class has conversion operator to `StatusOr<T>` based on value of +// `conv_traits`. +template <typename T, ConvTraits conv_traits = ConvTraits::kNone> +struct StatusOrConversionBase {}; + +template <typename T> +struct StatusOrConversionBase<T, ConvTraits::kImplicit> { + operator absl::StatusOr<T>() const& { // NOLINT + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } + operator absl::StatusOr<T>() && { // NOLINT + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } +}; + +template <typename T> +struct StatusOrConversionBase<T, ConvTraits::kExplicit> { + explicit operator absl::StatusOr<T>() const& { + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } + explicit operator absl::StatusOr<T>() && { + return absl::InvalidArgumentError("conversion to absl::StatusOr"); + } +}; + +// This class has conversion operator to `T` based on the value of +// `conv_traits`. +template <typename T, ConvTraits conv_traits = ConvTraits::kNone> +struct ConversionBase {}; + +template <typename T> +struct ConversionBase<T, ConvTraits::kImplicit> { + operator T() const& { return t; } // NOLINT + operator T() && { return std::move(t); } // NOLINT + T t; +}; + +template <typename T> +struct ConversionBase<T, ConvTraits::kExplicit> { + explicit operator T() const& { return t; } + explicit operator T() && { return std::move(t); } + T t; +}; + +// This class has conversion operator to `absl::Status` based on the value of +// `conv_traits`. +template <ConvTraits conv_traits = ConvTraits::kNone> +struct StatusConversionBase {}; + +template <> +struct StatusConversionBase<ConvTraits::kImplicit> { + operator absl::Status() const& { // NOLINT + return absl::InternalError("conversion to Status"); + } + operator absl::Status() && { // NOLINT + return absl::InternalError("conversion to Status"); + } +}; + +template <> +struct StatusConversionBase<ConvTraits::kExplicit> { + explicit operator absl::Status() const& { // NOLINT + return absl::InternalError("conversion to Status"); + } + explicit operator absl::Status() && { // NOLINT + return absl::InternalError("conversion to Status"); + } +}; + +static constexpr int kConvToStatus = 1; +static constexpr int kConvToStatusOr = 2; +static constexpr int kConvToT = 4; +static constexpr int kConvExplicit = 8; + +constexpr ConvTraits GetConvTraits(int bit, int config) { + return (config & bit) == 0 + ? ConvTraits::kNone + : ((config & kConvExplicit) == 0 ? ConvTraits::kImplicit + : ConvTraits::kExplicit); +} + +// This class conditionally has conversion operator to `absl::Status`, `T`, +// `StatusOr<T>`, based on values of the template parameters. +template <typename T, int config> +struct CustomType + : StatusOrConversionBase<T, GetConvTraits(kConvToStatusOr, config)>, + ConversionBase<T, GetConvTraits(kConvToT, config)>, + StatusConversionBase<GetConvTraits(kConvToStatus, config)> {}; + +struct ConvertibleToAnyStatusOr { + template <typename T> + operator absl::StatusOr<T>() const { // NOLINT + return absl::InvalidArgumentError("Conversion to absl::StatusOr"); + } +}; + +// Test the rank of overload resolution for `StatusOr<T>` constructor and +// assignment, from highest to lowest: +// 1. T/Status +// 2. U that has conversion operator to absl::StatusOr<T> +// 3. U that is convertible to Status +// 4. U that is convertible to T +TEST(StatusOr, ConstructionFromT) { + // Construct absl::StatusOr<T> from T when T is convertible to + // absl::StatusOr<T> + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor(v); + EXPECT_TRUE(statusor.ok()); + } + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor = v; + EXPECT_TRUE(statusor.ok()); + } + // Construct absl::StatusOr<T> from T when T is explicitly convertible to + // Status + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor( + v); + EXPECT_TRUE(statusor.ok()); + } + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor = + v; + EXPECT_TRUE(statusor.ok()); + } +} + +// Construct absl::StatusOr<T> from U when U is explicitly convertible to T +TEST(StatusOr, ConstructionFromTypeConvertibleToT) { + { + CustomType<MyType, kConvToT | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_TRUE(statusor.ok()); + } + { + CustomType<MyType, kConvToT> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_TRUE(statusor.ok()); + } +} + +// Construct absl::StatusOr<T> from U when U has explicit conversion operator to +// absl::StatusOr<T> +TEST(StatusOr, ConstructionFromTypeWithConversionOperatorToStatusOrT) { + { + CustomType<MyType, kConvToStatusOr | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, + kConvToT | kConvToStatusOr | kConvToStatus | kConvExplicit> + v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } +} + +TEST(StatusOr, ConstructionFromTypeConvertibleToStatus) { + // Construction fails because conversion to `Status` is explicit. + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } +} + +TEST(StatusOr, AssignmentFromT) { + // Assign to absl::StatusOr<T> from T when T is convertible to + // absl::StatusOr<T> + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); + } + // Assign to absl::StatusOr<T> from T when T is convertible to Status + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<CustomType<MyType, kConvToStatus>> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); + } +} + +TEST(StatusOr, AssignmentFromTypeConvertibleToT) { + // Assign to absl::StatusOr<T> from U when U is convertible to T + { + CustomType<MyType, kConvToT> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); + } +} + +TEST(StatusOr, AssignmentFromTypeWithConversionOperatortoStatusOrT) { + // Assign to absl::StatusOr<T> from U when U has conversion operator to + // absl::StatusOr<T> + { + CustomType<MyType, kConvToStatusOr> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } +} + +TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) { + // Assign to absl::StatusOr<T> from U when U is convertible to Status + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/strings/BUILD.bazel b/third_party/abseil-cpp/absl/strings/BUILD.bazel index b950ec769f..090fc58a2c 100644 --- a/third_party/abseil-cpp/absl/strings/BUILD.bazel +++ b/third_party/abseil-cpp/absl/strings/BUILD.bazel @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -54,6 +53,7 @@ cc_library( "ascii.h", "charconv.h", "escaping.h", + "internal/string_constant.h", "match.h", "numbers.h", "str_cat.h", @@ -68,7 +68,6 @@ cc_library( deps = [ ":internal", "//absl/base", - "//absl/base:bits", "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", @@ -76,6 +75,7 @@ cc_library( "//absl/base:throw_delegate", "//absl/memory", "//absl/meta:type_traits", + "//absl/numeric:bits", "//absl/numeric:int128", ], ) @@ -223,6 +223,19 @@ cc_test( ) cc_test( + name = "string_constant_test", + size = "small", + srcs = ["internal/string_constant_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":strings", + "//absl/meta:type_traits", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( name = "string_view_benchmark", srcs = ["string_view_benchmark.cc"], copts = ABSL_TEST_COPTS, @@ -253,12 +266,125 @@ cc_test( cc_library( name = "cord_internal", - hdrs = ["internal/cord_internal.h"], + srcs = [ + "internal/cord_internal.cc", + "internal/cord_rep_btree.cc", + "internal/cord_rep_btree_navigator.cc", + "internal/cord_rep_btree_reader.cc", + "internal/cord_rep_consume.cc", + "internal/cord_rep_ring.cc", + ], + hdrs = [ + "internal/cord_internal.h", + "internal/cord_rep_btree.h", + "internal/cord_rep_btree_navigator.h", + "internal/cord_rep_btree_reader.h", + "internal/cord_rep_consume.h", + "internal/cord_rep_flat.h", + "internal/cord_rep_ring.h", + "internal/cord_rep_ring_reader.h", + ], copts = ABSL_DEFAULT_COPTS, - visibility = ["//visibility:private"], + visibility = [ + "//visibility:private", + ], deps = [ ":strings", + "//absl/base:base_internal", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:endian", + "//absl/base:raw_logging_internal", + "//absl/base:throw_delegate", + "//absl/container:compressed_tuple", + "//absl/container:inlined_vector", + "//absl/container:layout", + "//absl/functional:function_ref", "//absl/meta:type_traits", + "//absl/types:span", + ], +) + +cc_test( + name = "cord_internal_test", + srcs = ["internal/cord_internal_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cord_rep_btree_test", + size = "medium", + srcs = ["internal/cord_rep_btree_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":cord_rep_test_util", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "//absl/cleanup", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cord_rep_btree_navigator_test", + size = "medium", + srcs = ["internal/cord_rep_btree_navigator_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":cord_rep_test_util", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cord_rep_btree_reader_test", + size = "medium", + srcs = ["internal/cord_rep_btree_reader_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord", + ":cord_internal", + ":cord_rep_test_util", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "cordz_update_tracker", + hdrs = ["internal/cordz_update_tracker.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = ["//absl/base:config"], +) + +cc_test( + name = "cordz_update_tracker_test", + srcs = ["internal/cordz_update_tracker_test.cc"], + deps = [ + ":cordz_update_tracker", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/synchronization", + "@com_google_googletest//:gtest_main", ], ) @@ -273,11 +399,16 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":cord_internal", + ":cordz_functions", + ":cordz_info", + ":cordz_statistics", + ":cordz_update_scope", + ":cordz_update_tracker", ":internal", ":str_format", ":strings", "//absl/base", - "//absl/base:base_internal", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", "//absl/base:raw_logging_internal", @@ -285,6 +416,216 @@ cc_library( "//absl/container:inlined_vector", "//absl/functional:function_ref", "//absl/meta:type_traits", + "//absl/types:optional", + ], +) + +cc_library( + name = "cordz_handle", + srcs = ["internal/cordz_handle.cc"], + hdrs = ["internal/cordz_handle.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + "//absl/base", + "//absl/base:config", + "//absl/base:raw_logging_internal", + "//absl/synchronization", + ], +) + +cc_library( + name = "cordz_info", + srcs = ["internal/cordz_info.cc"], + hdrs = ["internal/cordz_info.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":cord_internal", + ":cordz_functions", + ":cordz_handle", + ":cordz_statistics", + ":cordz_update_tracker", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/container:inlined_vector", + "//absl/debugging:stacktrace", + "//absl/synchronization", + "//absl/types:span", + ], +) + +cc_library( + name = "cordz_update_scope", + hdrs = ["internal/cordz_update_scope.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":cord_internal", + ":cordz_info", + ":cordz_update_tracker", + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_test( + name = "cordz_update_scope_test", + srcs = ["internal/cordz_update_scope_test.cc"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord_internal", + ":cordz_info", + ":cordz_test_helpers", + ":cordz_update_scope", + ":cordz_update_tracker", + "//absl/base:config", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "cordz_sample_token", + srcs = ["internal/cordz_sample_token.cc"], + hdrs = ["internal/cordz_sample_token.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":cordz_handle", + ":cordz_info", + "//absl/base:config", + ], +) + +cc_library( + name = "cordz_functions", + srcs = ["internal/cordz_functions.cc"], + hdrs = ["internal/cordz_functions.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/profiling:exponential_biased", + ], +) + +cc_library( + name = "cordz_statistics", + hdrs = ["internal/cordz_statistics.h"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [ + ":cordz_update_tracker", + "//absl/base:config", + ], +) + +cc_test( + name = "cordz_functions_test", + srcs = [ + "internal/cordz_functions_test.cc", + ], + deps = [ + ":cordz_functions", + ":cordz_test_helpers", + "//absl/base:config", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_handle_test", + srcs = [ + "internal/cordz_handle_test.cc", + ], + deps = [ + ":cordz_handle", + "//absl/base:config", + "//absl/memory", + "//absl/random", + "//absl/random:distributions", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_info_test", + srcs = [ + "internal/cordz_info_test.cc", + ], + deps = [ + ":cord_internal", + ":cordz_handle", + ":cordz_info", + ":cordz_statistics", + ":cordz_test_helpers", + ":cordz_update_tracker", + ":strings", + "//absl/base:config", + "//absl/debugging:stacktrace", + "//absl/debugging:symbolize", + "//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_info_statistics_test", + srcs = [ + "internal/cordz_info_statistics_test.cc", + ], + deps = [ + ":cord", + ":cord_internal", + ":cordz_info", + ":cordz_sample_token", + ":cordz_statistics", + ":cordz_update_scope", + ":cordz_update_tracker", + "//absl/base:config", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_sample_token_test", + srcs = [ + "internal/cordz_sample_token_test.cc", + ], + deps = [ + ":cord_internal", + ":cordz_handle", + ":cordz_info", + ":cordz_sample_token", + ":cordz_test_helpers", + "//absl/base:config", + "//absl/memory", + "//absl/random", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", ], ) @@ -297,6 +638,41 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":cord", + ":cord_internal", + ":strings", + "//absl/base:config", + ], +) + +cc_library( + name = "cord_rep_test_util", + testonly = 1, + hdrs = ["internal/cord_rep_test_util.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord_internal", + ":strings", + "//absl/base:config", + "//absl/base:raw_logging_internal", + ], +) + +cc_library( + name = "cordz_test_helpers", + testonly = 1, + hdrs = ["cordz_test_helpers.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":cord", + ":cord_internal", + ":cordz_info", + ":cordz_sample_token", + ":cordz_statistics", + ":cordz_update_tracker", + ":strings", + "//absl/base:config", + "//absl/base:core_headers", + "@com_google_googletest//:gtest", ], ) @@ -309,12 +685,98 @@ cc_test( deps = [ ":cord", ":cord_test_helpers", + ":cordz_functions", + ":cordz_test_helpers", + ":str_format", ":strings", "//absl/base", "//absl/base:config", + "//absl/base:core_headers", "//absl/base:endian", "//absl/base:raw_logging_internal", "//absl/container:fixed_array", + "//absl/random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cordz_test", + size = "medium", + srcs = ["cordz_test.cc"], + copts = ABSL_TEST_COPTS, + tags = [ + "benchmark", + "no_test_android_arm", + "no_test_android_arm64", + "no_test_android_x86", + "no_test_darwin_x86_64", + "no_test_ios_x86_64", + "no_test_loonix", + "no_test_msvc_x64", + ], + visibility = ["//visibility:private"], + deps = [ + ":cord", + ":cord_test_helpers", + ":cordz_functions", + ":cordz_info", + ":cordz_sample_token", + ":cordz_statistics", + ":cordz_test_helpers", + ":cordz_update_tracker", + ":strings", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cord_rep_consume_test", + size = "medium", + srcs = ["internal/cord_rep_consume_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":strings", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/debugging:leak_check", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cord_ring_test", + size = "medium", + srcs = ["cord_ring_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":strings", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:raw_logging_internal", + "//absl/debugging:leak_check", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "cord_ring_reader_test", + size = "medium", + srcs = ["cord_ring_reader_test.cc"], + copts = ABSL_TEST_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":cord_internal", + ":strings", + "//absl/base:core_headers", + "//absl/debugging:leak_check", "@com_google_googletest//:gtest_main", ], ) @@ -366,6 +828,9 @@ cc_test( ":strings", "//absl/base:core_headers", "//absl/base:dynamic_annotations", + "//absl/container:btree", + "//absl/container:flat_hash_map", + "//absl/container:node_hash_map", "@com_google_googletest//:gtest_main", ], ) @@ -484,6 +949,7 @@ cc_test( copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ + ":internal", ":pow10_helper", ":strings", "//absl/base:config", @@ -634,8 +1100,12 @@ cc_library( ":strings", "//absl/base:config", "//absl/base:core_headers", + "//absl/functional:function_ref", "//absl/meta:type_traits", + "//absl/numeric:bits", "//absl/numeric:int128", + "//absl/numeric:representation", + "//absl/types:optional", "//absl/types:span", ], ) @@ -646,6 +1116,7 @@ cc_test( copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ + ":cord", ":str_format", ":strings", "//absl/base:core_headers", @@ -663,6 +1134,7 @@ cc_test( deps = [ ":str_format", ":str_format_internal", + ":strings", "@com_google_googletest//:gtest_main", ], ) @@ -709,8 +1181,9 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":str_format_internal", + ":strings", "//absl/base:raw_logging_internal", - "//absl/numeric:int128", + "//absl/types:optional", "@com_google_googletest//:gtest_main", ], ) @@ -721,6 +1194,7 @@ cc_test( copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ + ":cord", ":str_format_internal", "@com_google_googletest//:gtest_main", ], diff --git a/third_party/abseil-cpp/absl/strings/CMakeLists.txt b/third_party/abseil-cpp/absl/strings/CMakeLists.txt index fbf0f5ac52..d6801fe6fd 100644 --- a/third_party/abseil-cpp/absl/strings/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/strings/CMakeLists.txt @@ -21,6 +21,7 @@ absl_cc_library( "ascii.h" "charconv.h" "escaping.h" + "internal/string_constant.h" "match.h" "numbers.h" "str_cat.h" @@ -100,7 +101,7 @@ absl_cc_test( DEPS absl::strings absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -114,7 +115,7 @@ absl_cc_test( absl::strings absl::core_headers absl::fixed_array - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -127,7 +128,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -141,7 +142,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -155,7 +156,20 @@ absl_cc_test( absl::strings_internal absl::base absl::core_headers - gmock_main + GTest::gmock_main +) + +absl_cc_test( + NAME + string_constant_test + SRCS + "internal/string_constant_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::type_traits + GTest::gmock_main ) absl_cc_test( @@ -170,7 +184,7 @@ absl_cc_test( absl::config absl::core_headers absl::dynamic_annotations - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -183,7 +197,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -195,7 +209,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -207,10 +221,12 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings - absl::base absl::core_headers absl::dynamic_annotations - gmock_main + absl::btree + absl::flat_hash_map + absl::node_hash_map + GTest::gmock_main ) absl_cc_test( @@ -222,7 +238,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -237,7 +253,7 @@ absl_cc_test( absl::base absl::core_headers absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -252,7 +268,7 @@ absl_cc_test( absl::base absl::core_headers absl::memory - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -265,7 +281,7 @@ absl_cc_test( DEPS absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -284,7 +300,8 @@ absl_cc_test( absl::raw_logging_internal absl::random_random absl::random_distributions - gmock_main + absl::strings_internal + GTest::gmock_main ) absl_cc_test( @@ -297,7 +314,7 @@ absl_cc_test( DEPS absl::strings absl::base - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -309,7 +326,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::strings_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -323,7 +340,7 @@ absl_cc_test( absl::strings absl::str_format absl::pow10_helper - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -338,7 +355,7 @@ absl_cc_test( absl::strings absl::config absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -353,7 +370,7 @@ absl_cc_test( DEPS absl::strings absl::config - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -389,9 +406,11 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::bits absl::strings absl::config absl::core_headers + absl::numeric_representation absl::type_traits absl::int128 absl::span @@ -406,9 +425,10 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format + absl::cord absl::strings absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -421,7 +441,8 @@ absl_cc_test( DEPS absl::str_format absl::str_format_internal - gmock_main + absl::strings + GTest::gmock_main ) absl_cc_test( @@ -434,7 +455,7 @@ absl_cc_test( DEPS absl::str_format absl::str_format_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -446,7 +467,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format_internal - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -458,7 +479,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -469,10 +490,11 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::strings absl::str_format_internal absl::raw_logging_internal absl::int128 - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -484,7 +506,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format_internal - gmock_main + absl::cord + GTest::gmock_main ) absl_cc_test( @@ -497,7 +520,7 @@ absl_cc_test( DEPS absl::str_format_internal absl::core_headers - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -524,7 +547,281 @@ absl_cc_test( DEPS absl::pow10_helper absl::str_format - gmock_main + GTest::gmock_main +) + +absl_cc_library( + NAME + cord_internal + HDRS + "internal/cord_internal.h" + "internal/cord_rep_btree.h" + "internal/cord_rep_btree_navigator.h" + "internal/cord_rep_btree_reader.h" + "internal/cord_rep_consume.h" + "internal/cord_rep_flat.h" + "internal/cord_rep_ring.h" + "internal/cord_rep_ring_reader.h" + SRCS + "internal/cord_internal.cc" + "internal/cord_rep_btree.cc" + "internal/cord_rep_btree_navigator.cc" + "internal/cord_rep_btree_reader.cc" + "internal/cord_rep_consume.cc" + "internal/cord_rep_ring.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base_internal + absl::compressed_tuple + absl::config + absl::core_headers + absl::endian + absl::inlined_vector + absl::layout + absl::raw_logging_internal + absl::strings + absl::throw_delegate + absl::type_traits +) + +absl_cc_library( + NAME + cordz_update_tracker + HDRS + "internal/cordz_update_tracker.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config +) + +absl_cc_test( + NAME + cordz_update_tracker_test + SRCS + "internal/cordz_update_tracker_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cordz_update_tracker + absl::core_headers + absl::synchronization + GTest::gmock_main +) + +absl_cc_library( + NAME + cordz_functions + HDRS + "internal/cordz_functions.h" + SRCS + "internal/cordz_functions.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + absl::exponential_biased + absl::raw_logging_internal +) + +absl_cc_test( + NAME + cordz_functions_test + SRCS + "internal/cordz_functions_test.cc" + DEPS + absl::config + absl::cordz_functions + absl::cordz_test_helpers + GTest::gmock_main +) + +absl_cc_library( + NAME + cordz_statistics + HDRS + "internal/cordz_statistics.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + absl::cordz_update_tracker + absl::synchronization +) + +absl_cc_library( + NAME + cordz_handle + HDRS + "internal/cordz_handle.h" + SRCS + "internal/cordz_handle.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::config + absl::raw_logging_internal + absl::synchronization +) + +absl_cc_test( + NAME + cordz_handle_test + SRCS + "internal/cordz_handle_test.cc" + DEPS + absl::config + absl::cordz_handle + absl::cordz_test_helpers + absl::memory + absl::random_random + absl::random_distributions + absl::synchronization + absl::time + GTest::gmock_main +) + +absl_cc_library( + NAME + cordz_info + HDRS + "internal/cordz_info.h" + SRCS + "internal/cordz_info.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::cordz_functions + absl::cordz_handle + absl::cordz_statistics + absl::cordz_update_tracker + absl::core_headers + absl::inlined_vector + absl::span + absl::raw_logging_internal + absl::stacktrace + absl::synchronization +) + +absl_cc_test( + NAME + cordz_info_test + SRCS + "internal/cordz_info_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_test_helpers + absl::cordz_handle + absl::cordz_info + absl::cordz_statistics + absl::cordz_test_helpers + absl::cordz_update_tracker + absl::span + absl::stacktrace + absl::symbolize + GTest::gmock_main +) + +absl_cc_test( + NAME + cordz_info_statistics_test + SRCS + "internal/cordz_info_statistics_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord + absl::cord_internal + absl::cordz_info + absl::cordz_sample_token + absl::cordz_statistics + absl::cordz_update_scope + absl::cordz_update_tracker + absl::thread_pool + GTest::gmock_main +) + +absl_cc_library( + NAME + cordz_sample_token + HDRS + "internal/cordz_sample_token.h" + SRCS + "internal/cordz_sample_token.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::cordz_handle + absl::cordz_info +) + +absl_cc_test( + NAME + cordz_sample_token_test + SRCS + "internal/cordz_sample_token_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_handle + absl::cordz_info + absl::cordz_info + absl::cordz_sample_token + absl::cordz_test_helpers + absl::memory + absl::random_random + absl::synchronization + absl::thread_pool + absl::time + GTest::gmock_main +) + +absl_cc_library( + NAME + cordz_update_scope + HDRS + "internal/cordz_update_scope.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_info + absl::cordz_update_tracker + absl::core_headers +) + +absl_cc_test( + NAME + cordz_update_scope_test + SRCS + "internal/cordz_update_scope_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::cordz_info + absl::cordz_test_helpers + absl::cordz_update_scope + absl::cordz_update_tracker + absl::core_headers + GTest::gmock_main ) absl_cc_library( @@ -534,33 +831,75 @@ absl_cc_library( "cord.h" SRCS "cord.cc" - "internal/cord_internal.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS - absl::strings - absl::strings_internal absl::base - absl::base_internal + absl::config + absl::cord_internal + absl::cordz_functions + absl::cordz_info + absl::cordz_update_scope + absl::cordz_update_tracker absl::core_headers absl::endian absl::fixed_array absl::function_ref absl::inlined_vector + absl::optional absl::raw_logging_internal + absl::strings absl::type_traits PUBLIC ) absl_cc_library( NAME + cord_rep_test_util + HDRS + "internal/cord_rep_test_util.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config + absl::cord_internal + absl::raw_logging_internal + absl::strings + TESTONLY +) + +absl_cc_library( + NAME cord_test_helpers HDRS "cord_test_helpers.h" COPTS ${ABSL_TEST_COPTS} DEPS + absl::config + absl::cord + absl::cord_internal + absl::strings + TESTONLY +) + +absl_cc_library( + NAME + cordz_test_helpers + HDRS + "cordz_test_helpers.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::config absl::cord + absl::cord_internal + absl::cordz_info + absl::cordz_sample_token + absl::cordz_statistics + absl::cordz_update_tracker + absl::core_headers + absl::strings TESTONLY ) @@ -573,11 +912,157 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::cord + absl::str_format absl::strings absl::base absl::config + absl::cord_test_helpers + absl::cordz_test_helpers + absl::core_headers absl::endian + absl::random_random absl::raw_logging_internal absl::fixed_array - gmock_main + GTest::gmock_main +) + +absl_cc_test( + NAME + cord_rep_consume_test + SRCS + "internal/cord_rep_consume_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::core_headers + absl::function_ref + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + +absl_cc_test( + NAME + cord_internal_test + SRCS + "internal/cord_internal_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::cord_internal + GTest::gmock_main +) + +absl_cc_test( + NAME + cord_rep_btree_test + SRCS + "internal/cord_rep_btree_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::cleanup + absl::config + absl::cord_internal + absl::cord_rep_test_util + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + +absl_cc_test( + NAME + cord_rep_btree_navigator_test + SRCS + "internal/cord_rep_btree_navigator_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::cord_rep_test_util + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + +absl_cc_test( + NAME + cord_rep_btree_reader_test + SRCS + "internal/cord_rep_btree_reader_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::cord_rep_test_util + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + +absl_cc_test( + NAME + cord_ring_test + SRCS + "cord_ring_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::config + absl::cord_internal + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main +) + +absl_cc_test( + NAME + cord_ring_reader_test + SRCS + "cord_ring_reader_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::cord_internal + absl::core_headers + absl::strings + GTest::gmock_main +) + +absl_cc_test( + NAME + cordz_test + SRCS + "cordz_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::cord + absl::cord_test_helpers + absl::cordz_test_helpers + absl::cordz_functions + absl::cordz_info + absl::cordz_sample_token + absl::cordz_statistics + absl::cordz_update_tracker + absl::base + absl::config + absl::core_headers + absl::raw_logging_internal + absl::strings + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/strings/ascii_test.cc b/third_party/abseil-cpp/absl/strings/ascii_test.cc index 5ecd23f869..83af7825e1 100644 --- a/third_party/abseil-cpp/absl/strings/ascii_test.cc +++ b/third_party/abseil-cpp/absl/strings/ascii_test.cc @@ -197,11 +197,15 @@ TEST(AsciiStrTo, Lower) { const std::string str("GHIJKL"); const std::string str2("MNOPQR"); const absl::string_view sp(str2); + std::string mutable_str("STUVWX"); EXPECT_EQ("abcdef", absl::AsciiStrToLower(buf)); EXPECT_EQ("ghijkl", absl::AsciiStrToLower(str)); EXPECT_EQ("mnopqr", absl::AsciiStrToLower(sp)); + absl::AsciiStrToLower(&mutable_str); + EXPECT_EQ("stuvwx", mutable_str); + char mutable_buf[] = "Mutable"; std::transform(mutable_buf, mutable_buf + strlen(mutable_buf), mutable_buf, absl::ascii_tolower); diff --git a/third_party/abseil-cpp/absl/strings/charconv.cc b/third_party/abseil-cpp/absl/strings/charconv.cc index bdba768dcd..fefcfc90a5 100644 --- a/third_party/abseil-cpp/absl/strings/charconv.cc +++ b/third_party/abseil-cpp/absl/strings/charconv.cc @@ -20,7 +20,7 @@ #include <cstring> #include "absl/base/casts.h" -#include "absl/base/internal/bits.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/strings/internal/charconv_bigint.h" #include "absl/strings/internal/charconv_parse.h" @@ -111,7 +111,7 @@ struct FloatTraits<double> { return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent); #else constexpr uint64_t kMantissaMask = - (uint64_t(1) << (kTargetMantissaBits - 1)) - 1; + (uint64_t{1} << (kTargetMantissaBits - 1)) - 1; uint64_t dbl = static_cast<uint64_t>(sign) << 63; if (mantissa > kMantissaMask) { // Normal value. @@ -151,7 +151,7 @@ struct FloatTraits<float> { return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent); #else constexpr uint32_t kMantissaMask = - (uint32_t(1) << (kTargetMantissaBits - 1)) - 1; + (uint32_t{1} << (kTargetMantissaBits - 1)) - 1; uint32_t flt = static_cast<uint32_t>(sign) << 31; if (mantissa > kMantissaMask) { // Normal value. @@ -242,11 +242,11 @@ struct CalculatedFloat { // Returns the bit width of the given uint128. (Equivalently, returns 128 // minus the number of leading zero bits.) -int BitWidth(uint128 value) { +unsigned BitWidth(uint128 value) { if (Uint128High64(value) == 0) { - return 64 - base_internal::CountLeadingZeros64(Uint128Low64(value)); + return static_cast<unsigned>(bit_width(Uint128Low64(value))); } - return 128 - base_internal::CountLeadingZeros64(Uint128High64(value)); + return 128 - countl_zero(Uint128High64(value)); } // Calculates how far to the right a mantissa needs to be shifted to create a @@ -499,7 +499,7 @@ bool MustRoundUp(uint64_t guess_mantissa, int guess_exponent, template <typename FloatType> CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) { CalculatedFloat result; - if (mantissa == uint64_t(1) << FloatTraits<FloatType>::kTargetMantissaBits) { + if (mantissa == uint64_t{1} << FloatTraits<FloatType>::kTargetMantissaBits) { mantissa >>= 1; exponent += 1; } @@ -519,7 +519,7 @@ CalculatedFloat CalculateFromParsedHexadecimal( const strings_internal::ParsedFloat& parsed_hex) { uint64_t mantissa = parsed_hex.mantissa; int exponent = parsed_hex.exponent; - int mantissa_width = 64 - base_internal::CountLeadingZeros64(mantissa); + auto mantissa_width = static_cast<unsigned>(bit_width(mantissa)); const int shift = NormalizedShiftSize<FloatType>(mantissa_width, exponent); bool result_exact; exponent += shift; @@ -619,10 +619,10 @@ from_chars_result FromCharsImpl(const char* first, const char* last, // Either we failed to parse a hex float after the "0x", or we read // "0xinf" or "0xnan" which we don't want to match. // - // However, a std::string that begins with "0x" also begins with "0", which + // However, a string that begins with "0x" also begins with "0", which // is normally a valid match for the number zero. So we want these // strings to match zero unless fmt_flags is `scientific`. (This flag - // means an exponent is required, which the std::string "0" does not have.) + // means an exponent is required, which the string "0" does not have.) if (fmt_flags == chars_format::scientific) { result.ec = std::errc::invalid_argument; } else { diff --git a/third_party/abseil-cpp/absl/strings/charconv.h b/third_party/abseil-cpp/absl/strings/charconv.h index e04be32f95..7c50981245 100644 --- a/third_party/abseil-cpp/absl/strings/charconv.h +++ b/third_party/abseil-cpp/absl/strings/charconv.h @@ -64,8 +64,9 @@ struct from_chars_result { // the result in `value`. // // The matching pattern format is almost the same as that of strtod(), except -// that C locale is not respected, and an initial '+' character in the input -// range will never be matched. +// that (1) C locale is not respected, (2) an initial '+' character in the +// input range will never be matched, and (3) leading whitespaces are not +// ignored. // // If `fmt` is set, it must be one of the enumerator values of the chars_format. // (This is despite the fact that chars_format is a bitmask type.) If set to diff --git a/third_party/abseil-cpp/absl/strings/charconv_benchmark.cc b/third_party/abseil-cpp/absl/strings/charconv_benchmark.cc index 644b2abdf6..e8c7371d65 100644 --- a/third_party/abseil-cpp/absl/strings/charconv_benchmark.cc +++ b/third_party/abseil-cpp/absl/strings/charconv_benchmark.cc @@ -132,7 +132,7 @@ BENCHMARK(BM_Absl_HugeMantissa); std::string MakeHardCase(int length) { // The number 1.1521...e-297 is exactly halfway between 12345 * 2**-1000 and // the next larger representable number. The digits of this number are in - // the std::string below. + // the string below. const std::string digits = "1." "152113937042223790993097181572444900347587985074226836242307364987727724" diff --git a/third_party/abseil-cpp/absl/strings/charconv_test.cc b/third_party/abseil-cpp/absl/strings/charconv_test.cc index 9090e9c89c..b83de5a0ba 100644 --- a/third_party/abseil-cpp/absl/strings/charconv_test.cc +++ b/third_party/abseil-cpp/absl/strings/charconv_test.cc @@ -653,7 +653,9 @@ TEST(FromChars, NaNFloats) { negative_from_chars_float); EXPECT_TRUE(std::signbit(negative_from_chars_float)); EXPECT_FALSE(Identical(negative_from_chars_float, from_chars_float)); - from_chars_float = std::copysign(from_chars_float, -1.0); + // Use the (float, float) overload of std::copysign to prevent narrowing; + // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98251. + from_chars_float = std::copysign(from_chars_float, -1.0f); EXPECT_TRUE(Identical(negative_from_chars_float, from_chars_float)); } } diff --git a/third_party/abseil-cpp/absl/strings/cord.cc b/third_party/abseil-cpp/absl/strings/cord.cc index d9503ae332..854047ca98 100644 --- a/third_party/abseil-cpp/absl/strings/cord.cc +++ b/third_party/abseil-cpp/absl/strings/cord.cc @@ -15,10 +15,12 @@ #include "absl/strings/cord.h" #include <algorithm> +#include <atomic> #include <cstddef> #include <cstdio> #include <cstdlib> #include <iomanip> +#include <iostream> #include <limits> #include <ostream> #include <sstream> @@ -28,11 +30,17 @@ #include "absl/base/casts.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/container/fixed_array.h" #include "absl/container/inlined_vector.h" #include "absl/strings/escaping.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_scope.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -43,144 +51,18 @@ namespace absl { ABSL_NAMESPACE_BEGIN using ::absl::cord_internal::CordRep; +using ::absl::cord_internal::CordRepBtree; using ::absl::cord_internal::CordRepConcat; using ::absl::cord_internal::CordRepExternal; +using ::absl::cord_internal::CordRepFlat; using ::absl::cord_internal::CordRepSubstring; +using ::absl::cord_internal::CordzUpdateTracker; +using ::absl::cord_internal::InlineData; +using ::absl::cord_internal::kMaxFlatLength; +using ::absl::cord_internal::kMinFlatLength; -// Various representations that we allow -enum CordRepKind { - CONCAT = 0, - EXTERNAL = 1, - SUBSTRING = 2, - - // We have different tags for different sized flat arrays, - // starting with FLAT - FLAT = 3, -}; - -namespace { - -// Type used with std::allocator for allocating and deallocating -// `CordRepExternal`. std::allocator is used because it opaquely handles the -// different new / delete overloads available on a given platform. -struct alignas(absl::cord_internal::ExternalRepAlignment()) ExternalAllocType { - unsigned char value[absl::cord_internal::ExternalRepAlignment()]; -}; - -// Returns the number of objects to pass in to std::allocator<ExternalAllocType> -// allocate() and deallocate() to create enough room for `CordRepExternal` with -// `releaser_size` bytes on the end. -constexpr size_t GetExternalAllocNumObjects(size_t releaser_size) { - // Be sure to round up since `releaser_size` could be smaller than - // `sizeof(ExternalAllocType)`. - return (sizeof(CordRepExternal) + releaser_size + sizeof(ExternalAllocType) - - 1) / - sizeof(ExternalAllocType); -} - -// Allocates enough memory for `CordRepExternal` and a releaser with size -// `releaser_size` bytes. -void* AllocateExternal(size_t releaser_size) { - return std::allocator<ExternalAllocType>().allocate( - GetExternalAllocNumObjects(releaser_size)); -} - -// Deallocates the memory for a `CordRepExternal` assuming it was allocated with -// a releaser of given size and alignment. -void DeallocateExternal(CordRepExternal* p, size_t releaser_size) { - std::allocator<ExternalAllocType>().deallocate( - reinterpret_cast<ExternalAllocType*>(p), - GetExternalAllocNumObjects(releaser_size)); -} - -// Returns a pointer to the type erased releaser for the given CordRepExternal. -void* GetExternalReleaser(CordRepExternal* rep) { - return rep + 1; -} - -} // namespace - -namespace cord_internal { - -inline CordRepConcat* CordRep::concat() { - assert(tag == CONCAT); - return static_cast<CordRepConcat*>(this); -} - -inline const CordRepConcat* CordRep::concat() const { - assert(tag == CONCAT); - return static_cast<const CordRepConcat*>(this); -} - -inline CordRepSubstring* CordRep::substring() { - assert(tag == SUBSTRING); - return static_cast<CordRepSubstring*>(this); -} - -inline const CordRepSubstring* CordRep::substring() const { - assert(tag == SUBSTRING); - return static_cast<const CordRepSubstring*>(this); -} - -inline CordRepExternal* CordRep::external() { - assert(tag == EXTERNAL); - return static_cast<CordRepExternal*>(this); -} - -inline const CordRepExternal* CordRep::external() const { - assert(tag == EXTERNAL); - return static_cast<const CordRepExternal*>(this); -} - -} // namespace cord_internal - -static const size_t kFlatOverhead = offsetof(CordRep, data); - -static_assert(kFlatOverhead == 13, "Unittests assume kFlatOverhead == 13"); - -// Largest and smallest flat node lengths we are willing to allocate -// Flat allocation size is stored in tag, which currently can encode sizes up -// to 4K, encoded as multiple of either 8 or 32 bytes. -// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc. -static constexpr size_t kMaxFlatSize = 4096; -static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; -static constexpr size_t kMinFlatLength = 32 - kFlatOverhead; - -// Prefer copying blocks of at most this size, otherwise reference count. -static const size_t kMaxBytesToCopy = 511; - -// Helper functions for rounded div, and rounding to exact sizes. -static size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; } -static size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; } - -// Returns the size to the nearest equal or larger value that can be -// expressed exactly as a tag value. -static size_t RoundUpForTag(size_t size) { - return RoundUp(size, (size <= 1024) ? 8 : 32); -} - -// Converts the allocated size to a tag, rounding down if the size -// does not exactly match a 'tag expressible' size value. The result is -// undefined if the size exceeds the maximum size that can be encoded in -// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>). -static uint8_t AllocatedSizeToTag(size_t size) { - const size_t tag = (size <= 1024) ? size / 8 : 128 + size / 32 - 1024 / 32; - assert(tag <= std::numeric_limits<uint8_t>::max()); - return tag; -} - -// Converts the provided tag to the corresponding allocated size -static constexpr size_t TagToAllocatedSize(uint8_t tag) { - return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32); -} - -// Converts the provided tag to the corresponding available data length -static constexpr size_t TagToLength(uint8_t tag) { - return TagToAllocatedSize(tag) - kFlatOverhead; -} - -// Enforce that kMaxFlatSize maps to a well-known exact tag value. -static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic"); +using ::absl::cord_internal::kInlinedVectorSize; +using ::absl::cord_internal::kMaxBytesToCopy; constexpr uint64_t Fibonacci(unsigned char n, uint64_t a = 0, uint64_t b = 1) { return n == 0 ? a : Fibonacci(n - 1, b, a + b); @@ -195,70 +77,30 @@ static_assert(Fibonacci(63) == 6557470319842, // The root node depth is allowed to become twice as large to reduce rebalancing // for larger strings (see IsRootBalanced). static constexpr uint64_t min_length[] = { - Fibonacci(2), - Fibonacci(3), - Fibonacci(4), - Fibonacci(5), - Fibonacci(6), - Fibonacci(7), - Fibonacci(8), - Fibonacci(9), - Fibonacci(10), - Fibonacci(11), - Fibonacci(12), - Fibonacci(13), - Fibonacci(14), - Fibonacci(15), - Fibonacci(16), - Fibonacci(17), - Fibonacci(18), - Fibonacci(19), - Fibonacci(20), - Fibonacci(21), - Fibonacci(22), - Fibonacci(23), - Fibonacci(24), - Fibonacci(25), - Fibonacci(26), - Fibonacci(27), - Fibonacci(28), - Fibonacci(29), - Fibonacci(30), - Fibonacci(31), - Fibonacci(32), - Fibonacci(33), - Fibonacci(34), - Fibonacci(35), - Fibonacci(36), - Fibonacci(37), - Fibonacci(38), - Fibonacci(39), - Fibonacci(40), - Fibonacci(41), - Fibonacci(42), - Fibonacci(43), - Fibonacci(44), - Fibonacci(45), - Fibonacci(46), - Fibonacci(47), + Fibonacci(2), Fibonacci(3), Fibonacci(4), Fibonacci(5), + Fibonacci(6), Fibonacci(7), Fibonacci(8), Fibonacci(9), + Fibonacci(10), Fibonacci(11), Fibonacci(12), Fibonacci(13), + Fibonacci(14), Fibonacci(15), Fibonacci(16), Fibonacci(17), + Fibonacci(18), Fibonacci(19), Fibonacci(20), Fibonacci(21), + Fibonacci(22), Fibonacci(23), Fibonacci(24), Fibonacci(25), + Fibonacci(26), Fibonacci(27), Fibonacci(28), Fibonacci(29), + Fibonacci(30), Fibonacci(31), Fibonacci(32), Fibonacci(33), + Fibonacci(34), Fibonacci(35), Fibonacci(36), Fibonacci(37), + Fibonacci(38), Fibonacci(39), Fibonacci(40), Fibonacci(41), + Fibonacci(42), Fibonacci(43), Fibonacci(44), Fibonacci(45), + Fibonacci(46), Fibonacci(47), 0xffffffffffffffffull, // Avoid overflow }; static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length); -// The inlined size to use with absl::InlinedVector. -// -// Note: The InlinedVectors in this file (and in cord.h) do not need to use -// the same value for their inlined size. The fact that they do is historical. -// It may be desirable for each to use a different inlined size optimized for -// that InlinedVector's usage. -// -// TODO(jgm): Benchmark to see if there's a more optimal value than 47 for -// the inlined vector size (47 exists for backward compatibility). -static const int kInlinedVectorSize = 47; +static inline bool btree_enabled() { + return cord_internal::cord_btree_enabled.load( + std::memory_order_relaxed); +} static inline bool IsRootBalanced(CordRep* node) { - if (node->tag != CONCAT) { + if (!node->IsConcat()) { return true; } else if (node->concat()->depth() <= 15) { return true; @@ -272,7 +114,8 @@ static inline bool IsRootBalanced(CordRep* node) { } static CordRep* Rebalance(CordRep* node); -static void DumpNode(CordRep* rep, bool include_data, std::ostream* os); +static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, + int indent = 0); static bool VerifyNode(CordRep* root, CordRep* start_node, bool full_validation); @@ -292,103 +135,9 @@ static inline CordRep* VerifyTree(CordRep* node) { return node; } -// -------------------------------------------------------------------- -// Memory management - -inline CordRep* Ref(CordRep* rep) { - if (rep != nullptr) { - rep->refcount.Increment(); - } - return rep; -} - -// This internal routine is called from the cold path of Unref below. Keeping it -// in a separate routine allows good inlining of Unref into many profitable call -// sites. However, the call to this function can be highly disruptive to the -// register pressure in those callers. To minimize the cost to callers, we use -// a special LLVM calling convention that preserves most registers. This allows -// the call to this routine in cold paths to not disrupt the caller's register -// pressure. This calling convention is not available on all platforms; we -// intentionally allow LLVM to ignore the attribute rather than attempting to -// hardcode the list of supported platforms. -#if defined(__clang__) && !defined(__i386__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wattributes" -__attribute__((preserve_most)) -#pragma clang diagnostic pop -#endif -static void UnrefInternal(CordRep* rep) { - assert(rep != nullptr); - - absl::InlinedVector<CordRep*, kInlinedVectorSize> pending; - while (true) { - if (rep->tag == CONCAT) { - CordRepConcat* rep_concat = rep->concat(); - CordRep* right = rep_concat->right; - if (!right->refcount.Decrement()) { - pending.push_back(right); - } - CordRep* left = rep_concat->left; - delete rep_concat; - rep = nullptr; - if (!left->refcount.Decrement()) { - rep = left; - continue; - } - } else if (rep->tag == EXTERNAL) { - CordRepExternal* rep_external = rep->external(); - absl::string_view data(rep_external->base, rep->length); - void* releaser = GetExternalReleaser(rep_external); - size_t releaser_size = rep_external->releaser_invoker(releaser, data); - rep_external->~CordRepExternal(); - DeallocateExternal(rep_external, releaser_size); - rep = nullptr; - } else if (rep->tag == SUBSTRING) { - CordRepSubstring* rep_substring = rep->substring(); - CordRep* child = rep_substring->child; - delete rep_substring; - rep = nullptr; - if (!child->refcount.Decrement()) { - rep = child; - continue; - } - } else { - // Flat CordReps are allocated and constructed with raw ::operator new - // and placement new, and must be destructed and deallocated - // accordingly. -#if defined(__cpp_sized_deallocation) - size_t size = TagToAllocatedSize(rep->tag); - rep->~CordRep(); - ::operator delete(rep, size); -#else - rep->~CordRep(); - ::operator delete(rep); -#endif - rep = nullptr; - } - - if (!pending.empty()) { - rep = pending.back(); - pending.pop_back(); - } else { - break; - } - } -} - -inline void Unref(CordRep* rep) { - // Fast-path for two common, hot cases: a null rep and a shared root. - if (ABSL_PREDICT_TRUE(rep == nullptr || - rep->refcount.DecrementExpectHighRefcount())) { - return; - } - - UnrefInternal(rep); -} - // Return the depth of a node static int Depth(const CordRep* rep) { - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { return rep->concat()->depth(); } else { return 0; @@ -409,17 +158,19 @@ static void SetConcatChildren(CordRepConcat* concat, CordRep* left, // The returned node has a refcount of 1. static CordRep* RawConcat(CordRep* left, CordRep* right) { // Avoid making degenerate concat nodes (one child is empty) - if (left == nullptr || left->length == 0) { - Unref(left); + if (left == nullptr) return right; + if (right == nullptr) return left; + if (left->length == 0) { + CordRep::Unref(left); return right; } - if (right == nullptr || right->length == 0) { - Unref(right); + if (right->length == 0) { + CordRep::Unref(right); return left; } CordRepConcat* rep = new CordRepConcat(); - rep->tag = CONCAT; + rep->tag = cord_internal::CONCAT; SetConcatChildren(rep, left, right); return rep; @@ -453,35 +204,41 @@ static CordRep* MakeBalancedTree(CordRep** reps, size_t n) { return reps[0]; } -// Create a new flat node. -static CordRep* NewFlat(size_t length_hint) { - if (length_hint <= kMinFlatLength) { - length_hint = kMinFlatLength; - } else if (length_hint > kMaxFlatLength) { - length_hint = kMaxFlatLength; - } +static CordRepFlat* CreateFlat(const char* data, size_t length, + size_t alloc_hint) { + CordRepFlat* flat = CordRepFlat::New(length + alloc_hint); + flat->length = length; + memcpy(flat->Data(), data, length); + return flat; +} - // Round size up so it matches a size we can exactly express in a tag. - const size_t size = RoundUpForTag(length_hint + kFlatOverhead); - void* const raw_rep = ::operator new(size); - CordRep* rep = new (raw_rep) CordRep(); - rep->tag = AllocatedSizeToTag(size); - return VerifyTree(rep); +// Creates a new flat or Btree out of the specified array. +// The returned node has a refcount of 1. +static CordRep* NewBtree(const char* data, size_t length, size_t alloc_hint) { + if (length <= kMaxFlatLength) { + return CreateFlat(data, length, alloc_hint); + } + CordRepFlat* flat = CreateFlat(data, kMaxFlatLength, 0); + data += kMaxFlatLength; + length -= kMaxFlatLength; + auto* root = CordRepBtree::Create(flat); + return CordRepBtree::Append(root, {data, length}, alloc_hint); } // Create a new tree out of the specified array. // The returned node has a refcount of 1. -static CordRep* NewTree(const char* data, - size_t length, - size_t alloc_hint) { +static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) { if (length == 0) return nullptr; + if (btree_enabled()) { + return NewBtree(data, length, alloc_hint); + } absl::FixedArray<CordRep*> reps((length - 1) / kMaxFlatLength + 1); size_t n = 0; do { const size_t len = std::min(length, kMaxFlatLength); - CordRep* rep = NewFlat(len + alloc_hint); + CordRepFlat* rep = CordRepFlat::New(len + alloc_hint); rep->length = len; - memcpy(rep->data, data, len); + memcpy(rep->Data(), data, len); reps[n++] = VerifyTree(rep); data += len; length -= len; @@ -491,18 +248,12 @@ static CordRep* NewTree(const char* data, namespace cord_internal { -ExternalRepReleaserPair NewExternalWithUninitializedReleaser( - absl::string_view data, ExternalReleaserInvoker invoker, - size_t releaser_size) { +void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) { assert(!data.empty()); - - void* raw_rep = AllocateExternal(releaser_size); - auto* rep = new (raw_rep) CordRepExternal(); rep->length = data.size(); rep->tag = EXTERNAL; rep->base = data.data(); - rep->releaser_invoker = invoker; - return {VerifyTree(rep), GetExternalReleaser(rep)}; + VerifyTree(rep); } } // namespace cord_internal @@ -510,87 +261,155 @@ ExternalRepReleaserPair NewExternalWithUninitializedReleaser( static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) { // Never create empty substring nodes if (length == 0) { - Unref(child); + CordRep::Unref(child); return nullptr; } else { CordRepSubstring* rep = new CordRepSubstring(); assert((offset + length) <= child->length); rep->length = length; - rep->tag = SUBSTRING; + rep->tag = cord_internal::SUBSTRING; rep->start = offset; rep->child = child; return VerifyTree(rep); } } +// Creates a CordRep from the provided string. If the string is large enough, +// and not wasteful, we move the string into an external cord rep, preserving +// the already allocated string contents. +// Requires the provided string length to be larger than `kMaxInline`. +static CordRep* CordRepFromString(std::string&& src) { + assert(src.length() > cord_internal::kMaxInline); + if ( + // String is short: copy data to avoid external block overhead. + src.size() <= kMaxBytesToCopy || + // String is wasteful: copy data to avoid pinning too much unused memory. + src.size() < src.capacity() / 2 + ) { + return NewTree(src.data(), src.size(), 0); + } + + struct StringReleaser { + void operator()(absl::string_view /* data */) {} + std::string data; + }; + const absl::string_view original_data = src; + auto* rep = + static_cast<::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>( + absl::cord_internal::NewExternalRep(original_data, + StringReleaser{std::move(src)})); + // Moving src may have invalidated its data pointer, so adjust it. + rep->base = rep->template get<0>().data.data(); + return rep; +} + // -------------------------------------------------------------------- // Cord::InlineRep functions -// This will trigger LNK2005 in MSVC. -#ifndef COMPILER_MSVC -const unsigned char Cord::InlineRep::kMaxInline; -#endif // COMPILER_MSVC +constexpr unsigned char Cord::InlineRep::kMaxInline; inline void Cord::InlineRep::set_data(const char* data, size_t n, bool nullify_tail) { static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15"); - cord_internal::SmallMemmove(data_, data, n, nullify_tail); - data_[kMaxInline] = static_cast<char>(n); + cord_internal::SmallMemmove(data_.as_chars(), data, n, nullify_tail); + set_inline_size(n); } inline char* Cord::InlineRep::set_data(size_t n) { assert(n <= kMaxInline); - memset(data_, 0, sizeof(data_)); - data_[kMaxInline] = static_cast<char>(n); - return data_; -} - -inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) { - size_t len = data_[kMaxInline]; - CordRep* result; - if (len > kMaxInline) { - memcpy(&result, data_, sizeof(result)); - } else { - result = NewFlat(len + extra_hint); - result->length = len; - memcpy(result->data, data_, len); - set_tree(result); - } - return result; + ResetToEmpty(); + set_inline_size(n); + return data_.as_chars(); } inline void Cord::InlineRep::reduce_size(size_t n) { - size_t tag = data_[kMaxInline]; + size_t tag = inline_size(); assert(tag <= kMaxInline); assert(tag >= n); tag -= n; - memset(data_ + tag, 0, n); - data_[kMaxInline] = static_cast<char>(tag); + memset(data_.as_chars() + tag, 0, n); + set_inline_size(static_cast<char>(tag)); } inline void Cord::InlineRep::remove_prefix(size_t n) { - cord_internal::SmallMemmove(data_, data_ + n, data_[kMaxInline] - n); + cord_internal::SmallMemmove(data_.as_chars(), data_.as_chars() + n, + inline_size() - n); reduce_size(n); } -void Cord::InlineRep::AppendTree(CordRep* tree) { - if (tree == nullptr) return; - size_t len = data_[kMaxInline]; - if (len == 0) { - set_tree(tree); +// Returns `rep` converted into a CordRepBtree. +// Directly returns `rep` if `rep` is already a CordRepBtree. +static CordRepBtree* ForceBtree(CordRep* rep) { + return rep->IsBtree() ? rep->btree() : CordRepBtree::Create(rep); +} + +void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, + MethodIdentifier method) { + assert(!is_tree()); + if (!data_.is_empty()) { + CordRepFlat* flat = MakeFlatWithExtraCapacity(0); + if (btree_enabled()) { + tree = CordRepBtree::Append(CordRepBtree::Create(flat), tree); + } else { + tree = Concat(flat, tree); + } + } + EmplaceTree(tree, method); +} + +void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) { + assert(is_tree()); + const CordzUpdateScope scope(data_.cordz_info(), method); + if (btree_enabled()) { + tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree); } else { - set_tree(Concat(force_tree(0), tree)); + tree = Concat(data_.as_tree(), tree); } + SetTree(tree, scope); } -void Cord::InlineRep::PrependTree(CordRep* tree) { +void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) { if (tree == nullptr) return; - size_t len = data_[kMaxInline]; - if (len == 0) { - set_tree(tree); + if (data_.is_tree()) { + AppendTreeToTree(tree, method); } else { - set_tree(Concat(tree, force_tree(0))); + AppendTreeToInlined(tree, method); + } +} + +void Cord::InlineRep::PrependTreeToInlined(CordRep* tree, + MethodIdentifier method) { + assert(!is_tree()); + if (!data_.is_empty()) { + CordRepFlat* flat = MakeFlatWithExtraCapacity(0); + if (btree_enabled()) { + tree = CordRepBtree::Prepend(CordRepBtree::Create(flat), tree); + } else { + tree = Concat(tree, flat); + } + } + EmplaceTree(tree, method); +} + +void Cord::InlineRep::PrependTreeToTree(CordRep* tree, + MethodIdentifier method) { + assert(is_tree()); + const CordzUpdateScope scope(data_.cordz_info(), method); + if (btree_enabled()) { + tree = CordRepBtree::Prepend(ForceBtree(data_.as_tree()), tree); + } else { + tree = Concat(tree, data_.as_tree()); + } + SetTree(tree, scope); +} + +void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { + assert(tree != nullptr); + if (data_.is_tree()) { + PrependTreeToTree(tree, method); + } else { + PrependTreeToInlined(tree, method); } } @@ -600,20 +419,29 @@ void Cord::InlineRep::PrependTree(CordRep* tree) { // written to region and the actual size increase will be written to size. static inline bool PrepareAppendRegion(CordRep* root, char** region, size_t* size, size_t max_length) { + if (root->IsBtree() && root->refcount.IsMutable()) { + Span<char> span = root->btree()->GetAppendBuffer(max_length); + if (!span.empty()) { + *region = span.data(); + *size = span.size(); + return true; + } + } + // Search down the right-hand path for a non-full FLAT node. CordRep* dst = root; - while (dst->tag == CONCAT && dst->refcount.IsOne()) { + while (dst->IsConcat() && dst->refcount.IsMutable()) { dst = dst->concat()->right; } - if (dst->tag < FLAT || !dst->refcount.IsOne()) { + if (!dst->IsFlat() || !dst->refcount.IsMutable()) { *region = nullptr; *size = 0; return false; } const size_t in_use = dst->length; - const size_t capacity = TagToLength(dst->tag); + const size_t capacity = dst->flat()->Capacity(); if (in_use == capacity) { *region = nullptr; *size = 0; @@ -628,214 +456,281 @@ static inline bool PrepareAppendRegion(CordRep* root, char** region, } dst->length += size_increase; - *region = dst->data + in_use; + *region = dst->flat()->Data() + in_use; *size = size_increase; return true; } +template <bool has_length> void Cord::InlineRep::GetAppendRegion(char** region, size_t* size, - size_t max_length) { - if (max_length == 0) { - *region = nullptr; - *size = 0; - return; - } + size_t length) { + auto constexpr method = CordzUpdateTracker::kGetAppendRegion; - // Try to fit in the inline buffer if possible. - size_t inline_length = data_[kMaxInline]; - if (inline_length < kMaxInline && max_length <= kMaxInline - inline_length) { - *region = data_ + inline_length; - *size = max_length; - data_[kMaxInline] = static_cast<char>(inline_length + max_length); - return; + CordRep* root = tree(); + size_t sz = root ? root->length : inline_size(); + if (root == nullptr) { + size_t available = kMaxInline - sz; + if (available >= (has_length ? length : 1)) { + *region = data_.as_chars() + sz; + *size = has_length ? length : available; + set_inline_size(has_length ? sz + length : kMaxInline); + return; + } } - CordRep* root = force_tree(max_length); - - if (PrepareAppendRegion(root, region, size, max_length)) { + size_t extra = has_length ? length : (std::max)(sz, kMinFlatLength); + CordRep* rep = root ? root : MakeFlatWithExtraCapacity(extra); + CordzUpdateScope scope(root ? data_.cordz_info() : nullptr, method); + if (PrepareAppendRegion(rep, region, size, length)) { + CommitTree(root, rep, scope, method); return; } // Allocate new node. - CordRep* new_node = - NewFlat(std::max(static_cast<size_t>(root->length), max_length)); - new_node->length = - std::min(static_cast<size_t>(TagToLength(new_node->tag)), max_length); - *region = new_node->data; + CordRepFlat* new_node = CordRepFlat::New(extra); + new_node->length = std::min(new_node->Capacity(), length); + *region = new_node->Data(); *size = new_node->length; - replace_tree(Concat(root, new_node)); -} - -void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) { - const size_t max_length = std::numeric_limits<size_t>::max(); - // Try to fit in the inline buffer if possible. - size_t inline_length = data_[kMaxInline]; - if (inline_length < kMaxInline) { - *region = data_ + inline_length; - *size = kMaxInline - inline_length; - data_[kMaxInline] = kMaxInline; - return; + if (btree_enabled()) { + rep = CordRepBtree::Append(ForceBtree(rep), new_node); + } else { + rep = Concat(rep, new_node); } + CommitTree(root, rep, scope, method); +} - CordRep* root = force_tree(max_length); - - if (PrepareAppendRegion(root, region, size, max_length)) { - return; +// Computes the memory side of the provided edge which must be a valid data edge +// for a btrtee, i.e., a FLAT, EXTERNAL or SUBSTRING of a FLAT or EXTERNAL node. +static bool RepMemoryUsageDataEdge(const CordRep* rep, + size_t* total_mem_usage) { + size_t maybe_sub_size = 0; + if (ABSL_PREDICT_FALSE(rep->IsSubstring())) { + maybe_sub_size = sizeof(cord_internal::CordRepSubstring); + rep = rep->substring()->child; } - - // Allocate new node. - CordRep* new_node = NewFlat(root->length); - new_node->length = TagToLength(new_node->tag); - *region = new_node->data; - *size = new_node->length; - replace_tree(Concat(root, new_node)); + if (rep->IsFlat()) { + *total_mem_usage += maybe_sub_size + rep->flat()->AllocatedSize(); + return true; + } + if (rep->IsExternal()) { + // We don't know anything about the embedded / bound data, but we can safely + // assume it is 'at least' a word / pointer to data. In the future we may + // choose to use the 'data' byte as a tag to identify the types of some + // well-known externals, such as a std::string instance. + *total_mem_usage += maybe_sub_size + + sizeof(cord_internal::CordRepExternalImpl<intptr_t>) + + rep->length; + return true; + } + return false; } // If the rep is a leaf, this will increment the value at total_mem_usage and // will return true. static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) { - if (rep->tag >= FLAT) { - *total_mem_usage += TagToAllocatedSize(rep->tag); + if (rep->IsFlat()) { + *total_mem_usage += rep->flat()->AllocatedSize(); return true; } - if (rep->tag == EXTERNAL) { - *total_mem_usage += sizeof(CordRepConcat) + rep->length; + if (rep->IsExternal()) { + // We don't know anything about the embedded / bound data, but we can safely + // assume it is 'at least' a word / pointer to data. In the future we may + // choose to use the 'data' byte as a tag to identify the types of some + // well-known externals, such as a std::string instance. + *total_mem_usage += + sizeof(cord_internal::CordRepExternalImpl<intptr_t>) + rep->length; return true; } return false; } void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) { - ClearSlow(); + assert(&src != this); + assert(is_tree() || src.is_tree()); + auto constexpr method = CordzUpdateTracker::kAssignCord; + if (ABSL_PREDICT_TRUE(!is_tree())) { + EmplaceTree(CordRep::Ref(src.as_tree()), src.data_, method); + return; + } - memcpy(data_, src.data_, sizeof(data_)); - if (is_tree()) { - Ref(tree()); + CordRep* tree = as_tree(); + if (CordRep* src_tree = src.tree()) { + // Leave any existing `cordz_info` in place, and let MaybeTrackCord() + // decide if this cord should be (or remains to be) sampled or not. + data_.set_tree(CordRep::Ref(src_tree)); + CordzInfo::MaybeTrackCord(data_, src.data_, method); + } else { + CordzInfo::MaybeUntrackCord(data_.cordz_info()); + data_ = src.data_; } + CordRep::Unref(tree); } -void Cord::InlineRep::ClearSlow() { +void Cord::InlineRep::UnrefTree() { if (is_tree()) { - Unref(tree()); + CordzInfo::MaybeUntrackCord(data_.cordz_info()); + CordRep::Unref(tree()); } - memset(data_, 0, sizeof(data_)); } // -------------------------------------------------------------------- // Constructors and destructors -Cord::Cord(const Cord& src) : contents_(src.contents_) { - Ref(contents_.tree()); // Does nothing if contents_ has embedded data -} - -Cord::Cord(absl::string_view src) { +Cord::Cord(absl::string_view src, MethodIdentifier method) + : contents_(InlineData::kDefaultInit) { const size_t n = src.size(); if (n <= InlineRep::kMaxInline) { - contents_.set_data(src.data(), n, false); + contents_.set_data(src.data(), n, true); + } else { + CordRep* rep = NewTree(src.data(), n, 0); + contents_.EmplaceTree(rep, method); + } +} + +template <typename T, Cord::EnableIfString<T>> +Cord::Cord(T&& src) : contents_(InlineData::kDefaultInit) { + if (src.size() <= InlineRep::kMaxInline) { + contents_.set_data(src.data(), src.size(), true); } else { - contents_.set_tree(NewTree(src.data(), n, 0)); + CordRep* rep = CordRepFromString(std::forward<T>(src)); + contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString); } } +template Cord::Cord(std::string&& src); + // The destruction code is separate so that the compiler can determine // that it does not need to call the destructor on a moved-from Cord. void Cord::DestroyCordSlow() { - Unref(VerifyTree(contents_.tree())); + assert(contents_.is_tree()); + CordzInfo::MaybeUntrackCord(contents_.cordz_info()); + CordRep::Unref(VerifyTree(contents_.as_tree())); } // -------------------------------------------------------------------- // Mutators void Cord::Clear() { - Unref(contents_.clear()); + if (CordRep* tree = contents_.clear()) { + CordRep::Unref(tree); + } } -Cord& Cord::operator=(absl::string_view src) { +Cord& Cord::AssignLargeString(std::string&& src) { + auto constexpr method = CordzUpdateTracker::kAssignString; + assert(src.size() > kMaxBytesToCopy); + CordRep* rep = CordRepFromString(std::move(src)); + if (CordRep* tree = contents_.tree()) { + CordzUpdateScope scope(contents_.cordz_info(), method); + contents_.SetTree(rep, scope); + CordRep::Unref(tree); + } else { + contents_.EmplaceTree(rep, method); + } + return *this; +} +Cord& Cord::operator=(absl::string_view src) { + auto constexpr method = CordzUpdateTracker::kAssignString; const char* data = src.data(); size_t length = src.size(); CordRep* tree = contents_.tree(); if (length <= InlineRep::kMaxInline) { - // Embed into this->contents_ + // Embed into this->contents_, which is somewhat subtle: + // - MaybeUntrackCord must be called before Unref(tree). + // - MaybeUntrackCord must be called before set_data() clobbers cordz_info. + // - set_data() must be called before Unref(tree) as it may reference tree. + if (tree != nullptr) CordzInfo::MaybeUntrackCord(contents_.cordz_info()); contents_.set_data(data, length, true); - Unref(tree); + if (tree != nullptr) CordRep::Unref(tree); return *this; } - if (tree != nullptr && tree->tag >= FLAT && - TagToLength(tree->tag) >= length && tree->refcount.IsOne()) { - // Copy in place if the existing FLAT node is reusable. - memmove(tree->data, data, length); - tree->length = length; - VerifyTree(tree); - return *this; + if (tree != nullptr) { + CordzUpdateScope scope(contents_.cordz_info(), method); + if (tree->IsFlat() && tree->flat()->Capacity() >= length && + tree->refcount.IsMutable()) { + // Copy in place if the existing FLAT node is reusable. + memmove(tree->flat()->Data(), data, length); + tree->length = length; + VerifyTree(tree); + return *this; + } + contents_.SetTree(NewTree(data, length, 0), scope); + CordRep::Unref(tree); + } else { + contents_.EmplaceTree(NewTree(data, length, 0), method); } - contents_.set_tree(NewTree(data, length, 0)); - Unref(tree); return *this; } // TODO(sanjay): Move to Cord::InlineRep section of file. For now, // we keep it here to make diffs easier. -void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) { - if (src_size == 0) return; // memcpy(_, nullptr, 0) is undefined. - // Try to fit in the inline buffer if possible. - size_t inline_length = data_[kMaxInline]; - if (inline_length < kMaxInline && src_size <= kMaxInline - inline_length) { - // Append new data to embedded array - data_[kMaxInline] = static_cast<char>(inline_length + src_size); - memcpy(data_ + inline_length, src_data, src_size); - return; - } - - CordRep* root = tree(); +void Cord::InlineRep::AppendArray(absl::string_view src, + MethodIdentifier method) { + if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined. size_t appended = 0; - if (root) { + CordRep* rep = tree(); + const CordRep* const root = rep; + CordzUpdateScope scope(root ? cordz_info() : nullptr, method); + if (root != nullptr) { char* region; - if (PrepareAppendRegion(root, ®ion, &appended, src_size)) { - memcpy(region, src_data, appended); + if (PrepareAppendRegion(rep, ®ion, &appended, src.size())) { + memcpy(region, src.data(), appended); } } else { - // It is possible that src_data == data_, but when we transition from an - // InlineRep to a tree we need to assign data_ = root via set_tree. To - // avoid corrupting the source data before we copy it, delay calling - // set_tree until after we've copied data. - // We are going from an inline size to beyond inline size. Make the new size - // either double the inlined size, or the added size + 10%. - const size_t size1 = inline_length * 2 + src_size; - const size_t size2 = inline_length + src_size / 10; - root = NewFlat(std::max<size_t>(size1, size2)); - appended = std::min(src_size, TagToLength(root->tag) - inline_length); - memcpy(root->data, data_, inline_length); - memcpy(root->data + inline_length, src_data, appended); - root->length = inline_length + appended; - set_tree(root); - } - - src_data += appended; - src_size -= appended; - if (src_size == 0) { + // Try to fit in the inline buffer if possible. + size_t inline_length = inline_size(); + if (src.size() <= kMaxInline - inline_length) { + // Append new data to embedded array + memcpy(data_.as_chars() + inline_length, src.data(), src.size()); + set_inline_size(inline_length + src.size()); + return; + } + + // Allocate flat to be a perfect fit on first append exceeding inlined size. + // Subsequent growth will use amortized growth until we reach maximum flat + // size. + rep = CordRepFlat::New(inline_length + src.size()); + appended = std::min(src.size(), rep->flat()->Capacity() - inline_length); + memcpy(rep->flat()->Data(), data_.as_chars(), inline_length); + memcpy(rep->flat()->Data() + inline_length, src.data(), appended); + rep->length = inline_length + appended; + } + + src.remove_prefix(appended); + if (src.empty()) { + CommitTree(root, rep, scope, method); return; } - // Use new block(s) for any remaining bytes that were not handled above. - // Alloc extra memory only if the right child of the root of the new tree is - // going to be a FLAT node, which will permit further inplace appends. - size_t length = src_size; - if (src_size < kMaxFlatLength) { - // The new length is either - // - old size + 10% - // - old_size + src_size - // This will cause a reasonable conservative step-up in size that is still - // large enough to avoid excessive amounts of small fragments being added. - length = std::max<size_t>(root->length / 10, src_size); + if (btree_enabled()) { + // TODO(b/192061034): keep legacy 10% growth rate: consider other rates. + rep = ForceBtree(rep); + const size_t min_growth = std::max<size_t>(rep->length / 10, src.size()); + rep = CordRepBtree::Append(rep->btree(), src, min_growth - src.size()); + } else { + // Use new block(s) for any remaining bytes that were not handled above. + // Alloc extra memory only if the right child of the root of the new tree + // is going to be a FLAT node, which will permit further inplace appends. + size_t length = src.size(); + if (src.size() < kMaxFlatLength) { + // The new length is either + // - old size + 10% + // - old_size + src.size() + // This will cause a reasonable conservative step-up in size that is + // still large enough to avoid excessive amounts of small fragments + // being added. + length = std::max<size_t>(rep->length / 10, src.size()); + } + rep = Concat(rep, NewTree(src.data(), src.size(), length - src.size())); } - set_tree(Concat(root, NewTree(src_data, src_size, length - src_size))); + CommitTree(root, rep, scope, method); } inline CordRep* Cord::TakeRep() const& { - return Ref(contents_.tree()); + return CordRep::Ref(contents_.tree()); } inline CordRep* Cord::TakeRep() && { @@ -846,10 +741,17 @@ inline CordRep* Cord::TakeRep() && { template <typename C> inline void Cord::AppendImpl(C&& src) { + auto constexpr method = CordzUpdateTracker::kAppendCord; if (empty()) { - // In case of an empty destination avoid allocating a new node, do not copy - // data. - *this = std::forward<C>(src); + // Since destination is empty, we can avoid allocating a node, + if (src.contents_.is_tree()) { + // by taking the tree directly + CordRep* rep = std::forward<C>(src).TakeRep(); + contents_.EmplaceTree(rep, method); + } else { + // or copying over inline data + contents_.data_ = src.contents_.data_; + } return; } @@ -859,12 +761,12 @@ inline void Cord::AppendImpl(C&& src) { CordRep* src_tree = src.contents_.tree(); if (src_tree == nullptr) { // src has embedded data. - contents_.AppendArray(src.contents_.data(), src_size); + contents_.AppendArray({src.contents_.data(), src_size}, method); return; } - if (src_tree->tag >= FLAT) { + if (src_tree->IsFlat()) { // src tree just has one flat node. - contents_.AppendArray(src_tree->data, src_size); + contents_.AppendArray({src_tree->flat()->Data(), src_size}, method); return; } if (&src == this) { @@ -879,18 +781,36 @@ inline void Cord::AppendImpl(C&& src) { return; } - contents_.AppendTree(std::forward<C>(src).TakeRep()); + // Guaranteed to be a tree (kMaxBytesToCopy > kInlinedSize) + CordRep* rep = std::forward<C>(src).TakeRep(); + contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord); +} + +void Cord::Append(const Cord& src) { + AppendImpl(src); } -void Cord::Append(const Cord& src) { AppendImpl(src); } +void Cord::Append(Cord&& src) { + AppendImpl(std::move(src)); +} -void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); } +template <typename T, Cord::EnableIfString<T>> +void Cord::Append(T&& src) { + if (src.size() <= kMaxBytesToCopy) { + Append(absl::string_view(src)); + } else { + CordRep* rep = CordRepFromString(std::forward<T>(src)); + contents_.AppendTree(rep, CordzUpdateTracker::kAppendString); + } +} + +template void Cord::Append(std::string&& src); void Cord::Prepend(const Cord& src) { CordRep* src_tree = src.contents_.tree(); if (src_tree != nullptr) { - Ref(src_tree); - contents_.PrependTree(src_tree); + CordRep::Ref(src_tree); + contents_.PrependTree(src_tree, CordzUpdateTracker::kPrependCord); return; } @@ -899,28 +819,42 @@ void Cord::Prepend(const Cord& src) { return Prepend(src_contents); } -void Cord::Prepend(absl::string_view src) { +void Cord::PrependArray(absl::string_view src, MethodIdentifier method) { if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined. - size_t cur_size = contents_.size(); - if (!contents_.is_tree() && cur_size + src.size() <= InlineRep::kMaxInline) { - // Use embedded storage. - char data[InlineRep::kMaxInline + 1] = {0}; - data[InlineRep::kMaxInline] = cur_size + src.size(); // set size - memcpy(data, src.data(), src.size()); - memcpy(data + src.size(), contents_.data(), cur_size); - memcpy(reinterpret_cast<void*>(&contents_), data, - InlineRep::kMaxInline + 1); + if (!contents_.is_tree()) { + size_t cur_size = contents_.inline_size(); + if (cur_size + src.size() <= InlineRep::kMaxInline) { + // Use embedded storage. + char data[InlineRep::kMaxInline + 1] = {0}; + memcpy(data, src.data(), src.size()); + memcpy(data + src.size(), contents_.data(), cur_size); + memcpy(contents_.data_.as_chars(), data, InlineRep::kMaxInline + 1); + contents_.set_inline_size(cur_size + src.size()); + return; + } + } + CordRep* rep = NewTree(src.data(), src.size(), 0); + contents_.PrependTree(rep, method); +} + +template <typename T, Cord::EnableIfString<T>> +inline void Cord::Prepend(T&& src) { + if (src.size() <= kMaxBytesToCopy) { + Prepend(absl::string_view(src)); } else { - contents_.PrependTree(NewTree(src.data(), src.size(), 0)); + CordRep* rep = CordRepFromString(std::forward<T>(src)); + contents_.PrependTree(rep, CordzUpdateTracker::kPrependString); } } +template void Cord::Prepend(std::string&& src); + static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { if (n >= node->length) return nullptr; - if (n == 0) return Ref(node); + if (n == 0) return CordRep::Ref(node); absl::InlinedVector<CordRep*, kInlinedVectorSize> rhs_stack; - while (node->tag == CONCAT) { + while (node->IsConcat()) { assert(n <= node->length); if (n < node->concat()->left->length) { // Push right to stack, descend left. @@ -935,19 +869,19 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { assert(n <= node->length); if (n == 0) { - Ref(node); + CordRep::Ref(node); } else { size_t start = n; size_t len = node->length - n; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { // Consider in-place update of node, similar to in RemoveSuffixFrom(). start += node->substring()->start; node = node->substring()->child; } - node = NewSubstring(Ref(node), start, len); + node = NewSubstring(CordRep::Ref(node), start, len); } while (!rhs_stack.empty()) { - node = Concat(node, Ref(rhs_stack.back())); + node = Concat(node, CordRep::Ref(rhs_stack.back())); rhs_stack.pop_back(); } return node; @@ -958,11 +892,11 @@ static CordRep* RemovePrefixFrom(CordRep* node, size_t n) { // edited in place iff that node and all its ancestors have a refcount of 1. static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { if (n >= node->length) return nullptr; - if (n == 0) return Ref(node); + if (n == 0) return CordRep::Ref(node); absl::InlinedVector<CordRep*, kInlinedVectorSize> lhs_stack; - bool inplace_ok = node->refcount.IsOne(); + bool inplace_ok = node->refcount.IsMutable(); - while (node->tag == CONCAT) { + while (node->IsConcat()) { assert(n <= node->length); if (n < node->concat()->right->length) { // Push left to stack, descend right. @@ -973,28 +907,28 @@ static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) { n -= node->concat()->right->length; node = node->concat()->left; } - inplace_ok = inplace_ok && node->refcount.IsOne(); + inplace_ok = inplace_ok && node->refcount.IsMutable(); } assert(n <= node->length); if (n == 0) { - Ref(node); - } else if (inplace_ok && node->tag != EXTERNAL) { + CordRep::Ref(node); + } else if (inplace_ok && !node->IsExternal()) { // Consider making a new buffer if the current node capacity is much // larger than the new length. - Ref(node); + CordRep::Ref(node); node->length -= n; } else { size_t start = 0; size_t len = node->length - n; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { start = node->substring()->start; node = node->substring()->child; } - node = NewSubstring(Ref(node), start, len); + node = NewSubstring(CordRep::Ref(node), start, len); } while (!lhs_stack.empty()) { - node = Concat(Ref(lhs_stack.back()), node); + node = Concat(CordRep::Ref(lhs_stack.back()), node); lhs_stack.pop_back(); } return node; @@ -1008,9 +942,18 @@ void Cord::RemovePrefix(size_t n) { if (tree == nullptr) { contents_.remove_prefix(n); } else { - CordRep* newrep = RemovePrefixFrom(tree, n); - Unref(tree); - contents_.replace_tree(VerifyTree(newrep)); + auto constexpr method = CordzUpdateTracker::kRemovePrefix; + CordzUpdateScope scope(contents_.cordz_info(), method); + if (tree->IsBtree()) { + CordRep* old = tree; + tree = tree->btree()->SubTree(n, tree->length - n); + CordRep::Unref(old); + } else { + CordRep* newrep = RemovePrefixFrom(tree, n); + CordRep::Unref(tree); + tree = VerifyTree(newrep); + } + contents_.SetTreeOrEmpty(tree, scope); } } @@ -1022,9 +965,16 @@ void Cord::RemoveSuffix(size_t n) { if (tree == nullptr) { contents_.reduce_size(n); } else { - CordRep* newrep = RemoveSuffixFrom(tree, n); - Unref(tree); - contents_.replace_tree(VerifyTree(newrep)); + auto constexpr method = CordzUpdateTracker::kRemoveSuffix; + CordzUpdateScope scope(contents_.cordz_info(), method); + if (tree->IsBtree()) { + tree = CordRepBtree::RemoveSuffix(tree->btree(), n); + } else { + CordRep* newrep = RemoveSuffixFrom(tree, n); + CordRep::Unref(tree); + tree = VerifyTree(newrep); + } + contents_.SetTreeOrEmpty(tree, scope); } } @@ -1056,13 +1006,13 @@ static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) { results.pop_back(); results.push_back(Concat(left, right)); } else if (pos == 0 && n == node->length) { - results.push_back(Ref(node)); - } else if (node->tag != CONCAT) { - if (node->tag == SUBSTRING) { + results.push_back(CordRep::Ref(node)); + } else if (!node->IsConcat()) { + if (node->IsSubstring()) { pos += node->substring()->start; node = node->substring()->child; } - results.push_back(NewSubstring(Ref(node), pos, n)); + results.push_back(NewSubstring(CordRep::Ref(node), pos, n)); } else if (pos + n <= node->concat()->left->length) { todo.push_back(SubRange(node->concat()->left, pos, n)); } else if (pos >= node->concat()->left->length) { @@ -1084,17 +1034,20 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { size_t length = size(); if (pos > length) pos = length; if (new_size > length - pos) new_size = length - pos; + if (new_size == 0) return sub_cord; + CordRep* tree = contents_.tree(); if (tree == nullptr) { // sub_cord is newly constructed, no need to re-zero-out the tail of // contents_ memory. sub_cord.contents_.set_data(contents_.data() + pos, new_size, false); - } else if (new_size == 0) { - // We want to return empty subcord, so nothing to do. - } else if (new_size <= InlineRep::kMaxInline) { + return sub_cord; + } + + if (new_size <= InlineRep::kMaxInline) { + char* dest = sub_cord.contents_.data_.as_chars(); Cord::ChunkIterator it = chunk_begin(); it.AdvanceBytes(pos); - char* dest = sub_cord.contents_.data_; size_t remaining_size = new_size; while (remaining_size > it->size()) { cord_internal::SmallMemmove(dest, it->data(), it->size()); @@ -1103,10 +1056,17 @@ Cord Cord::Subcord(size_t pos, size_t new_size) const { ++it; } cord_internal::SmallMemmove(dest, it->data(), remaining_size); - sub_cord.contents_.data_[InlineRep::kMaxInline] = new_size; + sub_cord.contents_.set_inline_size(new_size); + return sub_cord; + } + + if (tree->IsBtree()) { + tree = tree->btree()->SubTree(pos, new_size); } else { - sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size)); + tree = NewSubRange(tree, pos, new_size); } + sub_cord.contents_.EmplaceTree(tree, contents_.data_, + CordzUpdateTracker::kSubCord); return sub_cord; } @@ -1125,7 +1085,7 @@ class CordForest { CordRep* node = pending.back(); pending.pop_back(); CheckNode(node); - if (ABSL_PREDICT_FALSE(node->tag != CONCAT)) { + if (ABSL_PREDICT_FALSE(!node->IsConcat())) { AddNode(node); continue; } @@ -1140,9 +1100,9 @@ class CordForest { concat_node->left = concat_freelist_; concat_freelist_ = concat_node; } else { - Ref(concat_node->right); - Ref(concat_node->left); - Unref(concat_node); + CordRep::Ref(concat_node->right); + CordRep::Ref(concat_node->left); + CordRep::Unref(concat_node); } } else { AddNode(node); @@ -1175,7 +1135,7 @@ class CordForest { void AddNode(CordRep* node) { CordRep* sum = nullptr; - // Collect together everything with which we will merge node + // Collect together everything with which we will merge with node int i = 0; for (; node->length > min_length[i + 1]; ++i) { auto& tree_at_i = trees_[i]; @@ -1219,7 +1179,7 @@ class CordForest { static void CheckNode(CordRep* node) { ABSL_INTERNAL_CHECK(node->length != 0u, ""); - if (node->tag == CONCAT) { + if (node->IsConcat()) { ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ""); ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, ""); ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length + @@ -1239,7 +1199,7 @@ class CordForest { static CordRep* Rebalance(CordRep* node) { VerifyTree(node); - assert(node->tag == CONCAT); + assert(node->IsConcat()); if (node->length == 0) { return nullptr; @@ -1289,25 +1249,33 @@ bool ComputeCompareResult<bool>(int memcmp_res) { } // namespace -// Helper routine. Locates the first flat chunk of the Cord without -// initializing the iterator. +// Helper routine. Locates the first flat or external chunk of the Cord without +// initializing the iterator, and returns a string_view referencing the data. inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { - size_t n = data_[kMaxInline]; - if (n <= kMaxInline) { - return absl::string_view(data_, n); + if (!is_tree()) { + return absl::string_view(data_.as_chars(), data_.inline_size()); } CordRep* node = tree(); - if (node->tag >= FLAT) { - return absl::string_view(node->data, node->length); + if (node->IsFlat()) { + return absl::string_view(node->flat()->Data(), node->length); } - if (node->tag == EXTERNAL) { + if (node->IsExternal()) { return absl::string_view(node->external()->base, node->length); } + if (node->IsBtree()) { + CordRepBtree* tree = node->btree(); + int height = tree->height(); + while (--height >= 0) { + tree = tree->Edge(CordRepBtree::kFront)->btree(); + } + return tree->Data(tree->begin()); + } + // Walk down the left branches until we hit a non-CONCAT node. - while (node->tag == CONCAT) { + while (node->IsConcat()) { node = node->concat()->left; } @@ -1316,16 +1284,16 @@ inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const { size_t length = node->length; assert(length != 0); - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - if (node->tag >= FLAT) { - return absl::string_view(node->data + offset, length); + if (node->IsFlat()) { + return absl::string_view(node->flat()->Data() + offset, length); } - assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here"); + assert(node->IsExternal() && "Expect FLAT or EXTERNAL node here"); return absl::string_view(node->external()->base + offset, length); } @@ -1505,48 +1473,47 @@ void Cord::CopyToArraySlowPath(char* dst) const { } } -Cord::ChunkIterator& Cord::ChunkIterator::operator++() { - assert(bytes_remaining_ > 0 && "Attempted to iterate past `end()`"); - assert(bytes_remaining_ >= current_chunk_.size()); - bytes_remaining_ -= current_chunk_.size(); - - if (stack_of_right_children_.empty()) { +Cord::ChunkIterator& Cord::ChunkIterator::AdvanceStack() { + auto& stack_of_right_children = stack_of_right_children_; + if (stack_of_right_children.empty()) { assert(!current_chunk_.empty()); // Called on invalid iterator. // We have reached the end of the Cord. return *this; } // Process the next node on the stack. - CordRep* node = stack_of_right_children_.back(); - stack_of_right_children_.pop_back(); + CordRep* node = stack_of_right_children.back(); + stack_of_right_children.pop_back(); // Walk down the left branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { - stack_of_right_children_.push_back(node->concat()->right); + while (node->IsConcat()) { + stack_of_right_children.push_back(node->concat()->right); node = node->concat()->left; } // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length != 0); const char* data = - node->tag == EXTERNAL ? node->external()->base : node->data; + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset, length); current_leaf_ = node; return *this; } Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { - assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`"); + ABSL_HARDENING_ASSERT(bytes_remaining_ >= n && + "Attempted to iterate past `end()`"); Cord subcord; + auto constexpr method = CordzUpdateTracker::kCordReader; if (n <= InlineRep::kMaxInline) { // Range to read fits in inline data. Flatten it. @@ -1565,14 +1532,34 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { } return subcord; } + + if (btree_reader_) { + size_t chunk_size = current_chunk_.size(); + if (n <= chunk_size && n <= kMaxBytesToCopy) { + subcord = Cord(current_chunk_.substr(0, n), method); + if (n < chunk_size) { + current_chunk_.remove_prefix(n); + } else { + current_chunk_ = btree_reader_.Next(); + } + } else { + CordRep* rep; + current_chunk_ = btree_reader_.Read(n, chunk_size, rep); + subcord.contents_.EmplaceTree(rep, method); + } + bytes_remaining_ -= n; + return subcord; + } + + auto& stack_of_right_children = stack_of_right_children_; if (n < current_chunk_.size()) { // Range to read is a proper subrange of the current chunk. assert(current_leaf_ != nullptr); - CordRep* subnode = Ref(current_leaf_); - const char* data = - subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data; + CordRep* subnode = CordRep::Ref(current_leaf_); + const char* data = subnode->IsExternal() ? subnode->external()->base + : subnode->flat()->Data(); subnode = NewSubstring(subnode, current_chunk_.data() - data, n); - subcord.contents_.set_tree(VerifyTree(subnode)); + subcord.contents_.EmplaceTree(VerifyTree(subnode), method); RemoveChunkPrefix(n); return subcord; } @@ -1580,10 +1567,10 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Range to read begins with a proper subrange of the current chunk. assert(!current_chunk_.empty()); assert(current_leaf_ != nullptr); - CordRep* subnode = Ref(current_leaf_); + CordRep* subnode = CordRep::Ref(current_leaf_); if (current_chunk_.size() < subnode->length) { - const char* data = - subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data; + const char* data = subnode->IsExternal() ? subnode->external()->base + : subnode->flat()->Data(); subnode = NewSubstring(subnode, current_chunk_.data() - data, current_chunk_.size()); } @@ -1593,20 +1580,20 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Process the next node(s) on the stack, reading whole subtrees depending on // their length and how many bytes we are advancing. CordRep* node = nullptr; - while (!stack_of_right_children_.empty()) { - node = stack_of_right_children_.back(); - stack_of_right_children_.pop_back(); + while (!stack_of_right_children.empty()) { + node = stack_of_right_children.back(); + stack_of_right_children.pop_back(); if (node->length > n) break; // TODO(qrczak): This might unnecessarily recreate existing concat nodes. // Avoiding that would need pretty complicated logic (instead of - // current_leaf_, keep current_subtree_ which points to the highest node + // current_leaf, keep current_subtree_ which points to the highest node // such that the current leaf can be found on the path of left children // starting from current_subtree_; delay creating subnode while node is // below current_subtree_; find the proper node along the path of left // children starting from current_subtree_ if this loop exits while staying // below current_subtree_; etc.; alternatively, push parents instead of // right children on the stack). - subnode = Concat(subnode, Ref(node)); + subnode = Concat(subnode, CordRep::Ref(node)); n -= node->length; bytes_remaining_ -= node->length; node = nullptr; @@ -1615,20 +1602,20 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { if (node == nullptr) { // We have reached the end of the Cord. assert(bytes_remaining_ == 0); - subcord.contents_.set_tree(VerifyTree(subnode)); + subcord.contents_.EmplaceTree(VerifyTree(subnode), method); return subcord; } // Walk down the appropriate branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { if (node->concat()->left->length > n) { // Push right, descend left. - stack_of_right_children_.push_back(node->concat()->right); + stack_of_right_children.push_back(node->concat()->right); node = node->concat()->left; } else { // Read left, descend right. - subnode = Concat(subnode, Ref(node->concat()->left)); + subnode = Concat(subnode, CordRep::Ref(node->concat()->left)); n -= node->concat()->left->length; bytes_remaining_ -= node->concat()->left->length; node = node->concat()->right; @@ -1638,22 +1625,24 @@ Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } // Range to read ends with a proper (possibly empty) subrange of the current // chunk. - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length > n); - if (n > 0) subnode = Concat(subnode, NewSubstring(Ref(node), offset, n)); + if (n > 0) { + subnode = Concat(subnode, NewSubstring(CordRep::Ref(node), offset, n)); + } const char* data = - node->tag == EXTERNAL ? node->external()->base : node->data; + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset + n, length - n); current_leaf_ = node; bytes_remaining_ -= n; - subcord.contents_.set_tree(VerifyTree(subnode)); + subcord.contents_.EmplaceTree(VerifyTree(subnode), method); return subcord; } @@ -1665,12 +1654,19 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { n -= current_chunk_.size(); bytes_remaining_ -= current_chunk_.size(); + if (stack_of_right_children_.empty()) { + // We have reached the end of the Cord. + assert(bytes_remaining_ == 0); + return; + } + // Process the next node(s) on the stack, skipping whole subtrees depending on // their length and how many bytes we are advancing. CordRep* node = nullptr; - while (!stack_of_right_children_.empty()) { - node = stack_of_right_children_.back(); - stack_of_right_children_.pop_back(); + auto& stack_of_right_children = stack_of_right_children_; + while (!stack_of_right_children.empty()) { + node = stack_of_right_children.back(); + stack_of_right_children.pop_back(); if (node->length > n) break; n -= node->length; bytes_remaining_ -= node->length; @@ -1685,10 +1681,10 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { // Walk down the appropriate branches until we hit a non-CONCAT node. Save the // right children to the stack for subsequent traversal. - while (node->tag == CONCAT) { + while (node->IsConcat()) { if (node->concat()->left->length > n) { // Push right, descend left. - stack_of_right_children_.push_back(node->concat()->right); + stack_of_right_children.push_back(node->concat()->right); node = node->concat()->left; } else { // Skip left, descend right. @@ -1701,22 +1697,22 @@ void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) { // Get the child node if we encounter a SUBSTRING. size_t offset = 0; size_t length = node->length; - if (node->tag == SUBSTRING) { + if (node->IsSubstring()) { offset = node->substring()->start; node = node->substring()->child; } - assert(node->tag == EXTERNAL || node->tag >= FLAT); + assert(node->IsExternal() || node->IsFlat()); assert(length > n); const char* data = - node->tag == EXTERNAL ? node->external()->base : node->data; + node->IsExternal() ? node->external()->base : node->flat()->Data(); current_chunk_ = absl::string_view(data + offset + n, length - n); current_leaf_ = node; bytes_remaining_ -= n; } char Cord::operator[](size_t i) const { - assert(i < size()); + ABSL_HARDENING_ASSERT(i < size()); size_t offset = i; const CordRep* rep = contents_.tree(); if (rep == nullptr) { @@ -1725,13 +1721,15 @@ char Cord::operator[](size_t i) const { while (true) { assert(rep != nullptr); assert(offset < rep->length); - if (rep->tag >= FLAT) { + if (rep->IsFlat()) { // Get the "i"th character directly from the flat array. - return rep->data[offset]; - } else if (rep->tag == EXTERNAL) { + return rep->flat()->Data()[offset]; + } else if (rep->IsBtree()) { + return rep->btree()->GetCharacter(offset); + } else if (rep->IsExternal()) { // Get the "i"th character from the external array. return rep->external()->base[offset]; - } else if (rep->tag == CONCAT) { + } else if (rep->IsConcat()) { // Recursively branch to the side of the concatenation that the "i"th // character is on. size_t left_length = rep->concat()->left->length; @@ -1743,7 +1741,7 @@ char Cord::operator[](size_t i) const { } } else { // This must be a substring a node, so bypass it to get to the child. - assert(rep->tag == SUBSTRING); + assert(rep->IsSubstring()); offset += rep->substring()->start; rep = rep->substring()->child; } @@ -1751,6 +1749,7 @@ char Cord::operator[](size_t i) const { } absl::string_view Cord::FlattenSlowPath() { + assert(contents_.is_tree()); size_t total_size = size(); CordRep* new_rep; char* new_buffer; @@ -1758,9 +1757,9 @@ absl::string_view Cord::FlattenSlowPath() { // Try to put the contents into a new flat rep. If they won't fit in the // biggest possible flat node, use an external rep instead. if (total_size <= kMaxFlatLength) { - new_rep = NewFlat(total_size); + new_rep = CordRepFlat::New(total_size); new_rep->length = total_size; - new_buffer = new_rep->data; + new_buffer = new_rep->flat()->Data(); CopyToArraySlowPath(new_buffer); } else { new_buffer = std::allocator<char>().allocate(total_size); @@ -1771,29 +1770,35 @@ absl::string_view Cord::FlattenSlowPath() { s.size()); }); } - Unref(contents_.tree()); - contents_.set_tree(new_rep); + CordzUpdateScope scope(contents_.cordz_info(), CordzUpdateTracker::kFlatten); + CordRep::Unref(contents_.as_tree()); + contents_.SetTree(new_rep, scope); return absl::string_view(new_buffer, total_size); } /* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) { assert(rep != nullptr); - if (rep->tag >= FLAT) { - *fragment = absl::string_view(rep->data, rep->length); + if (rep->IsFlat()) { + *fragment = absl::string_view(rep->flat()->Data(), rep->length); return true; - } else if (rep->tag == EXTERNAL) { + } else if (rep->IsExternal()) { *fragment = absl::string_view(rep->external()->base, rep->length); return true; - } else if (rep->tag == SUBSTRING) { + } else if (rep->IsBtree()) { + return rep->btree()->IsFlat(fragment); + } else if (rep->IsSubstring()) { CordRep* child = rep->substring()->child; - if (child->tag >= FLAT) { - *fragment = - absl::string_view(child->data + rep->substring()->start, rep->length); + if (child->IsFlat()) { + *fragment = absl::string_view( + child->flat()->Data() + rep->substring()->start, rep->length); return true; - } else if (child->tag == EXTERNAL) { + } else if (child->IsExternal()) { *fragment = absl::string_view( child->external()->base + rep->substring()->start, rep->length); return true; + } else if (child->IsBtree()) { + return child->btree()->IsFlat(rep->substring()->start, rep->length, + fragment); } } return false; @@ -1802,6 +1807,15 @@ absl::string_view Cord::FlattenSlowPath() { /* static */ void Cord::ForEachChunkAux( absl::cord_internal::CordRep* rep, absl::FunctionRef<void(absl::string_view)> callback) { + if (rep->IsBtree()) { + ChunkIterator it(rep), end; + while (it != end) { + callback(*it); + ++it; + } + return; + } + assert(rep != nullptr); int stack_pos = 0; constexpr int stack_max = 128; @@ -1809,7 +1823,7 @@ absl::string_view Cord::FlattenSlowPath() { absl::cord_internal::CordRep* stack[stack_max]; absl::cord_internal::CordRep* current_node = rep; while (true) { - if (current_node->tag == CONCAT) { + if (current_node->IsConcat()) { if (stack_pos == stack_max) { // There's no more room on our stack array to add another right branch, // and the idea is to avoid allocations, so call this function @@ -1843,9 +1857,9 @@ absl::string_view Cord::FlattenSlowPath() { } } -static void DumpNode(CordRep* rep, bool include_data, std::ostream* os) { +static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, + int indent) { const int kIndentStep = 1; - int indent = 0; absl::InlinedVector<CordRep*, kInlinedVectorSize> stack; absl::InlinedVector<int, kInlinedVectorSize> indents; for (;;) { @@ -1856,27 +1870,29 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os) { *os << "]"; *os << " " << (IsRootBalanced(rep) ? 'b' : 'u'); *os << " " << std::setw(indent) << ""; - if (rep->tag == CONCAT) { + if (rep->IsConcat()) { *os << "CONCAT depth=" << Depth(rep) << "\n"; indent += kIndentStep; indents.push_back(indent); stack.push_back(rep->concat()->right); rep = rep->concat()->left; - } else if (rep->tag == SUBSTRING) { + } else if (rep->IsSubstring()) { *os << "SUBSTRING @ " << rep->substring()->start << "\n"; indent += kIndentStep; rep = rep->substring()->child; - } else { // Leaf - if (rep->tag == EXTERNAL) { + } else { // Leaf or ring + if (rep->IsExternal()) { *os << "EXTERNAL ["; if (include_data) *os << absl::CEscape(std::string(rep->external()->base, rep->length)); *os << "]\n"; - } else { - *os << "FLAT cap=" << TagToLength(rep->tag) << " ["; + } else if (rep->IsFlat()) { + *os << "FLAT cap=" << rep->flat()->Capacity() << " ["; if (include_data) - *os << absl::CEscape(std::string(rep->data, rep->length)); + *os << absl::CEscape(std::string(rep->flat()->Data(), rep->length)); *os << "]\n"; + } else { + CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os); } if (stack.empty()) break; rep = stack.back(); @@ -1908,7 +1924,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node)); } - if (node->tag == CONCAT) { + if (node->IsConcat()) { ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, ReportError(root, node)); ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, @@ -1920,13 +1936,13 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, worklist.push_back(node->concat()->right); worklist.push_back(node->concat()->left); } - } else if (node->tag >= FLAT) { - ABSL_INTERNAL_CHECK(node->length <= TagToLength(node->tag), + } else if (node->IsFlat()) { + ABSL_INTERNAL_CHECK(node->length <= node->flat()->Capacity(), ReportError(root, node)); - } else if (node->tag == EXTERNAL) { + } else if (node->IsExternal()) { ABSL_INTERNAL_CHECK(node->external()->base != nullptr, ReportError(root, node)); - } else if (node->tag == SUBSTRING) { + } else if (node->IsSubstring()) { ABSL_INTERNAL_CHECK( node->substring()->start < node->substring()->child->length, ReportError(root, node)); @@ -1955,7 +1971,7 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, while (true) { const CordRep* next_node = nullptr; - if (cur_node->tag == CONCAT) { + if (cur_node->IsConcat()) { total_mem_usage += sizeof(CordRepConcat); const CordRep* left = cur_node->concat()->left; if (!RepMemoryUsageLeaf(left, &total_mem_usage)) { @@ -1969,9 +1985,21 @@ static bool VerifyNode(CordRep* root, CordRep* start_node, } next_node = right; } + } else if (cur_node->IsBtree()) { + total_mem_usage += sizeof(CordRepBtree); + const CordRepBtree* node = cur_node->btree(); + if (node->height() == 0) { + for (const CordRep* edge : node->Edges()) { + RepMemoryUsageDataEdge(edge, &total_mem_usage); + } + } else { + for (const CordRep* edge : node->Edges()) { + tree_stack.push_back(edge); + } + } } else { // Since cur_node is not a leaf or a concat node it must be a substring. - assert(cur_node->tag == SUBSTRING); + assert(cur_node->IsSubstring()); total_mem_usage += sizeof(CordRepSubstring); next_node = cur_node->substring()->child; if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) { @@ -1998,14 +2026,14 @@ std::ostream& operator<<(std::ostream& out, const Cord& cord) { } namespace strings_internal { -size_t CordTestAccess::FlatOverhead() { return kFlatOverhead; } -size_t CordTestAccess::MaxFlatLength() { return kMaxFlatLength; } +size_t CordTestAccess::FlatOverhead() { return cord_internal::kFlatOverhead; } +size_t CordTestAccess::MaxFlatLength() { return cord_internal::kMaxFlatLength; } size_t CordTestAccess::FlatTagToLength(uint8_t tag) { - return TagToLength(tag); + return cord_internal::TagToLength(tag); } uint8_t CordTestAccess::LengthToTag(size_t s) { ABSL_INTERNAL_CHECK(s <= kMaxFlatLength, absl::StrCat("Invalid length ", s)); - return AllocatedSizeToTag(s + kFlatOverhead); + return cord_internal::AllocatedSizeToTag(s + cord_internal::kFlatOverhead); } size_t CordTestAccess::SizeofCordRepConcat() { return sizeof(CordRepConcat); } size_t CordTestAccess::SizeofCordRepExternal() { diff --git a/third_party/abseil-cpp/absl/strings/cord.h b/third_party/abseil-cpp/absl/strings/cord.h index 40566cbaa0..f0a1991471 100644 --- a/third_party/abseil-cpp/absl/strings/cord.h +++ b/third_party/abseil-cpp/absl/strings/cord.h @@ -11,25 +11,52 @@ // 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. - -// A Cord is a sequence of characters with some unusual access propreties. -// A Cord supports efficient insertions and deletions at the start and end of -// the byte sequence, but random access reads are slower, and random access -// modifications are not supported by the API. Cord also provides cheap copies -// (using a copy-on-write strategy) and cheap substring operations. // -// Thread safety -// ------------- +// ----------------------------------------------------------------------------- +// File: cord.h +// ----------------------------------------------------------------------------- +// +// This file defines the `absl::Cord` data structure and operations on that data +// structure. A Cord is a string-like sequence of characters optimized for +// specific use cases. Unlike a `std::string`, which stores an array of +// contiguous characters, Cord data is stored in a structure consisting of +// separate, reference-counted "chunks." (Currently, this implementation is a +// tree structure, though that implementation may change.) +// +// Because a Cord consists of these chunks, data can be added to or removed from +// a Cord during its lifetime. Chunks may also be shared between Cords. Unlike a +// `std::string`, a Cord can therefore accommodate data that changes over its +// lifetime, though it's not quite "mutable"; it can change only in the +// attachment, detachment, or rearrangement of chunks of its constituent data. +// +// A Cord provides some benefit over `std::string` under the following (albeit +// narrow) circumstances: +// +// * Cord data is designed to grow and shrink over a Cord's lifetime. Cord +// provides efficient insertions and deletions at the start and end of the +// character sequences, avoiding copies in those cases. Static data should +// generally be stored as strings. +// * External memory consisting of string-like data can be directly added to +// a Cord without requiring copies or allocations. +// * Cord data may be shared and copied cheaply. Cord provides a copy-on-write +// implementation and cheap sub-Cord operations. Copying a Cord is an O(1) +// operation. +// +// As a consequence to the above, Cord data is generally large. Small data +// should generally use strings, as construction of a Cord requires some +// overhead. Small Cords (<= 15 bytes) are represented inline, but most small +// Cords are expected to grow over their lifetimes. +// +// Note that because a Cord is made up of separate chunked data, random access +// to character data within a Cord is slower than within a `std::string`. +// +// Thread Safety +// // Cord has the same thread-safety properties as many other types like // std::string, std::vector<>, int, etc -- it is thread-compatible. In -// particular, if no thread may call a non-const method, then it is safe to -// concurrently call const methods. Copying a Cord produces a new instance that -// can be used concurrently with the original in arbitrary ways. -// -// Implementation is similar to the "Ropes" described in: -// Ropes: An alternative to strings -// Hans J. Boehm, Russ Atkinson, Michael Plass -// Software Practice and Experience, December 1995 +// particular, if threads do not call non-const methods, then it is safe to call +// const methods without synchronization. Copying a Cord produces a new instance +// that can be used concurrently with the original in arbitrary ways. #ifndef ABSL_STRINGS_CORD_H_ #define ABSL_STRINGS_CORD_H_ @@ -38,12 +65,13 @@ #include <cstddef> #include <cstdint> #include <cstring> -#include <iostream> +#include <iosfwd> #include <iterator> #include <string> +#include <type_traits> +#include "absl/base/config.h" #include "absl/base/internal/endian.h" -#include "absl/base/internal/invoke.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/macros.h" #include "absl/base/port.h" @@ -51,8 +79,18 @@ #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" #include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_reader.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/internal/cordz_functions.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_scope.h" +#include "absl/strings/internal/cordz_update_tracker.h" #include "absl/strings/internal/resize_uninitialized.h" +#include "absl/strings/internal/string_constant.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -61,12 +99,35 @@ class CordTestPeer; template <typename Releaser> Cord MakeCordFromExternal(absl::string_view, Releaser&&); void CopyCordToString(const Cord& src, std::string* dst); -namespace hash_internal { -template <typename H> -H HashFragmentedCord(H, const Cord&); -} -// A Cord is a sequence of characters. +// Cord +// +// A Cord is a sequence of characters, designed to be more efficient than a +// `std::string` in certain circumstances: namely, large string data that needs +// to change over its lifetime or shared, especially when such data is shared +// across API boundaries. +// +// A Cord stores its character data in a structure that allows efficient prepend +// and append operations. This makes a Cord useful for large string data sent +// over in a wire format that may need to be prepended or appended at some point +// during the data exchange (e.g. HTTP, protocol buffers). For example, a +// Cord is useful for storing an HTTP request, and prepending an HTTP header to +// such a request. +// +// Cords should not be used for storing general string data, however. They +// require overhead to construct and are slower than strings for random access. +// +// The Cord API provides the following common API operations: +// +// * Create or assign Cords out of existing string data, memory, or other Cords +// * Append and prepend data to an existing Cord +// * Create new Sub-Cords from existing Cord data +// * Swap Cord data and compare Cord equality +// * Write out Cord data by constructing a `std::string` +// +// Additionally, the API provides iterator utilities to iterate through Cord +// data via chunks or character bytes. +// class Cord { private: template <typename T> @@ -74,51 +135,53 @@ class Cord { absl::enable_if_t<std::is_same<T, std::string>::value, int>; public: - // -------------------------------------------------------------------- - // Constructors, destructors and helper factories + // Cord::Cord() Constructors. - // Create an empty cord + // Creates an empty Cord. constexpr Cord() noexcept; - // Cord is copyable and efficiently movable. - // The moved-from state is valid but unspecified. + // Creates a Cord from an existing Cord. Cord is copyable and efficiently + // movable. The moved-from state is valid but unspecified. Cord(const Cord& src); Cord(Cord&& src) noexcept; Cord& operator=(const Cord& x); Cord& operator=(Cord&& x) noexcept; - // Create a cord out of "src". This constructor is explicit on - // purpose so that people do not get automatic type conversions. + // Creates a Cord from a `src` string. This constructor is marked explicit to + // prevent implicit Cord constructions from arguments convertible to an + // `absl::string_view`. explicit Cord(absl::string_view src); Cord& operator=(absl::string_view src); - // These are templated to avoid ambiguities for types that are convertible to - // both `absl::string_view` and `std::string`, such as `const char*`. - // - // Note that these functions reserve the right to reuse the `string&&`'s - // memory and that they will do so in the future. + // Creates a Cord from a `std::string&&` rvalue. These constructors are + // templated to avoid ambiguities for types that are convertible to both + // `absl::string_view` and `std::string`, such as `const char*`. template <typename T, EnableIfString<T> = 0> - explicit Cord(T&& src) : Cord(absl::string_view(src)) {} + explicit Cord(T&& src); template <typename T, EnableIfString<T> = 0> Cord& operator=(T&& src); - // Destroy the cord + // Cord::~Cord() + // + // Destructs the Cord. ~Cord() { if (contents_.is_tree()) DestroyCordSlow(); } - // Creates a Cord that takes ownership of external memory. The contents of - // `data` are not copied. + // MakeCordFromExternal() + // + // Creates a Cord that takes ownership of external string memory. The + // contents of `data` are not copied to the Cord; instead, the external + // memory is added to the Cord and reference-counted. This data may not be + // changed for the life of the Cord, though it may be prepended or appended + // to. // - // This function takes a callable that is invoked when all Cords are - // finished with `data`. The data must remain live and unchanging until the - // releaser is called. The requirements for the releaser are that it: - // * is move constructible, - // * supports `void operator()(absl::string_view) const`, - // * does not have alignment requirement greater than what is guaranteed by - // ::operator new. This is dictated by alignof(std::max_align_t) before - // C++17 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ if compiling with C++17 or - // it is supported by the implementation. + // `MakeCordFromExternal()` takes a callable "releaser" that is invoked when + // the reference count for `data` reaches zero. As noted above, this data must + // remain live until the releaser is invoked. The callable releaser also must: + // + // * be move constructible + // * support `void operator()(absl::string_view) const` or `void operator()` // // Example: // @@ -127,13 +190,13 @@ class Cord { // FillBlock(block); // return absl::MakeCordFromExternal( // block->ToStringView(), - // [pool, block](absl::string_view /*ignored*/) { - // pool->FreeBlock(block); + // [pool, block](absl::string_view v) { + // pool->FreeBlock(block, v); // }); // } // - // WARNING: It's likely a bug if your releaser doesn't do anything. - // For example, consider the following: + // WARNING: Because a Cord can be reference-counted, it's likely a bug if your + // releaser doesn't do anything. For example, consider the following: // // void Foo(const char* buffer, int len) { // auto c = absl::MakeCordFromExternal(absl::string_view(buffer, len), @@ -147,97 +210,141 @@ class Cord { template <typename Releaser> friend Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser); - // -------------------------------------------------------------------- - // Mutations - + // Cord::Clear() + // + // Releases the Cord data. Any nodes that share data with other Cords, if + // applicable, will have their reference counts reduced by 1. void Clear(); + // Cord::Append() + // + // Appends data to the Cord, which may come from another Cord or other string + // data. void Append(const Cord& src); void Append(Cord&& src); void Append(absl::string_view src); template <typename T, EnableIfString<T> = 0> void Append(T&& src); + // Cord::Prepend() + // + // Prepends data to the Cord, which may come from another Cord or other string + // data. void Prepend(const Cord& src); void Prepend(absl::string_view src); template <typename T, EnableIfString<T> = 0> void Prepend(T&& src); + // Cord::RemovePrefix() + // + // Removes the first `n` bytes of a Cord. void RemovePrefix(size_t n); void RemoveSuffix(size_t n); - // Returns a new cord representing the subrange [pos, pos + new_size) of + // Cord::Subcord() + // + // Returns a new Cord representing the subrange [pos, pos + new_size) of // *this. If pos >= size(), the result is empty(). If // (pos + new_size) >= size(), the result is the subrange [pos, size()). Cord Subcord(size_t pos, size_t new_size) const; - friend void swap(Cord& x, Cord& y) noexcept; + // Cord::swap() + // + // Swaps the contents of the Cord with `other`. + void swap(Cord& other) noexcept; - // -------------------------------------------------------------------- - // Accessors + // swap() + // + // Swaps the contents of two Cords. + friend void swap(Cord& x, Cord& y) noexcept { x.swap(y); } + // Cord::size() + // + // Returns the size of the Cord. size_t size() const; + + // Cord::empty() + // + // Determines whether the given Cord is empty, returning `true` is so. bool empty() const; - // Returns the approximate number of bytes pinned by this Cord. Note that - // Cords that share memory could each be "charged" independently for the same - // shared memory. + // Cord::EstimatedMemoryUsage() + // + // Returns the *approximate* number of bytes held in full or in part by this + // Cord (which may not remain the same between invocations). Note that Cords + // that share memory could each be "charged" independently for the same shared + // memory. size_t EstimatedMemoryUsage() const; - // -------------------------------------------------------------------- - // Comparators - - // Compares 'this' Cord with rhs. This function and its relatives - // treat Cords as sequences of unsigned bytes. The comparison is a - // straightforward lexicographic comparison. Return value: + // Cord::Compare() + // + // Compares 'this' Cord with rhs. This function and its relatives treat Cords + // as sequences of unsigned bytes. The comparison is a straightforward + // lexicographic comparison. `Cord::Compare()` returns values as follows: + // // -1 'this' Cord is smaller // 0 two Cords are equal // 1 'this' Cord is larger int Compare(absl::string_view rhs) const; int Compare(const Cord& rhs) const; - // Does 'this' cord start/end with rhs + // Cord::StartsWith() + // + // Determines whether the Cord starts with the passed string data `rhs`. bool StartsWith(const Cord& rhs) const; bool StartsWith(absl::string_view rhs) const; + + // Cord::EndsWith() + // + // Determines whether the Cord ends with the passed string data `rhs`. bool EndsWith(absl::string_view rhs) const; bool EndsWith(const Cord& rhs) const; - // -------------------------------------------------------------------- - // Conversion to other types - + // Cord::operator std::string() + // + // Converts a Cord into a `std::string()`. This operator is marked explicit to + // prevent unintended Cord usage in functions that take a string. explicit operator std::string() const; - // Copies the contents from `src` to `*dst`. + // CopyCordToString() // - // This function optimizes the case of reusing the destination std::string since it + // Copies the contents of a `src` Cord into a `*dst` string. + // + // This function optimizes the case of reusing the destination string since it // can reuse previously allocated capacity. However, this function does not // guarantee that pointers previously returned by `dst->data()` remain valid // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new // object, prefer to simply use the conversion operator to `std::string`. friend void CopyCordToString(const Cord& src, std::string* dst); - // -------------------------------------------------------------------- - // Iteration - class CharIterator; - // Type for iterating over the chunks of a `Cord`. See comments for - // `Cord::chunk_begin()`, `Cord::chunk_end()` and `Cord::Chunks()` below for - // preferred usage. + //---------------------------------------------------------------------------- + // Cord::ChunkIterator + //---------------------------------------------------------------------------- + // + // A `Cord::ChunkIterator` allows iteration over the constituent chunks of its + // Cord. Such iteration allows you to perform non-const operatons on the data + // of a Cord without modifying it. + // + // Generally, you do not instantiate a `Cord::ChunkIterator` directly; + // instead, you create one implicitly through use of the `Cord::Chunks()` + // member function. + // + // The `Cord::ChunkIterator` has the following properties: // - // Additional notes: + // * The iterator is invalidated after any non-const operation on the + // Cord object over which it iterates. // * The `string_view` returned by dereferencing a valid, non-`end()` // iterator is guaranteed to be non-empty. - // * A `ChunkIterator` object is invalidated after any non-const - // operation on the `Cord` object over which it iterates. - // * Two `ChunkIterator` objects can be equality compared if and only if - // they remain valid and iterate over the same `Cord`. - // * This is a proxy iterator. This means the `string_view` returned by the - // iterator does not live inside the Cord, and its lifetime is limited to - // the lifetime of the iterator itself. To help prevent issues, - // `ChunkIterator::reference` is not a true reference type and is - // equivalent to `value_type`. - // * The iterator keeps state that can grow for `Cord`s that contain many + // * Two `ChunkIterator` objects can be compared equal if and only if they + // remain valid and iterate over the same Cord. + // * The iterator in this case is a proxy iterator; the `string_view` + // returned by the iterator does not live inside the Cord, and its + // lifetime is limited to the lifetime of the iterator itself. To help + // prevent lifetime issues, `ChunkIterator::reference` is not a true + // reference type and is equivalent to `value_type`. + // * The iterator keeps state that can grow for Cords that contain many // nodes and are imbalanced due to sharing. Prefer to pass this type by // const reference instead of by value. class ChunkIterator { @@ -261,14 +368,38 @@ class Cord { friend class CharIterator; private: + using CordRep = absl::cord_internal::CordRep; + using CordRepBtree = absl::cord_internal::CordRepBtree; + using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader; + + // Stack of right children of concat nodes that we have to visit. + // Keep this at the end of the structure to avoid cache-thrashing. + // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for + // the inlined vector size (47 exists for backward compatibility). + using Stack = absl::InlinedVector<absl::cord_internal::CordRep*, 47>; + + // Constructs a `begin()` iterator from `tree`. `tree` must not be null. + explicit ChunkIterator(cord_internal::CordRep* tree); + // Constructs a `begin()` iterator from `cord`. explicit ChunkIterator(const Cord* cord); + // Initializes this instance from a tree. Invoked by constructors. + void InitTree(cord_internal::CordRep* tree); + // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than // `current_chunk_.size()`. void RemoveChunkPrefix(size_t n); Cord AdvanceAndReadBytes(size_t n); void AdvanceBytes(size_t n); + + // Stack specific operator++ + ChunkIterator& AdvanceStack(); + + // Btree specific operator++ + ChunkIterator& AdvanceBtree(); + void AdvanceBytesBtree(size_t n); + // Iterates `n` bytes, where `n` is expected to be greater than or equal to // `current_chunk_.size()`. void AdvanceBytesSlowPath(size_t n); @@ -282,14 +413,21 @@ class Cord { absl::cord_internal::CordRep* current_leaf_ = nullptr; // The number of bytes left in the `Cord` over which we are iterating. size_t bytes_remaining_ = 0; - absl::InlinedVector<absl::cord_internal::CordRep*, 4> - stack_of_right_children_; + + // Cord reader for cord btrees. Empty if not traversing a btree. + CordRepBtreeReader btree_reader_; + + // See 'Stack' alias definition. + Stack stack_of_right_children_; }; + // Cord::ChunkIterator::chunk_begin() + // // Returns an iterator to the first chunk of the `Cord`. // - // This is useful for getting a `ChunkIterator` outside the context of a - // range-based for-loop (in which case see `Cord::Chunks()` below). + // Generally, prefer using `Cord::Chunks()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `ChunkIterator` where range-based for-loops are not useful. // // Example: // @@ -298,15 +436,40 @@ class Cord { // return std::find(c.chunk_begin(), c.chunk_end(), s); // } ChunkIterator chunk_begin() const; + + // Cord::ChunkItertator::chunk_end() + // // Returns an iterator one increment past the last chunk of the `Cord`. + // + // Generally, prefer using `Cord::Chunks()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `ChunkIterator` where range-based for-loops may not be available. ChunkIterator chunk_end() const; - // Convenience wrapper over `Cord::chunk_begin()` and `Cord::chunk_end()` to - // enable range-based for-loop iteration over `Cord` chunks. + //---------------------------------------------------------------------------- + // Cord::ChunkIterator::ChunkRange + //---------------------------------------------------------------------------- + // + // `ChunkRange` is a helper class for iterating over the chunks of the `Cord`, + // producing an iterator which can be used within a range-based for loop. + // Construction of a `ChunkRange` will return an iterator pointing to the + // first chunk of the Cord. Generally, do not construct a `ChunkRange` + // directly; instead, prefer to use the `Cord::Chunks()` method. // - // Prefer to use `Cord::Chunks()` below instead of constructing this directly. + // Implementation note: `ChunkRange` is simply a convenience wrapper over + // `Cord::chunk_begin()` and `Cord::chunk_end()`. class ChunkRange { public: + // Fulfill minimum c++ container requirements [container.requirements] + // Theses (partial) container type definitions allow ChunkRange to be used + // in various utilities expecting a subset of [container.requirements]. + // For example, the below enables using `::testing::ElementsAre(...)` + using value_type = absl::string_view; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = ChunkIterator; + using const_iterator = ChunkIterator; + explicit ChunkRange(const Cord* cord) : cord_(cord) {} ChunkIterator begin() const; @@ -316,8 +479,11 @@ class Cord { const Cord* cord_; }; - // Returns a range for iterating over the chunks of a `Cord` with a - // range-based for-loop. + // Cord::Chunks() + // + // Returns a `Cord::ChunkIterator::ChunkRange` for iterating over the chunks + // of a `Cord` with a range-based for-loop. For most iteration tasks on a + // Cord, use `Cord::Chunks()` to retrieve this iterator. // // Example: // @@ -334,22 +500,30 @@ class Cord { // } ChunkRange Chunks() const; - // Type for iterating over the characters of a `Cord`. See comments for - // `Cord::char_begin()`, `Cord::char_end()` and `Cord::Chars()` below for - // preferred usage. + //---------------------------------------------------------------------------- + // Cord::CharIterator + //---------------------------------------------------------------------------- + // + // A `Cord::CharIterator` allows iteration over the constituent characters of + // a `Cord`. // - // Additional notes: - // * A `CharIterator` object is invalidated after any non-const - // operation on the `Cord` object over which it iterates. - // * Two `CharIterator` objects can be equality compared if and only if - // they remain valid and iterate over the same `Cord`. - // * The iterator keeps state that can grow for `Cord`s that contain many + // Generally, you do not instantiate a `Cord::CharIterator` directly; instead, + // you create one implicitly through use of the `Cord::Chars()` member + // function. + // + // A `Cord::CharIterator` has the following properties: + // + // * The iterator is invalidated after any non-const operation on the + // Cord object over which it iterates. + // * Two `CharIterator` objects can be compared equal if and only if they + // remain valid and iterate over the same Cord. + // * The iterator keeps state that can grow for Cords that contain many // nodes and are imbalanced due to sharing. Prefer to pass this type by // const reference instead of by value. - // * This type cannot be a forward iterator because a `Cord` can reuse - // sections of memory. This violates the requirement that if dereferencing - // two iterators returns the same object, the iterators must compare - // equal. + // * This type cannot act as a forward iterator because a `Cord` can reuse + // sections of memory. This fact violates the requirement for forward + // iterators to compare equal if dereferencing them returns the same + // object. class CharIterator { public: using iterator_category = std::input_iterator_tag; @@ -375,36 +549,68 @@ class Cord { ChunkIterator chunk_iterator_; }; - // Advances `*it` by `n_bytes` and returns the bytes passed as a `Cord`. + // Cord::CharIterator::AdvanceAndRead() // - // `n_bytes` must be less than or equal to the number of bytes remaining for - // iteration. Otherwise the behavior is undefined. It is valid to pass - // `char_end()` and 0. + // Advances the `Cord::CharIterator` by `n_bytes` and returns the bytes + // advanced as a separate `Cord`. `n_bytes` must be less than or equal to the + // number of bytes within the Cord; otherwise, behavior is undefined. It is + // valid to pass `char_end()` and `0`. static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes); - // Advances `*it` by `n_bytes`. + // Cord::CharIterator::Advance() // - // `n_bytes` must be less than or equal to the number of bytes remaining for - // iteration. Otherwise the behavior is undefined. It is valid to pass - // `char_end()` and 0. + // Advances the `Cord::CharIterator` by `n_bytes`. `n_bytes` must be less than + // or equal to the number of bytes remaining within the Cord; otherwise, + // behavior is undefined. It is valid to pass `char_end()` and `0`. static void Advance(CharIterator* it, size_t n_bytes); + // Cord::CharIterator::ChunkRemaining() + // // Returns the longest contiguous view starting at the iterator's position. // // `it` must be dereferenceable. static absl::string_view ChunkRemaining(const CharIterator& it); + // Cord::CharIterator::char_begin() + // // Returns an iterator to the first character of the `Cord`. + // + // Generally, prefer using `Cord::Chars()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `CharIterator` where range-based for-loops may not be available. CharIterator char_begin() const; + + // Cord::CharIterator::char_end() + // // Returns an iterator to one past the last character of the `Cord`. + // + // Generally, prefer using `Cord::Chars()` within a range-based for loop for + // iterating over the chunks of a Cord. This method may be useful for getting + // a `CharIterator` where range-based for-loops are not useful. CharIterator char_end() const; - // Convenience wrapper over `Cord::char_begin()` and `Cord::char_end()` to - // enable range-based for-loop iterator over the characters of a `Cord`. + // Cord::CharIterator::CharRange // - // Prefer to use `Cord::Chars()` below instead of constructing this directly. + // `CharRange` is a helper class for iterating over the characters of a + // producing an iterator which can be used within a range-based for loop. + // Construction of a `CharRange` will return an iterator pointing to the first + // character of the Cord. Generally, do not construct a `CharRange` directly; + // instead, prefer to use the `Cord::Chars()` method show below. + // + // Implementation note: `CharRange` is simply a convenience wrapper over + // `Cord::char_begin()` and `Cord::char_end()`. class CharRange { public: + // Fulfill minimum c++ container requirements [container.requirements] + // Theses (partial) container type definitions allow CharRange to be used + // in various utilities expecting a subset of [container.requirements]. + // For example, the below enables using `::testing::ElementsAre(...)` + using value_type = char; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = CharIterator; + using const_iterator = CharIterator; + explicit CharRange(const Cord* cord) : cord_(cord) {} CharIterator begin() const; @@ -414,8 +620,11 @@ class Cord { const Cord* cord_; }; - // Returns a range for iterating over the characters of a `Cord` with a - // range-based for-loop. + // Cord::CharIterator::Chars() + // + // Returns a `Cord::CharIterator` for iterating over the characters of a + // `Cord` with a range-based for-loop. For most character-based iteration + // tasks on a Cord, use `Cord::Chars()` to retrieve this iterator. // // Example: // @@ -432,32 +641,73 @@ class Cord { // } CharRange Chars() const; - // -------------------------------------------------------------------- - // Miscellaneous - - // Get the "i"th character of 'this' and return it. - // NOTE: This routine is reasonably efficient. It is roughly - // logarithmic in the number of nodes that make up the cord. Still, - // if you need to iterate over the contents of a cord, you should - // use a CharIterator/CordIterator rather than call operator[] or Get() - // repeatedly in a loop. + // Cord::operator[] // - // REQUIRES: 0 <= i < size() + // Gets the "i"th character of the Cord and returns it, provided that + // 0 <= i < Cord.size(). + // + // NOTE: This routine is reasonably efficient. It is roughly + // logarithmic based on the number of chunks that make up the cord. Still, + // if you need to iterate over the contents of a cord, you should + // use a CharIterator/ChunkIterator rather than call operator[] or Get() + // repeatedly in a loop. char operator[](size_t i) const; + // Cord::TryFlat() + // + // If this cord's representation is a single flat array, returns a + // string_view referencing that array. Otherwise returns nullopt. + absl::optional<absl::string_view> TryFlat() const; + + // Cord::Flatten() + // // Flattens the cord into a single array and returns a view of the data. // // If the cord was already flat, the contents are not modified. absl::string_view Flatten(); + // Supports absl::Cord as a sink object for absl::Format(). + friend void AbslFormatFlush(absl::Cord* cord, absl::string_view part) { + cord->Append(part); + } + + template <typename H> + friend H AbslHashValue(H hash_state, const absl::Cord& c) { + absl::optional<absl::string_view> maybe_flat = c.TryFlat(); + if (maybe_flat.has_value()) { + return H::combine(std::move(hash_state), *maybe_flat); + } + return c.HashFragmented(std::move(hash_state)); + } + + // Create a Cord with the contents of StringConstant<T>::value. + // No allocations will be done and no data will be copied. + // This is an INTERNAL API and subject to change or removal. This API can only + // be used by spelling absl::strings_internal::MakeStringConstant, which is + // also an internal API. + template <typename T> + explicit constexpr Cord(strings_internal::StringConstant<T>); + private: + using CordRep = absl::cord_internal::CordRep; + using CordRepFlat = absl::cord_internal::CordRepFlat; + using CordzInfo = cord_internal::CordzInfo; + using CordzUpdateScope = cord_internal::CordzUpdateScope; + using CordzUpdateTracker = cord_internal::CordzUpdateTracker; + using InlineData = cord_internal::InlineData; + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + + // Creates a cord instance with `method` representing the originating + // public API call causing the cord to be created. + explicit Cord(absl::string_view src, MethodIdentifier method); + friend class CordTestPeer; - template <typename H> - friend H absl::hash_internal::HashFragmentedCord(H, const Cord&); friend bool operator==(const Cord& lhs, const Cord& rhs); friend bool operator==(const Cord& lhs, absl::string_view rhs); - // Call the provided function once for each cord chunk, in order. Unlike + friend const CordzInfo* GetCordzInfoForTesting(const Cord& cord); + + // Calls the provided function once for each cord chunk, in order. Unlike // Chunks(), this API will not allocate memory. void ForEachChunk(absl::FunctionRef<void(absl::string_view)>) const; @@ -469,60 +719,92 @@ class Cord { // class so that we can isolate the bulk of cord.cc from changes // to the representation. // - // InlineRep holds either either a tree pointer, or an array of kMaxInline - // bytes. + // InlineRep holds either a tree pointer, or an array of kMaxInline bytes. class InlineRep { public: - static const unsigned char kMaxInline = 15; + static constexpr unsigned char kMaxInline = cord_internal::kMaxInline; static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), ""); - // Tag byte & kMaxInline means we are storing a pointer. - static const unsigned char kTreeFlag = 1 << 4; - // Tag byte & kProfiledFlag means we are profiling the Cord. - static const unsigned char kProfiledFlag = 1 << 5; - constexpr InlineRep() : data_{} {} + constexpr InlineRep() : data_() {} + explicit InlineRep(InlineData::DefaultInitType init) : data_(init) {} InlineRep(const InlineRep& src); InlineRep(InlineRep&& src); InlineRep& operator=(const InlineRep& src); InlineRep& operator=(InlineRep&& src) noexcept; + explicit constexpr InlineRep(cord_internal::InlineData data); + void Swap(InlineRep* rhs); bool empty() const; size_t size() const; const char* data() const; // Returns nullptr if holding pointer void set_data(const char* data, size_t n, bool nullify_tail); // Discards pointer, if any - char* set_data(size_t n); // Write data to the result + char* set_data(size_t n); // Write data to the result // Returns nullptr if holding bytes absl::cord_internal::CordRep* tree() const; - // Discards old pointer, if any - void set_tree(absl::cord_internal::CordRep* rep); - // Replaces a tree with a new root. This is faster than set_tree, but it - // should only be used when it's clear that the old rep was a tree. - void replace_tree(absl::cord_internal::CordRep* rep); + absl::cord_internal::CordRep* as_tree() const; // Returns non-null iff was holding a pointer absl::cord_internal::CordRep* clear(); - // Convert to pointer if necessary - absl::cord_internal::CordRep* force_tree(size_t extra_hint); - void reduce_size(size_t n); // REQUIRES: holding data + // Converts to pointer if necessary. + void reduce_size(size_t n); // REQUIRES: holding data void remove_prefix(size_t n); // REQUIRES: holding data - void AppendArray(const char* src_data, size_t src_size); + void AppendArray(absl::string_view src, MethodIdentifier method); absl::string_view FindFlatStartPiece() const; - void AppendTree(absl::cord_internal::CordRep* tree); - void PrependTree(absl::cord_internal::CordRep* tree); - void GetAppendRegion(char** region, size_t* size, size_t max_length); - void GetAppendRegion(char** region, size_t* size); + + // Creates a CordRepFlat instance from the current inlined data with `extra' + // bytes of desired additional capacity. + CordRepFlat* MakeFlatWithExtraCapacity(size_t extra); + + // Sets the tree value for this instance. `rep` must not be null. + // Requires the current instance to hold a tree, and a lock to be held on + // any CordzInfo referenced by this instance. The latter is enforced through + // the CordzUpdateScope argument. If the current instance is sampled, then + // the CordzInfo instance is updated to reference the new `rep` value. + void SetTree(CordRep* rep, const CordzUpdateScope& scope); + + // Identical to SetTree(), except that `rep` is allowed to be null, in + // which case the current instance is reset to an empty value. + void SetTreeOrEmpty(CordRep* rep, const CordzUpdateScope& scope); + + // Sets the tree value for this instance, and randomly samples this cord. + // This function disregards existing contents in `data_`, and should be + // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined' + // value to a non-inlined (tree / ring) value. + void EmplaceTree(CordRep* rep, MethodIdentifier method); + + // Identical to EmplaceTree, except that it copies the parent stack from + // the provided `parent` data if the parent is sampled. + void EmplaceTree(CordRep* rep, const InlineData& parent, + MethodIdentifier method); + + // Commits the change of a newly created, or updated `rep` root value into + // this cord. `old_rep` indicates the old (inlined or tree) value of the + // cord, and determines if the commit invokes SetTree() or EmplaceTree(). + void CommitTree(const CordRep* old_rep, CordRep* rep, + const CordzUpdateScope& scope, MethodIdentifier method); + + void AppendTreeToInlined(CordRep* tree, MethodIdentifier method); + void AppendTreeToTree(CordRep* tree, MethodIdentifier method); + void AppendTree(CordRep* tree, MethodIdentifier method); + void PrependTreeToInlined(CordRep* tree, MethodIdentifier method); + void PrependTreeToTree(CordRep* tree, MethodIdentifier method); + void PrependTree(CordRep* tree, MethodIdentifier method); + + template <bool has_length> + void GetAppendRegion(char** region, size_t* size, size_t length); + bool IsSame(const InlineRep& other) const { - return memcmp(data_, other.data_, sizeof(data_)) == 0; + return memcmp(&data_, &other.data_, sizeof(data_)) == 0; } int BitwiseCompare(const InlineRep& other) const { uint64_t x, y; - // Use memcpy to avoid anti-aliasing issues. - memcpy(&x, data_, sizeof(x)); - memcpy(&y, other.data_, sizeof(y)); + // Use memcpy to avoid aliasing issues. + memcpy(&x, &data_, sizeof(x)); + memcpy(&y, &other.data_, sizeof(y)); if (x == y) { - memcpy(&x, data_ + 8, sizeof(x)); - memcpy(&y, other.data_ + 8, sizeof(y)); + memcpy(&x, reinterpret_cast<const char*>(&data_) + 8, sizeof(x)); + memcpy(&y, reinterpret_cast<const char*>(&other.data_) + 8, sizeof(y)); if (x == y) return 0; } return absl::big_endian::FromHost64(x) < absl::big_endian::FromHost64(y) @@ -531,43 +813,62 @@ class Cord { } void CopyTo(std::string* dst) const { // memcpy is much faster when operating on a known size. On most supported - // platforms, the small std::string optimization is large enough that resizing + // platforms, the small string optimization is large enough that resizing // to 15 bytes does not cause a memory allocation. absl::strings_internal::STLStringResizeUninitialized(dst, sizeof(data_) - 1); - memcpy(&(*dst)[0], data_, sizeof(data_) - 1); + memcpy(&(*dst)[0], &data_, sizeof(data_) - 1); // erase is faster than resize because the logic for memory allocation is // not needed. - dst->erase(data_[kMaxInline]); + dst->erase(inline_size()); } // Copies the inline contents into `dst`. Assumes the cord is not empty. void CopyToArray(char* dst) const; - bool is_tree() const { return data_[kMaxInline] > kMaxInline; } + bool is_tree() const { return data_.is_tree(); } + + // Returns true if the Cord is being profiled by cordz. + bool is_profiled() const { return data_.is_tree() && data_.is_profiled(); } + + // Returns the profiled CordzInfo, or nullptr if not sampled. + absl::cord_internal::CordzInfo* cordz_info() const { + return data_.cordz_info(); + } + + // Sets the profiled CordzInfo. `cordz_info` must not be null. + void set_cordz_info(cord_internal::CordzInfo* cordz_info) { + assert(cordz_info != nullptr); + data_.set_cordz_info(cordz_info); + } + + // Resets the current cordz_info to null / empty. + void clear_cordz_info() { data_.clear_cordz_info(); } private: friend class Cord; void AssignSlow(const InlineRep& src); - // Unrefs the tree, stops profiling, and zeroes the contents - void ClearSlow(); + // Unrefs the tree and stops profiling. + void UnrefTree(); + + void ResetToEmpty() { data_ = {}; } - // If the data has length <= kMaxInline, we store it in data_[0..len-1], - // and store the length in data_[kMaxInline]. Else we store it in a tree - // and store a pointer to that tree in data_[0..sizeof(CordRep*)-1]. - alignas(absl::cord_internal::CordRep*) char data_[kMaxInline + 1]; + void set_inline_size(size_t size) { data_.set_inline_size(size); } + size_t inline_size() const { return data_.inline_size(); } + + cord_internal::InlineData data_; }; InlineRep contents_; - // Helper for MemoryUsage() + // Helper for MemoryUsage(). static size_t MemoryUsageAux(const absl::cord_internal::CordRep* rep); - // Helper for GetFlat() + // Helper for GetFlat() and TryFlat(). static bool GetFlatAux(absl::cord_internal::CordRep* rep, absl::string_view* fragment); - // Helper for ForEachChunk() + // Helper for ForEachChunk(). static void ForEachChunkAux( absl::cord_internal::CordRep* rep, absl::FunctionRef<void(absl::string_view)> callback); @@ -596,9 +897,28 @@ class Cord { absl::cord_internal::CordRep* TakeRep() const&; absl::cord_internal::CordRep* TakeRep() &&; - // Helper for Append() + // Helper for Append(). template <typename C> void AppendImpl(C&& src); + + // Prepends the provided data to this instance. `method` contains the public + // API method for this action which is tracked for Cordz sampling purposes. + void PrependArray(absl::string_view src, MethodIdentifier method); + + // Assigns the value in 'src' to this instance, 'stealing' its contents. + // Requires src.length() > kMaxBytesToCopy. + Cord& AssignLargeString(std::string&& src); + + // Helper for AbslHashValue(). + template <typename H> + H HashFragmented(H hash_state) const { + typename H::AbslInternalPiecewiseCombiner combiner; + ForEachChunk([&combiner, &hash_state](absl::string_view chunk) { + hash_state = combiner.add_buffer(std::move(hash_state), chunk.data(), + chunk.size()); + }); + return H::combine(combiner.finalize(std::move(hash_state)), size()); + } }; ABSL_NAMESPACE_END @@ -655,52 +975,27 @@ inline void SmallMemmove(char* dst, const char* src, size_t n, } } -struct ExternalRepReleaserPair { - CordRep* rep; - void* releaser_address; -}; - -// Allocates a new external `CordRep` and returns a pointer to it and a pointer -// to `releaser_size` bytes where the desired releaser can be constructed. +// Does non-template-specific `CordRepExternal` initialization. // Expects `data` to be non-empty. -ExternalRepReleaserPair NewExternalWithUninitializedReleaser( - absl::string_view data, ExternalReleaserInvoker invoker, - size_t releaser_size); +void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep); // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer // to it, or `nullptr` if `data` was empty. template <typename Releaser> // NOLINTNEXTLINE - suppress clang-tidy raw pointer return. CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { - static_assert( -#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) - alignof(Releaser) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__, -#else - alignof(Releaser) <= alignof(max_align_t), -#endif - "Releasers with alignment requirement greater than what is returned by " - "default `::operator new()` are not supported."); - using ReleaserType = absl::decay_t<Releaser>; if (data.empty()) { // Never create empty external nodes. - ::absl::base_internal::Invoke( - ReleaserType(std::forward<Releaser>(releaser)), data); + InvokeReleaser(Rank0{}, ReleaserType(std::forward<Releaser>(releaser)), + data); return nullptr; } - auto releaser_invoker = [](void* type_erased_releaser, absl::string_view d) { - auto* my_releaser = static_cast<ReleaserType*>(type_erased_releaser); - ::absl::base_internal::Invoke(std::move(*my_releaser), d); - my_releaser->~ReleaserType(); - return sizeof(Releaser); - }; - - ExternalRepReleaserPair external = NewExternalWithUninitializedReleaser( - data, releaser_invoker, sizeof(releaser)); - ::new (external.releaser_address) - ReleaserType(std::forward<Releaser>(releaser)); - return external.rep; + CordRepExternal* rep = new CordRepExternalImpl<ReleaserType>( + std::forward<Releaser>(releaser), 0); + InitializeCordRepExternal(data, rep); + return rep; } // Overload for function reference types that dispatches using a function @@ -716,18 +1011,29 @@ inline CordRep* NewExternalRep(absl::string_view data, template <typename Releaser> Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) { Cord cord; - cord.contents_.set_tree(::absl::cord_internal::NewExternalRep( - data, std::forward<Releaser>(releaser))); + if (auto* rep = ::absl::cord_internal::NewExternalRep( + data, std::forward<Releaser>(releaser))) { + cord.contents_.EmplaceTree(rep, + Cord::MethodIdentifier::kMakeCordFromExternal); + } return cord; } -inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) { - cord_internal::SmallMemmove(data_, src.data_, sizeof(data_)); +constexpr Cord::InlineRep::InlineRep(cord_internal::InlineData data) + : data_(data) {} + +inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) + : data_(InlineData::kDefaultInit) { + if (CordRep* tree = src.tree()) { + EmplaceTree(CordRep::Ref(tree), src.data_, + CordzUpdateTracker::kConstructorCord); + } else { + data_ = src.data_; + } } -inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) { - memcpy(data_, src.data_, sizeof(data_)); - memset(src.data_, 0, sizeof(data_)); +inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) : data_(src.data_) { + src.ResetToEmpty(); } inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) { @@ -735,7 +1041,7 @@ inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) { return *this; } if (!is_tree() && !src.is_tree()) { - cord_internal::SmallMemmove(data_, src.data_, sizeof(data_)); + data_ = src.data_; return *this; } AssignSlow(src); @@ -745,10 +1051,10 @@ inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) { inline Cord::InlineRep& Cord::InlineRep::operator=( Cord::InlineRep&& src) noexcept { if (is_tree()) { - ClearSlow(); + UnrefTree(); } - memcpy(data_, src.data_, sizeof(data_)); - memset(src.data_, 0, sizeof(data_)); + data_ = src.data_; + src.ResetToEmpty(); return *this; } @@ -756,94 +1062,143 @@ inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) { if (rhs == this) { return; } - - Cord::InlineRep tmp; - cord_internal::SmallMemmove(tmp.data_, data_, sizeof(data_)); - cord_internal::SmallMemmove(data_, rhs->data_, sizeof(data_)); - cord_internal::SmallMemmove(rhs->data_, tmp.data_, sizeof(data_)); + std::swap(data_, rhs->data_); } inline const char* Cord::InlineRep::data() const { - return is_tree() ? nullptr : data_; + return is_tree() ? nullptr : data_.as_chars(); +} + +inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const { + assert(data_.is_tree()); + return data_.as_tree(); } inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const { if (is_tree()) { - absl::cord_internal::CordRep* rep; - memcpy(&rep, data_, sizeof(rep)); - return rep; + return as_tree(); } else { return nullptr; } } -inline bool Cord::InlineRep::empty() const { return data_[kMaxInline] == 0; } +inline bool Cord::InlineRep::empty() const { return data_.is_empty(); } inline size_t Cord::InlineRep::size() const { - const char tag = data_[kMaxInline]; - if (tag <= kMaxInline) return tag; - return static_cast<size_t>(tree()->length); + return is_tree() ? as_tree()->length : inline_size(); } -inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) { - if (rep == nullptr) { - memset(data_, 0, sizeof(data_)); +inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity( + size_t extra) { + static_assert(cord_internal::kMinFlatLength >= sizeof(data_), ""); + size_t len = data_.inline_size(); + auto* result = CordRepFlat::New(len + extra); + result->length = len; + memcpy(result->Data(), data_.as_chars(), sizeof(data_)); + return result; +} + +inline void Cord::InlineRep::EmplaceTree(CordRep* rep, + MethodIdentifier method) { + assert(rep); + data_.make_tree(rep); + CordzInfo::MaybeTrackCord(data_, method); +} + +inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent, + MethodIdentifier method) { + data_.make_tree(rep); + CordzInfo::MaybeTrackCord(data_, parent, method); +} + +inline void Cord::InlineRep::SetTree(CordRep* rep, + const CordzUpdateScope& scope) { + assert(rep); + assert(data_.is_tree()); + data_.set_tree(rep); + scope.SetCordRep(rep); +} + +inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep, + const CordzUpdateScope& scope) { + assert(data_.is_tree()); + if (rep) { + data_.set_tree(rep); } else { - bool was_tree = is_tree(); - memcpy(data_, &rep, sizeof(rep)); - memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1); - if (!was_tree) { - data_[kMaxInline] = kTreeFlag; - } + data_ = {}; } + scope.SetCordRep(rep); } -inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) { - ABSL_ASSERT(is_tree()); - if (ABSL_PREDICT_FALSE(rep == nullptr)) { - set_tree(rep); - return; +inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep, + const CordzUpdateScope& scope, + MethodIdentifier method) { + if (old_rep) { + SetTree(rep, scope); + } else { + EmplaceTree(rep, method); } - memcpy(data_, &rep, sizeof(rep)); - memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1); } inline absl::cord_internal::CordRep* Cord::InlineRep::clear() { - const char tag = data_[kMaxInline]; - absl::cord_internal::CordRep* result = nullptr; - if (tag > kMaxInline) { - memcpy(&result, data_, sizeof(result)); + if (is_tree()) { + CordzInfo::MaybeUntrackCord(cordz_info()); } - memset(data_, 0, sizeof(data_)); // Clear the cord + absl::cord_internal::CordRep* result = tree(); + ResetToEmpty(); return result; } inline void Cord::InlineRep::CopyToArray(char* dst) const { assert(!is_tree()); - size_t n = data_[kMaxInline]; + size_t n = inline_size(); assert(n != 0); - cord_internal::SmallMemmove(dst, data_, n); + cord_internal::SmallMemmove(dst, data_.as_chars(), n); } constexpr inline Cord::Cord() noexcept {} +inline Cord::Cord(absl::string_view src) + : Cord(src, CordzUpdateTracker::kConstructorString) {} + +template <typename T> +constexpr Cord::Cord(strings_internal::StringConstant<T>) + : contents_(strings_internal::StringConstant<T>::value.size() <= + cord_internal::kMaxInline + ? cord_internal::InlineData( + strings_internal::StringConstant<T>::value) + : cord_internal::InlineData( + &cord_internal::ConstInitExternalStorage< + strings_internal::StringConstant<T>>::value)) {} + inline Cord& Cord::operator=(const Cord& x) { contents_ = x.contents_; return *this; } +template <typename T, Cord::EnableIfString<T>> +Cord& Cord::operator=(T&& src) { + if (src.size() <= cord_internal::kMaxBytesToCopy) { + return operator=(absl::string_view(src)); + } else { + return AssignLargeString(std::forward<T>(src)); + } +} + +inline Cord::Cord(const Cord& src) : contents_(src.contents_) {} + inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {} +inline void Cord::swap(Cord& other) noexcept { + contents_.Swap(&other.contents_); +} + inline Cord& Cord::operator=(Cord&& x) noexcept { contents_ = std::move(x.contents_); return *this; } -template <typename T, Cord::EnableIfString<T>> -inline Cord& Cord::operator=(T&& src) { - *this = absl::string_view(src); - return *this; -} +extern template Cord::Cord(std::string&& src); inline size_t Cord::size() const { // Length is 1st field in str.rep_ @@ -860,6 +1215,18 @@ inline size_t Cord::EstimatedMemoryUsage() const { return result; } +inline absl::optional<absl::string_view> Cord::TryFlat() const { + absl::cord_internal::CordRep* rep = contents_.tree(); + if (rep == nullptr) { + return absl::string_view(contents_.data(), contents_.size()); + } + absl::string_view fragment; + if (GetFlatAux(rep, &fragment)) { + return fragment; + } + return absl::nullopt; +} + inline absl::string_view Cord::Flatten() { absl::cord_internal::CordRep* rep = contents_.tree(); if (rep == nullptr) { @@ -874,22 +1241,15 @@ inline absl::string_view Cord::Flatten() { } inline void Cord::Append(absl::string_view src) { - contents_.AppendArray(src.data(), src.size()); + contents_.AppendArray(src, CordzUpdateTracker::kAppendString); } -template <typename T, Cord::EnableIfString<T>> -inline void Cord::Append(T&& src) { - // Note that this function reserves the right to reuse the `string&&`'s - // memory and that it will do so in the future. - Append(absl::string_view(src)); +inline void Cord::Prepend(absl::string_view src) { + PrependArray(src, CordzUpdateTracker::kPrependString); } -template <typename T, Cord::EnableIfString<T>> -inline void Cord::Prepend(T&& src) { - // Note that this function reserves the right to reuse the `string&&`'s - // memory and that it will do so in the future. - Prepend(absl::string_view(src)); -} +extern template void Cord::Append(std::string&& src); +extern template void Cord::Prepend(std::string&& src); inline int Cord::Compare(const Cord& rhs) const { if (!contents_.is_tree() && !rhs.contents_.is_tree()) { @@ -913,17 +1273,64 @@ inline bool Cord::StartsWith(absl::string_view rhs) const { return EqualsImpl(rhs, rhs_size); } +inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) { + if (tree->tag == cord_internal::BTREE) { + current_chunk_ = btree_reader_.Init(tree->btree()); + return; + } + + stack_of_right_children_.push_back(tree); + operator++(); +} + +inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) + : bytes_remaining_(tree->length) { + InitTree(tree); +} + inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) : bytes_remaining_(cord->size()) { - if (cord->empty()) return; if (cord->contents_.is_tree()) { - stack_of_right_children_.push_back(cord->contents_.tree()); - operator++(); + InitTree(cord->contents_.as_tree()); } else { - current_chunk_ = absl::string_view(cord->contents_.data(), cord->size()); + current_chunk_ = + absl::string_view(cord->contents_.data(), bytes_remaining_); } } +inline Cord::ChunkIterator& Cord::ChunkIterator::AdvanceBtree() { + current_chunk_ = btree_reader_.Next(); + return *this; +} + +inline void Cord::ChunkIterator::AdvanceBytesBtree(size_t n) { + assert(n >= current_chunk_.size()); + bytes_remaining_ -= n; + if (bytes_remaining_) { + if (n == current_chunk_.size()) { + current_chunk_ = btree_reader_.Next(); + } else { + size_t offset = btree_reader_.length() - bytes_remaining_; + current_chunk_ = btree_reader_.Seek(offset); + } + } else { + current_chunk_ = {}; + } +} + +inline Cord::ChunkIterator& Cord::ChunkIterator::operator++() { + ABSL_HARDENING_ASSERT(bytes_remaining_ > 0 && + "Attempted to iterate past `end()`"); + assert(bytes_remaining_ >= current_chunk_.size()); + bytes_remaining_ -= current_chunk_.size(); + if (bytes_remaining_ > 0) { + return btree_reader_ ? AdvanceBtree() : AdvanceStack(); + } else { + current_chunk_ = {}; + } + return *this; +} + inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) { ChunkIterator tmp(*this); operator++(); @@ -939,12 +1346,12 @@ inline bool Cord::ChunkIterator::operator!=(const ChunkIterator& other) const { } inline Cord::ChunkIterator::reference Cord::ChunkIterator::operator*() const { - assert(bytes_remaining_ != 0); + ABSL_HARDENING_ASSERT(bytes_remaining_ != 0); return current_chunk_; } inline Cord::ChunkIterator::pointer Cord::ChunkIterator::operator->() const { - assert(bytes_remaining_ != 0); + ABSL_HARDENING_ASSERT(bytes_remaining_ != 0); return ¤t_chunk_; } @@ -955,10 +1362,11 @@ inline void Cord::ChunkIterator::RemoveChunkPrefix(size_t n) { } inline void Cord::ChunkIterator::AdvanceBytes(size_t n) { + assert(bytes_remaining_ >= n); if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) { RemoveChunkPrefix(n); } else if (n != 0) { - AdvanceBytesSlowPath(n); + btree_reader_ ? AdvanceBytesBtree(n) : AdvanceBytesSlowPath(n); } } @@ -1058,12 +1466,8 @@ inline bool operator==(const Cord& lhs, const Cord& rhs) { } inline bool operator!=(const Cord& x, const Cord& y) { return !(x == y); } -inline bool operator<(const Cord& x, const Cord& y) { - return x.Compare(y) < 0; -} -inline bool operator>(const Cord& x, const Cord& y) { - return x.Compare(y) > 0; -} +inline bool operator<(const Cord& x, const Cord& y) { return x.Compare(y) < 0; } +inline bool operator>(const Cord& x, const Cord& y) { return x.Compare(y) > 0; } inline bool operator<=(const Cord& x, const Cord& y) { return x.Compare(y) <= 0; } @@ -1098,10 +1502,6 @@ inline bool operator<=(absl::string_view x, const Cord& y) { return !(y < x); } inline bool operator>=(const Cord& x, absl::string_view y) { return !(x < y); } inline bool operator>=(absl::string_view x, const Cord& y) { return !(x < y); } -// Overload of swap for Cord. The use of non-const references is -// required. :( -inline void swap(Cord& x, Cord& y) noexcept { y.contents_.Swap(&x.contents_); } - // Some internals exposed to test code. namespace strings_internal { class CordTestAccess { diff --git a/third_party/abseil-cpp/absl/strings/cord_ring_reader_test.cc b/third_party/abseil-cpp/absl/strings/cord_ring_reader_test.cc new file mode 100644 index 0000000000..d9a9a76d1e --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/cord_ring_reader_test.cc @@ -0,0 +1,180 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <cstdlib> +#include <ctime> +#include <memory> +#include <random> +#include <sstream> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/debugging/leak_check.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/internal/cord_rep_ring_reader.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using testing::Eq; + +// Creates a flat for testing +CordRep* MakeFlat(absl::string_view s) { + CordRepFlat* flat = CordRepFlat::New(s.length()); + memcpy(flat->Data(), s.data(), s.length()); + flat->length = s.length(); + return flat; +} + +CordRepRing* FromFlats(Span<absl::string_view const> flats) { + CordRepRing* ring = CordRepRing::Create(MakeFlat(flats[0]), flats.size() - 1); + for (int i = 1; i < flats.size(); ++i) { + ring = CordRepRing::Append(ring, MakeFlat(flats[i])); + } + return ring; +} + +std::array<absl::string_view, 12> TestFlats() { + return {"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", + "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", + "+-=", "[]\\{}|;':", ",/<>?", "."}; +} + +TEST(CordRingReaderTest, DefaultInstance) { + CordRepRingReader reader; + EXPECT_FALSE(static_cast<bool>(reader)); + EXPECT_THAT(reader.ring(), Eq(nullptr)); +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED(reader.length(), ".*"); + EXPECT_DEATH_IF_SUPPORTED(reader.consumed(), ".*"); + EXPECT_DEATH_IF_SUPPORTED(reader.remaining(), ".*"); + EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*"); + EXPECT_DEATH_IF_SUPPORTED(reader.Seek(0), ".*"); +#endif +} + +TEST(CordRingReaderTest, Reset) { + CordRepRingReader reader; + auto flats = TestFlats(); + CordRepRing* ring = FromFlats(flats); + + absl::string_view first = reader.Reset(ring); + EXPECT_THAT(first, Eq(flats[0])); + EXPECT_TRUE(static_cast<bool>(reader)); + EXPECT_THAT(reader.ring(), Eq(ring)); + EXPECT_THAT(reader.index(), Eq(ring->head())); + EXPECT_THAT(reader.node(), Eq(ring->entry_child(ring->head()))); + EXPECT_THAT(reader.length(), Eq(ring->length)); + EXPECT_THAT(reader.consumed(), Eq(flats[0].length())); + EXPECT_THAT(reader.remaining(), Eq(ring->length - reader.consumed())); + + reader.Reset(); + EXPECT_FALSE(static_cast<bool>(reader)); + EXPECT_THAT(reader.ring(), Eq(nullptr)); + + CordRep::Unref(ring); +} + +TEST(CordRingReaderTest, Next) { + CordRepRingReader reader; + auto flats = TestFlats(); + CordRepRing* ring = FromFlats(flats); + CordRepRing::index_type head = ring->head(); + + reader.Reset(ring); + size_t consumed = reader.consumed(); + size_t remaining = reader.remaining(); + for (int i = 1; i < flats.size(); ++i) { + CordRepRing::index_type index = ring->advance(head, i); + consumed += flats[i].length(); + remaining -= flats[i].length(); + absl::string_view next = reader.Next(); + ASSERT_THAT(next, Eq(flats[i])); + ASSERT_THAT(reader.index(), Eq(index)); + ASSERT_THAT(reader.node(), Eq(ring->entry_child(index))); + ASSERT_THAT(reader.consumed(), Eq(consumed)); + ASSERT_THAT(reader.remaining(), Eq(remaining)); + } + +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*"); +#endif + + CordRep::Unref(ring); +} + +TEST(CordRingReaderTest, SeekForward) { + CordRepRingReader reader; + auto flats = TestFlats(); + CordRepRing* ring = FromFlats(flats); + CordRepRing::index_type head = ring->head(); + + reader.Reset(ring); + size_t consumed = 0; + size_t remaining = ring->length;; + for (int i = 0; i < flats.size(); ++i) { + CordRepRing::index_type index = ring->advance(head, i); + size_t offset = consumed; + consumed += flats[i].length(); + remaining -= flats[i].length(); + for (int off = 0; off < flats[i].length(); ++off) { + absl::string_view chunk = reader.Seek(offset + off); + ASSERT_THAT(chunk, Eq(flats[i].substr(off))); + ASSERT_THAT(reader.index(), Eq(index)); + ASSERT_THAT(reader.node(), Eq(ring->entry_child(index))); + ASSERT_THAT(reader.consumed(), Eq(consumed)); + ASSERT_THAT(reader.remaining(), Eq(remaining)); + } + } + + CordRep::Unref(ring); +} + +TEST(CordRingReaderTest, SeekBackward) { + CordRepRingReader reader; + auto flats = TestFlats(); + CordRepRing* ring = FromFlats(flats); + CordRepRing::index_type head = ring->head(); + + reader.Reset(ring); + size_t consumed = ring->length; + size_t remaining = 0; + for (int i = flats.size() - 1; i >= 0; --i) { + CordRepRing::index_type index = ring->advance(head, i); + size_t offset = consumed - flats[i].length(); + for (int off = 0; off < flats[i].length(); ++off) { + absl::string_view chunk = reader.Seek(offset + off); + ASSERT_THAT(chunk, Eq(flats[i].substr(off))); + ASSERT_THAT(reader.index(), Eq(index)); + ASSERT_THAT(reader.node(), Eq(ring->entry_child(index))); + ASSERT_THAT(reader.consumed(), Eq(consumed)); + ASSERT_THAT(reader.remaining(), Eq(remaining)); + } + consumed -= flats[i].length(); + remaining += flats[i].length(); + } +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED(reader.Seek(ring->length), ".*"); +#endif + CordRep::Unref(ring); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/cord_ring_test.cc b/third_party/abseil-cpp/absl/strings/cord_ring_test.cc new file mode 100644 index 0000000000..f131859532 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/cord_ring_test.cc @@ -0,0 +1,1495 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <cstdlib> +#include <ctime> +#include <memory> +#include <random> +#include <sstream> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/debugging/leak_check.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +extern thread_local bool cord_ring; + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace { + +using RandomEngine = std::mt19937_64; + +using ::absl::cord_internal::CordRep; +using ::absl::cord_internal::CordRepConcat; +using ::absl::cord_internal::CordRepExternal; +using ::absl::cord_internal::CordRepFlat; +using ::absl::cord_internal::CordRepRing; +using ::absl::cord_internal::CordRepSubstring; + +using ::absl::cord_internal::CONCAT; +using ::absl::cord_internal::EXTERNAL; +using ::absl::cord_internal::SUBSTRING; + +using testing::ElementsAre; +using testing::ElementsAreArray; +using testing::Eq; +using testing::Ge; +using testing::Le; +using testing::Lt; +using testing::Ne; +using testing::SizeIs; + +using index_type = CordRepRing::index_type; + +enum InputShareMode { kPrivate, kShared, kSharedIndirect }; + +// TestParam class used by all test fixtures. +// Not all fixtures use all possible input combinations +struct TestParam { + TestParam() = default; + explicit TestParam(InputShareMode input_share_mode) + : input_share_mode(input_share_mode) {} + + // Run the test with the 'rep under test' to be privately owned. + // Otherwise, the rep has a shared ref count of 2 or higher. + bool refcount_is_one = true; + + // Run the test with the 'rep under test' being allocated with enough capacity + // to accommodate any modifications made to it. Otherwise, the rep has zero + // extra (reserve) capacity. + bool with_capacity = true; + + // For test providing possibly shared input such as Append(.., CordpRep*), + // this field defines if that input is adopted with a refcount of one + // (privately owned / donated), or shared. For composite inputs such as + // 'substring of flat', we also have the 'shared indirect' value which means + // the top level node is not shared, but the contained child node is shared. + InputShareMode input_share_mode = kPrivate; + + std::string ToString() const { + return absl::StrCat(refcount_is_one ? "Private" : "Shared", + with_capacity ? "" : "_NoCapacity", + (input_share_mode == kPrivate) ? "" + : (input_share_mode == kShared) + ? "_SharedInput" + : "_IndirectSharedInput"); + } +}; +using TestParams = std::vector<TestParam>; + +// Matcher validating when mutable copies are required / performed. +MATCHER_P2(EqIfPrivate, param, rep, + absl::StrCat("Equal 0x", absl::Hex(rep), " if private")) { + return param.refcount_is_one ? arg == rep : true; +} + +// Matcher validating when mutable copies are required / performed. +MATCHER_P2(EqIfPrivateAndCapacity, param, rep, + absl::StrCat("Equal 0x", absl::Hex(rep), + " if private and capacity")) { + return (param.refcount_is_one && param.with_capacity) ? arg == rep : true; +} + +// Matcher validating a shared ring was re-allocated. Should only be used for +// tests doing exactly one update as subsequent updates could return the +// original (freed and re-used) pointer. +MATCHER_P2(NeIfShared, param, rep, + absl::StrCat("Not equal 0x", absl::Hex(rep), " if shared")) { + return param.refcount_is_one ? true : arg != rep; +} + +MATCHER_P2(EqIfInputPrivate, param, rep, "Equal if input is private") { + return param.input_share_mode == kPrivate ? arg == rep : arg != rep; +} + +// Matcher validating the core in-variants of the CordRepRing instance. +MATCHER(IsValidRingBuffer, "RingBuffer is valid") { + std::stringstream ss; + if (!arg->IsValid(ss)) { + *result_listener << "\nERROR: " << ss.str() << "\nRING = " << *arg; + return false; + } + return true; +} + +// Returns the flats contained in the provided CordRepRing +std::vector<string_view> ToFlats(const CordRepRing* r) { + std::vector<string_view> flats; + flats.reserve(r->entries()); + index_type pos = r->head(); + do { + flats.push_back(r->entry_data(pos)); + } while ((pos = r->advance(pos)) != r->tail()); + return flats; +} + +class not_a_string_view { + public: + explicit not_a_string_view(absl::string_view s) + : data_(s.data()), size_(s.size()) {} + explicit not_a_string_view(const void* data, size_t size) + : data_(data), size_(size) {} + + not_a_string_view remove_prefix(size_t n) const { + return not_a_string_view(static_cast<const char*>(data_) + n, size_ - n); + } + + not_a_string_view remove_suffix(size_t n) const { + return not_a_string_view(data_, size_ - n); + } + + const void* data() const { return data_; } + size_t size() const { return size_; } + + private: + const void* data_; + size_t size_; +}; + +bool operator==(not_a_string_view lhs, not_a_string_view rhs) { + return lhs.data() == rhs.data() && lhs.size() == rhs.size(); +} + +std::ostream& operator<<(std::ostream& s, not_a_string_view rhs) { + return s << "{ data: " << rhs.data() << " size: " << rhs.size() << "}"; +} + +std::vector<not_a_string_view> ToRawFlats(const CordRepRing* r) { + std::vector<not_a_string_view> flats; + flats.reserve(r->entries()); + index_type pos = r->head(); + do { + flats.emplace_back(r->entry_data(pos)); + } while ((pos = r->advance(pos)) != r->tail()); + return flats; +} + +// Returns the value contained in the provided CordRepRing +std::string ToString(const CordRepRing* r) { + std::string value; + value.reserve(r->length); + index_type pos = r->head(); + do { + absl::string_view sv = r->entry_data(pos); + value.append(sv.data(), sv.size()); + } while ((pos = r->advance(pos)) != r->tail()); + return value; +} + +// Creates a flat for testing +CordRep* MakeFlat(absl::string_view s, size_t extra = 0) { + CordRepFlat* flat = CordRepFlat::New(s.length() + extra); + memcpy(flat->Data(), s.data(), s.length()); + flat->length = s.length(); + return flat; +} + +// Creates an external node for testing +CordRepExternal* MakeExternal(absl::string_view s) { + struct Rep : public CordRepExternal { + std::string s; + explicit Rep(absl::string_view s) : s(s) { + this->tag = EXTERNAL; + this->base = s.data(); + this->length = s.length(); + this->releaser_invoker = [](CordRepExternal* self) { + delete static_cast<Rep*>(self); + }; + } + }; + return new Rep(s); +} + +CordRepExternal* MakeFakeExternal(size_t length) { + struct Rep : public CordRepExternal { + std::string s; + explicit Rep(size_t len) { + this->tag = EXTERNAL; + this->base = reinterpret_cast<const char*>(this->storage); + this->length = len; + this->releaser_invoker = [](CordRepExternal* self) { + delete static_cast<Rep*>(self); + }; + } + }; + return new Rep(length); +} + +// Creates a flat or an external node for testing depending on the size. +CordRep* MakeLeaf(absl::string_view s, size_t extra = 0) { + if (s.size() <= absl::cord_internal::kMaxFlatLength) { + return MakeFlat(s, extra); + } else { + return MakeExternal(s); + } +} + +// Creates a substring node +CordRepSubstring* MakeSubstring(size_t start, size_t len, CordRep* rep) { + auto* sub = new CordRepSubstring; + sub->tag = SUBSTRING; + sub->start = start; + sub->length = (len <= 0) ? rep->length - start + len : len; + sub->child = rep; + return sub; +} + +// Creates a substring node removing the specified prefix +CordRepSubstring* RemovePrefix(size_t start, CordRep* rep) { + return MakeSubstring(start, rep->length - start, rep); +} + +// Creates a substring node removing the specified suffix +CordRepSubstring* RemoveSuffix(size_t length, CordRep* rep) { + return MakeSubstring(0, rep->length - length, rep); +} + +CordRepConcat* MakeConcat(CordRep* left, CordRep* right, int depth = 0) { + auto* concat = new CordRepConcat; + concat->tag = CONCAT; + concat->length = left->length + right->length; + concat->left = left; + concat->right = right; + concat->set_depth(depth); + return concat; +} + +enum Composition { kMix, kAppend, kPrepend }; + +Composition RandomComposition() { + RandomEngine rng(GTEST_FLAG_GET(random_seed)); + return (rng() & 1) ? kMix : ((rng() & 1) ? kAppend : kPrepend); +} + +absl::string_view ToString(Composition composition) { + switch (composition) { + case kAppend: + return "Append"; + case kPrepend: + return "Prepend"; + case kMix: + return "Mix"; + } + assert(false); + return "???"; +} + +constexpr const char* kFox = "The quick brown fox jumps over the lazy dog"; +constexpr const char* kFoxFlats[] = {"The ", "quick ", "brown ", + "fox ", "jumps ", "over ", + "the ", "lazy ", "dog"}; +constexpr const char* kAlphabet = "abcdefghijklmnopqrstuvwxyz"; + +CordRepRing* FromFlats(Span<const char* const> flats, + Composition composition = kAppend) { + if (flats.empty()) return nullptr; + CordRepRing* ring = nullptr; + switch (composition) { + case kAppend: + ring = CordRepRing::Create(MakeLeaf(flats.front()), flats.size() - 1); + for (int i = 1; i < flats.size(); ++i) { + ring = CordRepRing::Append(ring, MakeLeaf(flats[i])); + } + break; + case kPrepend: + ring = CordRepRing::Create(MakeLeaf(flats.back()), flats.size() - 1); + for (int i = static_cast<int>(flats.size() - 2); i >= 0; --i) { + ring = CordRepRing::Prepend(ring, MakeLeaf(flats[i])); + } + break; + case kMix: + size_t middle1 = flats.size() / 2, middle2 = middle1; + ring = CordRepRing::Create(MakeLeaf(flats[middle1]), flats.size() - 1); + if (!flats.empty()) { + if ((flats.size() & 1) == 0) { + ring = CordRepRing::Prepend(ring, MakeLeaf(flats[--middle1])); + } + for (int i = 1; i <= middle1; ++i) { + ring = CordRepRing::Prepend(ring, MakeLeaf(flats[middle1 - i])); + ring = CordRepRing::Append(ring, MakeLeaf(flats[middle2 + i])); + } + } + break; + } + EXPECT_THAT(ToFlats(ring), ElementsAreArray(flats)); + return ring; +} + +std::ostream& operator<<(std::ostream& s, const TestParam& param) { + return s << param.ToString(); +} + +std::string TestParamToString(const testing::TestParamInfo<TestParam>& info) { + return info.param.ToString(); +} + +class CordRingTest : public testing::Test { + public: + ~CordRingTest() override { + for (CordRep* rep : unrefs_) { + CordRep::Unref(rep); + } + } + + template <typename CordRepType> + CordRepType* NeedsUnref(CordRepType* rep) { + assert(rep); + unrefs_.push_back(rep); + return rep; + } + + template <typename CordRepType> + CordRepType* Ref(CordRepType* rep) { + CordRep::Ref(rep); + return NeedsUnref(rep); + } + + private: + std::vector<CordRep*> unrefs_; +}; + +class CordRingTestWithParam : public testing::TestWithParam<TestParam> { + public: + ~CordRingTestWithParam() override { + for (CordRep* rep : unrefs_) { + CordRep::Unref(rep); + } + } + + CordRepRing* CreateWithCapacity(CordRep* child, size_t extra_capacity) { + if (!GetParam().with_capacity) extra_capacity = 0; + CordRepRing* ring = CordRepRing::Create(child, extra_capacity); + ring->SetCapacityForTesting(1 + extra_capacity); + return RefIfShared(ring); + } + + bool Shared() const { return !GetParam().refcount_is_one; } + bool InputShared() const { return GetParam().input_share_mode == kShared; } + bool InputSharedIndirect() const { + return GetParam().input_share_mode == kSharedIndirect; + } + + template <typename CordRepType> + CordRepType* NeedsUnref(CordRepType* rep) { + assert(rep); + unrefs_.push_back(rep); + return rep; + } + + template <typename CordRepType> + CordRepType* Ref(CordRepType* rep) { + CordRep::Ref(rep); + return NeedsUnref(rep); + } + + template <typename CordRepType> + CordRepType* RefIfShared(CordRepType* rep) { + return Shared() ? Ref(rep) : rep; + } + + template <typename CordRepType> + CordRepType* RefIfInputShared(CordRepType* rep) { + return InputShared() ? Ref(rep) : rep; + } + + template <typename CordRepType> + CordRepType* RefIfInputSharedIndirect(CordRepType* rep) { + return InputSharedIndirect() ? Ref(rep) : rep; + } + + private: + std::vector<CordRep*> unrefs_; +}; + +class CordRingCreateTest : public CordRingTestWithParam { + public: + static TestParams CreateTestParams() { + TestParams params; + params.emplace_back(InputShareMode::kPrivate); + params.emplace_back(InputShareMode::kShared); + return params; + } +}; + +class CordRingSubTest : public CordRingTestWithParam { + public: + static TestParams CreateTestParams() { + TestParams params; + for (bool refcount_is_one : {true, false}) { + TestParam param; + param.refcount_is_one = refcount_is_one; + params.push_back(param); + } + return params; + } +}; + +class CordRingBuildTest : public CordRingTestWithParam { + public: + static TestParams CreateTestParams() { + TestParams params; + for (bool refcount_is_one : {true, false}) { + for (bool with_capacity : {true, false}) { + TestParam param; + param.refcount_is_one = refcount_is_one; + param.with_capacity = with_capacity; + params.push_back(param); + } + } + return params; + } +}; + +class CordRingCreateFromTreeTest : public CordRingTestWithParam { + public: + static TestParams CreateTestParams() { + TestParams params; + params.emplace_back(InputShareMode::kPrivate); + params.emplace_back(InputShareMode::kShared); + params.emplace_back(InputShareMode::kSharedIndirect); + return params; + } +}; + +class CordRingBuildInputTest : public CordRingTestWithParam { + public: + static TestParams CreateTestParams() { + TestParams params; + for (bool refcount_is_one : {true, false}) { + for (bool with_capacity : {true, false}) { + for (InputShareMode share_mode : {kPrivate, kShared, kSharedIndirect}) { + TestParam param; + param.refcount_is_one = refcount_is_one; + param.with_capacity = with_capacity; + param.input_share_mode = share_mode; + params.push_back(param); + } + } + } + return params; + } +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordRingSubTest, + testing::ValuesIn(CordRingSubTest::CreateTestParams()), + TestParamToString); + +INSTANTIATE_TEST_SUITE_P( + WithParam, CordRingCreateTest, + testing::ValuesIn(CordRingCreateTest::CreateTestParams()), + TestParamToString); + +INSTANTIATE_TEST_SUITE_P( + WithParam, CordRingCreateFromTreeTest, + testing::ValuesIn(CordRingCreateFromTreeTest::CreateTestParams()), + TestParamToString); + +INSTANTIATE_TEST_SUITE_P( + WithParam, CordRingBuildTest, + testing::ValuesIn(CordRingBuildTest::CreateTestParams()), + TestParamToString); + +INSTANTIATE_TEST_SUITE_P( + WithParam, CordRingBuildInputTest, + testing::ValuesIn(CordRingBuildInputTest::CreateTestParams()), + TestParamToString); + +TEST_P(CordRingCreateTest, CreateFromFlat) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1))); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(str1.size())); + EXPECT_THAT(ToFlats(result), ElementsAre(str1)); +} + +TEST_P(CordRingCreateTest, CreateFromRing) { + CordRepRing* ring = RefIfShared(FromFlats(kFoxFlats)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(ring)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); +} + +TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringRing) { + CordRepRing* ring = RefIfInputSharedIndirect(FromFlats(kFoxFlats)); + CordRep* sub = RefIfInputShared(MakeSubstring(2, 11, ring)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(sub)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfInputPrivate(GetParam(), ring)); + EXPECT_THAT(ToString(result), string_view(kFox).substr(2, 11)); +} + +TEST_F(CordRingTest, CreateWithIllegalExtraCapacity) { +#if defined(ABSL_HAVE_EXCEPTIONS) + CordRep* flat = NeedsUnref(MakeFlat("Hello world")); + try { + CordRepRing::Create(flat, CordRepRing::kMaxCapacity); + GTEST_FAIL() << "expected std::length_error exception"; + } catch (const std::length_error&) { + } +#elif defined(GTEST_HAS_DEATH_TEST) + CordRep* flat = NeedsUnref(MakeFlat("Hello world")); + EXPECT_DEATH(CordRepRing::Create(flat, CordRepRing::kMaxCapacity), ".*"); +#endif +} + +TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfFlat) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + auto* flat = RefIfInputShared(MakeFlat(str1)); + auto* child = RefIfInputSharedIndirect(MakeSubstring(4, 20, flat)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(child)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(20)); + EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(4, 20))); +} + +TEST_P(CordRingCreateTest, CreateFromExternal) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + auto* child = RefIfInputShared(MakeExternal(str1)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(child)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(str1.size())); + EXPECT_THAT(ToFlats(result), ElementsAre(str1)); +} + +TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfExternal) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + auto* external = RefIfInputShared(MakeExternal(str1)); + auto* child = RefIfInputSharedIndirect(MakeSubstring(1, 24, external)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(child)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(24)); + EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(1, 24))); +} + +TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) { + auto* external = RefIfInputShared(MakeFakeExternal(1 << 20)); + auto str = not_a_string_view(external->base, 1 << 20) + .remove_prefix(1 << 19) + .remove_suffix(6); + auto* child = + RefIfInputSharedIndirect(MakeSubstring(1 << 19, (1 << 19) - 6, external)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(child)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(str.size())); + EXPECT_THAT(ToRawFlats(result), ElementsAre(str)); +} + +TEST_P(CordRingBuildInputTest, CreateFromConcat) { + CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; + auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]); + auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3])); + auto* concat = RefIfInputShared(MakeConcat(left, right)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(concat)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(26)); + EXPECT_THAT(ToString(result), Eq(kAlphabet)); +} + +TEST_P(CordRingBuildInputTest, CreateFromSubstringConcat) { + for (size_t off = 0; off < 26; ++off) { + for (size_t len = 1; len < 26 - off; ++len) { + CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; + auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]); + auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3])); + auto* concat = MakeConcat(left, right); + auto* child = RefIfInputShared(MakeSubstring(off, len, concat)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(child)); + ASSERT_THAT(result, IsValidRingBuffer()); + ASSERT_THAT(result->length, Eq(len)); + ASSERT_THAT(ToString(result), string_view(kAlphabet).substr(off, len)); + } + } +} + +TEST_P(CordRingCreateTest, Properties) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1), 120)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->head(), Eq(0)); + EXPECT_THAT(result->tail(), Eq(1)); + EXPECT_THAT(result->capacity(), Ge(120 + 1)); + EXPECT_THAT(result->capacity(), Le(2 * 120 + 1)); + EXPECT_THAT(result->entries(), Eq(1)); + EXPECT_THAT(result->begin_pos(), Eq(0)); +} + +TEST_P(CordRingCreateTest, EntryForNewFlat) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + CordRep* child = MakeFlat(str1); + CordRepRing* result = NeedsUnref(CordRepRing::Create(child, 120)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->entry_child(0), Eq(child)); + EXPECT_THAT(result->entry_end_pos(0), Eq(str1.length())); + EXPECT_THAT(result->entry_data_offset(0), Eq(0)); +} + +TEST_P(CordRingCreateTest, EntryForNewFlatSubstring) { + absl::string_view str1 = "1234567890abcdefghijklmnopqrstuvwxyz"; + CordRep* child = MakeFlat(str1); + CordRep* substring = MakeSubstring(10, 26, child); + CordRepRing* result = NeedsUnref(CordRepRing::Create(substring, 1)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->entry_child(0), Eq(child)); + EXPECT_THAT(result->entry_end_pos(0), Eq(26)); + EXPECT_THAT(result->entry_data_offset(0), Eq(10)); +} + +TEST_P(CordRingBuildTest, AppendFlat) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, MakeFlat(str2))); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); + EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); +} + +TEST_P(CordRingBuildTest, PrependFlat) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, MakeFlat(str2))); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); + EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1)); +} + +TEST_P(CordRingBuildTest, AppendString) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); + EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); +} + +TEST_P(CordRingBuildTest, AppendStringHavingExtra) { + absl::string_view str1 = "1234"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRepRing* ring = CreateWithCapacity(MakeFlat(str1, 26), 0); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); + EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); +} + +TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) { + absl::string_view str1 = "1234"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + // Create flat with at least one extra byte. We don't expect to have sized + // alloc and capacity rounding to grant us enough to not make it partial. + auto* flat = MakeFlat(str1, 1); + size_t avail = flat->flat()->Capacity() - flat->length; + ASSERT_THAT(avail, Lt(str2.size())) << " adjust test for larger flats!"; + + // Construct the flats we do expect using all of `avail`. + absl::string_view str1a = str2.substr(0, avail); + absl::string_view str2a = str2.substr(avail); + + CordRepRing* ring = CreateWithCapacity(flat, 1); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + if (GetParam().refcount_is_one) { + EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str1, str1a), str2a)); + } else { + EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2)); + } +} + +TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) { + absl::string_view str1 = "123456789_1234"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRep* flat = RemovePrefix(10, MakeFlat(str1, 26)); + CordRepRing* ring = CreateWithCapacity(flat, 0); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(result->length, Eq(4 + str2.size())); + if (GetParam().refcount_is_one) { + EXPECT_THAT(ToFlats(result), ElementsAre(StrCat("1234", str2))); + } else { + EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2)); + } +} + +TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) { + absl::string_view str1 = "123456789_1234"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (int shared_type = 0; shared_type < 2; ++shared_type) { + SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type)); + + // Create a flat that is shared in some way. + CordRep* flat = nullptr; + CordRep* flat1 = nullptr; + if (shared_type == 0) { + // Shared flat + flat = CordRep::Ref(MakeFlat(str1.substr(10), 100)); + } else if (shared_type == 1) { + // Shared flat inside private substring + flat1 = CordRep::Ref(MakeFlat(str1)); + flat = RemovePrefix(10, flat1); + } else { + // Private flat inside shared substring + flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100))); + } + + CordRepRing* ring = CreateWithCapacity(flat, 1); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(result->length, Eq(4 + str2.size())); + EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2)); + + CordRep::Unref(shared_type == 1 ? flat1 : flat); + } +} + +TEST_P(CordRingBuildTest, AppendStringWithExtra) { + absl::string_view str1 = "1234"; + absl::string_view str2 = "1234567890"; + absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2, 26)); + result = CordRepRing::Append(result, str3); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size())); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre(str1, StrCat(str2, str3))); +} + +TEST_P(CordRingBuildTest, PrependString) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + // Use external rep to avoid appending to first flat + CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + if (GetParam().with_capacity && GetParam().refcount_is_one) { + EXPECT_THAT(result, Eq(ring)); + } else { + EXPECT_THAT(result, Ne(ring)); + } + EXPECT_THAT(result->length, Eq(str1.size() + str2.size())); + EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1)); +} + +TEST_P(CordRingBuildTest, PrependStringHavingExtra) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz1234"; + absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRep* flat = RemovePrefix(26, MakeFlat(str1)); + CordRepRing* ring = CreateWithCapacity(flat, 0); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(result->length, Eq(4 + str2.size())); + if (GetParam().refcount_is_one) { + EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str2, "1234"))); + } else { + EXPECT_THAT(ToFlats(result), ElementsAre(str2, "1234")); + } +} + +TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) { + absl::string_view str1 = "123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + absl::string_view str2 = "abcdefghij"; + absl::string_view str1a = str1.substr(10); + for (int shared_type = 1; shared_type < 2; ++shared_type) { + SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type)); + + // Create a flat that is shared in some way. + CordRep* flat = nullptr; + CordRep* flat1 = nullptr; + if (shared_type == 1) { + // Shared flat inside private substring + flat = RemovePrefix(10, flat1 = CordRep::Ref(MakeFlat(str1))); + } else { + // Private flat inside shared substring + flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100))); + } + + CordRepRing* ring = CreateWithCapacity(flat, 1); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result->length, Eq(str1a.size() + str2.size())); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a)); + CordRep::Unref(shared_type == 1 ? flat1 : flat); + } +} + +TEST_P(CordRingBuildTest, PrependStringWithExtra) { + absl::string_view str1 = "1234"; + absl::string_view str2 = "1234567890"; + absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2, 26)); + ASSERT_THAT(result, IsValidRingBuffer()); + result = CordRepRing::Prepend(result, str3); + EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size())); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str3, str2), str1)); +} + +TEST_P(CordRingBuildTest, AppendPrependStringMix) { + const auto& flats = kFoxFlats; + CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4]), 8); + CordRepRing* result = ring; + for (int i = 1; i <= 4; ++i) { + result = CordRepRing::Prepend(result, flats[4 - i]); + result = CordRepRing::Append(result, flats[4 + i]); + } + NeedsUnref(result); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(ToString(result), kFox); +} + +TEST_P(CordRingBuildTest, AppendPrependStringMixWithExtra) { + const auto& flats = kFoxFlats; + CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4], 100), 8); + CordRepRing* result = ring; + for (int i = 1; i <= 4; ++i) { + result = CordRepRing::Prepend(result, flats[4 - i], 100); + result = CordRepRing::Append(result, flats[4 + i], 100); + } + NeedsUnref(result); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + if (GetParam().refcount_is_one) { + EXPECT_THAT(ToFlats(result), + ElementsAre("The quick brown fox ", "jumps over the lazy dog")); + } else { + EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ", + "over the lazy dog")); + } +} + +TEST_P(CordRingBuildTest, AppendPrependStringMixWithPrependedExtra) { + const auto& flats = kFoxFlats; + CordRep* flat = MakeFlat(StrCat(std::string(50, '.'), flats[4]), 50); + CordRepRing* ring = CreateWithCapacity(RemovePrefix(50, flat), 0); + CordRepRing* result = ring; + for (int i = 1; i <= 4; ++i) { + result = CordRepRing::Prepend(result, flats[4 - i], 100); + result = CordRepRing::Append(result, flats[4 + i], 100); + } + result = NeedsUnref(result); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + if (GetParam().refcount_is_one) { + EXPECT_THAT(ToFlats(result), ElementsAre(kFox)); + } else { + EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ", + "over the lazy dog")); + } +} + +TEST_P(CordRingSubTest, SubRing) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + string_view all = kFox; + for (size_t offset = 0; offset < all.size() - 1; ++offset) { + CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); + CordRepRing* result = CordRepRing::SubRing(ring, offset, 0); + EXPECT_THAT(result, nullptr); + + for (size_t len = 1; len < all.size() - offset; ++len) { + ring = RefIfShared(FromFlats(flats, composition)); + result = NeedsUnref(CordRepRing::SubRing(ring, offset, len)); + ASSERT_THAT(result, IsValidRingBuffer()); + ASSERT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); + ASSERT_THAT(ToString(result), Eq(all.substr(offset, len))); + } + } +} + +TEST_P(CordRingSubTest, SubRingFromLargeExternal) { + auto composition = RandomComposition(); + std::string large_string(1 << 20, '.'); + const char* flats[] = { + "abcdefghijklmnopqrstuvwxyz", + large_string.c_str(), + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + }; + std::string buffer = absl::StrCat(flats[0], flats[1], flats[2]); + absl::string_view all = buffer; + for (size_t offset = 0; offset < 30; ++offset) { + CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); + CordRepRing* result = CordRepRing::SubRing(ring, offset, 0); + EXPECT_THAT(result, nullptr); + + for (size_t len = all.size() - 30; len < all.size() - offset; ++len) { + ring = RefIfShared(FromFlats(flats, composition)); + result = NeedsUnref(CordRepRing::SubRing(ring, offset, len)); + ASSERT_THAT(result, IsValidRingBuffer()); + ASSERT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); + auto str = ToString(result); + ASSERT_THAT(str, SizeIs(len)); + ASSERT_THAT(str, Eq(all.substr(offset, len))); + } + } +} + +TEST_P(CordRingSubTest, RemovePrefix) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + string_view all = kFox; + CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); + CordRepRing* result = CordRepRing::RemovePrefix(ring, all.size()); + EXPECT_THAT(result, nullptr); + + for (size_t len = 1; len < all.size(); ++len) { + ring = RefIfShared(FromFlats(flats, composition)); + result = NeedsUnref(CordRepRing::RemovePrefix(ring, len)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToString(result), Eq(all.substr(len))); + } +} + +TEST_P(CordRingSubTest, RemovePrefixFromLargeExternal) { + CordRepExternal* external1 = MakeFakeExternal(1 << 20); + CordRepExternal* external2 = MakeFakeExternal(1 << 20); + CordRepRing* ring = CordRepRing::Create(external1, 1); + ring = CordRepRing::Append(ring, external2); + CordRepRing* result = NeedsUnref(CordRepRing::RemovePrefix(ring, 1 << 16)); + EXPECT_THAT( + ToRawFlats(result), + ElementsAre( + not_a_string_view(external1->base, 1 << 20).remove_prefix(1 << 16), + not_a_string_view(external2->base, 1 << 20))); +} + +TEST_P(CordRingSubTest, RemoveSuffix) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + string_view all = kFox; + CordRepRing* ring = RefIfShared(FromFlats(flats, composition)); + CordRepRing* result = CordRepRing::RemoveSuffix(ring, all.size()); + EXPECT_THAT(result, nullptr); + + for (size_t len = 1; len < all.size(); ++len) { + ring = RefIfShared(FromFlats(flats, composition)); + result = NeedsUnref(CordRepRing::RemoveSuffix(ring, len)); + ASSERT_THAT(result, IsValidRingBuffer()); + ASSERT_THAT(result, EqIfPrivate(GetParam(), ring)); + ASSERT_THAT(result, NeIfShared(GetParam(), ring)); + ASSERT_THAT(ToString(result), Eq(all.substr(0, all.size() - len))); + } +} + +TEST_P(CordRingSubTest, AppendRing) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats).subspan(1); + CordRepRing* ring = CreateWithCapacity(MakeFlat(kFoxFlats[0]), flats.size()); + CordRepRing* child = FromFlats(flats, composition); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, child)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivate(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); +} + +TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size()); + CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition)); + CordRep* stripped = RemovePrefix(10, child); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ", + "over ", "the ", "lazy ", "dog")); +} + +TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size()); + CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition)); + CordRep* stripped = RemovePrefix(21, child); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), + ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog")); +} + +TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size()); + CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition)); + CordRep* stripped = RemoveSuffix(8, child); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ", + "fox ", "jumps ", "over ", "the ")); +} + +TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size()); + CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition)); + CordRep* stripped = RemoveSuffix(15, child); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ", + "fox ", "jumps ", "ov")); +} + +TEST_P(CordRingBuildTest, AppendRingMiddlePiece) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size()); + CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition)); + CordRep* stripped = MakeSubstring(7, child->length - 27, child); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), + ElementsAre("Head", "ck ", "brown ", "fox ", "jum")); +} + +TEST_P(CordRingBuildTest, AppendRingSinglePiece) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size()); + CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row")); +} + +TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0); + CordRepRing* ring = CordRepRing::Create(MakeFlat("Head"), extra_capacity); + ring->SetCapacityForTesting(1 + extra_capacity); + ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend"))); + assert(ring->IsValid(std::cout)); + CordRepRing* child = RefIfInputSharedIndirect(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row")); +} + +TEST_P(CordRingBuildInputTest, PrependRing) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto fox = MakeSpan(kFoxFlats); + auto flats = MakeSpan(fox).subspan(0, fox.size() - 1); + CordRepRing* ring = CreateWithCapacity(MakeFlat(fox.back()), flats.size()); + CordRepRing* child = RefIfInputShared(FromFlats(flats, composition)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats)); +} + +TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size()); + CordRep* child = RefIfInputShared(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(10, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ", + "the ", "lazy ", "dog", "Tail")); +} + +TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size()); + CordRep* child = RefIfInputShared(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(21, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), + ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail")); +} + +TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size()); + CordRep* child = RefIfInputShared(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(8, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ", + "jumps ", "over ", "the ", "Tail")); +} + +TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size()); + CordRep* child = RefIfInputShared(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(15, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ", + "jumps ", "ov", "Tail")); +} + +TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size()); + CordRep* child = RefIfInputShared(FromFlats(flats, composition)); + CordRep* stripped = + RefIfInputSharedIndirect(MakeSubstring(7, child->length - 27, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), + ElementsAre("ck ", "brown ", "fox ", "jum", "Tail")); +} + +TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size()); + CordRep* child = RefIfInputShared(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail")); +} + +TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) { + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + auto flats = MakeSpan(kFoxFlats); + size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0); + CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), extra_capacity); + ring->SetCapacityForTesting(1 + extra_capacity); + ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend"))); + CordRep* child = RefIfInputShared(FromFlats(flats, composition)); + CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child)); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped)); + ASSERT_THAT(result, IsValidRingBuffer()); + EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring)); + EXPECT_THAT(result, NeIfShared(GetParam(), ring)); + EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail")); +} + +TEST_F(CordRingTest, Find) { + constexpr const char* flats[] = { + "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", + "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", + "+-=", "[]\\{}|;':", ",/<>?", "."}; + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + CordRepRing* ring = NeedsUnref(FromFlats(flats, composition)); + std::string value = ToString(ring); + for (int i = 0; i < value.length(); ++i) { + CordRepRing::Position found = ring->Find(i); + auto data = ring->entry_data(found.index); + ASSERT_THAT(found.offset, Lt(data.length())); + ASSERT_THAT(data[found.offset], Eq(value[i])); + } +} + +TEST_F(CordRingTest, FindWithHint) { + constexpr const char* flats[] = { + "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", + "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", + "+-=", "[]\\{}|;':", ",/<>?", "."}; + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + CordRepRing* ring = NeedsUnref(FromFlats(flats, composition)); + std::string value = ToString(ring); + +#if defined(GTEST_HAS_DEATH_TEST) + // Test hint beyond valid position + index_type head = ring->head(); + EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 0), ".*"); + EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 9), ".*"); + EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head, 3), 24), ".*"); +#endif + + int flat_pos = 0; + size_t flat_offset = 0; + for (auto sflat : flats) { + string_view flat(sflat); + for (int offset = 0; offset < flat.length(); ++offset) { + for (int start = 0; start <= flat_pos; ++start) { + index_type hint = ring->advance(ring->head(), start); + CordRepRing::Position found = ring->Find(hint, flat_offset + offset); + ASSERT_THAT(found.index, Eq(ring->advance(ring->head(), flat_pos))); + ASSERT_THAT(found.offset, Eq(offset)); + } + } + ++flat_pos; + flat_offset += flat.length(); + } +} + +TEST_F(CordRingTest, FindInLargeRing) { + constexpr const char* flats[] = { + "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", + "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", + "+-=", "[]\\{}|;':", ",/<>?", "."}; + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + CordRepRing* ring = FromFlats(flats, composition); + for (int i = 0; i < 13; ++i) { + ring = CordRepRing::Append(ring, FromFlats(flats, composition)); + } + NeedsUnref(ring); + std::string value = ToString(ring); + for (int i = 0; i < value.length(); ++i) { + CordRepRing::Position pos = ring->Find(i); + auto data = ring->entry_data(pos.index); + ASSERT_THAT(pos.offset, Lt(data.length())); + ASSERT_THAT(data[pos.offset], Eq(value[i])); + } +} + +TEST_F(CordRingTest, FindTail) { + constexpr const char* flats[] = { + "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", + "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", + "+-=", "[]\\{}|;':", ",/<>?", "."}; + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + CordRepRing* ring = NeedsUnref(FromFlats(flats, composition)); + std::string value = ToString(ring); + + for (int i = 0; i < value.length(); ++i) { + CordRepRing::Position pos = ring->FindTail(i + 1); + auto data = ring->entry_data(ring->retreat(pos.index)); + ASSERT_THAT(pos.offset, Lt(data.length())); + ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i])); + } +} + +TEST_F(CordRingTest, FindTailWithHint) { + constexpr const char* flats[] = { + "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", + "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", + "+-=", "[]\\{}|;':", ",/<>?", "."}; + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + CordRepRing* ring = NeedsUnref(FromFlats(flats, composition)); + std::string value = ToString(ring); + + // Test hint beyond valid position +#if defined(GTEST_HAS_DEATH_TEST) + index_type head = ring->head(); + EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 1), ".*"); + EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 10), ".*"); + EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head, 3), 26), ".*"); +#endif + + for (int i = 0; i < value.length(); ++i) { + CordRepRing::Position pos = ring->FindTail(i + 1); + auto data = ring->entry_data(ring->retreat(pos.index)); + ASSERT_THAT(pos.offset, Lt(data.length())); + ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i])); + } +} + +TEST_F(CordRingTest, FindTailInLargeRing) { + constexpr const char* flats[] = { + "abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", + "KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", + "+-=", "[]\\{}|;':", ",/<>?", "."}; + auto composition = RandomComposition(); + SCOPED_TRACE(ToString(composition)); + CordRepRing* ring = FromFlats(flats, composition); + for (int i = 0; i < 13; ++i) { + ring = CordRepRing::Append(ring, FromFlats(flats, composition)); + } + NeedsUnref(ring); + std::string value = ToString(ring); + for (int i = 0; i < value.length(); ++i) { + CordRepRing::Position pos = ring->FindTail(i + 1); + auto data = ring->entry_data(ring->retreat(pos.index)); + ASSERT_THAT(pos.offset, Lt(data.length())); + ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i])); + } +} + +TEST_F(CordRingTest, GetCharacter) { + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), flats.size()); + CordRep* child = FromFlats(flats, kAppend); + CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child)); + std::string value = ToString(result); + for (int i = 0; i < value.length(); ++i) { + ASSERT_THAT(result->GetCharacter(i), Eq(value[i])); + } +} + +TEST_F(CordRingTest, GetCharacterWithSubstring) { + absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz"; + auto* child = MakeSubstring(4, 20, MakeFlat(str1)); + CordRepRing* result = NeedsUnref(CordRepRing::Create(child)); + ASSERT_THAT(result, IsValidRingBuffer()); + std::string value = ToString(result); + for (int i = 0; i < value.length(); ++i) { + ASSERT_THAT(result->GetCharacter(i), Eq(value[i])); + } +} + +TEST_F(CordRingTest, IsFlatSingleFlat) { + for (bool external : {false, true}) { + SCOPED_TRACE(external ? "With External" : "With Flat"); + absl::string_view str = "Hello world"; + CordRep* rep = external ? MakeExternal(str) : MakeFlat(str); + CordRepRing* ring = NeedsUnref(CordRepRing::Create(rep)); + + // The ring is a single non-fragmented flat: + absl::string_view fragment; + EXPECT_TRUE(ring->IsFlat(nullptr)); + EXPECT_TRUE(ring->IsFlat(&fragment)); + EXPECT_THAT(fragment, Eq("Hello world")); + fragment = ""; + EXPECT_TRUE(ring->IsFlat(0, 11, nullptr)); + EXPECT_TRUE(ring->IsFlat(0, 11, &fragment)); + EXPECT_THAT(fragment, Eq("Hello world")); + + // Arbitrary ranges must check true as well. + EXPECT_TRUE(ring->IsFlat(1, 4, &fragment)); + EXPECT_THAT(fragment, Eq("ello")); + EXPECT_TRUE(ring->IsFlat(6, 5, &fragment)); + EXPECT_THAT(fragment, Eq("world")); + } +} + +TEST_F(CordRingTest, IsFlatMultiFlat) { + for (bool external : {false, true}) { + SCOPED_TRACE(external ? "With External" : "With Flat"); + absl::string_view str1 = "Hello world"; + absl::string_view str2 = "Halt and catch fire"; + CordRep* rep1 = external ? MakeExternal(str1) : MakeFlat(str1); + CordRep* rep2 = external ? MakeExternal(str2) : MakeFlat(str2); + CordRepRing* ring = CordRepRing::Append(CordRepRing::Create(rep1), rep2); + NeedsUnref(ring); + + // The ring is fragmented, IsFlat() on the entire cord must be false. + EXPECT_FALSE(ring->IsFlat(nullptr)); + absl::string_view fragment = "Don't touch this"; + EXPECT_FALSE(ring->IsFlat(&fragment)); + EXPECT_THAT(fragment, Eq("Don't touch this")); + + // Check for ranges exactly within both flats. + EXPECT_TRUE(ring->IsFlat(0, 11, &fragment)); + EXPECT_THAT(fragment, Eq("Hello world")); + EXPECT_TRUE(ring->IsFlat(11, 19, &fragment)); + EXPECT_THAT(fragment, Eq("Halt and catch fire")); + + // Check for arbitrary partial range inside each flat. + EXPECT_TRUE(ring->IsFlat(1, 4, &fragment)); + EXPECT_THAT(fragment, "ello"); + EXPECT_TRUE(ring->IsFlat(26, 4, &fragment)); + EXPECT_THAT(fragment, "fire"); + + // Check ranges spanning across both flats + fragment = "Don't touch this"; + EXPECT_FALSE(ring->IsFlat(1, 18, &fragment)); + EXPECT_FALSE(ring->IsFlat(10, 2, &fragment)); + EXPECT_THAT(fragment, Eq("Don't touch this")); + } +} + +TEST_F(CordRingTest, Dump) { + std::stringstream ss; + auto flats = MakeSpan(kFoxFlats); + CordRepRing* ring = NeedsUnref(FromFlats(flats, kPrepend)); + ss << *ring; +} + +} // namespace +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/cord_test.cc b/third_party/abseil-cpp/absl/strings/cord_test.cc index 434f3a247e..cced9bba39 100644 --- a/third_party/abseil-cpp/absl/strings/cord_test.cc +++ b/third_party/abseil-cpp/absl/strings/cord_test.cc @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/cord.h" #include <algorithm> @@ -18,9 +32,13 @@ #include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" #include "absl/container/fixed_array.h" +#include "absl/random/random.h" #include "absl/strings/cord_test_helpers.h" +#include "absl/strings/cordz_test_helpers.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" typedef std::mt19937_64 RandomEngine; @@ -70,9 +88,8 @@ static std::string RandomLowercaseString(RandomEngine* rng) { static std::string RandomLowercaseString(RandomEngine* rng, size_t length) { std::string result(length, '\0'); std::uniform_int_distribution<int> chars('a', 'z'); - std::generate(result.begin(), result.end(), [&]() { - return static_cast<char>(chars(*rng)); - }); + std::generate(result.begin(), result.end(), + [&]() { return static_cast<char>(chars(*rng)); }); return result; } @@ -166,16 +183,60 @@ class CordTestPeer { const Cord& c, absl::FunctionRef<void(absl::string_view)> callback) { c.ForEachChunk(callback); } + + static bool IsTree(const Cord& c) { return c.contents_.is_tree(); } + + static cord_internal::CordzInfo* GetCordzInfo(const Cord& c) { + return c.contents_.cordz_info(); + } + + static Cord MakeSubstring(Cord src, size_t offset, size_t length) { + ABSL_RAW_CHECK(src.contents_.is_tree(), "Can not be inlined"); + Cord cord; + auto* rep = new cord_internal::CordRepSubstring; + rep->tag = cord_internal::SUBSTRING; + rep->child = cord_internal::CordRep::Ref(src.contents_.tree()); + rep->start = offset; + rep->length = length; + cord.contents_.EmplaceTree(rep, + cord_internal::CordzUpdateTracker::kSubCord); + return cord; + } }; ABSL_NAMESPACE_END } // namespace absl -TEST(Cord, AllFlatSizes) { +// The CordTest fixture runs all tests with and without Cord Btree enabled. +class CordTest : public testing::TestWithParam<bool> { + public: + CordTest() : was_btree_(absl::cord_internal::cord_btree_enabled.load()) { + absl::cord_internal::cord_btree_enabled.store(UseBtree()); + } + ~CordTest() override { + absl::cord_internal::cord_btree_enabled.store(was_btree_); + } + + // Returns true if test is running with btree enabled. + bool UseBtree() const { return GetParam(); } + + // Returns human readable string representation of the test parameter. + static std::string ToString(testing::TestParamInfo<bool> param) { + return param.param ? "Btree" : "Concat"; + } + + private: + const bool was_btree_; +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordTest, testing::Bool(), + CordTest::ToString); + +TEST_P(CordTest, AllFlatSizes) { using absl::strings_internal::CordTestAccess; for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) { - // Make a std::string of length s. + // Make a string of length s. std::string src; while (src.size() < s) { src.push_back('a' + (src.size() % 26)); @@ -189,7 +250,7 @@ TEST(Cord, AllFlatSizes) { // We create a Cord at least 128GB in size using the fact that Cords can // internally reference-count; thus the Cord is enormous without actually // consuming very much memory. -TEST(GigabyteCord, FromExternal) { +TEST_P(CordTest, GigabyteCordFromExternal) { const size_t one_gig = 1024U * 1024U * 1024U; size_t max_size = 2 * one_gig; if (sizeof(max_size) > 4) max_size = 128 * one_gig; @@ -206,7 +267,6 @@ TEST(GigabyteCord, FromExternal) { // caused crashes in production. We grow exponentially so that the code will // execute in a reasonable amount of time. absl::Cord c; - ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size()); c.Append(from); while (c.size() < max_size) { c.Append(c); @@ -239,7 +299,7 @@ static absl::Cord MakeExternalCord(int size) { extern bool my_unique_true_boolean; bool my_unique_true_boolean = true; -TEST(Cord, Assignment) { +TEST_P(CordTest, Assignment) { absl::Cord x(absl::string_view("hi there")); absl::Cord y(x); ASSERT_EQ(std::string(x), "hi there"); @@ -293,7 +353,7 @@ TEST(Cord, Assignment) { } } -TEST(Cord, StartsEndsWith) { +TEST_P(CordTest, StartsEndsWith) { absl::Cord x(absl::string_view("abcde")); absl::Cord empty(""); @@ -326,13 +386,13 @@ TEST(Cord, StartsEndsWith) { ASSERT_TRUE(!empty.EndsWith("xyz")); } -TEST(Cord, Subcord) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); +TEST_P(CordTest, Subcord) { + RandomEngine rng(GTEST_FLAG_GET(random_seed)); const std::string s = RandomLowercaseString(&rng, 1024); absl::Cord a; AppendWithFragments(s, &rng, &a); - ASSERT_EQ(s.size(), a.size()); + ASSERT_EQ(s, std::string(a)); // Check subcords of a, from a variety of interesting points. std::set<size_t> positions; @@ -350,7 +410,7 @@ TEST(Cord, Subcord) { for (size_t end_pos : positions) { if (end_pos < pos || end_pos > a.size()) continue; absl::Cord sa = a.Subcord(pos, end_pos - pos); - EXPECT_EQ(absl::string_view(s).substr(pos, end_pos - pos), + ASSERT_EQ(absl::string_view(s).substr(pos, end_pos - pos), std::string(sa)) << a; } @@ -362,7 +422,7 @@ TEST(Cord, Subcord) { for (size_t pos = 0; pos <= sh.size(); ++pos) { for (size_t n = 0; n <= sh.size() - pos; ++n) { absl::Cord sc = c.Subcord(pos, n); - EXPECT_EQ(sh.substr(pos, n), std::string(sc)) << c; + ASSERT_EQ(sh.substr(pos, n), std::string(sc)) << c; } } @@ -372,7 +432,7 @@ TEST(Cord, Subcord) { while (sa.size() > 1) { sa = sa.Subcord(1, sa.size() - 2); ss = ss.substr(1, ss.size() - 2); - EXPECT_EQ(ss, std::string(sa)) << a; + ASSERT_EQ(ss, std::string(sa)) << a; if (HasFailure()) break; // halt cascade } @@ -387,7 +447,7 @@ TEST(Cord, Subcord) { EXPECT_TRUE(sa.empty()); } -TEST(Cord, Swap) { +TEST_P(CordTest, Swap) { absl::string_view a("Dexter"); absl::string_view b("Mandark"); absl::Cord x(a); @@ -395,6 +455,9 @@ TEST(Cord, Swap) { swap(x, y); ASSERT_EQ(x, absl::Cord(b)); ASSERT_EQ(y, absl::Cord(a)); + x.swap(y); + ASSERT_EQ(x, absl::Cord(a)); + ASSERT_EQ(y, absl::Cord(b)); } static void VerifyCopyToString(const absl::Cord& cord) { @@ -410,13 +473,13 @@ static void VerifyCopyToString(const absl::Cord& cord) { if (cord.size() <= kInitialLength) { EXPECT_EQ(has_initial_contents.data(), address_before_copy) - << "CopyCordToString allocated new std::string storage; " + << "CopyCordToString allocated new string storage; " "has_initial_contents = \"" << has_initial_contents << "\""; } } -TEST(Cord, CopyToString) { +TEST_P(CordTest, CopyToString) { VerifyCopyToString(absl::Cord()); VerifyCopyToString(absl::Cord("small cord")); VerifyCopyToString( @@ -424,6 +487,80 @@ TEST(Cord, CopyToString) { "copying ", "to ", "a ", "string."})); } +TEST_P(CordTest, TryFlatEmpty) { + absl::Cord c; + EXPECT_EQ(c.TryFlat(), ""); +} + +TEST_P(CordTest, TryFlatFlat) { + absl::Cord c("hello"); + EXPECT_EQ(c.TryFlat(), "hello"); +} + +TEST_P(CordTest, TryFlatSubstrInlined) { + absl::Cord c("hello"); + c.RemovePrefix(1); + EXPECT_EQ(c.TryFlat(), "ello"); +} + +TEST_P(CordTest, TryFlatSubstrFlat) { + absl::Cord c("longer than 15 bytes"); + absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); + EXPECT_EQ(sub.TryFlat(), "onger than 15 bytes"); +} + +TEST_P(CordTest, TryFlatConcat) { + absl::Cord c = absl::MakeFragmentedCord({"hel", "lo"}); + EXPECT_EQ(c.TryFlat(), absl::nullopt); +} + +TEST_P(CordTest, TryFlatExternal) { + absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); + EXPECT_EQ(c.TryFlat(), "hell"); +} + +TEST_P(CordTest, TryFlatSubstrExternal) { + absl::Cord c = absl::MakeCordFromExternal("hell", [](absl::string_view) {}); + absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); + EXPECT_EQ(sub.TryFlat(), "ell"); +} + +TEST_P(CordTest, TryFlatSubstrConcat) { + absl::Cord c = absl::MakeFragmentedCord({"hello", " world"}); + absl::Cord sub = absl::CordTestPeer::MakeSubstring(c, 1, c.size() - 1); + EXPECT_EQ(sub.TryFlat(), absl::nullopt); + c.RemovePrefix(1); + EXPECT_EQ(c.TryFlat(), absl::nullopt); +} + +TEST_P(CordTest, TryFlatCommonlyAssumedInvariants) { + // The behavior tested below is not part of the API contract of Cord, but it's + // something we intend to be true in our current implementation. This test + // exists to detect and prevent accidental breakage of the implementation. + absl::string_view fragments[] = {"A fragmented test", + " cord", + " to test subcords", + " of ", + "a", + " cord for", + " each chunk " + "returned by the ", + "iterator"}; + absl::Cord c = absl::MakeFragmentedCord(fragments); + int fragment = 0; + int offset = 0; + absl::Cord::CharIterator itc = c.char_begin(); + for (absl::string_view sv : c.Chunks()) { + absl::string_view expected = fragments[fragment]; + absl::Cord subcord1 = c.Subcord(offset, sv.length()); + absl::Cord subcord2 = absl::Cord::AdvanceAndRead(&itc, sv.size()); + EXPECT_EQ(subcord1.TryFlat(), expected); + EXPECT_EQ(subcord2.TryFlat(), expected); + ++fragment; + offset += sv.length(); + } +} + static bool IsFlat(const absl::Cord& c) { return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end(); } @@ -452,14 +589,14 @@ static void VerifyFlatten(absl::Cord c) { EXPECT_TRUE(IsFlat(c)); } -TEST(Cord, Flatten) { +TEST_P(CordTest, Flatten) { VerifyFlatten(absl::Cord()); VerifyFlatten(absl::Cord("small cord")); VerifyFlatten(absl::Cord("larger than small buffer optimization")); VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"})); // Test with a cord that is longer than the largest flat buffer - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192))); } @@ -506,29 +643,29 @@ class TestData { }; } // namespace -TEST(Cord, MultipleLengths) { +TEST_P(CordTest, MultipleLengths) { TestData d; for (size_t i = 0; i < d.size(); i++) { std::string a = d.data(i); - { // Construct from Cord + { // Construct from Cord absl::Cord tmp(a); absl::Cord x(tmp); EXPECT_EQ(a, std::string(x)) << "'" << a << "'"; } - { // Construct from absl::string_view + { // Construct from absl::string_view absl::Cord x(a); EXPECT_EQ(a, std::string(x)) << "'" << a << "'"; } - { // Append cord to self + { // Append cord to self absl::Cord self(a); self.Append(self); EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'"; } - { // Prepend cord to self + { // Prepend cord to self absl::Cord self(a); self.Prepend(self); EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'"; @@ -538,40 +675,40 @@ TEST(Cord, MultipleLengths) { for (size_t j = 0; j < d.size(); j++) { std::string b = d.data(j); - { // CopyFrom Cord + { // CopyFrom Cord absl::Cord x(a); absl::Cord y(b); x = y; EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'"; } - { // CopyFrom absl::string_view + { // CopyFrom absl::string_view absl::Cord x(a); x = b; EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'"; } - { // Cord::Append(Cord) + { // Cord::Append(Cord) absl::Cord x(a); absl::Cord y(b); x.Append(y); EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'"; } - { // Cord::Append(absl::string_view) + { // Cord::Append(absl::string_view) absl::Cord x(a); x.Append(b); EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'"; } - { // Cord::Prepend(Cord) + { // Cord::Prepend(Cord) absl::Cord x(a); absl::Cord y(b); x.Prepend(y); EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'"; } - { // Cord::Prepend(absl::string_view) + { // Cord::Prepend(absl::string_view) absl::Cord x(a); x.Prepend(b); EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'"; @@ -582,7 +719,7 @@ TEST(Cord, MultipleLengths) { namespace { -TEST(Cord, RemoveSuffixWithExternalOrSubstring) { +TEST_P(CordTest, RemoveSuffixWithExternalOrSubstring) { absl::Cord cord = absl::MakeCordFromExternal( "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); }); @@ -597,7 +734,7 @@ TEST(Cord, RemoveSuffixWithExternalOrSubstring) { EXPECT_EQ("foo", std::string(cord)); } -TEST(Cord, RemoveSuffixMakesZeroLengthNode) { +TEST_P(CordTest, RemoveSuffixMakesZeroLengthNode) { absl::Cord c; c.Append(absl::Cord(std::string(100, 'x'))); absl::Cord other_ref = c; // Prevent inplace appends @@ -624,7 +761,7 @@ absl::Cord CordWithZedBlock(size_t size) { } // Establish that ZedBlock does what we think it does. -TEST(CordSpliceTest, ZedBlock) { +TEST_P(CordTest, CordSpliceTestZedBlock) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); std::string s; @@ -632,7 +769,7 @@ TEST(CordSpliceTest, ZedBlock) { EXPECT_EQ("zzzzzzzzzz", s); } -TEST(CordSpliceTest, ZedBlock0) { +TEST_P(CordTest, CordSpliceTestZedBlock0) { absl::Cord blob = CordWithZedBlock(0); EXPECT_EQ(0, blob.size()); std::string s; @@ -640,7 +777,7 @@ TEST(CordSpliceTest, ZedBlock0) { EXPECT_EQ("", s); } -TEST(CordSpliceTest, ZedBlockSuffix1) { +TEST_P(CordTest, CordSpliceTestZedBlockSuffix1) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); @@ -652,7 +789,7 @@ TEST(CordSpliceTest, ZedBlockSuffix1) { } // Remove all of a prefix block -TEST(CordSpliceTest, ZedBlockSuffix0) { +TEST_P(CordTest, CordSpliceTestZedBlockSuffix0) { absl::Cord blob = CordWithZedBlock(10); EXPECT_EQ(10, blob.size()); absl::Cord suffix(blob); @@ -684,7 +821,7 @@ absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset, } // Taking an empty suffix of a block breaks appending. -TEST(CordSpliceTest, RemoveEntireBlock1) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock1) { absl::Cord zero = CordWithZedBlock(10); absl::Cord suffix(zero); suffix.RemovePrefix(10); @@ -692,7 +829,7 @@ TEST(CordSpliceTest, RemoveEntireBlock1) { result.Append(suffix); } -TEST(CordSpliceTest, RemoveEntireBlock2) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock2) { absl::Cord zero = CordWithZedBlock(10); absl::Cord prefix(zero); prefix.RemoveSuffix(10); @@ -702,7 +839,7 @@ TEST(CordSpliceTest, RemoveEntireBlock2) { result.Append(suffix); } -TEST(CordSpliceTest, RemoveEntireBlock3) { +TEST_P(CordTest, CordSpliceTestRemoveEntireBlock3) { absl::Cord blob = CordWithZedBlock(10); absl::Cord block = BigCord(10, 'b'); blob = SpliceCord(blob, 0, block); @@ -733,7 +870,7 @@ void VerifyComparison(const CordCompareTestCase& test_case) { << "LHS=" << rhs_string << "; RHS=" << lhs_string; } -TEST(Cord, Compare) { +TEST_P(CordTest, Compare) { absl::Cord subcord("aaaaaBBBBBcccccDDDDD"); subcord = subcord.Subcord(3, 10); @@ -796,7 +933,7 @@ TEST(Cord, Compare) { } } -TEST(Cord, CompareAfterAssign) { +TEST_P(CordTest, CompareAfterAssign) { absl::Cord a("aaaaaa1111111"); absl::Cord b("aaaaaa2222222"); a = "cccccc"; @@ -813,7 +950,7 @@ TEST(Cord, CompareAfterAssign) { } // Test CompareTo() and ComparePrefix() against string and substring -// comparison methods from std::basic_string. +// comparison methods from basic_string. static void TestCompare(const absl::Cord& c, const absl::Cord& d, RandomEngine* rng) { typedef std::basic_string<uint8_t> ustring; @@ -825,8 +962,8 @@ static void TestCompare(const absl::Cord& c, const absl::Cord& d, EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d; } -TEST(Compare, ComparisonIsUnsigned) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); +TEST_P(CordTest, CompareComparisonIsUnsigned) { + RandomEngine rng(GTEST_FLAG_GET(random_seed)); std::uniform_int_distribution<uint32_t> uniform_uint8(0, 255); char x = static_cast<char>(uniform_uint8(rng)); TestCompare( @@ -834,9 +971,9 @@ TEST(Compare, ComparisonIsUnsigned) { absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng); } -TEST(Compare, RandomComparisons) { +TEST_P(CordTest, CompareRandomComparisons) { const int kIters = 5000; - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); int n = GetUniformRandomUpTo(&rng, 5000); absl::Cord a[] = {MakeExternalCord(n), @@ -869,7 +1006,7 @@ void CompareOperators() { EXPECT_TRUE(a == a); // For pointer type (i.e. `const char*`), operator== compares the address - // instead of the std::string, so `a == const char*("a")` isn't necessarily true. + // instead of the string, so `a == const char*("a")` isn't necessarily true. EXPECT_TRUE(std::is_pointer<T1>::value || a == T1("a")); EXPECT_TRUE(std::is_pointer<T2>::value || a == T2("a")); EXPECT_FALSE(a == b); @@ -892,43 +1029,43 @@ void CompareOperators() { EXPECT_FALSE(b <= a); } -TEST(ComparisonOperators, Cord_Cord) { +TEST_P(CordTest, ComparisonOperators_Cord_Cord) { CompareOperators<absl::Cord, absl::Cord>(); } -TEST(ComparisonOperators, Cord_StringPiece) { +TEST_P(CordTest, ComparisonOperators_Cord_StringPiece) { CompareOperators<absl::Cord, absl::string_view>(); } -TEST(ComparisonOperators, StringPiece_Cord) { +TEST_P(CordTest, ComparisonOperators_StringPiece_Cord) { CompareOperators<absl::string_view, absl::Cord>(); } -TEST(ComparisonOperators, Cord_string) { +TEST_P(CordTest, ComparisonOperators_Cord_string) { CompareOperators<absl::Cord, std::string>(); } -TEST(ComparisonOperators, string_Cord) { +TEST_P(CordTest, ComparisonOperators_string_Cord) { CompareOperators<std::string, absl::Cord>(); } -TEST(ComparisonOperators, stdstring_Cord) { +TEST_P(CordTest, ComparisonOperators_stdstring_Cord) { CompareOperators<std::string, absl::Cord>(); } -TEST(ComparisonOperators, Cord_stdstring) { +TEST_P(CordTest, ComparisonOperators_Cord_stdstring) { CompareOperators<absl::Cord, std::string>(); } -TEST(ComparisonOperators, charstar_Cord) { +TEST_P(CordTest, ComparisonOperators_charstar_Cord) { CompareOperators<const char*, absl::Cord>(); } -TEST(ComparisonOperators, Cord_charstar) { +TEST_P(CordTest, ComparisonOperators_Cord_charstar) { CompareOperators<absl::Cord, const char*>(); } -TEST(ConstructFromExternal, ReleaserInvoked) { +TEST_P(CordTest, ConstructFromExternalReleaserInvoked) { // Empty external memory means the releaser should be called immediately. { bool invoked = false; @@ -970,8 +1107,8 @@ TEST(ConstructFromExternal, ReleaserInvoked) { } } -TEST(ConstructFromExternal, CompareContents) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); +TEST_P(CordTest, ConstructFromExternalCompareContents) { + RandomEngine rng(GTEST_FLAG_GET(random_seed)); for (int length = 1; length <= 2048; length *= 2) { std::string data = RandomLowercaseString(&rng, length); @@ -986,8 +1123,8 @@ TEST(ConstructFromExternal, CompareContents) { } } -TEST(ConstructFromExternal, LargeReleaser) { - RandomEngine rng(testing::GTEST_FLAG(random_seed)); +TEST_P(CordTest, ConstructFromExternalLargeReleaser) { + RandomEngine rng(GTEST_FLAG_GET(random_seed)); constexpr size_t kLength = 256; std::string data = RandomLowercaseString(&rng, kLength); std::array<char, kLength> data_array; @@ -1001,7 +1138,7 @@ TEST(ConstructFromExternal, LargeReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, FunctionPointerReleaser) { +TEST_P(CordTest, ConstructFromExternalFunctionPointerReleaser) { static absl::string_view data("hello world"); static bool invoked; auto* releaser = @@ -1018,7 +1155,7 @@ TEST(ConstructFromExternal, FunctionPointerReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, MoveOnlyReleaser) { +TEST_P(CordTest, ConstructFromExternalMoveOnlyReleaser) { struct Releaser { explicit Releaser(bool* invoked) : invoked(invoked) {} Releaser(Releaser&& other) noexcept : invoked(other.invoked) {} @@ -1032,7 +1169,20 @@ TEST(ConstructFromExternal, MoveOnlyReleaser) { EXPECT_TRUE(invoked); } -TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { +TEST_P(CordTest, ConstructFromExternalNoArgLambda) { + bool invoked = false; + (void)absl::MakeCordFromExternal("dummy", [&invoked]() { invoked = true; }); + EXPECT_TRUE(invoked); +} + +TEST_P(CordTest, ConstructFromExternalStringViewArgLambda) { + bool invoked = false; + (void)absl::MakeCordFromExternal( + "dummy", [&invoked](absl::string_view) { invoked = true; }); + EXPECT_TRUE(invoked); +} + +TEST_P(CordTest, ConstructFromExternalNonTrivialReleaserDestructor) { struct Releaser { explicit Releaser(bool* destroyed) : destroyed(destroyed) {} ~Releaser() { *destroyed = true; } @@ -1047,7 +1197,7 @@ TEST(ConstructFromExternal, NonTrivialReleaserDestructor) { EXPECT_TRUE(destroyed); } -TEST(ConstructFromExternal, ReferenceQualifierOverloads) { +TEST_P(CordTest, ConstructFromExternalReferenceQualifierOverloads) { struct Releaser { void operator()(absl::string_view) & { *lvalue_invoked = true; } void operator()(absl::string_view) && { *rvalue_invoked = true; } @@ -1075,8 +1225,8 @@ TEST(ConstructFromExternal, ReferenceQualifierOverloads) { EXPECT_TRUE(rvalue_invoked); } -TEST(ExternalMemory, BasicUsage) { - static const char* strings[] = { "", "hello", "there" }; +TEST_P(CordTest, ExternalMemoryBasicUsage) { + static const char* strings[] = {"", "hello", "there"}; for (const char* str : strings) { absl::Cord dst("(prefix)"); AddExternalMemory(str, &dst); @@ -1086,7 +1236,7 @@ TEST(ExternalMemory, BasicUsage) { } } -TEST(ExternalMemory, RemovePrefixSuffix) { +TEST_P(CordTest, ExternalMemoryRemovePrefixSuffix) { // Exhaustively try all sub-strings. absl::Cord cord = MakeComposite(); std::string s = std::string(cord); @@ -1101,7 +1251,7 @@ TEST(ExternalMemory, RemovePrefixSuffix) { } } -TEST(ExternalMemory, Get) { +TEST_P(CordTest, ExternalMemoryGet) { absl::Cord cord("hello"); AddExternalMemory(" world!", &cord); AddExternalMemory(" how are ", &cord); @@ -1120,16 +1270,16 @@ TEST(ExternalMemory, Get) { // Additionally we have some whiteboxed expectations based on our knowledge of // the layout and size of empty and inlined cords, and flat nodes. -TEST(CordMemoryUsage, Empty) { +TEST_P(CordTest, CordMemoryUsageEmpty) { EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage()); } -TEST(CordMemoryUsage, Embedded) { +TEST_P(CordTest, CordMemoryUsageEmbedded) { absl::Cord a("hello"); EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); } -TEST(CordMemoryUsage, EmbeddedAppend) { +TEST_P(CordTest, CordMemoryUsageEmbeddedAppend) { absl::Cord a("a"); absl::Cord b("bcd"); EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord)); @@ -1137,7 +1287,7 @@ TEST(CordMemoryUsage, EmbeddedAppend) { EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord)); } -TEST(CordMemoryUsage, ExternalMemory) { +TEST_P(CordTest, CordMemoryUsageExternalMemory) { static const int kLength = 1000; absl::Cord cord; AddExternalMemory(std::string(kLength, 'x'), &cord); @@ -1145,14 +1295,14 @@ TEST(CordMemoryUsage, ExternalMemory) { EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5); } -TEST(CordMemoryUsage, Flat) { +TEST_P(CordTest, CordMemoryUsageFlat) { static const int kLength = 125; absl::Cord a(std::string(kLength, 'a')); EXPECT_GT(a.EstimatedMemoryUsage(), kLength); EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5); } -TEST(CordMemoryUsage, AppendFlat) { +TEST_P(CordTest, CordMemoryUsageAppendFlat) { using absl::strings_internal::CordTestAccess; absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); size_t length = a.EstimatedMemoryUsage(); @@ -1162,9 +1312,32 @@ TEST(CordMemoryUsage, AppendFlat) { EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5); } +TEST_P(CordTest, CordMemoryUsageAppendExternal) { + static const int kLength = 1000; + using absl::strings_internal::CordTestAccess; + absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a')); + size_t length = a.EstimatedMemoryUsage(); + AddExternalMemory(std::string(kLength, 'b'), &a); + size_t delta = a.EstimatedMemoryUsage() - length; + EXPECT_GT(delta, kLength); + EXPECT_LE(delta, kLength * 1.5); +} + +TEST_P(CordTest, CordMemoryUsageSubString) { + static const int kLength = 2000; + using absl::strings_internal::CordTestAccess; + absl::Cord a(std::string(kLength, 'a')); + size_t length = a.EstimatedMemoryUsage(); + AddExternalMemory(std::string(kLength, 'b'), &a); + absl::Cord b = a.Subcord(0, kLength + kLength / 2); + size_t delta = b.EstimatedMemoryUsage() - length; + EXPECT_GT(delta, kLength); + EXPECT_LE(delta, kLength * 1.5); +} + // Regtest for a change that had to be rolled back because it expanded out // of the InlineRep too soon, which was observable through MemoryUsage(). -TEST(CordMemoryUsage, InlineRep) { +TEST_P(CordTest, CordMemoryUsageInlineRep) { constexpr size_t kMaxInline = 15; // Cord::InlineRep::N const std::string small_string(kMaxInline, 'x'); absl::Cord c1(small_string); @@ -1178,7 +1351,7 @@ TEST(CordMemoryUsage, InlineRep) { } // namespace // Regtest for 7510292 (fix a bug introduced by 7465150) -TEST(Cord, Concat_Append) { +TEST_P(CordTest, Concat_Append) { // Create a rep of type CONCAT absl::Cord s1("foobarbarbarbarbar"); s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg"); @@ -1193,7 +1366,80 @@ TEST(Cord, Concat_Append) { EXPECT_EQ(s2.size(), size + 1); } -TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) { +TEST_P(CordTest, DiabolicalGrowth) { + // This test exercises a diabolical Append(<one char>) on a cord, making the + // cord shared before each Append call resulting in a terribly fragmented + // resulting cord. + // TODO(b/183983616): Apply some minimum compaction when copying a shared + // source cord into a mutable copy for updates in CordRepRing. + RandomEngine rng(GTEST_FLAG_GET(random_seed)); + const std::string expected = RandomLowercaseString(&rng, 5000); + absl::Cord cord; + for (char c : expected) { + absl::Cord shared(cord); + cord.Append(absl::string_view(&c, 1)); + } + std::string value; + absl::CopyCordToString(cord, &value); + EXPECT_EQ(value, expected); + ABSL_RAW_LOG(INFO, "Diabolical size allocated = %zu", + cord.EstimatedMemoryUsage()); +} + +// The following tests check support for >4GB cords in 64-bit binaries, and +// 2GB-4GB cords in 32-bit binaries. This function returns the large cord size +// that's appropriate for the binary. + +// Construct a huge cord with the specified valid prefix. +static absl::Cord MakeHuge(absl::string_view prefix) { + absl::Cord cord; + if (sizeof(size_t) > 4) { + // In 64-bit binaries, test 64-bit Cord support. + const size_t size = + static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 314; + cord.Append(absl::MakeCordFromExternal( + absl::string_view(prefix.data(), size), + [](absl::string_view s) { DoNothing(s, nullptr); })); + } else { + // Cords are limited to 32-bit lengths in 32-bit binaries. The following + // tests check for use of "signed int" to represent Cord length/offset. + // However absl::string_view does not allow lengths >= (1u<<31), so we need + // to append in two parts; + const size_t s1 = (1u << 31) - 1; + // For shorter cord, `Append` copies the data rather than allocating a new + // node. The threshold is currently set to 511, so `s2` needs to be bigger + // to not trigger the copy. + const size_t s2 = 600; + cord.Append(absl::MakeCordFromExternal( + absl::string_view(prefix.data(), s1), + [](absl::string_view s) { DoNothing(s, nullptr); })); + cord.Append(absl::MakeCordFromExternal( + absl::string_view("", s2), + [](absl::string_view s) { DoNothing(s, nullptr); })); + } + return cord; +} + +TEST_P(CordTest, HugeCord) { + absl::Cord cord = MakeHuge("huge cord"); + EXPECT_LE(cord.size(), cord.EstimatedMemoryUsage()); + EXPECT_GE(cord.size() + 100, cord.EstimatedMemoryUsage()); +} + +// Tests that Append() works ok when handed a self reference +TEST_P(CordTest, AppendSelf) { + // We run the test until data is ~16K + // This guarantees it covers small, medium and large data. + std::string control_data = "Abc"; + absl::Cord data(control_data); + while (control_data.length() < 0x4000) { + data.Append(data); + control_data.append(control_data); + ASSERT_EQ(control_data, data); + } +} + +TEST_P(CordTest, MakeFragmentedCordFromInitializerList) { absl::Cord fragmented = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); @@ -1213,7 +1459,7 @@ TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) { ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); } -TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) { +TEST_P(CordTest, MakeFragmentedCordFromVector) { std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"}; absl::Cord fragmented = absl::MakeFragmentedCord(chunks); @@ -1233,7 +1479,7 @@ TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) { ASSERT_TRUE(++chunk_it == fragmented.chunk_end()); } -TEST(CordChunkIterator, Traits) { +TEST_P(CordTest, CordChunkIteratorTraits) { static_assert(std::is_copy_constructible<absl::Cord::ChunkIterator>::value, ""); static_assert(std::is_copy_assignable<absl::Cord::ChunkIterator>::value, ""); @@ -1314,7 +1560,7 @@ static void VerifyChunkIterator(const absl::Cord& cord, EXPECT_TRUE(post_iter == cord.chunk_end()); // NOLINT } -TEST(CordChunkIterator, Operations) { +TEST_P(CordTest, CordChunkIteratorOperations) { absl::Cord empty_cord; VerifyChunkIterator(empty_cord, 0); @@ -1339,14 +1585,14 @@ TEST(CordChunkIterator, Operations) { VerifyChunkIterator(reused_nodes_cord, expected_chunks); } - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); absl::Cord flat_cord(RandomLowercaseString(&rng, 256)); absl::Cord subcords; for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128)); VerifyChunkIterator(subcords, 128); } -TEST(CordCharIterator, Traits) { +TEST_P(CordTest, CharIteratorTraits) { static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value, ""); static_assert(std::is_copy_assignable<absl::Cord::CharIterator>::value, ""); @@ -1455,7 +1701,7 @@ static void VerifyCharIterator(const absl::Cord& cord) { } } -TEST(CordCharIterator, Operations) { +TEST_P(CordTest, CharIteratorOperations) { absl::Cord empty_cord; VerifyCharIterator(empty_cord); @@ -1477,14 +1723,49 @@ TEST(CordCharIterator, Operations) { VerifyCharIterator(reused_nodes_cord); } - RandomEngine rng(testing::GTEST_FLAG(random_seed)); + RandomEngine rng(GTEST_FLAG_GET(random_seed)); absl::Cord flat_cord(RandomLowercaseString(&rng, 256)); absl::Cord subcords; for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128)); VerifyCharIterator(subcords); } -TEST(Cord, StreamingOutput) { +TEST_P(CordTest, CharIteratorAdvanceAndRead) { + // Create a Cord holding 6 flats of 2500 bytes each, and then iterate over it + // reading 150, 1500, 2500 and 3000 bytes. This will result in all possible + // partial, full and straddled read combinations including reads below + // kMaxBytesToCopy. b/197776822 surfaced a bug for a specific partial, small + // read 'at end' on Cord which caused a failure on attempting to read past the + // end in CordRepBtreeReader which was not covered by any existing test. + constexpr int kBlocks = 6; + constexpr size_t kBlockSize = 2500; + constexpr size_t kChunkSize1 = 1500; + constexpr size_t kChunkSize2 = 2500; + constexpr size_t kChunkSize3 = 3000; + constexpr size_t kChunkSize4 = 150; + RandomEngine rng; + std::string data = RandomLowercaseString(&rng, kBlocks * kBlockSize); + absl::Cord cord; + for (int i = 0; i < kBlocks; ++i) { + const std::string block = data.substr(i * kBlockSize, kBlockSize); + cord.Append(absl::Cord(block)); + } + + for (size_t chunk_size : + {kChunkSize1, kChunkSize2, kChunkSize3, kChunkSize4}) { + absl::Cord::CharIterator it = cord.char_begin(); + size_t offset = 0; + while (offset < data.length()) { + const size_t n = std::min<size_t>(data.length() - offset, chunk_size); + absl::Cord chunk = cord.AdvanceAndRead(&it, n); + ASSERT_EQ(chunk.size(), n); + ASSERT_EQ(chunk.Compare(data.substr(offset, n)), 0); + offset += n; + } + } +} + +TEST_P(CordTest, StreamingOutput) { absl::Cord c = absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."}); std::stringstream output; @@ -1492,7 +1773,7 @@ TEST(Cord, StreamingOutput) { EXPECT_EQ("A small fragmented Cord.", output.str()); } -TEST(Cord, ForEachChunk) { +TEST_P(CordTest, ForEachChunk) { for (int num_elements : {1, 10, 200}) { SCOPED_TRACE(num_elements); std::vector<std::string> cord_chunks; @@ -1510,7 +1791,7 @@ TEST(Cord, ForEachChunk) { } } -TEST(Cord, SmallBufferAssignFromOwnData) { +TEST_P(CordTest, SmallBufferAssignFromOwnData) { constexpr size_t kMaxInline = 15; std::string contents = "small buff cord"; EXPECT_EQ(contents.size(), kMaxInline); @@ -1524,3 +1805,153 @@ TEST(Cord, SmallBufferAssignFromOwnData) { } } } + +TEST_P(CordTest, Format) { + absl::Cord c; + absl::Format(&c, "There were %04d little %s.", 3, "pigs"); + EXPECT_EQ(c, "There were 0003 little pigs."); + absl::Format(&c, "And %-3llx bad wolf!", 1); + EXPECT_EQ(c, "There were 0003 little pigs.And 1 bad wolf!"); +} + +TEST_P(CordTest, Hardening) { + absl::Cord cord("hello"); + // These statement should abort the program in all builds modes. + EXPECT_DEATH_IF_SUPPORTED(cord.RemovePrefix(6), ""); + EXPECT_DEATH_IF_SUPPORTED(cord.RemoveSuffix(6), ""); + + bool test_hardening = false; + ABSL_HARDENING_ASSERT([&]() { + // This only runs when ABSL_HARDENING_ASSERT is active. + test_hardening = true; + return true; + }()); + if (!test_hardening) return; + + EXPECT_DEATH_IF_SUPPORTED(cord[5], ""); + EXPECT_DEATH_IF_SUPPORTED(*cord.chunk_end(), ""); + EXPECT_DEATH_IF_SUPPORTED(static_cast<void>(cord.chunk_end()->empty()), ""); + EXPECT_DEATH_IF_SUPPORTED(++cord.chunk_end(), ""); +} + +// This test mimics a specific (and rare) application repeatedly splitting a +// cord, inserting (overwriting) a string value, and composing a new cord from +// the three pieces. This is hostile towards a Btree implementation: A split of +// a node at any level is likely to have the right-most edge of the left split, +// and the left-most edge of the right split shared. For example, splitting a +// leaf node with 6 edges will result likely in a 1-6, 2-5, 3-4, etc. split, +// sharing the 'split node'. When recomposing such nodes, we 'injected' an edge +// in that node. As this happens with some probability on each level of the +// tree, this will quickly grow the tree until it reaches maximum height. +TEST_P(CordTest, BtreeHostileSplitInsertJoin) { + absl::BitGen bitgen; + + // Start with about 1GB of data + std::string data(1 << 10, 'x'); + absl::Cord buffer(data); + absl::Cord cord; + for (int i = 0; i < 1000000; ++i) { + cord.Append(buffer); + } + + for (int j = 0; j < 1000; ++j) { + size_t offset = absl::Uniform(bitgen, 0u, cord.size()); + size_t length = absl::Uniform(bitgen, 100u, data.size()); + if (cord.size() == offset) { + cord.Append(absl::string_view(data.data(), length)); + } else { + absl::Cord suffix; + if (offset + length < cord.size()) { + suffix = cord; + suffix.RemovePrefix(offset + length); + } + if (cord.size() > offset) { + cord.RemoveSuffix(cord.size() - offset); + } + cord.Append(absl::string_view(data.data(), length)); + if (!suffix.empty()) { + cord.Append(suffix); + } + } + } +} + +class AfterExitCordTester { + public: + bool Set(absl::Cord* cord, absl::string_view expected) { + cord_ = cord; + expected_ = expected; + return true; + } + + ~AfterExitCordTester() { + EXPECT_EQ(*cord_, expected_); + } + private: + absl::Cord* cord_; + absl::string_view expected_; +}; + +template <typename Str> +void TestConstinitConstructor(Str) { + const auto expected = Str::value; + // Defined before `cord` to be destroyed after it. + static AfterExitCordTester exit_tester; // NOLINT + ABSL_CONST_INIT static absl::Cord cord(Str{}); // NOLINT + static bool init_exit_tester = exit_tester.Set(&cord, expected); + (void)init_exit_tester; + + EXPECT_EQ(cord, expected); + // Copy the object and test the copy, and the original. + { + absl::Cord copy = cord; + EXPECT_EQ(copy, expected); + } + // The original still works + EXPECT_EQ(cord, expected); + + // Try making adding more structure to the tree. + { + absl::Cord copy = cord; + std::string expected_copy(expected); + for (int i = 0; i < 10; ++i) { + copy.Append(cord); + absl::StrAppend(&expected_copy, expected); + EXPECT_EQ(copy, expected_copy); + } + } + + // Make sure we are using the right branch during constant evaluation. + EXPECT_EQ(absl::CordTestPeer::IsTree(cord), cord.size() >= 16); + + for (int i = 0; i < 10; ++i) { + // Make a few more Cords from the same global rep. + // This tests what happens when the refcount for it gets below 1. + EXPECT_EQ(expected, absl::Cord(Str{})); + } +} + +constexpr int SimpleStrlen(const char* p) { + return *p ? 1 + SimpleStrlen(p + 1) : 0; +} + +struct ShortView { + constexpr absl::string_view operator()() const { + return absl::string_view("SSO string", SimpleStrlen("SSO string")); + } +}; + +struct LongView { + constexpr absl::string_view operator()() const { + return absl::string_view("String that does not fit SSO.", + SimpleStrlen("String that does not fit SSO.")); + } +}; + + +TEST_P(CordTest, ConstinitConstructor) { + TestConstinitConstructor( + absl::strings_internal::MakeStringConstant(ShortView{})); + TestConstinitConstructor( + absl::strings_internal::MakeStringConstant(LongView{})); +} diff --git a/third_party/abseil-cpp/absl/strings/cord_test_helpers.h b/third_party/abseil-cpp/absl/strings/cord_test_helpers.h index f1036e3b13..31a1dc8980 100644 --- a/third_party/abseil-cpp/absl/strings/cord_test_helpers.h +++ b/third_party/abseil-cpp/absl/strings/cord_test_helpers.h @@ -17,11 +17,73 @@ #ifndef ABSL_STRINGS_CORD_TEST_HELPERS_H_ #define ABSL_STRINGS_CORD_TEST_HELPERS_H_ +#include <cstdint> +#include <iostream> +#include <string> + +#include "absl/base/config.h" #include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN +// Cord sizes relevant for testing +enum class TestCordSize { + // An empty value + kEmpty = 0, + + // An inlined string value + kInlined = cord_internal::kMaxInline / 2 + 1, + + // 'Well known' SSO lengths (excluding terminating zero). + // libstdcxx has a maximum SSO of 15, libc++ has a maximum SSO of 22. + kStringSso1 = 15, + kStringSso2 = 22, + + // A string value which is too large to fit in inlined data, but small enough + // such that Cord prefers copying the value if possible, i.e.: not stealing + // std::string inputs, or referencing existing CordReps on Append, etc. + kSmall = cord_internal::kMaxBytesToCopy / 2 + 1, + + // A string value large enough that Cord prefers to reference or steal from + // existing inputs rather than copying contents of the input. + kMedium = cord_internal::kMaxFlatLength / 2 + 1, + + // A string value large enough to cause it to be stored in mutliple flats. + kLarge = cord_internal::kMaxFlatLength * 4 +}; + +// To string helper +inline absl::string_view ToString(TestCordSize size) { + switch (size) { + case TestCordSize::kEmpty: + return "Empty"; + case TestCordSize::kInlined: + return "Inlined"; + case TestCordSize::kSmall: + return "Small"; + case TestCordSize::kStringSso1: + return "StringSso1"; + case TestCordSize::kStringSso2: + return "StringSso2"; + case TestCordSize::kMedium: + return "Medium"; + case TestCordSize::kLarge: + return "Large"; + } + return "???"; +} + +// Returns the length matching the specified size +inline size_t Length(TestCordSize size) { return static_cast<size_t>(size); } + +// Stream output helper +inline std::ostream& operator<<(std::ostream& stream, TestCordSize size) { + return stream << ToString(size); +} + // Creates a multi-segment Cord from an iterable container of strings. The // resulting Cord is guaranteed to have one segment for every string in the // container. This allows code to be unit tested with multi-segment Cord diff --git a/third_party/abseil-cpp/absl/strings/cordz_test.cc b/third_party/abseil-cpp/absl/strings/cordz_test.cc new file mode 100644 index 0000000000..2b7d30b0e0 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/cordz_test.cc @@ -0,0 +1,466 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <cstdint> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/strings/cord.h" +#include "absl/strings/cord_test_helpers.h" +#include "absl/strings/cordz_test_helpers.h" +#include "absl/strings/internal/cordz_functions.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_sample_token.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +using testing::Eq; +using testing::AnyOf; + +namespace absl { +ABSL_NAMESPACE_BEGIN + +using cord_internal::CordzInfo; +using cord_internal::CordzSampleToken; +using cord_internal::CordzStatistics; +using cord_internal::CordzUpdateTracker; +using Method = CordzUpdateTracker::MethodIdentifier; + +// Do not print cord contents, we only care about 'size' perhaps. +// Note that this method must be inside the named namespace. +inline void PrintTo(const Cord& cord, std::ostream* s) { + if (s) *s << "Cord[" << cord.size() << "]"; +} + +namespace { + +auto constexpr kMaxInline = cord_internal::kMaxInline; + +// Returns a string_view value of the specified length +// We do this to avoid 'consuming' large strings in Cord by default. +absl::string_view MakeString(size_t size) { + thread_local std::string str; + str = std::string(size, '.'); + return str; +} + +absl::string_view MakeString(TestCordSize size) { + return MakeString(Length(size)); +} + +// Returns a cord with a sampled method of kAppendString. +absl::Cord MakeAppendStringCord(TestCordSize size) { + CordzSamplingIntervalHelper always(1); + absl::Cord cord; + cord.Append(MakeString(size)); + return cord; +} + +std::string TestParamToString(::testing::TestParamInfo<TestCordSize> size) { + return absl::StrCat("On", ToString(size.param), "Cord"); +} + +class CordzUpdateTest : public testing::TestWithParam<TestCordSize> { + public: + Cord& cord() { return cord_; } + + Method InitialOr(Method method) const { + return (GetParam() > TestCordSize::kInlined) ? Method::kConstructorString + : method; + } + + private: + CordzSamplingIntervalHelper sample_every_{1}; + Cord cord_{MakeString(GetParam())}; +}; + +template <typename T> +std::string ParamToString(::testing::TestParamInfo<T> param) { + return std::string(ToString(param.param)); +} + +INSTANTIATE_TEST_SUITE_P(WithParam, CordzUpdateTest, + testing::Values(TestCordSize::kEmpty, + TestCordSize::kInlined, + TestCordSize::kLarge), + TestParamToString); + +class CordzStringTest : public testing::TestWithParam<TestCordSize> { + private: + CordzSamplingIntervalHelper sample_every_{1}; +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordzStringTest, + testing::Values(TestCordSize::kInlined, + TestCordSize::kStringSso1, + TestCordSize::kStringSso2, + TestCordSize::kSmall, + TestCordSize::kLarge), + ParamToString<TestCordSize>); + +TEST(CordzTest, ConstructSmallArray) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(MakeString(TestCordSize::kSmall)); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); +} + +TEST(CordzTest, ConstructLargeArray) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(MakeString(TestCordSize::kLarge)); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); +} + +TEST_P(CordzStringTest, ConstructString) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(std::string(Length(GetParam()), '.')); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + } +} + +TEST(CordzTest, CopyConstructFromUnsampled) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + Cord cord(src); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST(CordzTest, CopyConstructFromSampled) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord(src); + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); +} + +TEST(CordzTest, MoveConstruct) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src(MakeString(TestCordSize::kLarge)); + Cord cord(std::move(src)); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); +} + +TEST_P(CordzUpdateTest, AssignUnsampledCord) { + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + const CordzInfo* info = GetCordzInfoForTesting(cord()); + cord() = src; + EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); +} + +TEST_P(CordzUpdateTest, AssignSampledCord) { + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + cord() = src; + ASSERT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord())->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzUpdateTest, AssignSampledCordToInlined) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord cord; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzUpdateTest, AssignSampledCordToUnsampledCord) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord cord = UnsampledCord(MakeString(TestCordSize::kLarge)); + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzUpdateTest, AssignUnsampledCordToSampledCordWithoutSampling) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord cord = MakeAppendStringCord(TestCordSize::kLarge); + const CordzInfo* info = GetCordzInfoForTesting(cord); + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord = src; + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); +} + +TEST(CordzUpdateTest, AssignUnsampledCordToSampledCordWithSampling) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord = MakeAppendStringCord(TestCordSize::kLarge); + const CordzInfo* info = GetCordzInfoForTesting(cord); + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord = src; + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); +} + +TEST(CordzUpdateTest, AssignSampledCordToSampledCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord(MakeString(TestCordSize::kLarge)); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzUpdateTest, AssignUnsampledCordToSampledCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord(MakeString(TestCordSize::kLarge)); + cord = src; + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kAssignCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); + EXPECT_THAT(stats.update_tracker.Value(Method::kConstructorString), Eq(0)); +} + +TEST(CordzTest, AssignInlinedCordToSampledCord) { + CordzSampleToken token; + CordzSamplingIntervalHelper sample_every{1}; + Cord cord(MakeString(TestCordSize::kLarge)); + const CordzInfo* info = GetCordzInfoForTesting(cord); + Cord src = UnsampledCord(MakeString(TestCordSize::kInlined)); + cord = src; + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); + EXPECT_FALSE(CordzInfoIsListed(info)); +} + +TEST(CordzUpdateTest, MoveAssignCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord; + Cord src(MakeString(TestCordSize::kLarge)); + cord = std::move(src); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); +} + +TEST_P(CordzUpdateTest, AssignLargeArray) { + cord() = MakeString(TestCordSize::kSmall); + EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString)); +} + +TEST_P(CordzUpdateTest, AssignSmallArray) { + cord() = MakeString(TestCordSize::kSmall); + EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString)); +} + +TEST_P(CordzUpdateTest, AssignInlinedArray) { + cord() = MakeString(TestCordSize::kInlined); + EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr)); +} + +TEST_P(CordzStringTest, AssignStringToInlined) { + Cord cord; + cord = std::string(Length(GetParam()), '.'); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAssignString)); + } +} + +TEST_P(CordzStringTest, AssignStringToCord) { + Cord cord(MakeString(TestCordSize::kLarge)); + cord = std::string(Length(GetParam()), '.'); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kAssignString, 1)); + } +} + +TEST_P(CordzUpdateTest, AssignInlinedString) { + cord() = std::string(Length(TestCordSize::kInlined), '.'); + EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr)); +} + +TEST_P(CordzUpdateTest, AppendCord) { + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord().Append(src); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord))); +} + +TEST_P(CordzUpdateTest, MoveAppendCord) { + cord().Append(UnsampledCord(MakeString(TestCordSize::kLarge))); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord))); +} + +TEST_P(CordzUpdateTest, AppendSmallArray) { + cord().Append(MakeString(TestCordSize::kSmall)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString))); +} + +TEST_P(CordzUpdateTest, AppendLargeArray) { + cord().Append(MakeString(TestCordSize::kLarge)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString))); +} + +TEST_P(CordzStringTest, AppendStringToEmpty) { + Cord cord; + cord.Append(std::string(Length(GetParam()), '.')); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString)); + } +} + +TEST_P(CordzStringTest, AppendStringToInlined) { + Cord cord(MakeString(TestCordSize::kInlined)); + cord.Append(std::string(Length(GetParam()), '.')); + if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString)); + } +} + +TEST_P(CordzStringTest, AppendStringToCord) { + Cord cord(MakeString(TestCordSize::kLarge)); + cord.Append(std::string(Length(GetParam()), '.')); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kAppendString, 1)); +} + +TEST(CordzTest, MakeCordFromExternal) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord = MakeCordFromExternal("Hello world", [](absl::string_view) {}); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kMakeCordFromExternal)); +} + +TEST(CordzTest, MakeCordFromEmptyExternal) { + CordzSamplingIntervalHelper sample_every{1}; + Cord cord = MakeCordFromExternal({}, [](absl::string_view) {}); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST_P(CordzUpdateTest, PrependCord) { + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + cord().Prepend(src); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependCord))); +} + +TEST_P(CordzUpdateTest, PrependSmallArray) { + cord().Prepend(MakeString(TestCordSize::kSmall)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString))); +} + +TEST_P(CordzUpdateTest, PrependLargeArray) { + cord().Prepend(MakeString(TestCordSize::kLarge)); + EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString))); +} + +TEST_P(CordzStringTest, PrependStringToEmpty) { + Cord cord; + cord.Prepend(std::string(Length(GetParam()), '.')); + if (Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString)); + } +} + +TEST_P(CordzStringTest, PrependStringToInlined) { + Cord cord(MakeString(TestCordSize::kInlined)); + cord.Prepend(std::string(Length(GetParam()), '.')); + if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) { + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString)); + } +} + +TEST_P(CordzStringTest, PrependStringToCord) { + Cord cord(MakeString(TestCordSize::kLarge)); + cord.Prepend(std::string(Length(GetParam()), '.')); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kPrependString, 1)); +} + +TEST(CordzTest, RemovePrefix) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord(MakeString(TestCordSize::kLarge)); + + // Half the cord + cord.RemovePrefix(cord.size() / 2); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 1)); + + // TODO(mvels): RemovePrefix does not reset to inlined, except if empty? + cord.RemovePrefix(cord.size() - kMaxInline); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 2)); + + cord.RemovePrefix(cord.size()); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST(CordzTest, RemoveSuffix) { + CordzSamplingIntervalHelper sample_every(1); + Cord cord(MakeString(TestCordSize::kLarge)); + + // Half the cord + cord.RemoveSuffix(cord.size() / 2); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 1)); + + // TODO(mvels): RemoveSuffix does not reset to inlined, except if empty? + cord.RemoveSuffix(cord.size() - kMaxInline); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString)); + EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 2)); + + cord.RemoveSuffix(cord.size()); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST(CordzTest, SubCordFromUnsampledCord) { + CordzSamplingIntervalHelper sample_every{1}; + Cord src = UnsampledCord(MakeString(TestCordSize::kLarge)); + Cord cord = src.Subcord(10, src.size() / 2); + EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr)); +} + +TEST(CordzTest, SubCordFromSampledCord) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord = src.Subcord(10, src.size() / 2); + ASSERT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); + CordzStatistics stats = GetCordzInfoForTesting(cord)->GetCordzStatistics(); + EXPECT_THAT(stats.parent_method, Eq(Method::kAppendString)); + EXPECT_THAT(stats.update_tracker.Value(Method::kAppendString), Eq(1)); +} + +TEST(CordzTest, SmallSubCord) { + CordzSamplingIntervalHelper sample_never{99999}; + Cord src = MakeAppendStringCord(TestCordSize::kLarge); + Cord cord = src.Subcord(10, kMaxInline + 1); + EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kSubCord)); +} + +} // namespace + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_CORDZ_ENABLED diff --git a/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h b/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h new file mode 100644 index 0000000000..e410eecf7f --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h @@ -0,0 +1,151 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ +#define ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ + +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/macros.h" +#include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_sample_token.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/strings/str_cat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Returns the CordzInfo for the cord, or nullptr if the cord is not sampled. +inline const cord_internal::CordzInfo* GetCordzInfoForTesting( + const Cord& cord) { + if (!cord.contents_.is_tree()) return nullptr; + return cord.contents_.cordz_info(); +} + +// Returns true if the provided cordz_info is in the list of sampled cords. +inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info, + cord_internal::CordzSampleToken token = {}) { + for (const cord_internal::CordzInfo& info : token) { + if (cordz_info == &info) return true; + } + return false; +} + +// Matcher on Cord that verifies all of: +// - the cord is sampled +// - the CordzInfo of the cord is listed / discoverable. +// - the reported CordzStatistics match the cord's actual properties +// - the cord has an (initial) UpdateTracker count of 1 for `method` +MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") { + const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); + if (cord_info == nullptr) { + *result_listener << "cord is not sampled"; + return false; + } + if (!CordzInfoIsListed(cord_info)) { + *result_listener << "cord is sampled, but not listed"; + return false; + } + cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics(); + if (stat.size != arg.size()) { + *result_listener << "cordz size " << stat.size + << " does not match cord size " << arg.size(); + return false; + } + if (stat.update_tracker.Value(method) != 1) { + *result_listener << "Expected method count 1 for " << method << ", found " + << stat.update_tracker.Value(method); + return false; + } + return true; +} + +// Matcher on Cord that verifies that the cord is sampled and that the CordzInfo +// update tracker has 'method' with a call count of 'n' +MATCHER_P2(CordzMethodCountEq, method, n, + absl::StrCat("CordzInfo method count equals ", n)) { + const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg); + if (cord_info == nullptr) { + *result_listener << "cord is not sampled"; + return false; + } + cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics(); + if (stat.update_tracker.Value(method) != n) { + *result_listener << "Expected method count " << n << " for " << method + << ", found " << stat.update_tracker.Value(method); + return false; + } + return true; +} + +// Cordz will only update with a new rate once the previously scheduled event +// has fired. When we disable Cordz, a long delay takes place where we won't +// consider profiling new Cords. CordzSampleIntervalHelper will burn through +// that interval and allow for testing that assumes that the average sampling +// interval is a particular value. +class CordzSamplingIntervalHelper { + public: + explicit CordzSamplingIntervalHelper(int32_t interval) + : orig_mean_interval_(absl::cord_internal::get_cordz_mean_interval()) { + absl::cord_internal::set_cordz_mean_interval(interval); + absl::cord_internal::cordz_set_next_sample_for_testing(interval); + } + + ~CordzSamplingIntervalHelper() { + absl::cord_internal::set_cordz_mean_interval(orig_mean_interval_); + absl::cord_internal::cordz_set_next_sample_for_testing(orig_mean_interval_); + } + + private: + int32_t orig_mean_interval_; +}; + +// Wrapper struct managing a small CordRep `rep` +struct TestCordRep { + cord_internal::CordRepFlat* rep; + + TestCordRep() { + rep = cord_internal::CordRepFlat::New(100); + rep->length = 100; + memset(rep->Data(), 1, 100); + } + ~TestCordRep() { cord_internal::CordRep::Unref(rep); } +}; + +// Wrapper struct managing a small CordRep `rep`, and +// an InlineData `data` initialized with that CordRep. +struct TestCordData { + TestCordRep rep; + cord_internal::InlineData data{rep.rep}; +}; + +// Creates a Cord that is not sampled +template <typename... Args> +Cord UnsampledCord(Args... args) { + CordzSamplingIntervalHelper never(9999); + Cord cord(std::forward<Args>(args)...); + ABSL_ASSERT(GetCordzInfoForTesting(cord) == nullptr); + return cord; +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_TEST_HELPERS_H_ diff --git a/third_party/abseil-cpp/absl/strings/escaping.cc b/third_party/abseil-cpp/absl/strings/escaping.cc index 7adc1b6571..18b20b83fd 100644 --- a/third_party/abseil-cpp/absl/strings/escaping.cc +++ b/third_party/abseil-cpp/absl/strings/escaping.cc @@ -137,7 +137,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, // Copy the escape sequence for the null character const ptrdiff_t octal_size = p + 1 - octal_start; *d++ = '\\'; - memcpy(d, octal_start, octal_size); + memmove(d, octal_start, octal_size); d += octal_size; break; } @@ -170,7 +170,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, // Copy the escape sequence for the null character const ptrdiff_t hex_size = p + 1 - hex_start; *d++ = '\\'; - memcpy(d, hex_start, hex_size); + memmove(d, hex_start, hex_size); d += hex_size; break; } @@ -203,7 +203,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, if ((rune == 0) && leave_nulls_escaped) { // Copy the escape sequence for the null character *d++ = '\\'; - memcpy(d, hex_start, 5); // u0000 + memmove(d, hex_start, 5); // u0000 d += 5; break; } @@ -251,7 +251,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, if ((rune == 0) && leave_nulls_escaped) { // Copy the escape sequence for the null character *d++ = '\\'; - memcpy(d, hex_start, 9); // U00000000 + memmove(d, hex_start, 9); // U00000000 d += 9; break; } @@ -450,7 +450,7 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, // The GET_INPUT macro gets the next input character, skipping // over any whitespace, and stopping when we reach the end of the - // std::string or when we read any non-data character. The arguments are + // string or when we read any non-data character. The arguments are // an arbitrary identifier (used as a label for goto) and the number // of data bytes that must remain in the input to avoid aborting the // loop. @@ -473,18 +473,18 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, if (dest) { // This loop consumes 4 input bytes and produces 3 output bytes // per iteration. We can't know at the start that there is enough - // data left in the std::string for a full iteration, so the loop may + // data left in the string for a full iteration, so the loop may // break out in the middle; if so 'state' will be set to the // number of input bytes read. while (szsrc >= 4) { // We'll start by optimistically assuming that the next four - // bytes of the std::string (src[0..3]) are four good data bytes + // bytes of the string (src[0..3]) are four good data bytes // (that is, no nulls, whitespace, padding chars, or illegal // chars). We need to test src[0..2] for nulls individually // before constructing temp to preserve the property that we - // never read past a null in the std::string (no matter how long - // szsrc claims the std::string is). + // never read past a null in the string (no matter how long + // szsrc claims the string is). if (!src[0] || !src[1] || !src[2] || ((temp = ((unsigned(unbase64[src[0]]) << 18) | @@ -509,7 +509,7 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, temp = (temp << 6) | decode; } else { // We really did have four good data bytes, so advance four - // characters in the std::string. + // characters in the string. szsrc -= 4; src += 4; @@ -644,7 +644,7 @@ bool Base64UnescapeInternal(const char* src_param, size_t szsrc, char* dest, state); } - // The remainder of the std::string should be all whitespace, mixed with + // The remainder of the string should be all whitespace, mixed with // exactly 0 equals signs, or exactly 'expected_equals' equals // signs. (Always accepting 0 equals signs is an Abseil extension // not covered in the RFC, as is accepting dot as the pad character.) @@ -771,7 +771,7 @@ constexpr char kWebSafeBase64Chars[] = template <typename String> bool Base64UnescapeInternal(const char* src, size_t slen, String* dest, const signed char* unbase64) { - // Determine the size of the output std::string. Base64 encodes every 3 bytes into + // Determine the size of the output string. Base64 encodes every 3 bytes into // 4 characters. any leftover chars are added directly for good measure. // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548 const size_t dest_len = 3 * (slen / 4) + (slen % 4); @@ -779,7 +779,7 @@ bool Base64UnescapeInternal(const char* src, size_t slen, String* dest, strings_internal::STLStringResizeUninitialized(dest, dest_len); // We are getting the destination buffer by getting the beginning of the - // std::string and converting it into a char *. + // string and converting it into a char *. size_t len; const bool ok = Base64UnescapeInternal(src, slen, &(*dest)[0], dest_len, unbase64, &len); diff --git a/third_party/abseil-cpp/absl/strings/escaping_test.cc b/third_party/abseil-cpp/absl/strings/escaping_test.cc index 1967975b69..45671a0ed5 100644 --- a/third_party/abseil-cpp/absl/strings/escaping_test.cc +++ b/third_party/abseil-cpp/absl/strings/escaping_test.cc @@ -300,7 +300,7 @@ static struct { absl::string_view plaintext; absl::string_view cyphertext; } const base64_tests[] = { - // Empty std::string. + // Empty string. {{"", 0}, {"", 0}}, {{nullptr, 0}, {"", 0}}, // if length is zero, plaintext ptr must be ignored! @@ -586,7 +586,7 @@ void TestEscapeAndUnescape() { EXPECT_EQ(encoded, websafe); EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), websafe); - // Let's try the std::string version of the decoder + // Let's try the string version of the decoder decoded = "this junk should be ignored"; EXPECT_TRUE(absl::WebSafeBase64Unescape(websafe, &decoded)); EXPECT_EQ(decoded, tc.plaintext); @@ -625,7 +625,7 @@ TEST(Base64, DISABLED_HugeData) { std::string escaped; absl::Base64Escape(huge, &escaped); - // Generates the std::string that should match a base64 encoded "xxx..." std::string. + // Generates the string that should match a base64 encoded "xxx..." string. // "xxx" in base64 is "eHh4". std::string expected_encoding; expected_encoding.reserve(kSize / 3 * 4); diff --git a/third_party/abseil-cpp/absl/strings/internal/char_map.h b/third_party/abseil-cpp/absl/strings/internal/char_map.h index a76e60362b..61484de0b7 100644 --- a/third_party/abseil-cpp/absl/strings/internal/char_map.h +++ b/third_party/abseil-cpp/absl/strings/internal/char_map.h @@ -72,7 +72,7 @@ class Charmap { CharMaskForWord(x, 2), CharMaskForWord(x, 3)); } - // Containing all the chars in the C-std::string 's'. + // Containing all the chars in the C-string 's'. // Note that this is expensively recursive because of the C++11 constexpr // formulation. Use only in constexpr initializers. static constexpr Charmap FromString(const char* s) { diff --git a/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.cc b/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.cc index 66f33e7207..ebf8c0791a 100644 --- a/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.cc +++ b/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.cc @@ -208,7 +208,7 @@ int BigUnsigned<max_words>::ReadDigits(const char* begin, const char* end, ++dropped_digits; } if (begin < end && *std::prev(end) == '.') { - // If the std::string ends in '.', either before or after dropping zeroes, then + // If the string ends in '.', either before or after dropping zeroes, then // drop the decimal point and look for more digits to drop. dropped_digits = 0; --end; diff --git a/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.h b/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.h index 999e9ae3a2..8f702976a8 100644 --- a/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.h +++ b/third_party/abseil-cpp/absl/strings/internal/charconv_bigint.h @@ -66,7 +66,7 @@ class BigUnsigned { static_cast<uint32_t>(v >> 32)} {} // Constructs a BigUnsigned from the given string_view containing a decimal - // value. If the input std::string is not a decimal integer, constructs a 0 + // value. If the input string is not a decimal integer, constructs a 0 // instead. explicit BigUnsigned(absl::string_view sv) : size_(0), words_{} { // Check for valid input, returning a 0 otherwise. This is reasonable @@ -210,7 +210,7 @@ class BigUnsigned { return words_[index]; } - // Returns this integer as a decimal std::string. This is not used in the decimal- + // Returns this integer as a decimal string. This is not used in the decimal- // to-binary conversion; it is intended to aid in testing. std::string ToString() const; diff --git a/third_party/abseil-cpp/absl/strings/internal/charconv_bigint_test.cc b/third_party/abseil-cpp/absl/strings/internal/charconv_bigint_test.cc index 363bcb03d9..a8b9945829 100644 --- a/third_party/abseil-cpp/absl/strings/internal/charconv_bigint_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/charconv_bigint_test.cc @@ -69,6 +69,61 @@ TEST(BigUnsigned, ShiftLeft) { // And we should have fully rotated all bits off by now: EXPECT_EQ(a, BigUnsigned<84>(0u)); } + { + // Bit shifting large and small numbers by large and small offsets. + // Intended to exercise bounds-checking corner on ShiftLeft() (directly + // and under asan). + + // 2**(32*84)-1 + const BigUnsigned<84> all_bits_one( + "1474444211396924248063325089479706787923460402125687709454567433186613" + "6228083464060749874845919674257665016359189106695900028098437021384227" + "3285029708032466536084583113729486015826557532750465299832071590813090" + "2011853039837649252477307070509704043541368002938784757296893793903797" + "8180292336310543540677175225040919704702800559606097685920595947397024" + "8303316808753252115729411497720357971050627997031988036134171378490368" + "6008000778741115399296162550786288457245180872759047016734959330367829" + "5235612397427686310674725251378116268607113017720538636924549612987647" + "5767411074510311386444547332882472126067840027882117834454260409440463" + "9345147252664893456053258463203120637089916304618696601333953616715125" + "2115882482473279040772264257431663818610405673876655957323083702713344" + "4201105427930770976052393421467136557055"); + const BigUnsigned<84> zero(0u); + const BigUnsigned<84> one(1u); + // in bounds shifts + for (int i = 1; i < 84*32; ++i) { + // shifting all_bits_one to the left should result in a smaller number, + // since the high bits rotate off and the low bits are replaced with + // zeroes. + BigUnsigned<84> big_shifted = all_bits_one; + big_shifted.ShiftLeft(i); + EXPECT_GT(all_bits_one, big_shifted); + // Shifting 1 to the left should instead result in a larger number. + BigUnsigned<84> small_shifted = one; + small_shifted.ShiftLeft(i); + EXPECT_LT(one, small_shifted); + } + // Shifting by zero or a negative number has no effect + for (int no_op_shift : {0, -1, -84 * 32, std::numeric_limits<int>::min()}) { + BigUnsigned<84> big_shifted = all_bits_one; + big_shifted.ShiftLeft(no_op_shift); + EXPECT_EQ(all_bits_one, big_shifted); + BigUnsigned<84> small_shifted = one; + big_shifted.ShiftLeft(no_op_shift); + EXPECT_EQ(one, small_shifted); + } + // Shifting by an amount greater than the number of bits should result in + // zero. + for (int out_of_bounds_shift : + {84 * 32, 84 * 32 + 1, std::numeric_limits<int>::max()}) { + BigUnsigned<84> big_shifted = all_bits_one; + big_shifted.ShiftLeft(out_of_bounds_shift); + EXPECT_EQ(zero, big_shifted); + BigUnsigned<84> small_shifted = one; + small_shifted.ShiftLeft(out_of_bounds_shift); + EXPECT_EQ(zero, small_shifted); + } + } } TEST(BigUnsigned, MultiplyByUint32) { diff --git a/third_party/abseil-cpp/absl/strings/internal/charconv_parse.cc b/third_party/abseil-cpp/absl/strings/internal/charconv_parse.cc index d9a57a7822..d29acaf462 100644 --- a/third_party/abseil-cpp/absl/strings/internal/charconv_parse.cc +++ b/third_party/abseil-cpp/absl/strings/internal/charconv_parse.cc @@ -52,7 +52,7 @@ static_assert(std::numeric_limits<double>::digits == 53, "IEEE double fact"); // The lowest valued 19-digit decimal mantissa we can read still contains // sufficient information to reconstruct a binary mantissa. -static_assert(1000000000000000000u > (uint64_t(1) << (53 + 3)), "(b) above"); +static_assert(1000000000000000000u > (uint64_t{1} << (53 + 3)), "(b) above"); // ParseFloat<16> will read the first 15 significant digits of the mantissa. // @@ -246,8 +246,8 @@ constexpr int DigitMagnitude<16>() { // ConsumeDigits does not protect against overflow on *out; max_digits must // be chosen with respect to type T to avoid the possibility of overflow. template <int base, typename T> -std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits, - T* out, bool* dropped_nonzero_digit) { +int ConsumeDigits(const char* begin, const char* end, int max_digits, T* out, + bool* dropped_nonzero_digit) { if (base == 10) { assert(max_digits <= std::numeric_limits<T>::digits10); } else if (base == 16) { @@ -282,7 +282,7 @@ std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits, *dropped_nonzero_digit = true; } *out = accumulator; - return begin - original_begin; + return static_cast<int>(begin - original_begin); } // Returns true if `v` is one of the chars allowed inside parentheses following @@ -302,7 +302,7 @@ bool ParseInfinityOrNan(const char* begin, const char* end, switch (*begin) { case 'i': case 'I': { - // An infinity std::string consists of the characters "inf" or "infinity", + // An infinity string consists of the characters "inf" or "infinity", // case insensitive. if (strings_internal::memcasecmp(begin + 1, "nf", 2) != 0) { return false; @@ -326,7 +326,7 @@ bool ParseInfinityOrNan(const char* begin, const char* end, } out->type = strings_internal::FloatType::kNan; out->end = begin + 3; - // NaN is allowed to be followed by a parenthesized std::string, consisting of + // NaN is allowed to be followed by a parenthesized string, consisting of // only the characters [a-zA-Z0-9_]. Match that if it's present. begin += 3; if (begin < end && *begin == '(') { @@ -372,7 +372,7 @@ strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end, int exponent_adjustment = 0; bool mantissa_is_inexact = false; - std::size_t pre_decimal_digits = ConsumeDigits<base>( + int pre_decimal_digits = ConsumeDigits<base>( begin, end, MantissaDigitsMax<base>(), &mantissa, &mantissa_is_inexact); begin += pre_decimal_digits; int digits_left; @@ -398,14 +398,14 @@ strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end, while (begin < end && *begin == '0') { ++begin; } - std::size_t zeros_skipped = begin - begin_zeros; + int zeros_skipped = static_cast<int>(begin - begin_zeros); if (zeros_skipped >= DigitLimit<base>()) { // refuse to parse pathological inputs return result; } exponent_adjustment -= static_cast<int>(zeros_skipped); } - std::size_t post_decimal_digits = ConsumeDigits<base>( + int post_decimal_digits = ConsumeDigits<base>( begin, end, digits_left, &mantissa, &mantissa_is_inexact); begin += post_decimal_digits; diff --git a/third_party/abseil-cpp/absl/strings/internal/charconv_parse_test.cc b/third_party/abseil-cpp/absl/strings/internal/charconv_parse_test.cc index 9511c98745..bc2d111876 100644 --- a/third_party/abseil-cpp/absl/strings/internal/charconv_parse_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/charconv_parse_test.cc @@ -63,7 +63,7 @@ void ExpectParsedFloat(std::string s, absl::chars_format format_flags, } const std::string::size_type expected_characters_matched = s.find('$'); ABSL_RAW_CHECK(expected_characters_matched != std::string::npos, - "Input std::string must contain $"); + "Input string must contain $"); s.replace(expected_characters_matched, 1, ""); ParsedFloat parsed = diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_internal.cc b/third_party/abseil-cpp/absl/strings/internal/cord_internal.cc new file mode 100644 index 0000000000..1767e6fcc5 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_internal.cc @@ -0,0 +1,89 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_internal.h" + +#include <atomic> +#include <cassert> +#include <memory> + +#include "absl/container/inlined_vector.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cord_rep_ring.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +ABSL_CONST_INIT std::atomic<bool> cord_btree_enabled(kCordEnableBtreeDefault); +ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled( + kCordEnableRingBufferDefault); +ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled( + kCordShallowSubcordsDefault); +ABSL_CONST_INIT std::atomic<bool> cord_btree_exhaustive_validation(false); + +void CordRep::Destroy(CordRep* rep) { + assert(rep != nullptr); + + absl::InlinedVector<CordRep*, Constants::kInlinedVectorSize> pending; + while (true) { + assert(!rep->refcount.IsImmortal()); + if (rep->tag == CONCAT) { + CordRepConcat* rep_concat = rep->concat(); + CordRep* right = rep_concat->right; + if (!right->refcount.Decrement()) { + pending.push_back(right); + } + CordRep* left = rep_concat->left; + delete rep_concat; + rep = nullptr; + if (!left->refcount.Decrement()) { + rep = left; + continue; + } + } else if (rep->tag == BTREE) { + CordRepBtree::Destroy(rep->btree()); + rep = nullptr; + } else if (rep->tag == RING) { + CordRepRing::Destroy(rep->ring()); + rep = nullptr; + } else if (rep->tag == EXTERNAL) { + CordRepExternal::Delete(rep); + rep = nullptr; + } else if (rep->tag == SUBSTRING) { + CordRepSubstring* rep_substring = rep->substring(); + CordRep* child = rep_substring->child; + delete rep_substring; + rep = nullptr; + if (!child->refcount.Decrement()) { + rep = child; + continue; + } + } else { + CordRepFlat::Delete(rep); + rep = nullptr; + } + + if (!pending.empty()) { + rep = pending.back(); + pending.pop_back(); + } else { + break; + } + } +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_internal.h b/third_party/abseil-cpp/absl/strings/internal/cord_internal.h index 5b5d108308..bfe5564e46 100644 --- a/third_party/abseil-cpp/absl/strings/internal/cord_internal.h +++ b/third_party/abseil-cpp/absl/strings/internal/cord_internal.h @@ -1,4 +1,4 @@ -// Copyright 2020 The Abseil Authors. +// Copyright 2021 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,6 +21,11 @@ #include <cstdint> #include <type_traits> +#include "absl/base/config.h" +#include "absl/base/internal/endian.h" +#include "absl/base/internal/invoke.h" +#include "absl/base/optimization.h" +#include "absl/container/internal/compressed_tuple.h" #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" @@ -28,49 +33,153 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace cord_internal { -// Wraps std::atomic for reference counting. -class Refcount { +class CordzInfo; + +// Default feature enable states for cord ring buffers +enum CordFeatureDefaults { + kCordEnableBtreeDefault = true, + kCordEnableRingBufferDefault = false, + kCordShallowSubcordsDefault = false +}; + +extern std::atomic<bool> cord_btree_enabled; +extern std::atomic<bool> cord_ring_buffer_enabled; +extern std::atomic<bool> shallow_subcords_enabled; + +// `cord_btree_exhaustive_validation` can be set to force exhaustive validation +// in debug assertions, and code that calls `IsValid()` explicitly. By default, +// assertions should be relatively cheap and AssertValid() can easily lead to +// O(n^2) complexity as recursive / full tree validation is O(n). +extern std::atomic<bool> cord_btree_exhaustive_validation; + +inline void enable_cord_btree(bool enable) { + cord_btree_enabled.store(enable, std::memory_order_relaxed); +} + +inline void enable_cord_ring_buffer(bool enable) { + cord_ring_buffer_enabled.store(enable, std::memory_order_relaxed); +} + +inline void enable_shallow_subcords(bool enable) { + shallow_subcords_enabled.store(enable, std::memory_order_relaxed); +} + +enum Constants { + // The inlined size to use with absl::InlinedVector. + // + // Note: The InlinedVectors in this file (and in cord.h) do not need to use + // the same value for their inlined size. The fact that they do is historical. + // It may be desirable for each to use a different inlined size optimized for + // that InlinedVector's usage. + // + // TODO(jgm): Benchmark to see if there's a more optimal value than 47 for + // the inlined vector size (47 exists for backward compatibility). + kInlinedVectorSize = 47, + + // Prefer copying blocks of at most this size, otherwise reference count. + kMaxBytesToCopy = 511 +}; + +// Compact class for tracking the reference count and state flags for CordRep +// instances. Data is stored in an atomic int32_t for compactness and speed. +class RefcountAndFlags { public: - Refcount() : count_{1} {} - ~Refcount() {} + constexpr RefcountAndFlags() : count_{kRefIncrement} {} + struct Immortal {}; + explicit constexpr RefcountAndFlags(Immortal) : count_(kImmortalFlag) {} + struct WithCrc {}; + explicit constexpr RefcountAndFlags(WithCrc) + : count_(kCrcFlag | kRefIncrement) {} - // Increments the reference count by 1. Imposes no memory ordering. - inline void Increment() { count_.fetch_add(1, std::memory_order_relaxed); } + // Increments the reference count. Imposes no memory ordering. + inline void Increment() { + count_.fetch_add(kRefIncrement, std::memory_order_relaxed); + } // Asserts that the current refcount is greater than 0. If the refcount is - // greater than 1, decrements the reference count by 1. + // greater than 1, decrements the reference count. // // Returns false if there are no references outstanding; true otherwise. // Inserts barriers to ensure that state written before this method returns // false will be visible to a thread that just observed this method returning - // false. + // false. Always returns false when the immortal bit is set. inline bool Decrement() { - int32_t refcount = count_.load(std::memory_order_acquire); - assert(refcount > 0); - return refcount != 1 && count_.fetch_sub(1, std::memory_order_acq_rel) != 1; + int32_t refcount = count_.load(std::memory_order_acquire) & kRefcountMask; + assert(refcount > 0 || refcount & kImmortalFlag); + return refcount != kRefIncrement && + (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) & + kRefcountMask) != kRefIncrement; } // Same as Decrement but expect that refcount is greater than 1. inline bool DecrementExpectHighRefcount() { - int32_t refcount = count_.fetch_sub(1, std::memory_order_acq_rel); - assert(refcount > 0); - return refcount != 1; + int32_t refcount = + count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) & + kRefcountMask; + assert(refcount > 0 || refcount & kImmortalFlag); + return refcount != kRefIncrement; } // Returns the current reference count using acquire semantics. - inline int32_t Get() const { return count_.load(std::memory_order_acquire); } - - // Returns whether the atomic integer is 1. - // If the reference count is used in the conventional way, a - // reference count of 1 implies that the current thread owns the - // reference and no other thread shares it. - // This call performs the test for a reference count of one, and - // performs the memory barrier needed for the owning thread - // to act on the object, knowing that it has exclusive access to the - // object. - inline bool IsOne() { return count_.load(std::memory_order_acquire) == 1; } + inline int32_t Get() const { + return count_.load(std::memory_order_acquire) >> kNumFlags; + } + + // Returns true if the referenced object carries a CRC value. + bool HasCrc() const { + return (count_.load(std::memory_order_relaxed) & kCrcFlag) != 0; + } + + // Returns true iff the atomic integer is 1 and this node does not store + // a CRC. When both these conditions are met, the current thread owns + // the reference and no other thread shares it, so its contents may be + // safely mutated. + // + // If the referenced item is shared, carries a CRC, or is immortal, + // it should not be modified in-place, and this function returns false. + // + // This call performs the memory barrier needed for the owning thread + // to act on the object, so that if it returns true, it may safely + // assume exclusive access to the object. + inline bool IsMutable() { + return (count_.load(std::memory_order_acquire)) == kRefIncrement; + } + + // Returns whether the atomic integer is 1. Similar to IsMutable(), + // but does not check for a stored CRC. (An unshared node with a CRC is not + // mutable, because changing its data would invalidate the CRC.) + // + // When this returns true, there are no other references, and data sinks + // may safely adopt the children of the CordRep. + inline bool IsOne() { + return (count_.load(std::memory_order_acquire) & kRefcountMask) == + kRefIncrement; + } + + bool IsImmortal() const { + return (count_.load(std::memory_order_relaxed) & kImmortalFlag) != 0; + } private: + // We reserve the bottom bits for flags. + // kImmortalBit indicates that this entity should never be collected; it is + // used for the StringConstant constructor to avoid collecting immutable + // constant cords. + // kReservedFlag is reserved for future use. + enum { + kNumFlags = 2, + + kImmortalFlag = 0x1, + kCrcFlag = 0x2, + kRefIncrement = (1 << kNumFlags), + + // Bitmask to use when checking refcount by equality. This masks out + // all flags except kImmortalFlag, which is part of the refcount for + // purposes of equality. (A refcount of 0 or 1 does not count as 0 or 1 + // if the immortal bit is set.) + kRefcountMask = ~kCrcFlag, + }; + std::atomic<int32_t> count_; }; @@ -80,34 +189,106 @@ class Refcount { // functions in the base class. struct CordRepConcat; -struct CordRepSubstring; struct CordRepExternal; +struct CordRepFlat; +struct CordRepSubstring; +class CordRepRing; +class CordRepBtree; + +// Various representations that we allow +enum CordRepKind { + CONCAT = 0, + SUBSTRING = 1, + BTREE = 2, + RING = 3, + EXTERNAL = 4, + + // We have different tags for different sized flat arrays, + // starting with FLAT, and limited to MAX_FLAT_TAG. The 225 value is based on + // the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed + // in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well + // as the Tag <---> Size logic so that FLAT stil represents the minimum flat + // allocation size. (32 bytes as of now). + FLAT = 5, + MAX_FLAT_TAG = 225 +}; + +// There are various locations where we want to check if some rep is a 'plain' +// data edge, i.e. an external or flat rep. By having FLAT == EXTERNAL + 1, we +// can perform this check in a single branch as 'tag >= EXTERNAL' +// Likewise, we have some locations where we check for 'ring or external/flat', +// so likewise align RING to EXTERNAL. +// Note that we can leave this optimization to the compiler. The compiler will +// DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`. +static_assert(RING == BTREE + 1, "BTREE and RING not consecutive"); +static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive"); +static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive"); struct CordRep { + CordRep() = default; + constexpr CordRep(RefcountAndFlags::Immortal immortal, size_t l) + : length(l), refcount(immortal), tag(EXTERNAL), storage{} {} + // The following three fields have to be less than 32 bytes since // that is the smallest supported flat node size. - // We use uint64_t for the length even in 32-bit binaries. - uint64_t length; - Refcount refcount; + size_t length; + RefcountAndFlags refcount; // If tag < FLAT, it represents CordRepKind and indicates the type of node. // Otherwise, the node type is CordRepFlat and the tag is the encoded size. uint8_t tag; - char data[1]; // Starting point for flat array: MUST BE LAST FIELD of CordRep + // `storage` provides two main purposes: + // - the starting point for FlatCordRep.Data() [flexible-array-member] + // - 3 bytes of additional storage for use by derived classes. + // The latter is used by CordrepConcat and CordRepBtree. CordRepConcat stores + // a 'depth' value in storage[0], and the (future) CordRepBtree class stores + // `height`, `begin` and `end` in the 3 entries. Otherwise we would need to + // allocate room for these in the derived class, as not all compilers reuse + // padding space from the base class (clang and gcc do, MSVC does not, etc) + uint8_t storage[3]; + + // Returns true if this instance's tag matches the requested type. + constexpr bool IsRing() const { return tag == RING; } + constexpr bool IsConcat() const { return tag == CONCAT; } + constexpr bool IsSubstring() const { return tag == SUBSTRING; } + constexpr bool IsExternal() const { return tag == EXTERNAL; } + constexpr bool IsFlat() const { return tag >= FLAT; } + constexpr bool IsBtree() const { return tag == BTREE; } + + inline CordRepRing* ring(); + inline const CordRepRing* ring() const; inline CordRepConcat* concat(); inline const CordRepConcat* concat() const; inline CordRepSubstring* substring(); inline const CordRepSubstring* substring() const; inline CordRepExternal* external(); inline const CordRepExternal* external() const; + inline CordRepFlat* flat(); + inline const CordRepFlat* flat() const; + inline CordRepBtree* btree(); + inline const CordRepBtree* btree() const; + + // -------------------------------------------------------------------- + // Memory management + + // Destroys the provided `rep`. + static void Destroy(CordRep* rep); + + // Increments the reference count of `rep`. + // Requires `rep` to be a non-null pointer value. + static inline CordRep* Ref(CordRep* rep); + + // Decrements the reference count of `rep`. Destroys rep if count reaches + // zero. Requires `rep` to be a non-null pointer value. + static inline void Unref(CordRep* rep); }; struct CordRepConcat : public CordRep { CordRep* left; CordRep* right; - uint8_t depth() const { return static_cast<uint8_t>(data[0]); } - void set_depth(uint8_t depth) { data[0] = static_cast<char>(depth); } + uint8_t depth() const { return storage[0]; } + void set_depth(uint8_t depth) { storage[0] = depth; } }; struct CordRepSubstring : public CordRep { @@ -115,37 +296,325 @@ struct CordRepSubstring : public CordRep { CordRep* child; }; -// TODO(strel): replace the following logic (and related functions in cord.cc) -// with container_internal::Layout. - -// Alignment requirement for CordRepExternal so that the type erased releaser -// will be stored at a suitably aligned address. -constexpr size_t ExternalRepAlignment() { -#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__) - return __STDCPP_DEFAULT_NEW_ALIGNMENT__; -#else - return alignof(max_align_t); -#endif -} - -// Type for function pointer that will invoke and destroy the type-erased -// releaser function object. Accepts a pointer to the releaser and the -// `string_view` that were passed in to `NewExternalRep` below. The return value -// is the size of the `Releaser` type. -using ExternalReleaserInvoker = size_t (*)(void*, absl::string_view); +// Type for function pointer that will invoke the releaser function and also +// delete the `CordRepExternalImpl` corresponding to the passed in +// `CordRepExternal`. +using ExternalReleaserInvoker = void (*)(CordRepExternal*); // External CordReps are allocated together with a type erased releaser. The // releaser is stored in the memory directly following the CordRepExternal. -struct alignas(ExternalRepAlignment()) CordRepExternal : public CordRep { +struct CordRepExternal : public CordRep { + CordRepExternal() = default; + explicit constexpr CordRepExternal(absl::string_view str) + : CordRep(RefcountAndFlags::Immortal{}, str.size()), + base(str.data()), + releaser_invoker(nullptr) {} + const char* base; // Pointer to function that knows how to call and destroy the releaser. ExternalReleaserInvoker releaser_invoker; + + // Deletes (releases) the external rep. + // Requires rep != nullptr and rep->IsExternal() + static void Delete(CordRep* rep); +}; + +struct Rank1 {}; +struct Rank0 : Rank1 {}; + +template <typename Releaser, typename = ::absl::base_internal::invoke_result_t< + Releaser, absl::string_view>> +void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) { + ::absl::base_internal::invoke(std::forward<Releaser>(releaser), data); +} + +template <typename Releaser, + typename = ::absl::base_internal::invoke_result_t<Releaser>> +void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) { + ::absl::base_internal::invoke(std::forward<Releaser>(releaser)); +} + +// We use CompressedTuple so that we can benefit from EBCO. +template <typename Releaser> +struct CordRepExternalImpl + : public CordRepExternal, + public ::absl::container_internal::CompressedTuple<Releaser> { + // The extra int arg is so that we can avoid interfering with copy/move + // constructors while still benefitting from perfect forwarding. + template <typename T> + CordRepExternalImpl(T&& releaser, int) + : CordRepExternalImpl::CompressedTuple(std::forward<T>(releaser)) { + this->releaser_invoker = &Release; + } + + ~CordRepExternalImpl() { + InvokeReleaser(Rank0{}, std::move(this->template get<0>()), + absl::string_view(base, length)); + } + + static void Release(CordRepExternal* rep) { + delete static_cast<CordRepExternalImpl*>(rep); + } +}; + +inline void CordRepExternal::Delete(CordRep* rep) { + assert(rep != nullptr && rep->IsExternal()); + auto* rep_external = static_cast<CordRepExternal*>(rep); + assert(rep_external->releaser_invoker != nullptr); + rep_external->releaser_invoker(rep_external); +} + +template <typename Str> +struct ConstInitExternalStorage { + ABSL_CONST_INIT static CordRepExternal value; }; -// TODO(strel): look into removing, it doesn't seem like anything relies on this -static_assert(sizeof(CordRepConcat) == sizeof(CordRepSubstring), ""); +template <typename Str> +CordRepExternal ConstInitExternalStorage<Str>::value(Str::value); + +enum { + kMaxInline = 15, +}; + +constexpr char GetOrNull(absl::string_view data, size_t pos) { + return pos < data.size() ? data[pos] : '\0'; +} + +// We store cordz_info as 64 bit pointer value in big endian format. This +// guarantees that the least significant byte of cordz_info matches the last +// byte of the inline data representation in as_chars_, which holds the inlined +// size or the 'is_tree' bit. +using cordz_info_t = int64_t; + +// Assert that the `cordz_info` pointer value perfectly overlaps the last half +// of `as_chars_` and can hold a pointer value. +static_assert(sizeof(cordz_info_t) * 2 == kMaxInline + 1, ""); +static_assert(sizeof(cordz_info_t) >= sizeof(intptr_t), ""); + +// BigEndianByte() creates a big endian representation of 'value', i.e.: a big +// endian value where the last byte in the host's representation holds 'value`, +// with all other bytes being 0. +static constexpr cordz_info_t BigEndianByte(unsigned char value) { +#if defined(ABSL_IS_BIG_ENDIAN) + return value; +#else + return static_cast<cordz_info_t>(value) << ((sizeof(cordz_info_t) - 1) * 8); +#endif +} + +class InlineData { + public: + // DefaultInitType forces the use of the default initialization constructor. + enum DefaultInitType { kDefaultInit }; + + // kNullCordzInfo holds the big endian representation of intptr_t(1) + // This is the 'null' / initial value of 'cordz_info'. The null value + // is specifically big endian 1 as with 64-bit pointers, the last + // byte of cordz_info overlaps with the last byte holding the tag. + static constexpr cordz_info_t kNullCordzInfo = BigEndianByte(1); + + constexpr InlineData() : as_chars_{0} {} + explicit InlineData(DefaultInitType) {} + explicit constexpr InlineData(CordRep* rep) : as_tree_(rep) {} + explicit constexpr InlineData(absl::string_view chars) + : as_chars_{ + GetOrNull(chars, 0), GetOrNull(chars, 1), + GetOrNull(chars, 2), GetOrNull(chars, 3), + GetOrNull(chars, 4), GetOrNull(chars, 5), + GetOrNull(chars, 6), GetOrNull(chars, 7), + GetOrNull(chars, 8), GetOrNull(chars, 9), + GetOrNull(chars, 10), GetOrNull(chars, 11), + GetOrNull(chars, 12), GetOrNull(chars, 13), + GetOrNull(chars, 14), static_cast<char>((chars.size() << 1))} {} + + // Returns true if the current instance is empty. + // The 'empty value' is an inlined data value of zero length. + bool is_empty() const { return tag() == 0; } + + // Returns true if the current instance holds a tree value. + bool is_tree() const { return (tag() & 1) != 0; } + + // Returns true if the current instance holds a cordz_info value. + // Requires the current instance to hold a tree value. + bool is_profiled() const { + assert(is_tree()); + return as_tree_.cordz_info != kNullCordzInfo; + } + + // Returns true if either of the provided instances hold a cordz_info value. + // This method is more efficient than the equivalent `data1.is_profiled() || + // data2.is_profiled()`. Requires both arguments to hold a tree. + static bool is_either_profiled(const InlineData& data1, + const InlineData& data2) { + assert(data1.is_tree() && data2.is_tree()); + return (data1.as_tree_.cordz_info | data2.as_tree_.cordz_info) != + kNullCordzInfo; + } + + // Returns the cordz_info sampling instance for this instance, or nullptr + // if the current instance is not sampled and does not have CordzInfo data. + // Requires the current instance to hold a tree value. + CordzInfo* cordz_info() const { + assert(is_tree()); + intptr_t info = + static_cast<intptr_t>(absl::big_endian::ToHost64(as_tree_.cordz_info)); + assert(info & 1); + return reinterpret_cast<CordzInfo*>(info - 1); + } + + // Sets the current cordz_info sampling instance for this instance, or nullptr + // if the current instance is not sampled and does not have CordzInfo data. + // Requires the current instance to hold a tree value. + void set_cordz_info(CordzInfo* cordz_info) { + assert(is_tree()); + intptr_t info = reinterpret_cast<intptr_t>(cordz_info) | 1; + as_tree_.cordz_info = absl::big_endian::FromHost64(info); + } + + // Resets the current cordz_info to null / empty. + void clear_cordz_info() { + assert(is_tree()); + as_tree_.cordz_info = kNullCordzInfo; + } + + // Returns a read only pointer to the character data inside this instance. + // Requires the current instance to hold inline data. + const char* as_chars() const { + assert(!is_tree()); + return as_chars_; + } + + // Returns a mutable pointer to the character data inside this instance. + // Should be used for 'write only' operations setting an inlined value. + // Applications can set the value of inlined data either before or after + // setting the inlined size, i.e., both of the below are valid: + // + // // Set inlined data and inline size + // memcpy(data_.as_chars(), data, size); + // data_.set_inline_size(size); + // + // // Set inlined size and inline data + // data_.set_inline_size(size); + // memcpy(data_.as_chars(), data, size); + // + // It's an error to read from the returned pointer without a preceding write + // if the current instance does not hold inline data, i.e.: is_tree() == true. + char* as_chars() { return as_chars_; } + + // Returns the tree value of this value. + // Requires the current instance to hold a tree value. + CordRep* as_tree() const { + assert(is_tree()); + return as_tree_.rep; + } + + // Initialize this instance to holding the tree value `rep`, + // initializing the cordz_info to null, i.e.: 'not profiled'. + void make_tree(CordRep* rep) { + as_tree_.rep = rep; + as_tree_.cordz_info = kNullCordzInfo; + } + + // Set the tree value of this instance to 'rep`. + // Requires the current instance to already hold a tree value. + // Does not affect the value of cordz_info. + void set_tree(CordRep* rep) { + assert(is_tree()); + as_tree_.rep = rep; + } + + // Returns the size of the inlined character data inside this instance. + // Requires the current instance to hold inline data. + size_t inline_size() const { + assert(!is_tree()); + return tag() >> 1; + } + + // Sets the size of the inlined character data inside this instance. + // Requires `size` to be <= kMaxInline. + // See the documentation on 'as_chars()' for more information and examples. + void set_inline_size(size_t size) { + ABSL_ASSERT(size <= kMaxInline); + tag() = static_cast<char>(size << 1); + } + + private: + // See cordz_info_t for forced alignment and size of `cordz_info` details. + struct AsTree { + explicit constexpr AsTree(absl::cord_internal::CordRep* tree) + : rep(tree), cordz_info(kNullCordzInfo) {} + // This union uses up extra space so that whether rep is 32 or 64 bits, + // cordz_info will still start at the eighth byte, and the last + // byte of cordz_info will still be the last byte of InlineData. + union { + absl::cord_internal::CordRep* rep; + cordz_info_t unused_aligner; + }; + cordz_info_t cordz_info; + }; + + char& tag() { return reinterpret_cast<char*>(this)[kMaxInline]; } + char tag() const { return reinterpret_cast<const char*>(this)[kMaxInline]; } + + // If the data has length <= kMaxInline, we store it in `as_chars_`, and + // store the size in the last char of `as_chars_` shifted left + 1. + // Else we store it in a tree and store a pointer to that tree in + // `as_tree_.rep` and store a tag in `tagged_size`. + union { + char as_chars_[kMaxInline + 1]; + AsTree as_tree_; + }; +}; + +static_assert(sizeof(InlineData) == kMaxInline + 1, ""); + +inline CordRepConcat* CordRep::concat() { + assert(IsConcat()); + return static_cast<CordRepConcat*>(this); +} + +inline const CordRepConcat* CordRep::concat() const { + assert(IsConcat()); + return static_cast<const CordRepConcat*>(this); +} + +inline CordRepSubstring* CordRep::substring() { + assert(IsSubstring()); + return static_cast<CordRepSubstring*>(this); +} + +inline const CordRepSubstring* CordRep::substring() const { + assert(IsSubstring()); + return static_cast<const CordRepSubstring*>(this); +} + +inline CordRepExternal* CordRep::external() { + assert(IsExternal()); + return static_cast<CordRepExternal*>(this); +} + +inline const CordRepExternal* CordRep::external() const { + assert(IsExternal()); + return static_cast<const CordRepExternal*>(this); +} + +inline CordRep* CordRep::Ref(CordRep* rep) { + assert(rep != nullptr); + rep->refcount.Increment(); + return rep; +} + +inline void CordRep::Unref(CordRep* rep) { + assert(rep != nullptr); + // Expect refcount to be 0. Avoiding the cost of an atomic decrement should + // typically outweigh the cost of an extra branch checking for ref == 1. + if (ABSL_PREDICT_FALSE(!rep->refcount.DecrementExpectHighRefcount())) { + Destroy(rep); + } +} } // namespace cord_internal + ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_internal_test.cc b/third_party/abseil-cpp/absl/strings/internal/cord_internal_test.cc new file mode 100644 index 0000000000..0758dfef38 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_internal_test.cc @@ -0,0 +1,116 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_internal.h" + +#include "gmock/gmock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +TEST(RefcountAndFlags, NormalRefcount) { + for (bool expect_high_refcount : {false, true}) { + SCOPED_TRACE(expect_high_refcount); + RefcountAndFlags refcount; + // count = 1 + + EXPECT_FALSE(refcount.HasCrc()); + EXPECT_TRUE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + refcount.Increment(); + // count = 2 + + EXPECT_FALSE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_FALSE(refcount.IsOne()); + + // Decrementing should return true, since a reference is outstanding. + if (expect_high_refcount) { + EXPECT_TRUE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_TRUE(refcount.Decrement()); + } + // count = 1 + + EXPECT_FALSE(refcount.HasCrc()); + EXPECT_TRUE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + // One more decremnt will return false, as no references remain. + if (expect_high_refcount) { + EXPECT_FALSE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_FALSE(refcount.Decrement()); + } + } +} + +TEST(RefcountAndFlags, CrcRefcount) { + for (bool expect_high_refcount : {false, true}) { + SCOPED_TRACE(expect_high_refcount); + RefcountAndFlags refcount(RefcountAndFlags::WithCrc{}); + // count = 1 + + // A CRC-carrying node is never mutable, but can be unshared + EXPECT_TRUE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + refcount.Increment(); + // count = 2 + + EXPECT_TRUE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_FALSE(refcount.IsOne()); + + // Decrementing should return true, since a reference is outstanding. + if (expect_high_refcount) { + EXPECT_TRUE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_TRUE(refcount.Decrement()); + } + // count = 1 + + EXPECT_TRUE(refcount.HasCrc()); + EXPECT_FALSE(refcount.IsMutable()); + EXPECT_TRUE(refcount.IsOne()); + + // One more decremnt will return false, as no references remain. + if (expect_high_refcount) { + EXPECT_FALSE(refcount.DecrementExpectHighRefcount()); + } else { + EXPECT_FALSE(refcount.Decrement()); + } + } +} + +TEST(RefcountAndFlags, ImmortalRefcount) { + RefcountAndFlags immortal_refcount(RefcountAndFlags::Immortal{}); + + for (int i = 0; i < 100; ++i) { + // An immortal refcount is never unshared, and decrementing never causes + // a collection. + EXPECT_FALSE(immortal_refcount.HasCrc()); + EXPECT_FALSE(immortal_refcount.IsMutable()); + EXPECT_FALSE(immortal_refcount.IsOne()); + EXPECT_TRUE(immortal_refcount.Decrement()); + EXPECT_TRUE(immortal_refcount.DecrementExpectHighRefcount()); + } +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.cc new file mode 100644 index 0000000000..4404f33a12 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.cc @@ -0,0 +1,1128 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_btree.h" + +#include <cassert> +#include <cstdint> +#include <iostream> +#include <string> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_consume.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +constexpr size_t CordRepBtree::kMaxCapacity; // NOLINT: needed for c++ < c++17 + +namespace { + +using NodeStack = CordRepBtree * [CordRepBtree::kMaxDepth]; +using EdgeType = CordRepBtree::EdgeType; +using OpResult = CordRepBtree::OpResult; +using CopyResult = CordRepBtree::CopyResult; + +constexpr auto kFront = CordRepBtree::kFront; +constexpr auto kBack = CordRepBtree::kBack; + +inline bool exhaustive_validation() { + return cord_btree_exhaustive_validation.load(std::memory_order_relaxed); +} + +// Implementation of the various 'Dump' functions. +// Prints the entire tree structure or 'rep'. External callers should +// not specify 'depth' and leave it to its default (0) value. +// Rep may be a CordRepBtree tree, or a SUBSTRING / EXTERNAL / FLAT node. +void DumpAll(const CordRep* rep, bool include_contents, std::ostream& stream, + int depth = 0) { + // Allow for full height trees + substring -> flat / external nodes. + assert(depth <= CordRepBtree::kMaxDepth + 2); + std::string sharing = const_cast<CordRep*>(rep)->refcount.IsOne() + ? std::string("Private") + : absl::StrCat("Shared(", rep->refcount.Get(), ")"); + std::string sptr = absl::StrCat("0x", absl::Hex(rep)); + + // Dumps the data contents of `rep` if `include_contents` is true. + // Always emits a new line character. + auto maybe_dump_data = [&stream, include_contents](const CordRep* r) { + if (include_contents) { + // Allow for up to 60 wide display of content data, which with some + // indentation and prefix / labels keeps us within roughly 80-100 wide. + constexpr size_t kMaxDataLength = 60; + stream << ", data = \"" + << CordRepBtree::EdgeData(r).substr(0, kMaxDataLength) + << (r->length > kMaxDataLength ? "\"..." : "\""); + } + stream << '\n'; + }; + + // For each level, we print the 'shared/private' state and the rep pointer, + // indented by two spaces per recursive depth. + stream << std::string(depth * 2, ' ') << sharing << " (" << sptr << ") "; + + if (rep->IsBtree()) { + const CordRepBtree* node = rep->btree(); + std::string label = + node->height() ? absl::StrCat("Node(", node->height(), ")") : "Leaf"; + stream << label << ", len = " << node->length + << ", begin = " << node->begin() << ", end = " << node->end() + << "\n"; + for (CordRep* edge : node->Edges()) { + DumpAll(edge, include_contents, stream, depth + 1); + } + } else if (rep->tag == SUBSTRING) { + const CordRepSubstring* substring = rep->substring(); + stream << "Substring, len = " << rep->length + << ", start = " << substring->start; + maybe_dump_data(rep); + DumpAll(substring->child, include_contents, stream, depth + 1); + } else if (rep->tag >= FLAT) { + stream << "Flat, len = " << rep->length + << ", cap = " << rep->flat()->Capacity(); + maybe_dump_data(rep); + } else if (rep->tag == EXTERNAL) { + stream << "Extn, len = " << rep->length; + maybe_dump_data(rep); + } +} + +// TODO(b/192061034): add 'bytes to copy' logic to avoid large slop on substring +// small data out of large reps, and general efficiency of 'always copy small +// data'. Consider making this a cord rep internal library function. +CordRepSubstring* CreateSubstring(CordRep* rep, size_t offset, size_t n) { + assert(n != 0); + assert(offset + n <= rep->length); + assert(offset != 0 || n != rep->length); + + if (rep->tag == SUBSTRING) { + CordRepSubstring* substring = rep->substring(); + offset += substring->start; + rep = CordRep::Ref(substring->child); + CordRep::Unref(substring); + } + CordRepSubstring* substring = new CordRepSubstring(); + substring->length = n; + substring->tag = SUBSTRING; + substring->start = offset; + substring->child = rep; + return substring; +} + +// TODO(b/192061034): consider making this a cord rep library function. +inline CordRep* MakeSubstring(CordRep* rep, size_t offset, size_t n) { + if (n == rep->length) return rep; + if (n == 0) return CordRep::Unref(rep), nullptr; + return CreateSubstring(rep, offset, n); +} + +// TODO(b/192061034): consider making this a cord rep library function. +inline CordRep* MakeSubstring(CordRep* rep, size_t offset) { + if (offset == 0) return rep; + return CreateSubstring(rep, offset, rep->length - offset); +} + +// Resizes `edge` to the provided `length`. Adopts a reference on `edge`. +// This method directly returns `edge` if `length` equals `edge->length`. +// If `is_mutable` is set to true, this function may return `edge` with +// `edge->length` set to the new length depending on the type and size of +// `edge`. Otherwise, this function returns a new CordRepSubstring value. +// Requires `length > 0 && length <= edge->length`. +CordRep* ResizeEdge(CordRep* edge, size_t length, bool is_mutable) { + assert(length > 0); + assert(length <= edge->length); + assert(CordRepBtree::IsDataEdge(edge)); + if (length >= edge->length) return edge; + + if (is_mutable && (edge->tag >= FLAT || edge->tag == SUBSTRING)) { + edge->length = length; + return edge; + } + + return CreateSubstring(edge, 0, length); +} + +template <EdgeType edge_type> +inline absl::string_view Consume(absl::string_view s, size_t n) { + return edge_type == kBack ? s.substr(n) : s.substr(0, s.size() - n); +} + +template <EdgeType edge_type> +inline absl::string_view Consume(char* dst, absl::string_view s, size_t n) { + if (edge_type == kBack) { + memcpy(dst, s.data(), n); + return s.substr(n); + } else { + const size_t offset = s.size() - n; + memcpy(dst, s.data() + offset, n); + return s.substr(0, offset); + } +} + +// Known issue / optimization weirdness: the store associated with the +// decrement introduces traffic between cpus (even if the result of that +// traffic does nothing), making this faster than a single call to +// refcount.Decrement() checking the zero refcount condition. +template <typename R, typename Fn> +inline void FastUnref(R* r, Fn&& fn) { + if (r->refcount.IsOne()) { + fn(r); + } else if (!r->refcount.DecrementExpectHighRefcount()) { + fn(r); + } +} + +// Deletes a leaf node data edge. Requires `rep` to be an EXTERNAL or FLAT +// node, or a SUBSTRING of an EXTERNAL or FLAT node. +void DeleteLeafEdge(CordRep* rep) { + for (;;) { + if (rep->tag >= FLAT) { + CordRepFlat::Delete(rep->flat()); + return; + } + if (rep->tag == EXTERNAL) { + CordRepExternal::Delete(rep->external()); + return; + } + assert(rep->tag == SUBSTRING); + CordRepSubstring* substring = rep->substring(); + rep = substring->child; + assert(rep->tag == EXTERNAL || rep->tag >= FLAT); + delete substring; + if (rep->refcount.Decrement()) return; + } +} + +// StackOperations contains the logic to build a left-most or right-most stack +// (leg) down to the leaf level of a btree, and 'unwind' / 'Finalize' methods to +// propagate node changes up the stack. +template <EdgeType edge_type> +struct StackOperations { + // Returns true if the node at 'depth' is mutable, i.e. has a refcount + // of one, carries no CRC, and all of its parent nodes have a refcount of one. + inline bool owned(int depth) const { return depth < share_depth; } + + // Returns the node at 'depth'. + inline CordRepBtree* node(int depth) const { return stack[depth]; } + + // Builds a `depth` levels deep stack starting at `tree` recording which nodes + // are private in the form of the 'share depth' where nodes are shared. + inline CordRepBtree* BuildStack(CordRepBtree* tree, int depth) { + assert(depth <= tree->height()); + int current_depth = 0; + while (current_depth < depth && tree->refcount.IsMutable()) { + stack[current_depth++] = tree; + tree = tree->Edge(edge_type)->btree(); + } + share_depth = current_depth + (tree->refcount.IsMutable() ? 1 : 0); + while (current_depth < depth) { + stack[current_depth++] = tree; + tree = tree->Edge(edge_type)->btree(); + } + return tree; + } + + // Builds a stack with the invariant that all nodes are private owned / not + // shared and carry no CRC data. This is used in iterative updates where a + // previous propagation guaranteed all nodes have this property. + inline void BuildOwnedStack(CordRepBtree* tree, int height) { + assert(height <= CordRepBtree::kMaxHeight); + int depth = 0; + while (depth < height) { + assert(tree->refcount.IsMutable()); + stack[depth++] = tree; + tree = tree->Edge(edge_type)->btree(); + } + assert(tree->refcount.IsMutable()); + share_depth = depth + 1; + } + + // Processes the final 'top level' result action for the tree. + // See the 'Action' enum for the various action implications. + static inline CordRepBtree* Finalize(CordRepBtree* tree, OpResult result) { + switch (result.action) { + case CordRepBtree::kPopped: + tree = edge_type == kBack ? CordRepBtree::New(tree, result.tree) + : CordRepBtree::New(result.tree, tree); + if (ABSL_PREDICT_FALSE(tree->height() > CordRepBtree::kMaxHeight)) { + tree = CordRepBtree::Rebuild(tree); + ABSL_RAW_CHECK(tree->height() <= CordRepBtree::kMaxHeight, + "Max height exceeded"); + } + return tree; + case CordRepBtree::kCopied: + CordRep::Unref(tree); + ABSL_FALLTHROUGH_INTENDED; + case CordRepBtree::kSelf: + return result.tree; + } + ABSL_INTERNAL_UNREACHABLE; + return result.tree; + } + + // Propagate the action result in 'result' up into all nodes of the stack + // starting at depth 'depth'. 'length' contains the extra length of data that + // was added at the lowest level, and is updated into all nodes of the stack. + // See the 'Action' enum for the various action implications. + // If 'propagate' is true, then any copied node values are updated into the + // stack, which is used for iterative processing on the same stack. + template <bool propagate = false> + inline CordRepBtree* Unwind(CordRepBtree* tree, int depth, size_t length, + OpResult result) { + // TODO(mvels): revisit the below code to check if 3 loops with 3 + // (incremental) conditions is faster than 1 loop with a switch. + // Benchmarking and perf recordings indicate the loop with switch is + // fastest, likely because of indirect jumps on the tight case values and + // dense branches. But it's worth considering 3 loops, as the `action` + // transitions are mono directional. E.g.: + // while (action == kPopped) { + // ... + // } + // while (action == kCopied) { + // ... + // } + // ... + // We also found that an "if () do {}" loop here seems faster, possibly + // because it allows the branch predictor more granular heuristics on + // 'single leaf' (`depth` == 0) and 'single depth' (`depth` == 1) cases + // which appear to be the most common use cases. + if (depth != 0) { + do { + CordRepBtree* node = stack[--depth]; + const bool owned = depth < share_depth; + switch (result.action) { + case CordRepBtree::kPopped: + assert(!propagate); + result = node->AddEdge<edge_type>(owned, result.tree, length); + break; + case CordRepBtree::kCopied: + result = node->SetEdge<edge_type>(owned, result.tree, length); + if (propagate) stack[depth] = result.tree; + break; + case CordRepBtree::kSelf: + node->length += length; + while (depth > 0) { + node = stack[--depth]; + node->length += length; + } + return node; + } + } while (depth > 0); + } + return Finalize(tree, result); + } + + // Invokes `Unwind` with `propagate=true` to update the stack node values. + inline CordRepBtree* Propagate(CordRepBtree* tree, int depth, size_t length, + OpResult result) { + return Unwind</*propagate=*/true>(tree, depth, length, result); + } + + // `share_depth` contains the depth at which the nodes in the stack cannot + // be mutated. I.e., if the top most level is shared (i.e.: + // `!refcount.IsMutable()`), then `share_depth` is 0. If the 2nd node + // is shared (and implicitly all nodes below that) then `share_depth` is 1, + // etc. A `share_depth` greater than the depth of the stack indicates that + // none of the nodes in the stack are shared. + int share_depth; + + NodeStack stack; +}; + +} // namespace + +void CordRepBtree::Dump(const CordRep* rep, absl::string_view label, + bool include_contents, std::ostream& stream) { + stream << "===================================\n"; + if (!label.empty()) { + stream << label << '\n'; + stream << "-----------------------------------\n"; + } + if (rep) { + DumpAll(rep, include_contents, stream); + } else { + stream << "NULL\n"; + } +} + +void CordRepBtree::Dump(const CordRep* rep, absl::string_view label, + std::ostream& stream) { + Dump(rep, label, false, stream); +} + +void CordRepBtree::Dump(const CordRep* rep, std::ostream& stream) { + Dump(rep, absl::string_view(), false, stream); +} + +void CordRepBtree::DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end) { + for (CordRep* edge : tree->Edges(begin, end)) { + FastUnref(edge, DeleteLeafEdge); + } + Delete(tree); +} + +void CordRepBtree::DestroyNonLeaf(CordRepBtree* tree, size_t begin, + size_t end) { + for (CordRep* edge : tree->Edges(begin, end)) { + FastUnref(edge->btree(), Destroy); + } + Delete(tree); +} + +bool CordRepBtree::IsValid(const CordRepBtree* tree, bool shallow) { +#define NODE_CHECK_VALID(x) \ + if (!(x)) { \ + ABSL_RAW_LOG(ERROR, "CordRepBtree::CheckValid() FAILED: %s", #x); \ + return false; \ + } +#define NODE_CHECK_EQ(x, y) \ + if ((x) != (y)) { \ + ABSL_RAW_LOG(ERROR, \ + "CordRepBtree::CheckValid() FAILED: %s != %s (%s vs %s)", #x, \ + #y, absl::StrCat(x).c_str(), absl::StrCat(y).c_str()); \ + return false; \ + } + + NODE_CHECK_VALID(tree != nullptr); + NODE_CHECK_VALID(tree->IsBtree()); + NODE_CHECK_VALID(tree->height() <= kMaxHeight); + NODE_CHECK_VALID(tree->begin() < tree->capacity()); + NODE_CHECK_VALID(tree->end() <= tree->capacity()); + NODE_CHECK_VALID(tree->begin() <= tree->end()); + size_t child_length = 0; + for (CordRep* edge : tree->Edges()) { + NODE_CHECK_VALID(edge != nullptr); + if (tree->height() > 0) { + NODE_CHECK_VALID(edge->IsBtree()); + NODE_CHECK_VALID(edge->btree()->height() == tree->height() - 1); + } else { + NODE_CHECK_VALID(IsDataEdge(edge)); + } + child_length += edge->length; + } + NODE_CHECK_EQ(child_length, tree->length); + if ((!shallow || exhaustive_validation()) && tree->height() > 0) { + for (CordRep* edge : tree->Edges()) { + if (!IsValid(edge->btree(), shallow)) return false; + } + } + return true; + +#undef NODE_CHECK_VALID +#undef NODE_CHECK_EQ +} + +#ifndef NDEBUG + +CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, bool shallow) { + if (!IsValid(tree, shallow)) { + Dump(tree, "CordRepBtree validation failed:", false, std::cout); + ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); + } + return tree; +} + +const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree, + bool shallow) { + if (!IsValid(tree, shallow)) { + Dump(tree, "CordRepBtree validation failed:", false, std::cout); + ABSL_RAW_LOG(FATAL, "CordRepBtree::CheckValid() FAILED"); + } + return tree; +} + +#endif // NDEBUG + +template <EdgeType edge_type> +inline OpResult CordRepBtree::AddEdge(bool owned, CordRep* edge, size_t delta) { + if (size() >= kMaxCapacity) return {New(edge), kPopped}; + OpResult result = ToOpResult(owned); + result.tree->Add<edge_type>(edge); + result.tree->length += delta; + return result; +} + +template <EdgeType edge_type> +OpResult CordRepBtree::SetEdge(bool owned, CordRep* edge, size_t delta) { + OpResult result; + const size_t idx = index(edge_type); + if (owned) { + result = {this, kSelf}; + CordRep::Unref(edges_[idx]); + } else { + // Create a copy containing all unchanged edges. Unchanged edges are the + // open interval [begin, back) or [begin + 1, end) depending on `edge_type`. + // We conveniently cover both case using a constexpr `shift` being 0 or 1 + // as `end :== back + 1`. + result = {CopyRaw(), kCopied}; + constexpr int shift = edge_type == kFront ? 1 : 0; + for (CordRep* r : Edges(begin() + shift, back() + shift)) { + CordRep::Ref(r); + } + } + result.tree->edges_[idx] = edge; + result.tree->length += delta; + return result; +} + +template <EdgeType edge_type> +CordRepBtree* CordRepBtree::AddCordRep(CordRepBtree* tree, CordRep* rep) { + const int depth = tree->height(); + const size_t length = rep->length; + StackOperations<edge_type> ops; + CordRepBtree* leaf = ops.BuildStack(tree, depth); + const OpResult result = + leaf->AddEdge<edge_type>(ops.owned(depth), rep, length); + return ops.Unwind(tree, depth, length, result); +} + +template <> +CordRepBtree* CordRepBtree::NewLeaf<kBack>(absl::string_view data, + size_t extra) { + CordRepBtree* leaf = CordRepBtree::New(0); + size_t length = 0; + size_t end = 0; + const size_t cap = leaf->capacity(); + while (!data.empty() && end != cap) { + auto* flat = CordRepFlat::New(data.length() + extra); + flat->length = (std::min)(data.length(), flat->Capacity()); + length += flat->length; + leaf->edges_[end++] = flat; + data = Consume<kBack>(flat->Data(), data, flat->length); + } + leaf->length = length; + leaf->set_end(end); + return leaf; +} + +template <> +CordRepBtree* CordRepBtree::NewLeaf<kFront>(absl::string_view data, + size_t extra) { + CordRepBtree* leaf = CordRepBtree::New(0); + size_t length = 0; + size_t begin = leaf->capacity(); + leaf->set_end(leaf->capacity()); + while (!data.empty() && begin != 0) { + auto* flat = CordRepFlat::New(data.length() + extra); + flat->length = (std::min)(data.length(), flat->Capacity()); + length += flat->length; + leaf->edges_[--begin] = flat; + data = Consume<kFront>(flat->Data(), data, flat->length); + } + leaf->length = length; + leaf->set_begin(begin); + return leaf; +} + +template <> +absl::string_view CordRepBtree::AddData<kBack>(absl::string_view data, + size_t extra) { + assert(!data.empty()); + assert(size() < capacity()); + AlignBegin(); + const size_t cap = capacity(); + do { + CordRepFlat* flat = CordRepFlat::New(data.length() + extra); + const size_t n = (std::min)(data.length(), flat->Capacity()); + flat->length = n; + edges_[fetch_add_end(1)] = flat; + data = Consume<kBack>(flat->Data(), data, n); + } while (!data.empty() && end() != cap); + return data; +} + +template <> +absl::string_view CordRepBtree::AddData<kFront>(absl::string_view data, + size_t extra) { + assert(!data.empty()); + assert(size() < capacity()); + AlignEnd(); + do { + CordRepFlat* flat = CordRepFlat::New(data.length() + extra); + const size_t n = (std::min)(data.length(), flat->Capacity()); + flat->length = n; + edges_[sub_fetch_begin(1)] = flat; + data = Consume<kFront>(flat->Data(), data, n); + } while (!data.empty() && begin() != 0); + return data; +} + +template <EdgeType edge_type> +CordRepBtree* CordRepBtree::AddData(CordRepBtree* tree, absl::string_view data, + size_t extra) { + if (ABSL_PREDICT_FALSE(data.empty())) return tree; + + const size_t original_data_size = data.size(); + int depth = tree->height(); + StackOperations<edge_type> ops; + CordRepBtree* leaf = ops.BuildStack(tree, depth); + + // If there is capacity in the last edge, append as much data + // as possible into this last edge. + if (leaf->size() < leaf->capacity()) { + OpResult result = leaf->ToOpResult(ops.owned(depth)); + data = result.tree->AddData<edge_type>(data, extra); + if (data.empty()) { + result.tree->length += original_data_size; + return ops.Unwind(tree, depth, original_data_size, result); + } + + // We added some data into this leaf, but not all. Propagate the added + // length to the top most node, and rebuild the stack with any newly copied + // or updated nodes. From this point on, the path (leg) from the top most + // node to the right-most node towards the leaf node is privately owned. + size_t delta = original_data_size - data.size(); + assert(delta > 0); + result.tree->length += delta; + tree = ops.Propagate(tree, depth, delta, result); + ops.share_depth = depth + 1; + } + + // We were unable to append all data into the existing right-most leaf node. + // This means all remaining data must be put into (a) new leaf node(s) which + // we append to the tree. To make this efficient, we iteratively build full + // leaf nodes from `data` until the created leaf contains all remaining data. + // We utilize the `Unwind` method to merge the created leaf into the first + // level towards root that has capacity. On each iteration with remaining + // data, we rebuild the stack in the knowledge that right-most nodes are + // privately owned after the first `Unwind` completes. + for (;;) { + OpResult result = {CordRepBtree::NewLeaf<edge_type>(data, extra), kPopped}; + if (result.tree->length == data.size()) { + return ops.Unwind(tree, depth, result.tree->length, result); + } + data = Consume<edge_type>(data, result.tree->length); + tree = ops.Unwind(tree, depth, result.tree->length, result); + depth = tree->height(); + ops.BuildOwnedStack(tree, depth); + } +} + +template <EdgeType edge_type> +CordRepBtree* CordRepBtree::Merge(CordRepBtree* dst, CordRepBtree* src) { + assert(dst->height() >= src->height()); + + // Capture source length as we may consume / destroy `src`. + const size_t length = src->length; + + // We attempt to merge `src` at its corresponding height in `dst`. + const int depth = dst->height() - src->height(); + StackOperations<edge_type> ops; + CordRepBtree* merge_node = ops.BuildStack(dst, depth); + + // If there is enough space in `merge_node` for all edges from `src`, add all + // edges to this node, making a fresh copy as needed if not privately owned. + // If `merge_node` does not have capacity for `src`, we rely on `Unwind` and + // `Finalize` to merge `src` into the first level towards `root` where there + // is capacity for another edge, or create a new top level node. + OpResult result; + if (merge_node->size() + src->size() <= kMaxCapacity) { + result = merge_node->ToOpResult(ops.owned(depth)); + result.tree->Add<edge_type>(src->Edges()); + result.tree->length += src->length; + if (src->refcount.IsOne()) { + Delete(src); + } else { + for (CordRep* edge : src->Edges()) CordRep::Ref(edge); + CordRepBtree::Unref(src); + } + } else { + result = {src, kPopped}; + } + + // Unless we merged at the top level (i.e.: src and dst are equal height), + // unwind the result towards the top level, and finalize the result. + if (depth) { + return ops.Unwind(dst, depth, length, result); + } + return ops.Finalize(dst, result); +} + +CopyResult CordRepBtree::CopySuffix(size_t offset) { + assert(offset < this->length); + + // As long as `offset` starts inside the last edge, we can 'drop' the current + // depth. For the most extreme example: if offset references the last data + // edge in the tree, there is only a single edge / path from the top of the + // tree to that last edge, so we can drop all the nodes except that edge. + // The fast path check for this is `back->length >= length - offset`. + int height = this->height(); + CordRepBtree* node = this; + size_t len = node->length - offset; + CordRep* back = node->Edge(kBack); + while (back->length >= len) { + offset = back->length - len; + if (--height < 0) { + return {MakeSubstring(CordRep::Ref(back), offset), height}; + } + node = back->btree(); + back = node->Edge(kBack); + } + if (offset == 0) return {CordRep::Ref(node), height}; + + // Offset does not point into the last edge, so we span at least two edges. + // Find the index of offset with `IndexBeyond` which provides us the edge + // 'beyond' the offset if offset is not a clean starting point of an edge. + Position pos = node->IndexBeyond(offset); + CordRepBtree* sub = node->CopyToEndFrom(pos.index, len); + const CopyResult result = {sub, height}; + + // `pos.n` contains a non zero value if the offset is not an exact starting + // point of an edge. In this case, `pos.n` contains the 'trailing' amount of + // bytes of the edge preceding that in `pos.index`. We need to iteratively + // adjust the preceding edge with the 'broken' offset until we have a perfect + // start of the edge. + while (pos.n != 0) { + assert(pos.index >= 1); + const size_t begin = pos.index - 1; + sub->set_begin(begin); + CordRep* const edge = node->Edge(begin); + + len = pos.n; + offset = edge->length - len; + + if (--height < 0) { + sub->edges_[begin] = MakeSubstring(CordRep::Ref(edge), offset, len); + return result; + } + + node = edge->btree(); + pos = node->IndexBeyond(offset); + + CordRepBtree* nsub = node->CopyToEndFrom(pos.index, len); + sub->edges_[begin] = nsub; + sub = nsub; + } + sub->set_begin(pos.index); + return result; +} + +CopyResult CordRepBtree::CopyPrefix(size_t n, bool allow_folding) { + assert(n > 0); + assert(n <= this->length); + + // As long as `n` does not exceed the length of the first edge, we can 'drop' + // the current depth. For the most extreme example: if we'd copy a 1 byte + // prefix from a tree, there is only a single edge / path from the top of the + // tree to the single data edge containing this byte, so we can drop all the + // nodes except the data node. + int height = this->height(); + CordRepBtree* node = this; + CordRep* front = node->Edge(kFront); + if (allow_folding) { + while (front->length >= n) { + if (--height < 0) return {MakeSubstring(CordRep::Ref(front), 0, n), -1}; + node = front->btree(); + front = node->Edge(kFront); + } + } + if (node->length == n) return {CordRep::Ref(node), height}; + + // `n` spans at least two nodes, find the end point of the span. + Position pos = node->IndexOf(n); + + // Create a partial copy of the node up to `pos.index`, with a defined length + // of `n`. Any 'partial last edge' is added further below as needed. + CordRepBtree* sub = node->CopyBeginTo(pos.index, n); + const CopyResult result = {sub, height}; + + // `pos.n` contains the 'offset inside the edge for IndexOf(n)'. As long as + // this is not zero, we don't have a 'clean cut', so we need to make a + // (partial) copy of that last edge, and repeat this until pos.n is zero. + while (pos.n != 0) { + size_t end = pos.index; + n = pos.n; + + CordRep* edge = node->Edge(pos.index); + if (--height < 0) { + sub->edges_[end++] = MakeSubstring(CordRep::Ref(edge), 0, n); + sub->set_end(end); + AssertValid(result.edge->btree()); + return result; + } + + node = edge->btree(); + pos = node->IndexOf(n); + CordRepBtree* nsub = node->CopyBeginTo(pos.index, n); + sub->edges_[end++] = nsub; + sub->set_end(end); + sub = nsub; + } + sub->set_end(pos.index); + AssertValid(result.edge->btree()); + return result; +} + +CordRep* CordRepBtree::ExtractFront(CordRepBtree* tree) { + CordRep* front = tree->Edge(tree->begin()); + if (tree->refcount.IsMutable()) { + Unref(tree->Edges(tree->begin() + 1, tree->end())); + CordRepBtree::Delete(tree); + } else { + CordRep::Ref(front); + CordRep::Unref(tree); + } + return front; +} + +CordRepBtree* CordRepBtree::ConsumeBeginTo(CordRepBtree* tree, size_t end, + size_t new_length) { + assert(end <= tree->end()); + if (tree->refcount.IsMutable()) { + Unref(tree->Edges(end, tree->end())); + tree->set_end(end); + tree->length = new_length; + } else { + CordRepBtree* old = tree; + tree = tree->CopyBeginTo(end, new_length); + CordRep::Unref(old); + } + return tree; +} + +CordRep* CordRepBtree::RemoveSuffix(CordRepBtree* tree, size_t n) { + // Check input and deal with trivial cases 'Remove all/none' + assert(tree != nullptr); + assert(n <= tree->length); + const size_t len = tree->length; + if (ABSL_PREDICT_FALSE(n == 0)) { + return tree; + } + if (ABSL_PREDICT_FALSE(n >= len)) { + CordRepBtree::Unref(tree); + return nullptr; + } + + size_t length = len - n; + int height = tree->height(); + bool is_mutable = tree->refcount.IsMutable(); + + // Extract all top nodes which are reduced to size = 1 + Position pos = tree->IndexOfLength(length); + while (pos.index == tree->begin()) { + CordRep* edge = ExtractFront(tree); + is_mutable &= edge->refcount.IsMutable(); + if (height-- == 0) return ResizeEdge(edge, length, is_mutable); + tree = edge->btree(); + pos = tree->IndexOfLength(length); + } + + // Repeat the following sequence traversing down the tree: + // - Crop the top node to the 'last remaining edge' adjusting length. + // - Set the length for down edges to the partial length in that last edge. + // - Repeat this until the last edge is 'included in full' + // - If we hit the data edge level, resize and return the last data edge + CordRepBtree* top = tree = ConsumeBeginTo(tree, pos.index + 1, length); + CordRep* edge = tree->Edge(pos.index); + length = pos.n; + while (length != edge->length) { + // ConsumeBeginTo guarantees `tree` is a clean, privately owned copy. + assert(tree->refcount.IsMutable()); + const bool edge_is_mutable = edge->refcount.IsMutable(); + + if (height-- == 0) { + tree->edges_[pos.index] = ResizeEdge(edge, length, edge_is_mutable); + return AssertValid(top); + } + + if (!edge_is_mutable) { + // We can't 'in place' remove any suffixes down this edge. + // Replace this edge with a prefix copy instead. + tree->edges_[pos.index] = edge->btree()->CopyPrefix(length, false).edge; + CordRep::Unref(edge); + return AssertValid(top); + } + + // Move down one level, rinse repeat. + tree = edge->btree(); + pos = tree->IndexOfLength(length); + tree = ConsumeBeginTo(edge->btree(), pos.index + 1, length); + edge = tree->Edge(pos.index); + length = pos.n; + } + + return AssertValid(top); +} + +CordRep* CordRepBtree::SubTree(size_t offset, size_t n) { + assert(n <= this->length); + assert(offset <= this->length - n); + if (ABSL_PREDICT_FALSE(n == 0)) return nullptr; + + CordRepBtree* node = this; + int height = node->height(); + Position front = node->IndexOf(offset); + CordRep* left = node->edges_[front.index]; + while (front.n + n <= left->length) { + if (--height < 0) return MakeSubstring(CordRep::Ref(left), front.n, n); + node = left->btree(); + front = node->IndexOf(front.n); + left = node->edges_[front.index]; + } + + const Position back = node->IndexBefore(front, n); + CordRep* const right = node->edges_[back.index]; + assert(back.index > front.index); + + // Get partial suffix and prefix entries. + CopyResult prefix; + CopyResult suffix; + if (height > 0) { + // Copy prefix and suffix of the boundary nodes. + prefix = left->btree()->CopySuffix(front.n); + suffix = right->btree()->CopyPrefix(back.n); + + // If there is an edge between the prefix and suffix edges, then the tree + // must remain at its previous (full) height. If we have no edges between + // prefix and suffix edges, then the tree must be as high as either the + // suffix or prefix edges (which are collapsed to their minimum heights). + if (front.index + 1 == back.index) { + height = (std::max)(prefix.height, suffix.height) + 1; + } + + // Raise prefix and suffixes to the new tree height. + for (int h = prefix.height + 1; h < height; ++h) { + prefix.edge = CordRepBtree::New(prefix.edge); + } + for (int h = suffix.height + 1; h < height; ++h) { + suffix.edge = CordRepBtree::New(suffix.edge); + } + } else { + // Leaf node, simply take substrings for prefix and suffix. + prefix = CopyResult{MakeSubstring(CordRep::Ref(left), front.n), -1}; + suffix = CopyResult{MakeSubstring(CordRep::Ref(right), 0, back.n), -1}; + } + + // Compose resulting tree. + CordRepBtree* sub = CordRepBtree::New(height); + size_t end = 0; + sub->edges_[end++] = prefix.edge; + for (CordRep* r : node->Edges(front.index + 1, back.index)) { + sub->edges_[end++] = CordRep::Ref(r); + } + sub->edges_[end++] = suffix.edge; + sub->set_end(end); + sub->length = n; + return AssertValid(sub); +} + +CordRepBtree* CordRepBtree::MergeTrees(CordRepBtree* left, + CordRepBtree* right) { + return left->height() >= right->height() ? Merge<kBack>(left, right) + : Merge<kFront>(right, left); +} + +bool CordRepBtree::IsFlat(absl::string_view* fragment) const { + if (height() == 0 && size() == 1) { + if (fragment) *fragment = Data(begin()); + return true; + } + return false; +} + +bool CordRepBtree::IsFlat(size_t offset, const size_t n, + absl::string_view* fragment) const { + assert(n <= this->length); + assert(offset <= this->length - n); + if (ABSL_PREDICT_FALSE(n == 0)) return false; + int height = this->height(); + const CordRepBtree* node = this; + for (;;) { + const Position front = node->IndexOf(offset); + const CordRep* edge = node->Edge(front.index); + if (edge->length < front.n + n) return false; + if (--height < 0) { + if (fragment) *fragment = EdgeData(edge).substr(front.n, n); + return true; + } + offset = front.n; + node = node->Edge(front.index)->btree(); + } +} + +char CordRepBtree::GetCharacter(size_t offset) const { + assert(offset < length); + const CordRepBtree* node = this; + int height = node->height(); + for (;;) { + Position front = node->IndexOf(offset); + if (--height < 0) return node->Data(front.index)[front.n]; + offset = front.n; + node = node->Edge(front.index)->btree(); + } +} + +Span<char> CordRepBtree::GetAppendBufferSlow(size_t size) { + // The inlined version in `GetAppendBuffer()` deals with all heights <= 3. + assert(height() >= 4); + assert(refcount.IsMutable()); + + // Build a stack of nodes we may potentially need to update if we find a + // non-shared FLAT with capacity at the leaf level. + const int depth = height(); + CordRepBtree* node = this; + CordRepBtree* stack[kMaxDepth]; + for (int i = 0; i < depth; ++i) { + node = node->Edge(kBack)->btree(); + if (!node->refcount.IsMutable()) return {}; + stack[i] = node; + } + + // Must be a privately owned, mutable flat. + CordRep* const edge = node->Edge(kBack); + if (!edge->refcount.IsMutable() || edge->tag < FLAT) return {}; + + // Must have capacity. + const size_t avail = edge->flat()->Capacity() - edge->length; + if (avail == 0) return {}; + + // Build span on remaining capacity. + size_t delta = (std::min)(size, avail); + Span<char> span = {edge->flat()->Data() + edge->length, delta}; + edge->length += delta; + this->length += delta; + for (int i = 0; i < depth; ++i) { + stack[i]->length += delta; + } + return span; +} + +CordRepBtree* CordRepBtree::CreateSlow(CordRep* rep) { + if (rep->IsBtree()) return rep->btree(); + + CordRepBtree* node = nullptr; + auto consume = [&node](CordRep* r, size_t offset, size_t length) { + r = MakeSubstring(r, offset, length); + if (node == nullptr) { + node = New(r); + } else { + node = CordRepBtree::AddCordRep<kBack>(node, r); + } + }; + Consume(rep, consume); + return node; +} + +CordRepBtree* CordRepBtree::AppendSlow(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(rep->IsBtree())) { + return MergeTrees(tree, rep->btree()); + } + auto consume = [&tree](CordRep* r, size_t offset, size_t length) { + r = MakeSubstring(r, offset, length); + tree = CordRepBtree::AddCordRep<kBack>(tree, r); + }; + Consume(rep, consume); + return tree; +} + +CordRepBtree* CordRepBtree::PrependSlow(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(rep->IsBtree())) { + return MergeTrees(rep->btree(), tree); + } + auto consume = [&tree](CordRep* r, size_t offset, size_t length) { + r = MakeSubstring(r, offset, length); + tree = CordRepBtree::AddCordRep<kFront>(tree, r); + }; + ReverseConsume(rep, consume); + return tree; +} + +CordRepBtree* CordRepBtree::Append(CordRepBtree* tree, absl::string_view data, + size_t extra) { + return CordRepBtree::AddData<kBack>(tree, data, extra); +} + +CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, absl::string_view data, + size_t extra) { + return CordRepBtree::AddData<kFront>(tree, data, extra); +} + +template CordRepBtree* CordRepBtree::AddCordRep<kFront>(CordRepBtree* tree, + CordRep* rep); +template CordRepBtree* CordRepBtree::AddCordRep<kBack>(CordRepBtree* tree, + CordRep* rep); +template CordRepBtree* CordRepBtree::AddData<kFront>(CordRepBtree* tree, + absl::string_view data, + size_t extra); +template CordRepBtree* CordRepBtree::AddData<kBack>(CordRepBtree* tree, + absl::string_view data, + size_t extra); + +void CordRepBtree::Rebuild(CordRepBtree** stack, CordRepBtree* tree, + bool consume) { + bool owned = consume && tree->refcount.IsOne(); + if (tree->height() == 0) { + for (CordRep* edge : tree->Edges()) { + if (!owned) edge = CordRep::Ref(edge); + size_t height = 0; + size_t length = edge->length; + CordRepBtree* node = stack[0]; + OpResult result = node->AddEdge<kBack>(true, edge, length); + while (result.action == CordRepBtree::kPopped) { + stack[height] = result.tree; + if (stack[++height] == nullptr) { + result.action = CordRepBtree::kSelf; + stack[height] = CordRepBtree::New(node, result.tree); + } else { + node = stack[height]; + result = node->AddEdge<kBack>(true, result.tree, length); + } + } + while (stack[++height] != nullptr) { + stack[height]->length += length; + } + } + } else { + for (CordRep* rep : tree->Edges()) { + Rebuild(stack, rep->btree(), owned); + } + } + if (consume) { + if (owned) { + CordRepBtree::Delete(tree); + } else { + CordRepBtree::Unref(tree); + } + } +} + +CordRepBtree* CordRepBtree::Rebuild(CordRepBtree* tree) { + // Set up initial stack with empty leaf node. + CordRepBtree* node = CordRepBtree::New(); + CordRepBtree* stack[CordRepBtree::kMaxDepth + 1] = {node}; + + // Recursively build the tree, consuming the input tree. + Rebuild(stack, tree, /* consume reference */ true); + + // Return top most node + for (CordRepBtree* parent : stack) { + if (parent == nullptr) return node; + node = parent; + } + + // Unreachable + assert(false); + return nullptr; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.h new file mode 100644 index 0000000000..bb38f0c3fe --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree.h @@ -0,0 +1,939 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ + +#include <cassert> +#include <cstdint> +#include <iosfwd> + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/optimization.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +class CordRepBtreeNavigator; + +// CordRepBtree is as the name implies a btree implementation of a Cordrep tree. +// Data is stored at the leaf level only, non leaf nodes contain down pointers +// only. Allowed types of data edges are FLAT, EXTERNAL and SUBSTRINGs of FLAT +// or EXTERNAL nodes. The implementation allows for data to be added to either +// end of the tree only, it does not provide any 'insert' logic. This has the +// benefit that we can expect good fill ratios: all nodes except the outer +// 'legs' will have 100% fill ratios for trees built using Append/Prepend +// methods. Merged trees will typically have a fill ratio well above 50% as in a +// similar fashion, one side of the merged tree will typically have a 100% fill +// ratio, and the 'open' end will average 50%. All operations are O(log(n)) or +// better, and the tree never needs balancing. +// +// All methods accepting a CordRep* or CordRepBtree* adopt a reference on that +// input unless explicitly stated otherwise. All functions returning a CordRep* +// or CordRepBtree* instance transfer a reference back to the caller. +// Simplified, callers both 'donate' and 'consume' a reference count on each +// call, simplifying the API. An example of building a tree: +// +// CordRepBtree* tree = CordRepBtree::Create(MakeFlat("Hello")); +// tree = CordRepBtree::Append(tree, MakeFlat("world")); +// +// In the above example, all inputs are consumed, making each call affecting +// `tree` reference count neutral. The returned `tree` value can be different +// from the input if the input is shared with other threads, or if the tree +// grows in height, but callers typically never have to concern themselves with +// that and trust that all methods DTRT at all times. +class CordRepBtree : public CordRep { + public: + // EdgeType identifies `front` and `back` enum values. + // Various implementations in CordRepBtree such as `Add` and `Edge` are + // generic and templated on operating on either of the boundary edges. + // For more information on the possible edges contained in a CordRepBtree + // instance see the documentation for `edges_`. + enum class EdgeType { kFront, kBack }; + + // Convenience constants into `EdgeType` + static constexpr EdgeType kFront = EdgeType::kFront; + static constexpr EdgeType kBack = EdgeType::kBack; + + // Maximum number of edges: based on experiments and performance data, we can + // pick suitable values resulting in optimum cacheline aligned values. The + // preferred values are based on 64-bit systems where we aim to align this + // class onto 64 bytes, i.e.: 6 = 64 bytes, 14 = 128 bytes, etc. + // TODO(b/192061034): experiment with alternative sizes. + static constexpr size_t kMaxCapacity = 6; + + // Reasonable maximum height of the btree. We can expect a fill ratio of at + // least 50%: trees are always expanded at the front or back. Concatenating + // trees will then typically fold at the top most node, where the lower nodes + // are at least at capacity on one side of joined inputs. At a lower fill + // rate of 4 edges per node, we have capacity for ~16 million leaf nodes. + // We will fail / abort if an application ever exceeds this height, which + // should be extremely rare (near impossible) and be an indication of an + // application error: we do not assume it reasonable for any application to + // operate correctly with such monster trees. + // Another compelling reason for the number `12` is that any contextual stack + // required for navigation or insertion requires 12 words and 12 bytes, which + // fits inside 2 cache lines with some room to spare, and is reasonable as a + // local stack variable compared to Cord's current near 400 bytes stack use. + // The maximum `height` value of a node is then `kMaxDepth - 1` as node height + // values start with a value of 0 for leaf nodes. + static constexpr int kMaxDepth = 12; + static constexpr int kMaxHeight = kMaxDepth - 1; + + // `Action` defines the action for unwinding changes done at the btree's leaf + // level that need to be propagated up to the parent node(s). Each operation + // on a node has an effect / action defined as follows: + // - kSelf + // The operation (add / update, etc) was performed directly on the node as + // the node is private to the current thread (i.e.: not shared directly or + // indirectly through a refcount > 1). Changes can be propagated directly to + // all parent nodes as all parent nodes are also then private to the current + // thread. + // - kCopied + // The operation (add / update, etc) was performed on a copy of the original + // node, as the node is (potentially) directly or indirectly shared with + // other threads. Changes need to be propagated into the parent nodes where + // the old down pointer must be unreffed and replaced with this new copy. + // Such changes to parent nodes may themselves require a copy if the parent + // node is also shared. A kCopied action can propagate all the way to the + // top node where we then must unref the `tree` input provided by the + // caller, and return the new copy. + // - kPopped + // The operation (typically add) could not be satisfied due to insufficient + // capacity in the targeted node, and a new 'leg' was created that needs to + // be added into the parent node. For example, adding a FLAT inside a leaf + // node that is at capacity will create a new leaf node containing that + // FLAT, that needs to be 'popped' up the btree. Such 'pop' actions can + // cascade up the tree if parent nodes are also at capacity. A 'Popped' + // action propagating all the way to the top of the tree will result in + // the tree becoming one level higher than the current tree through a final + // `CordRepBtree::New(tree, popped)` call, resulting in a new top node + // referencing the old tree and the new (fully popped upwards) 'leg'. + enum Action { kSelf, kCopied, kPopped }; + + // Result of an operation on a node. See the `Action` enum for details. + struct OpResult { + CordRepBtree* tree; + Action action; + }; + + // Return value of the CopyPrefix and CopySuffix methods which can + // return a node or data edge at any height inside the tree. + // A height of 0 defines the lowest (leaf) node, a height of -1 identifies + // `edge` as being a plain data node: EXTERNAL / FLAT or SUBSTRING thereof. + struct CopyResult { + CordRep* edge; + int height; + }; + + // Logical position inside a node: + // - index: index of the edge. + // - n: size or offset value depending on context. + struct Position { + size_t index; + size_t n; + }; + + // Creates a btree from the given input. Adopts a ref of `rep`. + // If the input `rep` is itself a btree, i.e., `IsBtree()`, then this + // function immediately returns `rep->btree()`. If the input is a valid data + // edge (see IsDataEdge()), then a new leaf node is returned containing `rep` + // as the sole data edge. Else, the input is assumed to be a (legacy) concat + // tree, and the input is consumed and transformed into a btree(). + static CordRepBtree* Create(CordRep* rep); + + // Destroys the provided tree. Should only be called by cord internal API's, + // typically after a ref_count.Decrement() on the last reference count. + static void Destroy(CordRepBtree* tree); + + // Use CordRep::Unref() as we overload for absl::Span<CordRep* const>. + using CordRep::Unref; + + // Unrefs all edges in `edges` which are assumed to be 'likely one'. + static void Unref(absl::Span<CordRep* const> edges); + + // Appends / Prepends an existing CordRep instance to this tree. + // The below methods accept three types of input: + // 1) `rep` is a data node (See `IsDataNode` for valid data edges). + // `rep` is appended or prepended to this tree 'as is'. + // 2) `rep` is a BTREE. + // `rep` is merged into `tree` respecting the Append/Prepend order. + // 3) `rep` is some other (legacy) type. + // `rep` is converted in place and added to `tree` + // Requires `tree` and `rep` to be not null. + static CordRepBtree* Append(CordRepBtree* tree, CordRep* rep); + static CordRepBtree* Prepend(CordRepBtree* tree, CordRep* rep); + + // Append/Prepend the data in `data` to this tree. + // The `extra` parameter defines how much extra capacity should be allocated + // for any additional FLAT being allocated. This is an optimization hint from + // the caller. For example, a caller may need to add 2 string_views of data + // "abc" and "defghi" which are not consecutive. The caller can in this case + // invoke `AddData(tree, "abc", 6)`, and any newly added flat is allocated + // where possible with at least 6 bytes of extra capacity beyond `length`. + // This helps avoiding data getting fragmented over multiple flats. + // There is no limit on the size of `data`. If `data` can not be stored inside + // a single flat, then the function will iteratively add flats until all data + // has been consumed and appended or prepended to the tree. + static CordRepBtree* Append(CordRepBtree* tree, string_view data, + size_t extra = 0); + static CordRepBtree* Prepend(CordRepBtree* tree, string_view data, + size_t extra = 0); + + // Returns a new tree, containing `n` bytes of data from this instance + // starting at offset `offset`. Where possible, the returned tree shares + // (re-uses) data edges and nodes with this instance to minimize the + // combined memory footprint of both trees. + // Requires `offset + n <= length`. Returns `nullptr` if `n` is zero. + CordRep* SubTree(size_t offset, size_t n); + + // Removes `n` trailing bytes from `tree`, and returns the resulting tree + // or data edge. Returns `tree` if n is zero, and nullptr if n == length. + // This function is logically identical to: + // result = tree->SubTree(0, tree->length - n); + // Unref(tree); + // return result; + // However, the actual implementation will as much as possible perform 'in + // place' modifications on the tree on all nodes and edges that are mutable. + // For example, in a fully privately owned tree with the last edge being a + // flat of length 12, RemoveSuffix(1) will simply set the length of that data + // edge to 11, and reduce the length of all nodes on the edge path by 1. + static CordRep* RemoveSuffix(CordRepBtree* tree, size_t n); + + // Returns the character at the given offset. + char GetCharacter(size_t offset) const; + + // Returns true if this node holds a single data edge, and if so, sets + // `fragment` to reference the contained data. `fragment` is an optional + // output parameter and allowed to be null. + bool IsFlat(absl::string_view* fragment) const; + + // Returns true if the data of `n` bytes starting at offset `offset` + // is contained in a single data edge, and if so, sets fragment to reference + // the contained data. `fragment` is an optional output parameter and allowed + // to be null. + bool IsFlat(size_t offset, size_t n, absl::string_view* fragment) const; + + // Returns a span (mutable range of bytes) of up to `size` bytes into the + // last FLAT data edge inside this tree under the following conditions: + // - none of the nodes down into the FLAT node are shared. + // - the last data edge in this tree is a non-shared FLAT. + // - the referenced FLAT has additional capacity available. + // If all these conditions are met, a non-empty span is returned, and the + // length of the flat node and involved tree nodes have been increased by + // `span.length()`. The caller is responsible for immediately assigning values + // to all uninitialized data reference by the returned span. + // Requires `this->refcount.IsMutable()`: this function forces the + // caller to do this fast path check on the top level node, as this is the + // most commonly shared node of a cord tree. + Span<char> GetAppendBuffer(size_t size); + + // Returns the `height` of the tree. The height of a tree is limited to + // kMaxHeight. `height` is implemented as an `int` as in some places we + // use negative (-1) values for 'data edges'. + int height() const { return static_cast<int>(storage[0]); } + + // Properties: begin, back, end, front/back boundary indexes. + size_t begin() const { return static_cast<size_t>(storage[1]); } + size_t back() const { return static_cast<size_t>(storage[2]) - 1; } + size_t end() const { return static_cast<size_t>(storage[2]); } + size_t index(EdgeType edge) const { + return edge == kFront ? begin() : back(); + } + + // Properties: size and capacity. + // `capacity` contains the current capacity of this instance, where + // `kMaxCapacity` contains the maximum capacity of a btree node. + // For now, `capacity` and `kMaxCapacity` return the same value, but this may + // change in the future if we see benefit in dynamically sizing 'small' nodes + // to 'large' nodes for large data trees. + size_t size() const { return end() - begin(); } + size_t capacity() const { return kMaxCapacity; } + + // Edge access + inline CordRep* Edge(size_t index) const; + inline CordRep* Edge(EdgeType edge_type) const; + inline absl::Span<CordRep* const> Edges() const; + inline absl::Span<CordRep* const> Edges(size_t begin, size_t end) const; + + // Returns reference to the data edge at `index`. + // Requires this instance to be a leaf node, and `index` to be valid index. + inline absl::string_view Data(size_t index) const; + + static const char* EdgeDataPtr(const CordRep* r); + static absl::string_view EdgeData(const CordRep* r); + + // Returns true if the provided rep is a FLAT, EXTERNAL or a SUBSTRING node + // holding a FLAT or EXTERNAL child rep. + static bool IsDataEdge(const CordRep* rep); + + // Diagnostics: returns true if `tree` is valid and internally consistent. + // If `shallow` is false, then the provided top level node and all child nodes + // below it are recursively checked. If `shallow` is true, only the provided + // node in `tree` and the cumulative length, type and height of the direct + // child nodes of `tree` are checked. The value of `shallow` is ignored if the + // internal `cord_btree_exhaustive_validation` diagnostics variable is true, + // in which case the performed validations works as if `shallow` were false. + // This function is intended for debugging and testing purposes only. + static bool IsValid(const CordRepBtree* tree, bool shallow = false); + + // Diagnostics: asserts that the provided tree is valid. + // `AssertValid()` performs a shallow validation by default. `shallow` can be + // set to false in which case an exhaustive validation is performed. This + // function is implemented in terms of calling `IsValid()` and asserting the + // return value to be true. See `IsValid()` for more information. + // This function is intended for debugging and testing purposes only. + static CordRepBtree* AssertValid(CordRepBtree* tree, bool shallow = true); + static const CordRepBtree* AssertValid(const CordRepBtree* tree, + bool shallow = true); + + // Diagnostics: dump the contents of this tree to `stream`. + // This function is intended for debugging and testing purposes only. + static void Dump(const CordRep* rep, std::ostream& stream); + static void Dump(const CordRep* rep, absl::string_view label, + std::ostream& stream); + static void Dump(const CordRep* rep, absl::string_view label, + bool include_contents, std::ostream& stream); + + // Adds the edge `edge` to this node if possible. `owned` indicates if the + // current node is potentially shared or not with other threads. Returns: + // - {kSelf, <this>} + // The edge was directly added to this node. + // - {kCopied, <node>} + // The edge was added to a copy of this node. + // - {kPopped, New(edge, height())} + // A new leg with the edge was created as this node has no extra capacity. + template <EdgeType edge_type> + inline OpResult AddEdge(bool owned, CordRep* edge, size_t delta); + + // Replaces the front or back edge with the provided new edge. Returns: + // - {kSelf, <this>} + // The edge was directly set in this node. The old edge is unreffed. + // - {kCopied, <node>} + // A copy of this node was created with the new edge value. + // In both cases, the function adopts a reference on `edge`. + template <EdgeType edge_type> + OpResult SetEdge(bool owned, CordRep* edge, size_t delta); + + // Creates a new empty node at the specified height. + static CordRepBtree* New(int height = 0); + + // Creates a new node containing `rep`, with the height being computed + // automatically based on the type of `rep`. + static CordRepBtree* New(CordRep* rep); + + // Creates a new node containing both `front` and `back` at height + // `front.height() + 1`. Requires `back.height() == front.height()`. + static CordRepBtree* New(CordRepBtree* front, CordRepBtree* back); + + // Creates a fully balanced tree from the provided tree by rebuilding a new + // tree from all data edges in the input. This function is automatically + // invoked internally when the tree exceeds the maximum height. + static CordRepBtree* Rebuild(CordRepBtree* tree); + + private: + CordRepBtree() = default; + ~CordRepBtree() = default; + + // Initializes the main properties `tag`, `begin`, `end`, `height`. + inline void InitInstance(int height, size_t begin = 0, size_t end = 0); + + // Direct property access begin / end + void set_begin(size_t begin) { storage[1] = static_cast<uint8_t>(begin); } + void set_end(size_t end) { storage[2] = static_cast<uint8_t>(end); } + + // Decreases the value of `begin` by `n`, and returns the new value. Notice + // how this returns the new value unlike atomic::fetch_add which returns the + // old value. This is because this is used to prepend edges at 'begin - 1'. + size_t sub_fetch_begin(size_t n) { + storage[1] -= static_cast<uint8_t>(n); + return storage[1]; + } + + // Increases the value of `end` by `n`, and returns the previous value. This + // function is typically used to append edges at 'end'. + size_t fetch_add_end(size_t n) { + const uint8_t current = storage[2]; + storage[2] = static_cast<uint8_t>(current + n); + return current; + } + + // Returns the index of the last edge starting on, or before `offset`, with + // `n` containing the relative offset of `offset` inside that edge. + // Requires `offset` < length. + Position IndexOf(size_t offset) const; + + // Returns the index of the last edge starting before `offset`, with `n` + // containing the relative offset of `offset` inside that edge. + // This function is useful to find the edges for some span of bytes ending at + // `offset` (i.e., `n` bytes). For example: + // + // Position pos = IndexBefore(n) + // edges = Edges(begin(), pos.index) // All full edges (may be empty) + // last = Sub(Edge(pos.index), 0, pos.n) // Last partial edge (may be empty) + // + // Requires 0 < `offset` <= length. + Position IndexBefore(size_t offset) const; + + // Returns the index of the edge ending at (or on) length `length`, and the + // number of bytes inside that edge up to `length`. For example, if we have a + // Node with 2 edges, one of 10 and one of 20 long, then IndexOfLength(27) + // will return {1, 17}, and IndexOfLength(10) will return {0, 10}. + Position IndexOfLength(size_t n) const; + + // Identical to the above function except starting from the position `front`. + // This function is equivalent to `IndexBefore(front.n + offset)`, with + // the difference that this function is optimized to start at `front.index`. + Position IndexBefore(Position front, size_t offset) const; + + // Returns the index of the edge directly beyond the edge containing offset + // `offset`, with `n` containing the distance of that edge from `offset`. + // This function is useful for iteratively finding suffix nodes and remaining + // partial bytes in left-most suffix nodes as for example in CopySuffix. + // Requires `offset` < length. + Position IndexBeyond(size_t offset) const; + + // Destruction + static void DestroyLeaf(CordRepBtree* tree, size_t begin, size_t end); + static void DestroyNonLeaf(CordRepBtree* tree, size_t begin, size_t end); + static void DestroyTree(CordRepBtree* tree, size_t begin, size_t end); + static void Delete(CordRepBtree* tree) { delete tree; } + + // Creates a new leaf node containing as much data as possible from `data`. + // The data is added either forwards or reversed depending on `edge_type`. + // Callers must check the length of the returned node to determine if all data + // was copied or not. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template <EdgeType edge_type> + static CordRepBtree* NewLeaf(absl::string_view data, size_t extra); + + // Creates a raw copy of this Btree node, copying all properties, but + // without adding any references to existing edges. + CordRepBtree* CopyRaw() const; + + // Creates a full copy of this Btree node, adding a reference on all edges. + CordRepBtree* Copy() const; + + // Creates a partial copy of this Btree node, copying all edges up to `end`, + // adding a reference on each copied edge, and sets the length of the newly + // created copy to `new_length`. + CordRepBtree* CopyBeginTo(size_t end, size_t new_length) const; + + // Returns a tree containing the edges [tree->begin(), end) and length + // of `new_length`. This method consumes a reference on the provided + // tree, and logically performs the following operation: + // result = tree->CopyBeginTo(end, new_length); + // CordRep::Unref(tree); + // return result; + static CordRepBtree* ConsumeBeginTo(CordRepBtree* tree, size_t end, + size_t new_length); + + // Creates a partial copy of this Btree node, copying all edges starting at + // `begin`, adding a reference on each copied edge, and sets the length of + // the newly created copy to `new_length`. + CordRepBtree* CopyToEndFrom(size_t begin, size_t new_length) const; + + // Extracts and returns the front edge from the provided tree. + // This method consumes a reference on the provided tree, and logically + // performs the following operation: + // edge = CordRep::Ref(tree->Edge(kFront)); + // CordRep::Unref(tree); + // return edge; + static CordRep* ExtractFront(CordRepBtree* tree); + + // Returns a tree containing the result of appending `right` to `left`. + static CordRepBtree* MergeTrees(CordRepBtree* left, CordRepBtree* right); + + // Fallback functions for `Create()`, `Append()` and `Prepend()` which + // deal with legacy / non conforming input, i.e.: CONCAT trees. + static CordRepBtree* CreateSlow(CordRep* rep); + static CordRepBtree* AppendSlow(CordRepBtree*, CordRep* rep); + static CordRepBtree* PrependSlow(CordRepBtree*, CordRep* rep); + + // Recursively rebuilds `tree` into `stack`. If 'consume` is set to true, the + // function will consume a reference on `tree`. `stack` is a null terminated + // array containing the new tree's state, with the current leaf node at + // stack[0], and parent nodes above that, or null for 'top of tree'. + static void Rebuild(CordRepBtree** stack, CordRepBtree* tree, bool consume); + + // Aligns existing edges to start at index 0, to allow for a new edge to be + // added to the back of the current edges. + inline void AlignBegin(); + + // Aligns existing edges to end at `capacity`, to allow for a new edge to be + // added in front of the current edges. + inline void AlignEnd(); + + // Adds the provided edge to this node. + // Requires this node to have capacity for the edge. Realigns / moves + // existing edges as needed to prepend or append the new edge. + template <EdgeType edge_type> + inline void Add(CordRep* rep); + + // Adds the provided edges to this node. + // Requires this node to have capacity for the edges. Realigns / moves + // existing edges as needed to prepend or append the new edges. + template <EdgeType edge_type> + inline void Add(absl::Span<CordRep* const>); + + // Adds data from `data` to this node until either all data has been consumed, + // or there is no more capacity for additional flat nodes inside this node. + // Requires the current node to be a leaf node, data to be non empty, and the + // current node to have capacity for at least one more data edge. + // Returns any remaining data from `data` that was not added, which is + // depending on the edge type (front / back) either the remaining prefix of + // suffix of the input. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template <EdgeType edge_type> + absl::string_view AddData(absl::string_view data, size_t extra); + + // Replace the front or back edge with the provided value. + // Adopts a reference on `edge` and unrefs the old edge. + template <EdgeType edge_type> + inline void SetEdge(CordRep* edge); + + // Returns a partial copy of the current tree containing the first `n` bytes + // of data. `CopyResult` contains both the resulting edge and its height. The + // resulting tree may be less high than the current tree, or even be a single + // matching data edge if `allow_folding` is set to true. + // For example, if `n == 1`, then the result will be the single data edge, and + // height will be set to -1 (one below the owning leaf node). If n == 0, this + // function returns null. Requires `n <= length` + CopyResult CopyPrefix(size_t n, bool allow_folding = true); + + // Returns a partial copy of the current tree containing all data starting + // after `offset`. `CopyResult` contains both the resulting edge and its + // height. The resulting tree may be less high than the current tree, or even + // be a single matching data edge. For example, if `n == length - 1`, then the + // result will be a single data edge, and height will be set to -1 (one below + // the owning leaf node). + // Requires `offset < length` + CopyResult CopySuffix(size_t offset); + + // Returns a OpResult value of {this, kSelf} or {Copy(), kCopied} + // depending on the value of `owned`. + inline OpResult ToOpResult(bool owned); + + // Adds `rep` to the specified tree, returning the modified tree. + template <EdgeType edge_type> + static CordRepBtree* AddCordRep(CordRepBtree* tree, CordRep* rep); + + // Adds `data` to the specified tree, returning the modified tree. + // See the `Append/Prepend` function for the meaning and purpose of `extra`. + template <EdgeType edge_type> + static CordRepBtree* AddData(CordRepBtree* tree, absl::string_view data, + size_t extra = 0); + + // Merges `src` into `dst` with `src` being added either before (kFront) or + // after (kBack) `dst`. Requires the height of `dst` to be greater than or + // equal to the height of `src`. + template <EdgeType edge_type> + static CordRepBtree* Merge(CordRepBtree* dst, CordRepBtree* src); + + // Fallback version of GetAppendBuffer for large trees: GetAppendBuffer() + // implements an inlined version for trees of limited height (3 levels), + // GetAppendBufferSlow implements the logic for large trees. + Span<char> GetAppendBufferSlow(size_t size); + + // `edges_` contains all edges starting from this instance. + // These are explicitly `child` edges only, a cord btree (or any cord tree in + // that respect) does not store `parent` pointers anywhere: multiple trees / + // parents can reference the same shared child edge. The type of these edges + // depends on the height of the node. `Leaf nodes` (height == 0) contain `data + // edges` (external or flat nodes, or sub-strings thereof). All other nodes + // (height > 0) contain pointers to BTREE nodes with a height of `height - 1`. + CordRep* edges_[kMaxCapacity]; + + friend class CordRepBtreeTestPeer; + friend class CordRepBtreeNavigator; +}; + +inline CordRepBtree* CordRep::btree() { + assert(IsBtree()); + return static_cast<CordRepBtree*>(this); +} + +inline const CordRepBtree* CordRep::btree() const { + assert(IsBtree()); + return static_cast<const CordRepBtree*>(this); +} + +inline void CordRepBtree::InitInstance(int height, size_t begin, size_t end) { + tag = BTREE; + storage[0] = static_cast<uint8_t>(height); + storage[1] = static_cast<uint8_t>(begin); + storage[2] = static_cast<uint8_t>(end); +} + +inline CordRep* CordRepBtree::Edge(size_t index) const { + assert(index >= begin()); + assert(index < end()); + return edges_[index]; +} + +inline CordRep* CordRepBtree::Edge(EdgeType edge_type) const { + return edges_[edge_type == kFront ? begin() : back()]; +} + +inline absl::Span<CordRep* const> CordRepBtree::Edges() const { + return {edges_ + begin(), size()}; +} + +inline absl::Span<CordRep* const> CordRepBtree::Edges(size_t begin, + size_t end) const { + assert(begin <= end); + assert(begin >= this->begin()); + assert(end <= this->end()); + return {edges_ + begin, static_cast<size_t>(end - begin)}; +} + +inline const char* CordRepBtree::EdgeDataPtr(const CordRep* r) { + assert(IsDataEdge(r)); + size_t offset = 0; + if (r->tag == SUBSTRING) { + offset = r->substring()->start; + r = r->substring()->child; + } + return (r->tag >= FLAT ? r->flat()->Data() : r->external()->base) + offset; +} + +inline absl::string_view CordRepBtree::EdgeData(const CordRep* r) { + return absl::string_view(EdgeDataPtr(r), r->length); +} + +inline absl::string_view CordRepBtree::Data(size_t index) const { + assert(height() == 0); + return EdgeData(Edge(index)); +} + +inline bool CordRepBtree::IsDataEdge(const CordRep* rep) { + // The fast path is that `rep` is an EXTERNAL or FLAT node, making the below + // if a single, well predicted branch. We then repeat the FLAT or EXTERNAL + // check in the slow path the SUBSTRING check to optimize for the hot path. + if (rep->tag == EXTERNAL || rep->tag >= FLAT) return true; + if (rep->tag == SUBSTRING) rep = rep->substring()->child; + return rep->tag == EXTERNAL || rep->tag >= FLAT; +} + +inline CordRepBtree* CordRepBtree::New(int height) { + CordRepBtree* tree = new CordRepBtree; + tree->length = 0; + tree->InitInstance(height); + return tree; +} + +inline CordRepBtree* CordRepBtree::New(CordRep* rep) { + CordRepBtree* tree = new CordRepBtree; + int height = rep->IsBtree() ? rep->btree()->height() + 1 : 0; + tree->length = rep->length; + tree->InitInstance(height, /*begin=*/0, /*end=*/1); + tree->edges_[0] = rep; + return tree; +} + +inline CordRepBtree* CordRepBtree::New(CordRepBtree* front, + CordRepBtree* back) { + assert(front->height() == back->height()); + CordRepBtree* tree = new CordRepBtree; + tree->length = front->length + back->length; + tree->InitInstance(front->height() + 1, /*begin=*/0, /*end=*/2); + tree->edges_[0] = front; + tree->edges_[1] = back; + return tree; +} + +inline void CordRepBtree::DestroyTree(CordRepBtree* tree, size_t begin, + size_t end) { + if (tree->height() == 0) { + DestroyLeaf(tree, begin, end); + } else { + DestroyNonLeaf(tree, begin, end); + } +} + +inline void CordRepBtree::Destroy(CordRepBtree* tree) { + DestroyTree(tree, tree->begin(), tree->end()); +} + +inline void CordRepBtree::Unref(absl::Span<CordRep* const> edges) { + for (CordRep* edge : edges) { + if (ABSL_PREDICT_FALSE(!edge->refcount.Decrement())) { + CordRep::Destroy(edge); + } + } +} + +inline CordRepBtree* CordRepBtree::CopyRaw() const { + auto* tree = static_cast<CordRepBtree*>(::operator new(sizeof(CordRepBtree))); + memcpy(static_cast<void*>(tree), this, sizeof(CordRepBtree)); + new (&tree->refcount) RefcountAndFlags; + return tree; +} + +inline CordRepBtree* CordRepBtree::Copy() const { + CordRepBtree* tree = CopyRaw(); + for (CordRep* rep : Edges()) CordRep::Ref(rep); + return tree; +} + +inline CordRepBtree* CordRepBtree::CopyToEndFrom(size_t begin, + size_t new_length) const { + assert(begin >= this->begin()); + assert(begin <= this->end()); + CordRepBtree* tree = CopyRaw(); + tree->length = new_length; + tree->set_begin(begin); + for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); + return tree; +} + +inline CordRepBtree* CordRepBtree::CopyBeginTo(size_t end, + size_t new_length) const { + assert(end <= capacity()); + assert(end >= this->begin()); + CordRepBtree* tree = CopyRaw(); + tree->length = new_length; + tree->set_end(end); + for (CordRep* edge : tree->Edges()) CordRep::Ref(edge); + return tree; +} + +inline void CordRepBtree::AlignBegin() { + // The below code itself does not need to be fast as typically we have + // mono-directional append/prepend calls, and `begin` / `end` are typically + // adjusted no more than once. But we want to avoid potential register clobber + // effects, making the compiler emit register save/store/spills, and minimize + // the size of code. + const size_t delta = begin(); + if (ABSL_PREDICT_FALSE(delta != 0)) { + const size_t new_end = end() - delta; + set_begin(0); + set_end(new_end); + // TODO(mvels): we can write this using 2 loads / 2 stores depending on + // total size for the kMaxCapacity = 6 case. I.e., we can branch (switch) on + // size, and then do overlapping load/store of up to 4 pointers (inlined as + // XMM, YMM or ZMM load/store) and up to 2 pointers (XMM / YMM), which is a) + // compact and b) not clobbering any registers. + ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity); +#ifdef __clang__ +#pragma unroll 1 +#endif + for (size_t i = 0; i < new_end; ++i) { + edges_[i] = edges_[i + delta]; + } + } +} + +inline void CordRepBtree::AlignEnd() { + // See comments in `AlignBegin` for motivation on the hand-rolled for loops. + const size_t delta = capacity() - end(); + if (delta != 0) { + const size_t new_begin = begin() + delta; + const size_t new_end = end() + delta; + set_begin(new_begin); + set_end(new_end); + ABSL_INTERNAL_ASSUME(new_end <= kMaxCapacity); +#ifdef __clang__ +#pragma unroll 1 +#endif + for (size_t i = new_end - 1; i >= new_begin; --i) { + edges_[i] = edges_[i - delta]; + } + } +} + +template <> +inline void CordRepBtree::Add<CordRepBtree::kBack>(CordRep* rep) { + AlignBegin(); + edges_[fetch_add_end(1)] = rep; +} + +template <> +inline void CordRepBtree::Add<CordRepBtree::kBack>( + absl::Span<CordRep* const> edges) { + AlignBegin(); + size_t new_end = end(); + for (CordRep* edge : edges) edges_[new_end++] = edge; + set_end(new_end); +} + +template <> +inline void CordRepBtree::Add<CordRepBtree::kFront>(CordRep* rep) { + AlignEnd(); + edges_[sub_fetch_begin(1)] = rep; +} + +template <> +inline void CordRepBtree::Add<CordRepBtree::kFront>( + absl::Span<CordRep* const> edges) { + AlignEnd(); + size_t new_begin = begin() - edges.size(); + set_begin(new_begin); + for (CordRep* edge : edges) edges_[new_begin++] = edge; +} + +template <CordRepBtree::EdgeType edge_type> +inline void CordRepBtree::SetEdge(CordRep* edge) { + const int idx = edge_type == kFront ? begin() : back(); + CordRep::Unref(edges_[idx]); + edges_[idx] = edge; +} + +inline CordRepBtree::OpResult CordRepBtree::ToOpResult(bool owned) { + return owned ? OpResult{this, kSelf} : OpResult{Copy(), kCopied}; +} + +inline CordRepBtree::Position CordRepBtree::IndexOf(size_t offset) const { + assert(offset < length); + size_t index = begin(); + while (offset >= edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBefore(size_t offset) const { + assert(offset > 0); + assert(offset <= length); + size_t index = begin(); + while (offset > edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBefore(Position front, + size_t offset) const { + size_t index = front.index; + offset = offset + front.n; + while (offset > edges_[index]->length) offset -= edges_[index++]->length; + return {index, offset}; +} + +inline CordRepBtree::Position CordRepBtree::IndexOfLength(size_t n) const { + assert(n <= length); + size_t index = back(); + size_t strip = length - n; + while (strip >= edges_[index]->length) strip -= edges_[index--]->length; + return {index, edges_[index]->length - strip}; +} + +inline CordRepBtree::Position CordRepBtree::IndexBeyond( + const size_t offset) const { + // We need to find the edge which `starting offset` is beyond (>=)`offset`. + // For this we can't use the `offset -= length` logic of IndexOf. Instead, we + // track the offset of the `current edge` in `off`, which we increase as we + // iterate over the edges until we find the matching edge. + size_t off = 0; + size_t index = begin(); + while (offset > off) off += edges_[index++]->length; + return {index, off - offset}; +} + +inline CordRepBtree* CordRepBtree::Create(CordRep* rep) { + if (IsDataEdge(rep)) return New(rep); + return CreateSlow(rep); +} + +inline Span<char> CordRepBtree::GetAppendBuffer(size_t size) { + assert(refcount.IsMutable()); + CordRepBtree* tree = this; + const int height = this->height(); + CordRepBtree* n1 = tree; + CordRepBtree* n2 = tree; + CordRepBtree* n3 = tree; + switch (height) { + case 3: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsMutable()) return {}; + n2 = tree; + ABSL_FALLTHROUGH_INTENDED; + case 2: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsMutable()) return {}; + n1 = tree; + ABSL_FALLTHROUGH_INTENDED; + case 1: + tree = tree->Edge(kBack)->btree(); + if (!tree->refcount.IsMutable()) return {}; + ABSL_FALLTHROUGH_INTENDED; + case 0: + CordRep* edge = tree->Edge(kBack); + if (!edge->refcount.IsMutable()) return {}; + if (edge->tag < FLAT) return {}; + size_t avail = edge->flat()->Capacity() - edge->length; + if (avail == 0) return {}; + size_t delta = (std::min)(size, avail); + Span<char> span = {edge->flat()->Data() + edge->length, delta}; + edge->length += delta; + switch (height) { + case 3: + n3->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 2: + n2->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 1: + n1->length += delta; + ABSL_FALLTHROUGH_INTENDED; + case 0: + tree->length += delta; + return span; + } + break; + } + return GetAppendBufferSlow(size); +} + +extern template CordRepBtree* CordRepBtree::AddCordRep<CordRepBtree::kBack>( + CordRepBtree* tree, CordRep* rep); + +extern template CordRepBtree* CordRepBtree::AddCordRep<CordRepBtree::kFront>( + CordRepBtree* tree, CordRep* rep); + +inline CordRepBtree* CordRepBtree::Append(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) { + return CordRepBtree::AddCordRep<kBack>(tree, rep); + } + return AppendSlow(tree, rep); +} + +inline CordRepBtree* CordRepBtree::Prepend(CordRepBtree* tree, CordRep* rep) { + if (ABSL_PREDICT_TRUE(IsDataEdge(rep))) { + return CordRepBtree::AddCordRep<kFront>(tree, rep); + } + return PrependSlow(tree, rep); +} + +#ifdef NDEBUG + +inline CordRepBtree* CordRepBtree::AssertValid(CordRepBtree* tree, + bool /* shallow */) { + return tree; +} + +inline const CordRepBtree* CordRepBtree::AssertValid(const CordRepBtree* tree, + bool /* shallow */) { + return tree; +} + +#endif + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.cc new file mode 100644 index 0000000000..d1f9995d00 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.cc @@ -0,0 +1,185 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_btree_navigator.h" + +#include <cassert> + +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +using ReadResult = CordRepBtreeNavigator::ReadResult; + +namespace { + +// Returns a `CordRepSubstring` from `rep` starting at `offset` of size `n`. +// If `rep` is already a `CordRepSubstring` instance, an adjusted instance is +// created based on the old offset and new offset. +// Adopts a reference on `rep`. Rep must be a valid data edge. Returns +// nullptr if `n == 0`, `rep` if `n == rep->length`. +// Requires `offset < rep->length` and `offset + n <= rep->length`. +// TODO(192061034): move to utility library in internal and optimize for small +// substrings of larger reps. +inline CordRep* Substring(CordRep* rep, size_t offset, size_t n) { + assert(n <= rep->length); + assert(offset < rep->length); + assert(offset <= rep->length - n); + assert(CordRepBtree::IsDataEdge(rep)); + + if (n == 0) return nullptr; + if (n == rep->length) return CordRep::Ref(rep); + + if (rep->tag == SUBSTRING) { + offset += rep->substring()->start; + rep = rep->substring()->child; + } + + CordRepSubstring* substring = new CordRepSubstring(); + substring->length = n; + substring->tag = SUBSTRING; + substring->start = offset; + substring->child = CordRep::Ref(rep); + return substring; +} + +inline CordRep* Substring(CordRep* rep, size_t offset) { + return Substring(rep, offset, rep->length - offset); +} + +} // namespace + +CordRepBtreeNavigator::Position CordRepBtreeNavigator::Skip(size_t n) { + int height = 0; + size_t index = index_[0]; + CordRepBtree* node = node_[0]; + CordRep* edge = node->Edge(index); + + // Overall logic: Find an edge of at least the length we need to skip. + // We consume all edges which are smaller (i.e., must be 100% skipped). + // If we exhausted all edges on the current level, we move one level + // up the tree, and repeat until we either find the edge, or until we hit + // the top of the tree meaning the skip exceeds tree->length. + while (n >= edge->length) { + n -= edge->length; + while (++index == node->end()) { + if (++height > height_) return {nullptr, n}; + node = node_[height]; + index = index_[height]; + } + edge = node->Edge(index); + } + + // If we moved up the tree, descend down to the leaf level, consuming all + // edges that must be skipped. + while (height > 0) { + node = edge->btree(); + index_[height] = index; + node_[--height] = node; + index = node->begin(); + edge = node->Edge(index); + while (n >= edge->length) { + n -= edge->length; + ++index; + assert(index != node->end()); + edge = node->Edge(index); + } + } + index_[0] = index; + return {edge, n}; +} + +ReadResult CordRepBtreeNavigator::Read(size_t edge_offset, size_t n) { + int height = 0; + size_t length = edge_offset + n; + size_t index = index_[0]; + CordRepBtree* node = node_[0]; + CordRep* edge = node->Edge(index); + assert(edge_offset < edge->length); + + if (length < edge->length) { + return {Substring(edge, edge_offset, n), length}; + } + + // Similar to 'Skip', we consume all edges that are inside the 'length' of + // data that needs to be read. If we exhaust the current level, we move one + // level up the tree and repeat until we hit the final edge that must be + // (partially) read. We consume all edges into `subtree`. + CordRepBtree* subtree = CordRepBtree::New(Substring(edge, edge_offset)); + size_t subtree_end = 1; + do { + length -= edge->length; + while (++index == node->end()) { + index_[height] = index; + if (++height > height_) { + subtree->set_end(subtree_end); + if (length == 0) return {subtree, 0}; + CordRep::Unref(subtree); + return {nullptr, length}; + } + if (length != 0) { + subtree->set_end(subtree_end); + subtree = CordRepBtree::New(subtree); + subtree_end = 1; + } + node = node_[height]; + index = index_[height]; + } + edge = node->Edge(index); + if (length >= edge->length) { + subtree->length += edge->length; + subtree->edges_[subtree_end++] = CordRep::Ref(edge); + } + } while (length >= edge->length); + CordRepBtree* tree = subtree; + subtree->length += length; + + // If we moved up the tree, descend down to the leaf level, consuming all + // edges that must be read, adding 'down' nodes to `subtree`. + while (height > 0) { + node = edge->btree(); + index_[height] = index; + node_[--height] = node; + index = node->begin(); + edge = node->Edge(index); + + if (length != 0) { + CordRepBtree* right = CordRepBtree::New(height); + right->length = length; + subtree->edges_[subtree_end++] = right; + subtree->set_end(subtree_end); + subtree = right; + subtree_end = 0; + while (length >= edge->length) { + subtree->edges_[subtree_end++] = CordRep::Ref(edge); + length -= edge->length; + edge = node->Edge(++index); + } + } + } + // Add any (partial) edge still remaining at the leaf level. + if (length != 0) { + subtree->edges_[subtree_end++] = Substring(edge, 0, length); + } + subtree->set_end(subtree_end); + index_[0] = index; + return {tree, length}; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h new file mode 100644 index 0000000000..971b92eda6 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator.h @@ -0,0 +1,265 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ + +#include <cassert> +#include <iostream> + +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepBtreeNavigator is a bi-directional navigator allowing callers to +// navigate all the (leaf) data edges in a CordRepBtree instance. +// +// A CordRepBtreeNavigator instance is by default empty. Callers initialize a +// navigator instance by calling one of `InitFirst()`, `InitLast()` or +// `InitOffset()`, which establishes a current position. Callers can then +// navigate using the `Next`, `Previous`, `Skip` and `Seek` methods. +// +// The navigator instance does not take or adopt a reference on the provided +// `tree` on any of the initialization calls. Callers are responsible for +// guaranteeing the lifecycle of the provided tree. A navigator instance can +// be reset to the empty state by calling `Reset`. +// +// A navigator only keeps positional state on the 'current data edge', it does +// explicitly not keep any 'offset' state. The class does accept and return +// offsets in the `Read()`, `Skip()` and 'Seek()` methods as these would +// otherwise put a big burden on callers. Callers are expected to maintain +// (returned) offset info if they require such granular state. +class CordRepBtreeNavigator { + public: + // The logical position as returned by the Seek() and Skip() functions. + // Returns the current leaf edge for the desired seek or skip position and + // the offset of that position inside that edge. + struct Position { + CordRep* edge; + size_t offset; + }; + + // The read result as returned by the Read() function. + // `tree` contains the resulting tree which is identical to the result + // of calling CordRepBtree::SubTree(...) on the tree being navigated. + // `n` contains the number of bytes used from the last navigated to + // edge of the tree. + struct ReadResult { + CordRep* tree; + size_t n; + }; + + // Returns true if this instance is not empty. + explicit operator bool() const; + + // Returns the tree for this instance or nullptr if empty. + CordRepBtree* btree() const; + + // Returns the data edge of the current position. + // Requires this instance to not be empty. + CordRep* Current() const; + + // Resets this navigator to `tree`, returning the first data edge in the tree. + CordRep* InitFirst(CordRepBtree* tree); + + // Resets this navigator to `tree`, returning the last data edge in the tree. + CordRep* InitLast(CordRepBtree* tree); + + // Resets this navigator to `tree` returning the data edge at position + // `offset` and the relative offset of `offset` into that data edge. + // Returns `Position.edge = nullptr` if the provided offset is greater + // than or equal to the length of the tree, in which case the state of + // the navigator instance remains unchanged. + Position InitOffset(CordRepBtree* tree, size_t offset); + + // Navigates to the next data edge. + // Returns the next data edge or nullptr if there is no next data edge, in + // which case the current position remains unchanged. + CordRep* Next(); + + // Navigates to the previous data edge. + // Returns the previous data edge or nullptr if there is no previous data + // edge, in which case the current position remains unchanged. + CordRep* Previous(); + + // Navigates to the data edge at position `offset`. Returns the navigated to + // data edge in `Position.edge` and the relative offset of `offset` into that + // data edge in `Position.offset`. Returns `Position.edge = nullptr` if the + // provide offset is greater than or equal to the tree's length. + Position Seek(size_t offset); + + // Reads `n` bytes of data starting at offset `edge_offset` of the current + // data edge, and returns the result in `ReadResult.tree`. `ReadResult.n` + // contains the 'bytes used` from the last / current data edge in the tree. + // This allows users that mix regular navigation (using string views) and + // 'read into cord' navigation to keep track of the current state, and which + // bytes have been consumed from a navigator. + // This function returns `ReadResult.tree = nullptr` if the requested length + // exceeds the length of the tree starting at the current data edge. + ReadResult Read(size_t edge_offset, size_t n); + + // Skips `n` bytes forward from the current data edge, returning the navigated + // to data edge in `Position.edge` and `Position.offset` containing the offset + // inside that data edge. Note that the state of the navigator is left + // unchanged if `n` is smaller than the length of the current data edge. + Position Skip(size_t n); + + // Resets this instance to the default / empty state. + void Reset(); + + private: + // Slow path for Next() if Next() reached the end of a leaf node. Backtracks + // up the stack until it finds a node that has a 'next' position available, + // and then does a 'front dive' towards the next leaf node. + CordRep* NextUp(); + + // Slow path for Previous() if Previous() reached the beginning of a leaf + // node. Backtracks up the stack until it finds a node that has a 'previous' + // position available, and then does a 'back dive' towards the previous leaf + // node. + CordRep* PreviousUp(); + + // Generic implementation of InitFirst() and InitLast(). + template <CordRepBtree::EdgeType edge_type> + CordRep* Init(CordRepBtree* tree); + + // `height_` contains the height of the current tree, or -1 if empty. + int height_ = -1; + + // `index_` and `node_` contain the navigation state as the 'path' to the + // current data edge which is at `node_[0]->Edge(index_[0])`. The contents + // of these are undefined until the instance is initialized (`height_ >= 0`). + uint8_t index_[CordRepBtree::kMaxHeight]; + CordRepBtree* node_[CordRepBtree::kMaxHeight]; +}; + +// Returns true if this instance is not empty. +inline CordRepBtreeNavigator::operator bool() const { return height_ >= 0; } + +inline CordRepBtree* CordRepBtreeNavigator::btree() const { + return height_ >= 0 ? node_[height_] : nullptr; +} + +inline CordRep* CordRepBtreeNavigator::Current() const { + assert(height_ >= 0); + return node_[0]->Edge(index_[0]); +} + +inline void CordRepBtreeNavigator::Reset() { height_ = -1; } + +inline CordRep* CordRepBtreeNavigator::InitFirst(CordRepBtree* tree) { + return Init<CordRepBtree::kFront>(tree); +} + +inline CordRep* CordRepBtreeNavigator::InitLast(CordRepBtree* tree) { + return Init<CordRepBtree::kBack>(tree); +} + +template <CordRepBtree::EdgeType edge_type> +inline CordRep* CordRepBtreeNavigator::Init(CordRepBtree* tree) { + assert(tree != nullptr); + assert(tree->size() > 0); + int height = height_ = tree->height(); + size_t index = tree->index(edge_type); + node_[height] = tree; + index_[height] = static_cast<uint8_t>(index); + while (--height >= 0) { + tree = tree->Edge(index)->btree(); + node_[height] = tree; + index = tree->index(edge_type); + index_[height] = static_cast<uint8_t>(index); + } + return node_[0]->Edge(index); +} + +inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::Seek( + size_t offset) { + assert(btree() != nullptr); + int height = height_; + CordRepBtree* edge = node_[height]; + if (ABSL_PREDICT_FALSE(offset >= edge->length)) return {nullptr, 0}; + CordRepBtree::Position index = edge->IndexOf(offset); + index_[height] = static_cast<uint8_t>(index.index); + while (--height >= 0) { + edge = edge->Edge(index.index)->btree(); + node_[height] = edge; + index = edge->IndexOf(index.n); + index_[height] = static_cast<uint8_t>(index.index); + } + return {edge->Edge(index.index), index.n}; +} + +inline CordRepBtreeNavigator::Position CordRepBtreeNavigator::InitOffset( + CordRepBtree* tree, size_t offset) { + assert(tree != nullptr); + if (ABSL_PREDICT_FALSE(offset >= tree->length)) return {nullptr, 0}; + height_ = tree->height(); + node_[height_] = tree; + return Seek(offset); +} + +inline CordRep* CordRepBtreeNavigator::Next() { + CordRepBtree* edge = node_[0]; + return index_[0] == edge->back() ? NextUp() : edge->Edge(++index_[0]); +} + +inline CordRep* CordRepBtreeNavigator::Previous() { + CordRepBtree* edge = node_[0]; + return index_[0] == edge->begin() ? PreviousUp() : edge->Edge(--index_[0]); +} + +inline CordRep* CordRepBtreeNavigator::NextUp() { + assert(index_[0] == node_[0]->back()); + CordRepBtree* edge; + size_t index; + int height = 0; + do { + if (++height > height_) return nullptr; + edge = node_[height]; + index = index_[height] + 1; + } while (index == edge->end()); + index_[height] = static_cast<uint8_t>(index); + do { + node_[--height] = edge = edge->Edge(index)->btree(); + index_[height] = static_cast<uint8_t>(index = edge->begin()); + } while (height > 0); + return edge->Edge(index); +} + +inline CordRep* CordRepBtreeNavigator::PreviousUp() { + assert(index_[0] == node_[0]->begin()); + CordRepBtree* edge; + size_t index; + int height = 0; + do { + if (++height > height_) return nullptr; + edge = node_[height]; + index = index_[height]; + } while (index == edge->begin()); + index_[height] = static_cast<uint8_t>(--index); + do { + node_[--height] = edge = edge->Edge(index)->btree(); + index_[height] = static_cast<uint8_t>(index = edge->back()); + } while (height > 0); + return edge->Edge(index); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_NAVIGATOR_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator_test.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator_test.cc new file mode 100644 index 0000000000..ce09b1992a --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_navigator_test.cc @@ -0,0 +1,325 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_btree_navigator.h" + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_test_util.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::Eq; +using ::testing::Ne; + +using ::absl::cordrep_testing::CordRepBtreeFromFlats; +using ::absl::cordrep_testing::CordToString; +using ::absl::cordrep_testing::CreateFlatsFromString; +using ::absl::cordrep_testing::CreateRandomString; +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::MakeSubstring; + +using ReadResult = CordRepBtreeNavigator::ReadResult; +using Position = CordRepBtreeNavigator::Position; + +// CordRepBtreeNavigatorTest is a test fixture which automatically creates a +// tree to test navigation logic on. The parameter `count' defines the number of +// data edges in the test tree. +class CordRepBtreeNavigatorTest : public testing::TestWithParam<int> { + public: + using Flats = std::vector<CordRep*>; + static constexpr size_t kCharsPerFlat = 3; + + CordRepBtreeNavigatorTest() { + data_ = CreateRandomString(count() * kCharsPerFlat); + flats_ = CreateFlatsFromString(data_, kCharsPerFlat); + + // Turn flat 0 or 1 into a substring to cover partial reads on substrings. + if (count() > 1) { + CordRep::Unref(flats_[1]); + flats_[1] = MakeSubstring(kCharsPerFlat, kCharsPerFlat, MakeFlat(data_)); + } else { + CordRep::Unref(flats_[0]); + flats_[0] = MakeSubstring(0, kCharsPerFlat, MakeFlat(data_)); + } + + tree_ = CordRepBtreeFromFlats(flats_); + } + + ~CordRepBtreeNavigatorTest() override { CordRep::Unref(tree_); } + + int count() const { return GetParam(); } + CordRepBtree* tree() { return tree_; } + const std::string& data() const { return data_; } + const std::vector<CordRep*>& flats() const { return flats_; } + + static std::string ToString(testing::TestParamInfo<int> param) { + return absl::StrCat(param.param, "_Flats"); + } + + private: + std::string data_; + Flats flats_; + CordRepBtree* tree_; +}; + +INSTANTIATE_TEST_SUITE_P( + WithParam, CordRepBtreeNavigatorTest, + testing::Values(1, CordRepBtree::kMaxCapacity - 1, + CordRepBtree::kMaxCapacity, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity - 1, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity + 1, + CordRepBtree::kMaxCapacity* CordRepBtree::kMaxCapacity * 2 + + 17), + CordRepBtreeNavigatorTest::ToString); + +TEST(CordRepBtreeNavigatorTest, Uninitialized) { + CordRepBtreeNavigator nav; + EXPECT_FALSE(nav); + EXPECT_THAT(nav.btree(), Eq(nullptr)); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH(nav.Current(), ".*"); +#endif +} + +TEST_P(CordRepBtreeNavigatorTest, InitFirst) { + CordRepBtreeNavigator nav; + CordRep* edge = nav.InitFirst(tree()); + EXPECT_TRUE(nav); + EXPECT_THAT(nav.btree(), Eq(tree())); + EXPECT_THAT(nav.Current(), Eq(flats().front())); + EXPECT_THAT(edge, Eq(flats().front())); +} + +TEST_P(CordRepBtreeNavigatorTest, InitLast) { + CordRepBtreeNavigator nav; + CordRep* edge = nav.InitLast(tree()); + EXPECT_TRUE(nav); + EXPECT_THAT(nav.btree(), Eq(tree())); + EXPECT_THAT(nav.Current(), Eq(flats().back())); + EXPECT_THAT(edge, Eq(flats().back())); +} + +TEST_P(CordRepBtreeNavigatorTest, NextPrev) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + const Flats& flats = this->flats(); + + EXPECT_THAT(nav.Previous(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.front())); + for (int i = 1; i < flats.size(); ++i) { + ASSERT_THAT(nav.Next(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Next(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.back())); + for (int i = static_cast<int>(flats.size()) - 2; i >= 0; --i) { + ASSERT_THAT(nav.Previous(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Previous(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.front())); +} + +TEST_P(CordRepBtreeNavigatorTest, PrevNext) { + CordRepBtreeNavigator nav; + nav.InitLast(tree()); + const Flats& flats = this->flats(); + + EXPECT_THAT(nav.Next(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.back())); + for (int i = static_cast<int>(flats.size()) - 2; i >= 0; --i) { + ASSERT_THAT(nav.Previous(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Previous(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.front())); + for (int i = 1; i < flats.size(); ++i) { + ASSERT_THAT(nav.Next(), Eq(flats[i])); + EXPECT_THAT(nav.Current(), Eq(flats[i])); + } + EXPECT_THAT(nav.Next(), Eq(nullptr)); + EXPECT_THAT(nav.Current(), Eq(flats.back())); +} + +TEST(CordRepBtreeNavigatorTest, Reset) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + CordRepBtreeNavigator nav; + nav.InitFirst(tree); + nav.Reset(); + EXPECT_FALSE(nav); + EXPECT_THAT(nav.btree(), Eq(nullptr)); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH(nav.Current(), ".*"); +#endif + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeNavigatorTest, Skip) { + int count = this->count(); + const Flats& flats = this->flats(); + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + Position pos = nav.Skip(char_offset); + EXPECT_THAT(pos.edge, Eq(nav.Current())); + EXPECT_THAT(pos.edge, Eq(flats[0])); + EXPECT_THAT(pos.offset, Eq(char_offset)); + } + + for (int index1 = 0; index1 < count; ++index1) { + for (int index2 = index1; index2 < count; ++index2) { + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + size_t length1 = index1 * kCharsPerFlat; + Position pos1 = nav.Skip(length1 + char_offset); + ASSERT_THAT(pos1.edge, Eq(flats[index1])); + ASSERT_THAT(pos1.edge, Eq(nav.Current())); + ASSERT_THAT(pos1.offset, Eq(char_offset)); + + size_t length2 = index2 * kCharsPerFlat; + Position pos2 = nav.Skip(length2 - length1 + char_offset); + ASSERT_THAT(pos2.edge, Eq(flats[index2])); + ASSERT_THAT(pos2.edge, Eq(nav.Current())); + ASSERT_THAT(pos2.offset, Eq(char_offset)); + } + } + } +} + +TEST_P(CordRepBtreeNavigatorTest, Seek) { + int count = this->count(); + const Flats& flats = this->flats(); + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + Position pos = nav.Seek(char_offset); + EXPECT_THAT(pos.edge, Eq(nav.Current())); + EXPECT_THAT(pos.edge, Eq(flats[0])); + EXPECT_THAT(pos.offset, Eq(char_offset)); + } + + for (int index = 0; index < count; ++index) { + for (int char_offset = 0; char_offset < kCharsPerFlat; ++char_offset) { + size_t offset = index * kCharsPerFlat + char_offset; + Position pos1 = nav.Seek(offset); + ASSERT_THAT(pos1.edge, Eq(flats[index])); + ASSERT_THAT(pos1.edge, Eq(nav.Current())); + ASSERT_THAT(pos1.offset, Eq(char_offset)); + } + } +} + +TEST(CordRepBtreeNavigatorTest, InitOffset) { + // Whitebox: InitOffset() is implemented in terms of Seek() which is + // exhaustively tested. Only test it initializes / forwards properly.. + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + tree = CordRepBtree::Append(tree, MakeFlat("def")); + CordRepBtreeNavigator nav; + Position pos = nav.InitOffset(tree, 5); + EXPECT_TRUE(nav); + EXPECT_THAT(nav.btree(), Eq(tree)); + EXPECT_THAT(pos.edge, Eq(tree->Edges()[1])); + EXPECT_THAT(pos.edge, Eq(nav.Current())); + EXPECT_THAT(pos.offset, Eq(2)); + CordRep::Unref(tree); +} + +TEST(CordRepBtreeNavigatorTest, InitOffsetAndSeekBeyondLength) { + CordRepBtree* tree1 = CordRepBtree::Create(MakeFlat("abc")); + CordRepBtree* tree2 = CordRepBtree::Create(MakeFlat("def")); + + CordRepBtreeNavigator nav; + nav.InitFirst(tree1); + EXPECT_THAT(nav.Seek(3).edge, Eq(nullptr)); + EXPECT_THAT(nav.Seek(100).edge, Eq(nullptr)); + EXPECT_THAT(nav.btree(), Eq(tree1)); + EXPECT_THAT(nav.Current(), Eq(tree1->Edges().front())); + + EXPECT_THAT(nav.InitOffset(tree2, 3).edge, Eq(nullptr)); + EXPECT_THAT(nav.InitOffset(tree2, 100).edge, Eq(nullptr)); + EXPECT_THAT(nav.btree(), Eq(tree1)); + EXPECT_THAT(nav.Current(), Eq(tree1->Edges().front())); + + CordRep::Unref(tree1); + CordRep::Unref(tree2); +} + +TEST_P(CordRepBtreeNavigatorTest, Read) { + const Flats& flats = this->flats(); + const std::string& data = this->data(); + + for (size_t offset = 0; offset < data.size(); ++offset) { + for (size_t length = 1; length <= data.size() - offset; ++length) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + + // Skip towards edge holding offset + size_t edge_offset = nav.Skip(offset).offset; + + // Read node + ReadResult result = nav.Read(edge_offset, length); + ASSERT_THAT(result.tree, Ne(nullptr)); + EXPECT_THAT(result.tree->length, Eq(length)); + if (result.tree->tag == BTREE) { + ASSERT_TRUE(CordRepBtree::IsValid(result.tree->btree())); + } + + // Verify contents + std::string value = CordToString(result.tree); + EXPECT_THAT(value, Eq(data.substr(offset, length))); + + // Verify 'partial last edge' reads. + size_t partial = (offset + length) % kCharsPerFlat; + ASSERT_THAT(result.n, Eq(partial)); + + // Verify ending position if not EOF + if (offset + length < data.size()) { + size_t index = (offset + length) / kCharsPerFlat; + EXPECT_THAT(nav.Current(), Eq(flats[index])); + } + + CordRep::Unref(result.tree); + } + } +} + +TEST_P(CordRepBtreeNavigatorTest, ReadBeyondLengthOfTree) { + CordRepBtreeNavigator nav; + nav.InitFirst(tree()); + ReadResult result = nav.Read(2, tree()->length); + ASSERT_THAT(result.tree, Eq(nullptr)); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.cc new file mode 100644 index 0000000000..5dc76966d2 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.cc @@ -0,0 +1,68 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_btree_reader.h" + +#include <cassert> + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_navigator.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +absl::string_view CordRepBtreeReader::Read(size_t n, size_t chunk_size, + CordRep*& tree) { + assert(chunk_size <= navigator_.Current()->length); + + // If chunk_size is non-zero, we need to start inside last returned edge. + // Else we start reading at the next data edge of the tree. + CordRep* edge = chunk_size ? navigator_.Current() : navigator_.Next(); + const size_t offset = chunk_size ? edge->length - chunk_size : 0; + + // Read the sub tree and verify we got what we wanted. + ReadResult result = navigator_.Read(offset, n); + tree = result.tree; + + // If the data returned in `tree` was covered entirely by `chunk_size`, i.e., + // read from the 'previous' edge, we did not consume any additional data, and + // can directly return the substring into the current data edge as the next + // chunk. We can easily establish from the above code that `navigator_.Next()` + // has not been called as that requires `chunk_size` to be zero. + if (n < chunk_size) return CordRepBtree::EdgeData(edge).substr(result.n); + + // The amount of data taken from the last edge is `chunk_size` and `result.n` + // contains the offset into the current edge trailing the read data (which can + // be 0). As the call to `navigator_.Read()` could have consumed all remaining + // data, calling `navigator_.Current()` is not safe before checking if we + // already consumed all remaining data. + const size_t consumed_by_read = n - chunk_size - result.n; + if (consumed_by_read >= remaining_) { + remaining_ = 0; + return {}; + } + + // We did not read all data, return remaining data from current edge. + edge = navigator_.Current(); + remaining_ -= consumed_by_read + edge->length; + return CordRepBtree::EdgeData(edge).substr(result.n); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.h new file mode 100644 index 0000000000..7aa79dbf10 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader.h @@ -0,0 +1,211 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ + +#include <cassert> + +#include "absl/base/config.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_btree_navigator.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepBtreeReader implements logic to iterate over cord btrees. +// References to the underlying data are returned as absl::string_view values. +// The most typical use case is a forward only iteration over tree data. +// The class also provides `Skip()`, `Seek()` and `Read()` methods similar to +// CordRepBtreeNavigator that allow more advanced navigation. +// +// Example: iterate over all data inside a cord btree: +// +// CordRepBtreeReader reader; +// for (string_view sv = reader.Init(tree); !sv.Empty(); sv = sv.Next()) { +// DoSomethingWithDataIn(sv); +// } +// +// All navigation methods always return the next 'chunk' of data. The class +// assumes that all data is directly 'consumed' by the caller. For example: +// invoking `Skip()` will skip the desired number of bytes, and directly +// read and return the next chunk of data directly after the skipped bytes. +// +// Example: iterate over all data inside a btree skipping the first 100 bytes: +// +// CordRepBtreeReader reader; +// absl::string_view sv = reader.Init(tree); +// if (sv.length() > 100) { +// sv.RemovePrefix(100); +// } else { +// sv = reader.Skip(100 - sv.length()); +// } +// while (!sv.empty()) { +// DoSomethingWithDataIn(sv); +// absl::string_view sv = reader.Next(); +// } +// +// It is important to notice that `remaining` is based on the end position of +// the last data edge returned to the caller, not the cumulative data returned +// to the caller which can be less in cases of skipping or seeking over data. +// +// For example, consider a cord btree with five data edges: "abc", "def", "ghi", +// "jkl" and "mno": +// +// absl::string_view sv; +// CordRepBtreeReader reader; +// +// sv = reader.Init(tree); // sv = "abc", remaining = 12 +// sv = reader.Skip(4); // sv = "hi", remaining = 6 +// sv = reader.Skip(2); // sv = "l", remaining = 3 +// sv = reader.Next(); // sv = "mno", remaining = 0 +// sv = reader.Seek(1); // sv = "bc", remaining = 12 +// +class CordRepBtreeReader { + public: + using ReadResult = CordRepBtreeNavigator::ReadResult; + using Position = CordRepBtreeNavigator::Position; + + // Returns true if this instance is not empty. + explicit operator bool() const { return navigator_.btree() != nullptr; } + + // Returns the tree referenced by this instance or nullptr if empty. + CordRepBtree* btree() const { return navigator_.btree(); } + + // Returns the current data edge inside the referenced btree. + // Requires that the current instance is not empty. + CordRep* node() const { return navigator_.Current(); } + + // Returns the length of the referenced tree. + // Requires that the current instance is not empty. + size_t length() const; + + // Returns the number of remaining bytes available for iteration, which is the + // number of bytes directly following the end of the last chunk returned. + // This value will be zero if we iterated over the last edge in the bound + // tree, in which case any call to Next() or Skip() will return an empty + // string_view reflecting the EOF state. + // Note that a call to `Seek()` resets `remaining` to a value based on the + // end position of the chunk returned by that call. + size_t remaining() const { return remaining_; } + + // Resets this instance to an empty value. + void Reset() { navigator_.Reset(); } + + // Initializes this instance with `tree`. `tree` must not be null. + // Returns a reference to the first data edge of the provided tree. + absl::string_view Init(CordRepBtree* tree); + + // Navigates to and returns the next data edge of the referenced tree. + // Returns an empty string_view if an attempt is made to read beyond the end + // of the tree, i.e.: if `remaining()` is zero indicating an EOF condition. + // Requires that the current instance is not empty. + absl::string_view Next(); + + // Skips the provided amount of bytes and returns a reference to the data + // directly following the skipped bytes. + absl::string_view Skip(size_t skip); + + // Reads `n` bytes into `tree`. + // If `chunk_size` is zero, starts reading at the next data edge. If + // `chunk_size` is non zero, the read starts at the last `chunk_size` bytes of + // the last returned data edge. Effectively, this means that the read starts + // at offset `consumed() - chunk_size`. + // Requires that `chunk_size` is less than or equal to the length of the + // last returned data edge. The purpose of `chunk_size` is to simplify code + // partially consuming a returned chunk and wanting to include the remaining + // bytes in the Read call. For example, the below code will read 1000 bytes of + // data into a cord tree if the first chunk starts with "big:": + // + // CordRepBtreeReader reader; + // absl::string_view sv = reader.Init(tree); + // if (absl::StartsWith(sv, "big:")) { + // CordRepBtree tree; + // sv = reader.Read(1000, sv.size() - 4 /* "big:" */, &tree); + // } + // + // This method will return an empty string view if all remaining data was + // read. If `n` exceeded the amount of remaining data this function will + // return an empty string view and `tree` will be set to nullptr. + // In both cases, `consumed` will be set to `length`. + absl::string_view Read(size_t n, size_t chunk_size, CordRep*& tree); + + // Navigates to the chunk at offset `offset`. + // Returns a reference into the navigated to chunk, adjusted for the relative + // position of `offset` into that chunk. For example, calling `Seek(13)` on a + // cord tree containing 2 chunks of 10 and 20 bytes respectively will return + // a string view into the second chunk starting at offset 3 with a size of 17. + // Returns an empty string view if `offset` is equal to or greater than the + // length of the referenced tree. + absl::string_view Seek(size_t offset); + + private: + size_t remaining_ = 0; + CordRepBtreeNavigator navigator_; +}; + +inline size_t CordRepBtreeReader::length() const { + assert(btree() != nullptr); + return btree()->length; +} + +inline absl::string_view CordRepBtreeReader::Init(CordRepBtree* tree) { + assert(tree != nullptr); + const CordRep* edge = navigator_.InitFirst(tree); + remaining_ = tree->length - edge->length; + return CordRepBtree::EdgeData(edge); +} + +inline absl::string_view CordRepBtreeReader::Next() { + if (remaining_ == 0) return {}; + const CordRep* edge = navigator_.Next(); + assert(edge != nullptr); + remaining_ -= edge->length; + return CordRepBtree::EdgeData(edge); +} + +inline absl::string_view CordRepBtreeReader::Skip(size_t skip) { + // As we are always positioned on the last 'consumed' edge, we + // need to skip the current edge as well as `skip`. + const size_t edge_length = navigator_.Current()->length; + CordRepBtreeNavigator::Position pos = navigator_.Skip(skip + edge_length); + if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { + remaining_ = 0; + return {}; + } + // The combined length of all edges skipped before `pos.edge` is `skip - + // pos.offset`, all of which are 'consumed', as well as the current edge. + remaining_ -= skip - pos.offset + pos.edge->length; + return CordRepBtree::EdgeData(pos.edge).substr(pos.offset); +} + +inline absl::string_view CordRepBtreeReader::Seek(size_t offset) { + const CordRepBtreeNavigator::Position pos = navigator_.Seek(offset); + if (ABSL_PREDICT_FALSE(pos.edge == nullptr)) { + remaining_ = 0; + return {}; + } + absl::string_view chunk = CordRepBtree::EdgeData(pos.edge).substr(pos.offset); + remaining_ = length() - offset - chunk.length(); + return chunk; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_BTREE_READER_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader_test.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader_test.cc new file mode 100644 index 0000000000..9b27a81fdb --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_reader_test.cc @@ -0,0 +1,293 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_btree_reader.h" + +#include <iostream> +#include <random> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_test_util.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::Ne; +using ::testing::Not; + +using ::absl::cordrep_testing::CordRepBtreeFromFlats; +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::CordToString; +using ::absl::cordrep_testing::CreateFlatsFromString; +using ::absl::cordrep_testing::CreateRandomString; + +using ReadResult = CordRepBtreeReader::ReadResult; + +TEST(CordRepBtreeReaderTest, Next) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + CordRepBtreeReader reader; + size_t remaining = data.length(); + absl::string_view chunk = reader.Init(node); + EXPECT_THAT(chunk, Eq(data.substr(0, chunk.length()))); + + remaining -= chunk.length(); + EXPECT_THAT(reader.remaining(), Eq(remaining)); + + while (remaining > 0) { + const size_t offset = data.length() - remaining; + chunk = reader.Next(); + EXPECT_THAT(chunk, Eq(data.substr(offset, chunk.length()))); + + remaining -= chunk.length(); + EXPECT_THAT(reader.remaining(), Eq(remaining)); + } + + EXPECT_THAT(reader.remaining(), Eq(0)); + + // Verify trying to read beyond EOF returns empty string_view + EXPECT_THAT(reader.Next(), testing::IsEmpty()); + + CordRep::Unref(node); + } +} + +TEST(CordRepBtreeReaderTest, Skip) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + for (size_t skip1 = 0; skip1 < data.length() - kChars; ++skip1) { + for (size_t skip2 = 0; skip2 < data.length() - kChars; ++skip2) { + CordRepBtreeReader reader; + size_t remaining = data.length(); + absl::string_view chunk = reader.Init(node); + remaining -= chunk.length(); + + chunk = reader.Skip(skip1); + size_t offset = data.length() - remaining; + ASSERT_THAT(chunk, Eq(data.substr(offset + skip1, chunk.length()))); + remaining -= chunk.length() + skip1; + ASSERT_THAT(reader.remaining(), Eq(remaining)); + + if (remaining == 0) continue; + + size_t skip = std::min(remaining - 1, skip2); + chunk = reader.Skip(skip); + offset = data.length() - remaining; + ASSERT_THAT(chunk, Eq(data.substr(offset + skip, chunk.length()))); + } + } + + CordRep::Unref(node); + } +} + +TEST(CordRepBtreeReaderTest, SkipBeyondLength) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + tree = CordRepBtree::Append(tree, MakeFlat("def")); + CordRepBtreeReader reader; + reader.Init(tree); + EXPECT_THAT(reader.Skip(100), IsEmpty()); + EXPECT_THAT(reader.remaining(), Eq(0)); + CordRep::Unref(tree); +} + +TEST(CordRepBtreeReaderTest, Seek) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap, cap * cap + 1, cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + for (size_t seek = 0; seek < data.length() - 1; ++seek) { + CordRepBtreeReader reader; + reader.Init(node); + absl::string_view chunk = reader.Seek(seek); + ASSERT_THAT(chunk, Not(IsEmpty())); + ASSERT_THAT(chunk, Eq(data.substr(seek, chunk.length()))); + ASSERT_THAT(reader.remaining(), + Eq(data.length() - seek - chunk.length())); + } + + CordRep::Unref(node); + } +} + +TEST(CordRepBtreeReaderTest, SeekBeyondLength) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + tree = CordRepBtree::Append(tree, MakeFlat("def")); + CordRepBtreeReader reader; + reader.Init(tree); + EXPECT_THAT(reader.Seek(6), IsEmpty()); + EXPECT_THAT(reader.remaining(), Eq(0)); + EXPECT_THAT(reader.Seek(100), IsEmpty()); + EXPECT_THAT(reader.remaining(), Eq(0)); + CordRep::Unref(tree); +} + +TEST(CordRepBtreeReaderTest, Read) { + std::string data = "abcdefghijklmno"; + std::vector<CordRep*> flats = CreateFlatsFromString(data, 5); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + CordRep* tree; + CordRepBtreeReader reader; + absl::string_view chunk; + + // Read zero bytes + chunk = reader.Init(node); + chunk = reader.Read(0, chunk.length(), tree); + EXPECT_THAT(tree, Eq(nullptr)); + EXPECT_THAT(chunk, Eq("abcde")); + EXPECT_THAT(reader.remaining(), Eq(10)); + EXPECT_THAT(reader.Next(), Eq("fghij")); + + // Read in full + chunk = reader.Init(node); + chunk = reader.Read(15, chunk.length(), tree); + EXPECT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("abcdefghijklmno")); + EXPECT_THAT(chunk, Eq("")); + EXPECT_THAT(reader.remaining(), Eq(0)); + CordRep::Unref(tree); + + // Read < chunk bytes + chunk = reader.Init(node); + chunk = reader.Read(3, chunk.length(), tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("abc")); + EXPECT_THAT(chunk, Eq("de")); + EXPECT_THAT(reader.remaining(), Eq(10)); + EXPECT_THAT(reader.Next(), Eq("fghij")); + CordRep::Unref(tree); + + // Read < chunk bytes at offset + chunk = reader.Init(node); + chunk = reader.Read(2, chunk.length() - 2, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("cd")); + EXPECT_THAT(chunk, Eq("e")); + EXPECT_THAT(reader.remaining(), Eq(10)); + EXPECT_THAT(reader.Next(), Eq("fghij")); + CordRep::Unref(tree); + + // Read from consumed chunk + chunk = reader.Init(node); + chunk = reader.Read(3, 0, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("fgh")); + EXPECT_THAT(chunk, Eq("ij")); + EXPECT_THAT(reader.remaining(), Eq(5)); + EXPECT_THAT(reader.Next(), Eq("klmno")); + CordRep::Unref(tree); + + // Read across chunks + chunk = reader.Init(node); + chunk = reader.Read(12, chunk.length() - 2, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("cdefghijklmn")); + EXPECT_THAT(chunk, Eq("o")); + EXPECT_THAT(reader.remaining(), Eq(0)); + CordRep::Unref(tree); + + // Read across chunks landing on exact edge boundary + chunk = reader.Init(node); + chunk = reader.Read(10 - 2, chunk.length() - 2, tree); + ASSERT_THAT(tree, Ne(nullptr)); + EXPECT_THAT(CordToString(tree), Eq("cdefghij")); + EXPECT_THAT(chunk, Eq("klmno")); + EXPECT_THAT(reader.remaining(), Eq(0)); + CordRep::Unref(tree); + + CordRep::Unref(node); +} + +TEST(CordRepBtreeReaderTest, ReadExhaustive) { + constexpr size_t kChars = 3; + const size_t cap = CordRepBtree::kMaxCapacity; + int counts[] = {1, 2, cap, cap * cap + 1, cap * cap * cap * 2 + 17}; + + for (int count : counts) { + std::string data = CreateRandomString(count * kChars); + std::vector<CordRep*> flats = CreateFlatsFromString(data, kChars); + CordRepBtree* node = CordRepBtreeFromFlats(flats); + + for (size_t read_size : {kChars - 1, kChars, kChars + 7, cap * cap}) { + CordRepBtreeReader reader; + absl::string_view chunk = reader.Init(node); + + // `consumed` tracks the end of last consumed chunk which is the start of + // the next chunk: we always read with `chunk_size = chunk.length()`. + size_t consumed = 0; + size_t remaining = data.length(); + while (remaining > 0) { + CordRep* tree; + size_t n = (std::min)(remaining, read_size); + chunk = reader.Read(n, chunk.length(), tree); + EXPECT_THAT(tree, Ne(nullptr)); + if (tree) { + EXPECT_THAT(CordToString(tree), Eq(data.substr(consumed, n))); + CordRep::Unref(tree); + } + + consumed += n; + remaining -= n; + EXPECT_THAT(reader.remaining(), Eq(remaining - chunk.length())); + + if (remaining > 0) { + ASSERT_FALSE(chunk.empty()); + ASSERT_THAT(chunk, Eq(data.substr(consumed, chunk.length()))); + } else { + ASSERT_TRUE(chunk.empty()) << chunk; + } + } + } + + CordRep::Unref(node); + } +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_test.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_test.cc new file mode 100644 index 0000000000..be9473d41d --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_btree_test.cc @@ -0,0 +1,1489 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_btree.h" + +#include <cmath> +#include <deque> +#include <iostream> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/cleanup/cleanup.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_test_util.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +class CordRepBtreeTestPeer { + public: + static void SetEdge(CordRepBtree* node, size_t idx, CordRep* edge) { + node->edges_[idx] = edge; + } + static void AddEdge(CordRepBtree* node, CordRep* edge) { + node->edges_[node->fetch_add_end(1)] = edge; + } +}; + +namespace { + +using ::absl::cordrep_testing::AutoUnref; +using ::absl::cordrep_testing::CordCollectRepsIf; +using ::absl::cordrep_testing::CordToString; +using ::absl::cordrep_testing::CordVisitReps; +using ::absl::cordrep_testing::CreateFlatsFromString; +using ::absl::cordrep_testing::CreateRandomString; +using ::absl::cordrep_testing::MakeConcat; +using ::absl::cordrep_testing::MakeExternal; +using ::absl::cordrep_testing::MakeFlat; +using ::absl::cordrep_testing::MakeSubstring; +using ::testing::_; +using ::testing::AllOf; +using ::testing::AnyOf; +using ::testing::Conditional; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Le; +using ::testing::Ne; +using ::testing::Not; +using ::testing::SizeIs; +using ::testing::TypedEq; + +MATCHER_P(EqFlatHolding, data, "Equals flat holding data") { + if (arg->tag < FLAT) { + *result_listener << "Expected FLAT, got tag " << static_cast<int>(arg->tag); + return false; + } + std::string actual = CordToString(arg); + if (actual != data) { + *result_listener << "Expected flat holding \"" << data + << "\", got flat holding \"" << actual << "\""; + return false; + } + return true; +} + +MATCHER_P(IsNode, height, absl::StrCat("Is a valid node of height ", height)) { + if (arg == nullptr) { + *result_listener << "Expected NODE, got nullptr"; + return false; + } + if (arg->tag != BTREE) { + *result_listener << "Expected NODE, got " << static_cast<int>(arg->tag); + return false; + } + if (!CordRepBtree::IsValid(arg->btree())) { + CordRepBtree::Dump(arg->btree(), "Expected valid NODE, got:", false, + *result_listener->stream()); + return false; + } + if (arg->btree()->height() != height) { + *result_listener << "Expected NODE of height " << height << ", got " + << arg->btree()->height(); + return false; + } + return true; +} + +MATCHER_P2(IsSubstring, start, length, + absl::StrCat("Is a substring(start = ", start, ", length = ", length, + ")")) { + if (arg == nullptr) { + *result_listener << "Expected substring, got nullptr"; + return false; + } + if (arg->tag != SUBSTRING) { + *result_listener << "Expected SUBSTRING, got " + << static_cast<int>(arg->tag); + return false; + } + const CordRepSubstring* const substr = arg->substring(); + if (substr->start != start || substr->length != length) { + *result_listener << "Expected substring(" << start << ", " << length + << "), got substring(" << substr->start << ", " + << substr->length << ")"; + return false; + } + return true; +} + +// DataConsumer is a simple helper class used by tests to 'consume' string +// fragments from the provided input in forward or backward direction. +class DataConsumer { + public: + // Starts consumption of `data`. Caller must make sure `data` outlives this + // instance. Consumes data starting at the front if `forward` is true, else + // consumes data from the back. + DataConsumer(absl::string_view data, bool forward) + : data_(data), forward_(forward) {} + + // Return the next `n` bytes from referenced data. + absl::string_view Next(size_t n) { + assert(n <= data_.size() - consumed_); + consumed_ += n; + return data_.substr(forward_ ? consumed_ - n : data_.size() - consumed_, n); + } + + // Returns all data consumed so far. + absl::string_view Consumed() const { + return forward_ ? data_.substr(0, consumed_) + : data_.substr(data_.size() - consumed_); + } + + private: + absl::string_view data_; + size_t consumed_ = 0; + bool forward_; +}; + +// BtreeAdd returns either CordRepBtree::Append or CordRepBtree::Prepend. +CordRepBtree* BtreeAdd(CordRepBtree* node, bool append, + absl::string_view data) { + return append ? CordRepBtree::Append(node, data) + : CordRepBtree::Prepend(node, data); +} + +// Recursively collects all leaf edges from `tree` and appends them to `edges`. +void GetLeafEdges(const CordRepBtree* tree, std::vector<CordRep*>& edges) { + if (tree->height() == 0) { + for (CordRep* edge : tree->Edges()) { + edges.push_back(edge); + } + } else { + for (CordRep* edge : tree->Edges()) { + GetLeafEdges(edge->btree(), edges); + } + } +} + +// Recursively collects and returns all leaf edges from `tree`. +std::vector<CordRep*> GetLeafEdges(const CordRepBtree* tree) { + std::vector<CordRep*> edges; + GetLeafEdges(tree, edges); + return edges; +} + +// Creates a flat containing the hexadecimal value of `i` zero padded +// to at least 4 digits prefixed with "0x", e.g.: "0x04AC". +CordRepFlat* MakeHexFlat(size_t i) { + return MakeFlat(absl::StrCat("0x", absl::Hex(i, absl::kZeroPad4))); +} + +CordRepBtree* MakeLeaf(size_t size = CordRepBtree::kMaxCapacity) { + assert(size <= CordRepBtree::kMaxCapacity); + CordRepBtree* leaf = CordRepBtree::Create(MakeHexFlat(0)); + for (size_t i = 1; i < size; ++i) { + leaf = CordRepBtree::Append(leaf, MakeHexFlat(i)); + } + return leaf; +} + +CordRepBtree* MakeTree(size_t size, bool append = true) { + CordRepBtree* tree = CordRepBtree::Create(MakeHexFlat(0)); + for (size_t i = 1; i < size; ++i) { + tree = append ? CordRepBtree::Append(tree, MakeHexFlat(i)) + : CordRepBtree::Prepend(tree, MakeHexFlat(i)); + } + return tree; +} + +CordRepBtree* CreateTree(absl::Span<CordRep* const> reps) { + auto it = reps.begin(); + CordRepBtree* tree = CordRepBtree::Create(*it); + while (++it != reps.end()) tree = CordRepBtree::Append(tree, *it); + return tree; +} + +CordRepBtree* CreateTree(absl::string_view data, size_t chunk_size) { + return CreateTree(CreateFlatsFromString(data, chunk_size)); +} + +CordRepBtree* CreateTreeReverse(absl::string_view data, size_t chunk_size) { + std::vector<CordRep*> flats = CreateFlatsFromString(data, chunk_size); + auto rit = flats.rbegin(); + CordRepBtree* tree = CordRepBtree::Create(*rit); + while (++rit != flats.rend()) tree = CordRepBtree::Prepend(tree, *rit); + return tree; +} + +class CordRepBtreeTest : public testing::TestWithParam<bool> { + public: + bool shared() const { return GetParam(); } + + static std::string ToString(testing::TestParamInfo<bool> param) { + return param.param ? "Shared" : "Private"; + } +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordRepBtreeTest, testing::Bool(), + CordRepBtreeTest::ToString); + +class CordRepBtreeHeightTest : public testing::TestWithParam<int> { + public: + int height() const { return GetParam(); } + + static std::string ToString(testing::TestParamInfo<int> param) { + return absl::StrCat(param.param); + } +}; + +INSTANTIATE_TEST_SUITE_P(WithHeights, CordRepBtreeHeightTest, + testing::Range(0, CordRepBtree::kMaxHeight), + CordRepBtreeHeightTest::ToString); + +using TwoBools = testing::tuple<bool, bool>; + +class CordRepBtreeDualTest : public testing::TestWithParam<TwoBools> { + public: + bool first_shared() const { return std::get<0>(GetParam()); } + bool second_shared() const { return std::get<1>(GetParam()); } + + static std::string ToString(testing::TestParamInfo<TwoBools> param) { + if (std::get<0>(param.param)) { + return std::get<1>(param.param) ? "BothShared" : "FirstShared"; + } + return std::get<1>(param.param) ? "SecondShared" : "Private"; + } +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordRepBtreeDualTest, + testing::Combine(testing::Bool(), testing::Bool()), + CordRepBtreeDualTest::ToString); + +TEST(CordRepBtreeTest, SizeIsMultipleOf64) { + // Only enforce for fully 64-bit platforms. + if (sizeof(size_t) == 8 && sizeof(void*) == 8) { + EXPECT_THAT(sizeof(CordRepBtree) % 64, Eq(0)) << "Should be multiple of 64"; + } +} + +TEST(CordRepBtreeTest, NewDestroyEmptyTree) { + auto* tree = CordRepBtree::New(); + EXPECT_THAT(tree->size(), Eq(0)); + EXPECT_THAT(tree->height(), Eq(0)); + EXPECT_THAT(tree->Edges(), ElementsAre()); + CordRepBtree::Destroy(tree); +} + +TEST(CordRepBtreeTest, NewDestroyEmptyTreeAtHeight) { + auto* tree = CordRepBtree::New(3); + EXPECT_THAT(tree->size(), Eq(0)); + EXPECT_THAT(tree->height(), Eq(3)); + EXPECT_THAT(tree->Edges(), ElementsAre()); + CordRepBtree::Destroy(tree); +} + +TEST(CordRepBtreeTest, Btree) { + CordRep* rep = CordRepBtree::New(); + EXPECT_THAT(rep->btree(), Eq(rep)); + EXPECT_THAT(static_cast<const CordRep*>(rep)->btree(), Eq(rep)); + CordRep::Unref(rep); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + rep = MakeFlat("Hello world"); + EXPECT_DEATH(rep->btree(), ".*"); + EXPECT_DEATH(static_cast<const CordRep*>(rep)->btree(), ".*"); + CordRep::Unref(rep); +#endif +} + +TEST(CordRepBtreeTest, EdgeData) { + CordRepFlat* flat = MakeFlat("Hello world"); + CordRepExternal* external = MakeExternal("Hello external"); + CordRep* substr1 = MakeSubstring(1, 6, CordRep::Ref(flat)); + CordRep* substr2 = MakeSubstring(1, 6, CordRep::Ref(external)); + CordRep* concat = MakeConcat(CordRep::Ref(flat), CordRep::Ref(external)); + CordRep* bad_substr = MakeSubstring(1, 2, CordRep::Ref(substr1)); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(flat)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(flat), + TypedEq<const void*>(flat->Data())); + EXPECT_THAT(CordRepBtree::EdgeData(flat), Eq("Hello world")); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(external)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(external), + TypedEq<const void*>(external->base)); + EXPECT_THAT(CordRepBtree::EdgeData(external), Eq("Hello external")); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(substr1)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr1), + TypedEq<const void*>(flat->Data() + 1)); + EXPECT_THAT(CordRepBtree::EdgeData(substr1), Eq("ello w")); + + EXPECT_TRUE(CordRepBtree::IsDataEdge(substr2)); + EXPECT_THAT(CordRepBtree::EdgeDataPtr(substr2), + TypedEq<const void*>(external->base + 1)); + EXPECT_THAT(CordRepBtree::EdgeData(substr2), Eq("ello e")); + + EXPECT_FALSE(CordRepBtree::IsDataEdge(concat)); + EXPECT_FALSE(CordRepBtree::IsDataEdge(bad_substr)); +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH(CordRepBtree::EdgeData(concat), ".*"); + EXPECT_DEATH(CordRepBtree::EdgeDataPtr(concat), ".*"); + EXPECT_DEATH(CordRepBtree::EdgeData(bad_substr), ".*"); + EXPECT_DEATH(CordRepBtree::EdgeDataPtr(bad_substr), ".*"); +#endif + + CordRep::Unref(bad_substr); + CordRep::Unref(concat); + CordRep::Unref(substr2); + CordRep::Unref(substr1); + CordRep::Unref(external); + CordRep::Unref(flat); +} + +TEST(CordRepBtreeTest, CreateUnrefLeaf) { + auto* flat = MakeFlat("a"); + auto* leaf = CordRepBtree::Create(flat); + EXPECT_THAT(leaf->size(), Eq(1)); + EXPECT_THAT(leaf->height(), Eq(0)); + EXPECT_THAT(leaf->Edges(), ElementsAre(flat)); + CordRepBtree::Unref(leaf); +} + +TEST(CordRepBtreeTest, NewUnrefNode) { + auto* leaf = CordRepBtree::Create(MakeFlat("a")); + CordRepBtree* tree = CordRepBtree::New(leaf); + EXPECT_THAT(tree->size(), Eq(1)); + EXPECT_THAT(tree->height(), Eq(1)); + EXPECT_THAT(tree->Edges(), ElementsAre(leaf)); + CordRepBtree::Unref(tree); +} + +TEST_P(CordRepBtreeTest, AppendToLeafToCapacity) { + AutoUnref refs; + std::vector<CordRep*> flats; + flats.push_back(MakeHexFlat(0)); + auto* leaf = CordRepBtree::Create(flats.back()); + + for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { + refs.RefIf(shared(), leaf); + flats.push_back(MakeHexFlat(i)); + auto* result = CordRepBtree::Append(leaf, flats.back()); + EXPECT_THAT(result->height(), Eq(0)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(result->Edges(), ElementsAreArray(flats)); + leaf = result; + } + CordRep::Unref(leaf); +} + +TEST_P(CordRepBtreeTest, PrependToLeafToCapacity) { + AutoUnref refs; + std::deque<CordRep*> flats; + flats.push_front(MakeHexFlat(0)); + auto* leaf = CordRepBtree::Create(flats.front()); + + for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { + refs.RefIf(shared(), leaf); + flats.push_front(MakeHexFlat(i)); + auto* result = CordRepBtree::Prepend(leaf, flats.front()); + EXPECT_THAT(result->height(), Eq(0)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(result->Edges(), ElementsAreArray(flats)); + leaf = result; + } + CordRep::Unref(leaf); +} + +// This test specifically aims at code aligning data at either the front or the +// back of the contained `edges[]` array, alternating Append and Prepend will +// move `begin()` and `end()` values as needed for each added value. +TEST_P(CordRepBtreeTest, AppendPrependToLeafToCapacity) { + AutoUnref refs; + std::deque<CordRep*> flats; + flats.push_front(MakeHexFlat(0)); + auto* leaf = CordRepBtree::Create(flats.front()); + + for (size_t i = 1; i < CordRepBtree::kMaxCapacity; ++i) { + refs.RefIf(shared(), leaf); + CordRepBtree* result; + if (i % 2 != 0) { + flats.push_front(MakeHexFlat(i)); + result = CordRepBtree::Prepend(leaf, flats.front()); + } else { + flats.push_back(MakeHexFlat(i)); + result = CordRepBtree::Append(leaf, flats.back()); + } + EXPECT_THAT(result->height(), Eq(0)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(result->Edges(), ElementsAreArray(flats)); + leaf = result; + } + CordRep::Unref(leaf); +} + +TEST_P(CordRepBtreeTest, AppendToLeafBeyondCapacity) { + AutoUnref refs; + auto* leaf = MakeLeaf(); + refs.RefIf(shared(), leaf); + CordRep* flat = MakeFlat("abc"); + auto* result = CordRepBtree::Append(leaf, flat); + ASSERT_THAT(result, IsNode(1)); + EXPECT_THAT(result, Ne(leaf)); + absl::Span<CordRep* const> edges = result->Edges(); + ASSERT_THAT(edges, ElementsAre(leaf, IsNode(0))); + EXPECT_THAT(edges[1]->btree()->Edges(), ElementsAre(flat)); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, PrependToLeafBeyondCapacity) { + AutoUnref refs; + auto* leaf = MakeLeaf(); + refs.RefIf(shared(), leaf); + CordRep* flat = MakeFlat("abc"); + auto* result = CordRepBtree::Prepend(leaf, flat); + ASSERT_THAT(result, IsNode(1)); + EXPECT_THAT(result, Ne(leaf)); + absl::Span<CordRep* const> edges = result->Edges(); + ASSERT_THAT(edges, ElementsAre(IsNode(0), leaf)); + EXPECT_THAT(edges[0]->btree()->Edges(), ElementsAre(flat)); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, AppendToTreeOneDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::vector<CordRep*> flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap; ++i) { + flats.push_back(MakeHexFlat(i)); + tree = CordRepBtree::Append(tree, flats.back()); + } + ASSERT_THAT(tree, IsNode(1)); + + for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref leaf node once every 4 iterations, which should not have an + // observable effect other than that the leaf itself is copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 4 == 0, tree->Edges().back()); + + flats.push_back(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Append(tree, flats.back()); + ASSERT_THAT(result, IsNode(1)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector<CordRep*> edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, AppendToTreeTwoDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::vector<CordRep*> flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap * max_cap; ++i) { + flats.push_back(MakeHexFlat(i)); + tree = CordRepBtree::Append(tree, flats.back()); + } + ASSERT_THAT(tree, IsNode(2)); + for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref child node once every 16 iterations, and leaf node every 4 + // iterrations which which should not have an observable effect other than + // the node and/or the leaf below it being copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 16 == 0, tree->Edges().back()); + refs.RefIf(i % 4 == 0, tree->Edges().back()->btree()->Edges().back()); + + flats.push_back(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Append(tree, flats.back()); + ASSERT_THAT(result, IsNode(2)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector<CordRep*> edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, PrependToTreeOneDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::deque<CordRep*> flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap; ++i) { + flats.push_front(MakeHexFlat(i)); + tree = CordRepBtree::Prepend(tree, flats.front()); + } + ASSERT_THAT(tree, IsNode(1)); + + for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref leaf node once every 4 iterations which should not have an observable + // effect other than than the leaf itself is copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 4 == 0, tree->Edges().back()); + + flats.push_front(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Prepend(tree, flats.front()); + ASSERT_THAT(result, IsNode(1)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector<CordRep*> edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, PrependToTreeTwoDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + AutoUnref refs; + std::deque<CordRep*> flats; + flats.push_back(MakeHexFlat(0)); + CordRepBtree* tree = CordRepBtree::Create(flats.back()); + for (size_t i = 1; i <= max_cap * max_cap; ++i) { + flats.push_front(MakeHexFlat(i)); + tree = CordRepBtree::Prepend(tree, flats.front()); + } + ASSERT_THAT(tree, IsNode(2)); + for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; ++i) { + // Ref top level tree based on param. + // Ref child node once every 16 iterations, and leaf node every 4 + // iterrations which which should not have an observable effect other than + // the node and/or the leaf below it being copied. + refs.RefIf(shared(), tree); + refs.RefIf(i % 16 == 0, tree->Edges().back()); + refs.RefIf(i % 4 == 0, tree->Edges().back()->btree()->Edges().back()); + + flats.push_front(MakeHexFlat(i)); + CordRepBtree* result = CordRepBtree::Prepend(tree, flats.front()); + ASSERT_THAT(result, IsNode(2)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + std::vector<CordRep*> edges = GetLeafEdges(result); + ASSERT_THAT(edges, ElementsAreArray(flats)); + tree = result; + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeDualTest, MergeLeafsNotExceedingCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector<CordRep*> flats; + + // Build `left` side leaf appending all contained flats to `flats` + CordRepBtree* left = MakeLeaf(3); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side leaf appending all contained flats to `flats` + CordRepBtree* right = MakeLeaf(2); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(0)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(tree->Edges(), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeLeafsExceedingCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + + // Build `left` side tree appending all contained flats to `flats` + CordRepBtree* left = MakeLeaf(CordRepBtree::kMaxCapacity - 2); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all contained flats to `flats` + CordRepBtree* right = MakeLeaf(CordRepBtree::kMaxCapacity - 1); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), ElementsAre(left, right)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeEqualHeightTrees) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector<CordRep*> flats; + + // Build `left` side tree appending all contained flats to `flats` + CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 3); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all contained flats to `flats` + CordRepBtree* right = MakeTree(CordRepBtree::kMaxCapacity * 2); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), SizeIs(5)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeLeafWithTreeNotExceedingLeafCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector<CordRep*> flats; + + // Build `left` side tree appending all added flats to `flats` + CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 2 + 2); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all added flats to `flats` + CordRepBtree* right = MakeTree(3); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), SizeIs(3)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeDualTest, MergeLeafWithTreeExceedingLeafCapacity) { + for (bool use_append : {false, true}) { + SCOPED_TRACE(use_append ? "Using Append" : "Using Prepend"); + + AutoUnref refs; + std::vector<CordRep*> flats; + + // Build `left` side tree appending all added flats to `flats` + CordRepBtree* left = MakeTree(CordRepBtree::kMaxCapacity * 3 - 2); + GetLeafEdges(left, flats); + refs.RefIf(first_shared(), left); + + // Build `right` side tree appending all added flats to `flats` + CordRepBtree* right = MakeTree(3); + GetLeafEdges(right, flats); + refs.RefIf(second_shared(), right); + + CordRepBtree* tree = use_append ? CordRepBtree::Append(left, right) + : CordRepBtree::Prepend(right, left); + EXPECT_THAT(tree, IsNode(1)); + EXPECT_THAT(tree->Edges(), SizeIs(4)); + + // `tree` contains all flats originally belonging to `left` and `right`. + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +void RefEdgesAt(size_t depth, AutoUnref& refs, CordRepBtree* tree) { + absl::Span<CordRep* const> edges = tree->Edges(); + if (depth == 0) { + refs.Ref(edges.front()); + refs.Ref(edges.back()); + } else { + assert(tree->height() > 0); + RefEdgesAt(depth - 1, refs, edges.front()->btree()); + RefEdgesAt(depth - 1, refs, edges.back()->btree()); + } +} + +TEST(CordRepBtreeTest, MergeFuzzTest) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + std::minstd_rand rnd; + std::uniform_int_distribution<int> coin_flip(0, 1); + std::uniform_int_distribution<int> dice_throw(1, 6); + + auto random_leaf_count = [&]() { + std::uniform_int_distribution<int> dist_height(0, 3); + std::uniform_int_distribution<int> dist_leaf(0, max_cap - 1); + const size_t height = dist_height(rnd); + return (height ? pow(max_cap, height) : 0) + dist_leaf(rnd); + }; + + for (int i = 0; i < 10000; ++i) { + AutoUnref refs; + std::vector<CordRep*> flats; + + CordRepBtree* left = MakeTree(random_leaf_count(), coin_flip(rnd)); + GetLeafEdges(left, flats); + if (dice_throw(rnd) == 1) { + std::uniform_int_distribution<int> dist(0, left->height()); + RefEdgesAt(dist(rnd), refs, left); + } + + CordRepBtree* right = MakeTree(random_leaf_count(), coin_flip(rnd)); + GetLeafEdges(right, flats); + if (dice_throw(rnd) == 1) { + std::uniform_int_distribution<int> dist(0, right->height()); + RefEdgesAt(dist(rnd), refs, right); + } + + CordRepBtree* tree = CordRepBtree::Append(left, right); + EXPECT_THAT(GetLeafEdges(tree), ElementsAreArray(flats)); + CordRepBtree::Unref(tree); + } +} + +TEST_P(CordRepBtreeTest, RemoveSuffix) { + // Create tree of 1, 2 and 3 levels high + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + for (size_t cap : {max_cap - 1, max_cap * 2, max_cap * max_cap * 2}) { + const std::string data = CreateRandomString(cap * 512); + + { + // Verify RemoveSuffix(<all>) + AutoUnref refs; + CordRepBtree* node = refs.RefIf(shared(), CreateTree(data, 512)); + EXPECT_THAT(CordRepBtree::RemoveSuffix(node, data.length()), Eq(nullptr)); + + // Verify RemoveSuffix(<none>) + node = refs.RefIf(shared(), CreateTree(data, 512)); + EXPECT_THAT(CordRepBtree::RemoveSuffix(node, 0), Eq(node)); + CordRep::Unref(node); + } + + for (int n = 1; n < data.length(); ++n) { + AutoUnref refs; + auto flats = CreateFlatsFromString(data, 512); + CordRepBtree* node = refs.RefIf(shared(), CreateTree(flats)); + CordRep* rep = refs.Add(CordRepBtree::RemoveSuffix(node, n)); + EXPECT_THAT(CordToString(rep), Eq(data.substr(0, data.length() - n))); + + // Collect all flats + auto is_flat = [](CordRep* rep) { return rep->tag >= FLAT; }; + std::vector<CordRep*> edges = CordCollectRepsIf(is_flat, rep); + ASSERT_THAT(edges.size(), Le(flats.size())); + + // Isolate last edge + CordRep* last_edge = edges.back(); + edges.pop_back(); + const size_t last_length = rep->length - edges.size() * 512; + + // All flats except the last edge must be kept or copied 'as is' + int index = 0; + for (CordRep* edge : edges) { + ASSERT_THAT(edge, Eq(flats[index++])); + ASSERT_THAT(edge->length, Eq(512)); + } + + // CordRepBtree may optimize small substrings to avoid waste, so only + // check for flat sharing / updates where the code should always do this. + if (last_length >= 500) { + EXPECT_THAT(last_edge, Eq(flats[index++])); + if (shared()) { + EXPECT_THAT(last_edge->length, Eq(512)); + } else { + EXPECT_TRUE(last_edge->refcount.IsOne()); + EXPECT_THAT(last_edge->length, Eq(last_length)); + } + } + } + } +} + +TEST(CordRepBtreeTest, SubTree) { + // Create tree of at least 2 levels high + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * 2; + const std::string data = CreateRandomString(n * 3); + std::vector<CordRep*> flats; + for (absl::string_view s = data; !s.empty(); s.remove_prefix(3)) { + flats.push_back(MakeFlat(s.substr(0, 3))); + } + CordRepBtree* node = CordRepBtree::Create(CordRep::Ref(flats[0])); + for (size_t i = 1; i < flats.size(); ++i) { + node = CordRepBtree::Append(node, CordRep::Ref(flats[i])); + } + + for (int offset = 0; offset < data.length(); ++offset) { + for (int length = 1; length <= data.length() - offset; ++length) { + CordRep* rep = node->SubTree(offset, length); + EXPECT_THAT(CordToString(rep), Eq(data.substr(offset, length))); + CordRep::Unref(rep); + } + } + CordRepBtree::Unref(node); + for (CordRep* rep : flats) { + CordRep::Unref(rep); + } +} + +TEST(CordRepBtreeTest, SubTreeOnExistingSubstring) { + // This test verifies that a SubTree call on a pre-existing (large) substring + // adjusts the existing substring if not shared, and else rewrites the + // existing substring. + AutoUnref refs; + std::string data = CreateRandomString(1000); + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc")); + CordRep* flat = MakeFlat(data); + leaf = CordRepBtree::Append(leaf, flat); + + // Setup tree containing substring. + CordRep* result = leaf->SubTree(0, 3 + 990); + ASSERT_THAT(result->tag, Eq(BTREE)); + CordRep::Unref(leaf); + leaf = result->btree(); + ASSERT_THAT(leaf->Edges(), ElementsAre(_, IsSubstring(0, 990))); + EXPECT_THAT(leaf->Edges()[1]->substring()->child, Eq(flat)); + + // Verify substring of substring. + result = leaf->SubTree(3 + 5, 970); + ASSERT_THAT(result, IsSubstring(5, 970)); + EXPECT_THAT(result->substring()->child, Eq(flat)); + CordRep::Unref(result); + + CordRep::Unref(leaf); +} + +TEST_P(CordRepBtreeTest, AddDataToLeaf) { + const size_t n = CordRepBtree::kMaxCapacity; + const std::string data = CreateRandomString(n * 3); + + for (bool append : {true, false}) { + AutoUnref refs; + DataConsumer consumer(data, append); + SCOPED_TRACE(append ? "Append" : "Prepend"); + + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat(consumer.Next(3))); + for (size_t i = 1; i < n; ++i) { + refs.RefIf(shared(), leaf); + CordRepBtree* result = BtreeAdd(leaf, append, consumer.Next(3)); + EXPECT_THAT(result, Conditional(shared(), Ne(leaf), Eq(leaf))); + EXPECT_THAT(CordToString(result), Eq(consumer.Consumed())); + leaf = result; + } + CordRep::Unref(leaf); + } +} + +TEST_P(CordRepBtreeTest, AppendDataToTree) { + AutoUnref refs; + size_t n = CordRepBtree::kMaxCapacity + CordRepBtree::kMaxCapacity / 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = refs.RefIf(shared(), CreateTree(data, 3)); + CordRepBtree* leaf0 = tree->Edges()[0]->btree(); + CordRepBtree* leaf1 = tree->Edges()[1]->btree(); + CordRepBtree* result = CordRepBtree::Append(tree, "123456789"); + EXPECT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + EXPECT_THAT(result->Edges(), + ElementsAre(leaf0, Conditional(shared(), Ne(leaf1), Eq(leaf1)))); + EXPECT_THAT(CordToString(result), Eq(data + "123456789")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, PrependDataToTree) { + AutoUnref refs; + size_t n = CordRepBtree::kMaxCapacity + CordRepBtree::kMaxCapacity / 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = refs.RefIf(shared(), CreateTreeReverse(data, 3)); + CordRepBtree* leaf0 = tree->Edges()[0]->btree(); + CordRepBtree* leaf1 = tree->Edges()[1]->btree(); + CordRepBtree* result = CordRepBtree::Prepend(tree, "123456789"); + EXPECT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + EXPECT_THAT(result->Edges(), + ElementsAre(Conditional(shared(), Ne(leaf0), Eq(leaf0)), leaf1)); + EXPECT_THAT(CordToString(result), Eq("123456789" + data)); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, AddDataToTreeThreeLevelsDeep) { + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * max_cap; + const std::string data = CreateRandomString(n * 3); + + for (bool append : {true, false}) { + AutoUnref refs; + DataConsumer consumer(data, append); + SCOPED_TRACE(append ? "Append" : "Prepend"); + + // Fill leaf + CordRepBtree* tree = CordRepBtree::Create(MakeFlat(consumer.Next(3))); + for (size_t i = 1; i < max_cap; ++i) { + tree = BtreeAdd(tree, append, consumer.Next(3)); + } + ASSERT_THAT(CordToString(tree), Eq(consumer.Consumed())); + + // Fill to maximum at one deep + refs.RefIf(shared(), tree); + CordRepBtree* result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, IsNode(1)); + ASSERT_THAT(result, Ne(tree)); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + for (size_t i = max_cap + 1; i < max_cap * max_cap; ++i) { + refs.RefIf(shared(), tree); + result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + } + + // Fill to maximum at two deep + refs.RefIf(shared(), tree); + result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, IsNode(2)); + ASSERT_THAT(result, Ne(tree)); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + for (size_t i = max_cap * max_cap + 1; i < max_cap * max_cap * max_cap; + ++i) { + refs.RefIf(shared(), tree); + result = BtreeAdd(tree, append, consumer.Next(3)); + ASSERT_THAT(result, Conditional(shared(), Ne(tree), Eq(tree))); + ASSERT_THAT(CordToString(result), Eq(consumer.Consumed())); + tree = result; + } + + CordRep::Unref(tree); + } +} + +TEST_P(CordRepBtreeTest, AddLargeDataToLeaf) { + const size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * max_cap * 3 + 2; + const std::string data = CreateRandomString(n * kMaxFlatLength); + + for (bool append : {true, false}) { + AutoUnref refs; + SCOPED_TRACE(append ? "Append" : "Prepend"); + + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc")); + refs.RefIf(shared(), leaf); + CordRepBtree* result = BtreeAdd(leaf, append, data); + EXPECT_THAT(CordToString(result), Eq(append ? "abc" + data : data + "abc")); + CordRep::Unref(result); + } +} + +TEST_P(CordRepBtreeDualTest, CreateFromConcat) { + AutoUnref refs; + CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; + auto* left = MakeConcat(flats[0], flats[1]); + auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); + auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); + CordRepBtree* result = CordRepBtree::Create(concat); + ASSERT_TRUE(CordRepBtree::IsValid(result)); + EXPECT_THAT(result->length, Eq(26)); + EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeDualTest, AppendConcat) { + AutoUnref refs; + CordRep* flats[] = {MakeFlat("defgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wxyz")}; + auto* left = MakeConcat(flats[0], flats[1]); + auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); + auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); + CordRepBtree* result = CordRepBtree::Create(MakeFlat("abc")); + result = CordRepBtree::Append(result, concat); + ASSERT_TRUE(CordRepBtree::IsValid(result)); + EXPECT_THAT(result->length, Eq(26)); + EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeDualTest, PrependConcat) { + AutoUnref refs; + CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"), + MakeFlat("nopqrstuv"), MakeFlat("wx")}; + auto* left = MakeConcat(flats[0], flats[1]); + auto* right = MakeConcat(flats[2], refs.RefIf(first_shared(), flats[3])); + auto* concat = refs.RefIf(second_shared(), MakeConcat(left, right)); + CordRepBtree* result = CordRepBtree::Create(MakeFlat("yz")); + result = CordRepBtree::Prepend(result, concat); + ASSERT_TRUE(CordRepBtree::IsValid(result)); + EXPECT_THAT(result->length, Eq(26)); + EXPECT_THAT(CordToString(result), Eq("abcdefghijklmnopqrstuvwxyz")); + CordRep::Unref(result); +} + +TEST_P(CordRepBtreeTest, CreateFromTreeReturnsTree) { + AutoUnref refs; + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("Hello world")); + refs.RefIf(shared(), leaf); + CordRepBtree* result = CordRepBtree::Create(leaf); + EXPECT_THAT(result, Eq(leaf)); + CordRep::Unref(result); +} + +TEST(CordRepBtreeTest, GetCharacter) { + size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = CreateTree(data, 3); + // Add a substring node for good measure. + tree = tree->Append(tree, MakeSubstring(4, 5, MakeFlat("abcdefghijklm"))); + data += "efghi"; + for (size_t i = 0; i < data.length(); ++i) { + ASSERT_THAT(tree->GetCharacter(i), Eq(data[i])); + } + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, IsFlatSingleFlat) { + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("Hello world")); + + absl::string_view fragment; + EXPECT_TRUE(leaf->IsFlat(nullptr)); + EXPECT_TRUE(leaf->IsFlat(&fragment)); + EXPECT_THAT(fragment, Eq("Hello world")); + fragment = ""; + EXPECT_TRUE(leaf->IsFlat(0, 11, nullptr)); + EXPECT_TRUE(leaf->IsFlat(0, 11, &fragment)); + EXPECT_THAT(fragment, Eq("Hello world")); + + // Arbitrary ranges must check true as well. + EXPECT_TRUE(leaf->IsFlat(1, 4, &fragment)); + EXPECT_THAT(fragment, Eq("ello")); + EXPECT_TRUE(leaf->IsFlat(6, 5, &fragment)); + EXPECT_THAT(fragment, Eq("world")); + + CordRep::Unref(leaf); +} + +TEST(CordRepBtreeTest, IsFlatMultiFlat) { + size_t n = CordRepBtree::kMaxCapacity * CordRepBtree::kMaxCapacity + 2; + std::string data = CreateRandomString(n * 3); + CordRepBtree* tree = CreateTree(data, 3); + // Add substring nodes for good measure. + tree = tree->Append(tree, MakeSubstring(4, 3, MakeFlat("abcdefghijklm"))); + tree = tree->Append(tree, MakeSubstring(8, 3, MakeFlat("abcdefghijklm"))); + data += "efgijk"; + + EXPECT_FALSE(tree->IsFlat(nullptr)); + absl::string_view fragment = "Can't touch this"; + EXPECT_FALSE(tree->IsFlat(&fragment)); + EXPECT_THAT(fragment, Eq("Can't touch this")); + + for (size_t offset = 0; offset < data.size(); offset += 3) { + EXPECT_TRUE(tree->IsFlat(offset, 3, nullptr)); + EXPECT_TRUE(tree->IsFlat(offset, 3, &fragment)); + EXPECT_THAT(fragment, Eq(data.substr(offset, 3))); + + fragment = "Can't touch this"; + if (offset > 0) { + EXPECT_FALSE(tree->IsFlat(offset - 1, 4, nullptr)); + EXPECT_FALSE(tree->IsFlat(offset - 1, 4, &fragment)); + EXPECT_THAT(fragment, Eq("Can't touch this")); + } + if (offset < data.size() - 4) { + EXPECT_FALSE(tree->IsFlat(offset, 4, nullptr)); + EXPECT_FALSE(tree->IsFlat(offset, 4, &fragment)); + EXPECT_THAT(fragment, Eq("Can't touch this")); + } + } + + CordRep::Unref(tree); +} + +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferNotPrivate) { + CordRepBtree* tree = CordRepBtree::Create(MakeExternal("Foo")); + CordRepBtree::Ref(tree); + EXPECT_DEATH(tree->GetAppendBuffer(1), ".*"); + CordRepBtree::Unref(tree); + CordRepBtree::Unref(tree); +} + +#endif // defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferNotFlat) { + CordRepBtree* tree = CordRepBtree::Create(MakeExternal("Foo")); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatNotPrivate) { + CordRepFlat* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(CordRep::Ref(flat)); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); + CordRep::Unref(flat); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferTreeNotPrivate) { + if (height() == 0) return; + AutoUnref refs; + CordRepFlat* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(CordRep::Ref(flat)); + for (int i = 1; i <= height(); ++i) { + if (i == (height() + 1) / 2) refs.Ref(tree); + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); + CordRep::Unref(flat); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatNoCapacity) { + CordRepFlat* flat = MakeFlat("abc"); + flat->length = flat->Capacity(); + CordRepBtree* tree = CordRepBtree::Create(flat); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + EXPECT_THAT(tree->GetAppendBuffer(1), SizeIs(0)); + CordRepBtree::Unref(tree); +} + +TEST_P(CordRepBtreeHeightTest, GetAppendBufferFlatWithCapacity) { + CordRepFlat* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(flat); + for (int i = 1; i <= height(); ++i) { + tree = CordRepBtree::New(tree); + } + absl::Span<char> span = tree->GetAppendBuffer(2); + EXPECT_THAT(span, SizeIs(2)); + EXPECT_THAT(span.data(), TypedEq<void*>(flat->Data() + 3)); + EXPECT_THAT(tree->length, Eq(5)); + + size_t avail = flat->Capacity() - 5; + span = tree->GetAppendBuffer(avail + 100); + EXPECT_THAT(span, SizeIs(avail)); + EXPECT_THAT(span.data(), TypedEq<void*>(flat->Data() + 5)); + EXPECT_THAT(tree->length, Eq(5 + avail)); + + CordRepBtree::Unref(tree); +} + +TEST(CordRepBtreeTest, Dump) { + // Handles nullptr + std::stringstream ss; + CordRepBtree::Dump(nullptr, ss); + CordRepBtree::Dump(nullptr, "Once upon a label", ss); + CordRepBtree::Dump(nullptr, "Once upon a label", false, ss); + CordRepBtree::Dump(nullptr, "Once upon a label", true, ss); + + // Cover legal edges + CordRepFlat* flat = MakeFlat("Hello world"); + CordRepExternal* external = MakeExternal("Hello external"); + CordRep* substr_flat = MakeSubstring(1, 6, CordRep::Ref(flat)); + CordRep* substr_external = MakeSubstring(2, 7, CordRep::Ref(external)); + + // Build tree + CordRepBtree* tree = CordRepBtree::Create(flat); + tree = CordRepBtree::Append(tree, external); + tree = CordRepBtree::Append(tree, substr_flat); + tree = CordRepBtree::Append(tree, substr_external); + + // Repeat until we have a tree + while (tree->height() == 0) { + tree = CordRepBtree::Append(tree, CordRep::Ref(flat)); + tree = CordRepBtree::Append(tree, CordRep::Ref(external)); + tree = CordRepBtree::Append(tree, CordRep::Ref(substr_flat)); + tree = CordRepBtree::Append(tree, CordRep::Ref(substr_external)); + } + + for (int api = 0; api <= 3; ++api) { + absl::string_view api_scope; + std::stringstream ss; + switch (api) { + case 0: + api_scope = "Bare"; + CordRepBtree::Dump(tree, ss); + break; + case 1: + api_scope = "Label only"; + CordRepBtree::Dump(tree, "Once upon a label", ss); + break; + case 2: + api_scope = "Label no content"; + CordRepBtree::Dump(tree, "Once upon a label", false, ss); + break; + default: + api_scope = "Label and content"; + CordRepBtree::Dump(tree, "Once upon a label", true, ss); + break; + } + SCOPED_TRACE(api_scope); + std::string str = ss.str(); + + // Contains Node(depth) / Leaf and private / shared indicators + EXPECT_THAT(str, AllOf(HasSubstr("Node(1)"), HasSubstr("Leaf"), + HasSubstr("Private"), HasSubstr("Shared"))); + + // Contains length and start offset of all data edges + EXPECT_THAT(str, AllOf(HasSubstr("len = 11"), HasSubstr("len = 14"), + HasSubstr("len = 6"), HasSubstr("len = 7"), + HasSubstr("start = 1"), HasSubstr("start = 2"))); + + // Contains address of all data edges + EXPECT_THAT( + str, AllOf(HasSubstr(absl::StrCat("0x", absl::Hex(flat))), + HasSubstr(absl::StrCat("0x", absl::Hex(external))), + HasSubstr(absl::StrCat("0x", absl::Hex(substr_flat))), + HasSubstr(absl::StrCat("0x", absl::Hex(substr_external))))); + + if (api != 0) { + // Contains label + EXPECT_THAT(str, HasSubstr("Once upon a label")); + } + + if (api != 3) { + // Does not contain contents + EXPECT_THAT(str, Not(AnyOf((HasSubstr("data = \"Hello world\""), + HasSubstr("data = \"Hello external\""), + HasSubstr("data = \"ello w\""), + HasSubstr("data = \"llo ext\""))))); + } else { + // Contains contents + EXPECT_THAT(str, AllOf((HasSubstr("data = \"Hello world\""), + HasSubstr("data = \"Hello external\""), + HasSubstr("data = \"ello w\""), + HasSubstr("data = \"llo ext\"")))); + } + } + + CordRep::Unref(tree); +} + +TEST(CordRepBtreeTest, IsValid) { + EXPECT_FALSE(CordRepBtree::IsValid(nullptr)); + + CordRepBtree* empty = CordRepBtree::New(0); + EXPECT_TRUE(CordRepBtree::IsValid(empty)); + CordRep::Unref(empty); + + for (bool as_tree : {false, true}) { + CordRepBtree* leaf = CordRepBtree::Create(MakeFlat("abc")); + CordRepBtree* tree = as_tree ? CordRepBtree::New(leaf) : nullptr; + CordRepBtree* check = as_tree ? tree : leaf; + + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->length--; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->length++; + + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->tag--; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->tag++; + + // Height + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->storage[0] = static_cast<uint8_t>(CordRepBtree::kMaxHeight + 1); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[0] = 1; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[0] = 0; + + // Begin + ASSERT_TRUE(CordRepBtree::IsValid(check)); + const uint8_t begin = leaf->storage[1]; + leaf->storage[1] = static_cast<uint8_t>(CordRepBtree::kMaxCapacity); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[1] = 2; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[1] = begin; + + // End + ASSERT_TRUE(CordRepBtree::IsValid(check)); + const uint8_t end = leaf->storage[2]; + leaf->storage[2] = static_cast<uint8_t>(CordRepBtree::kMaxCapacity + 1); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->storage[2] = end; + + // DataEdge tag and value + ASSERT_TRUE(CordRepBtree::IsValid(check)); + CordRep* const edge = leaf->Edges()[0]; + const uint8_t tag = edge->tag; + CordRepBtreeTestPeer::SetEdge(leaf, begin, nullptr); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + CordRepBtreeTestPeer::SetEdge(leaf, begin, edge); + edge->tag = BTREE; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + edge->tag = tag; + + if (as_tree) { + ASSERT_TRUE(CordRepBtree::IsValid(check)); + leaf->length--; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + leaf->length++; + + // Height + ASSERT_TRUE(CordRepBtree::IsValid(check)); + tree->storage[0] = static_cast<uint8_t>(2); + EXPECT_FALSE(CordRepBtree::IsValid(check)); + tree->storage[0] = 1; + + // Btree edge + ASSERT_TRUE(CordRepBtree::IsValid(check)); + CordRep* const edge = tree->Edges()[0]; + const uint8_t tag = edge->tag; + edge->tag = FLAT; + EXPECT_FALSE(CordRepBtree::IsValid(check)); + edge->tag = tag; + } + + ASSERT_TRUE(CordRepBtree::IsValid(check)); + CordRep::Unref(check); + } +} + +TEST(CordRepBtreeTest, AssertValid) { + CordRepBtree* tree = CordRepBtree::Create(MakeFlat("abc")); + const CordRepBtree* ctree = tree; + EXPECT_THAT(CordRepBtree::AssertValid(tree), Eq(tree)); + EXPECT_THAT(CordRepBtree::AssertValid(ctree), Eq(ctree)); + +#if defined(GTEST_HAS_DEATH_TEST) + CordRepBtree* nulltree = nullptr; + const CordRepBtree* cnulltree = nullptr; + EXPECT_DEBUG_DEATH( + EXPECT_THAT(CordRepBtree::AssertValid(nulltree), Eq(nulltree)), ".*"); + EXPECT_DEBUG_DEATH( + EXPECT_THAT(CordRepBtree::AssertValid(cnulltree), Eq(cnulltree)), ".*"); + + tree->length--; + EXPECT_DEBUG_DEATH(EXPECT_THAT(CordRepBtree::AssertValid(tree), Eq(tree)), + ".*"); + EXPECT_DEBUG_DEATH(EXPECT_THAT(CordRepBtree::AssertValid(ctree), Eq(ctree)), + ".*"); + tree->length++; +#endif + CordRep::Unref(tree); +} + +TEST(CordRepBtreeTest, CheckAssertValidShallowVsDeep) { + // Restore exhaustive validation on any exit. + const bool exhaustive_validation = cord_btree_exhaustive_validation.load(); + auto cleanup = absl::MakeCleanup([exhaustive_validation] { + cord_btree_exhaustive_validation.store(exhaustive_validation); + }); + + // Create a tree of at least 2 levels, and mess with the original flat, which + // should go undetected in shallow mode as the flat is too far away, but + // should be detected in forced non-shallow mode. + CordRep* flat = MakeFlat("abc"); + CordRepBtree* tree = CordRepBtree::Create(flat); + constexpr size_t max_cap = CordRepBtree::kMaxCapacity; + const size_t n = max_cap * max_cap * 2; + for (size_t i = 0; i < n; ++i) { + tree = CordRepBtree::Append(tree, MakeFlat("Hello world")); + } + flat->length = 100; + + cord_btree_exhaustive_validation.store(false); + EXPECT_FALSE(CordRepBtree::IsValid(tree)); + EXPECT_TRUE(CordRepBtree::IsValid(tree, true)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, false)); + CordRepBtree::AssertValid(tree); + CordRepBtree::AssertValid(tree, true); +#if defined(GTEST_HAS_DEATH_TEST) + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, false), ".*"); +#endif + + cord_btree_exhaustive_validation.store(true); + EXPECT_FALSE(CordRepBtree::IsValid(tree)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, true)); + EXPECT_FALSE(CordRepBtree::IsValid(tree, false)); +#if defined(GTEST_HAS_DEATH_TEST) + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree), ".*"); + EXPECT_DEBUG_DEATH(CordRepBtree::AssertValid(tree, true), ".*"); +#endif + + flat->length = 3; + CordRep::Unref(tree); +} + +TEST_P(CordRepBtreeTest, Rebuild) { + for (size_t size : {3, 8, 100, 10000, 1000000}) { + SCOPED_TRACE(absl::StrCat("Rebuild @", size)); + + std::vector<CordRepFlat*> flats; + for (int i = 0; i < size; ++i) { + flats.push_back(CordRepFlat::New(2)); + flats.back()->Data()[0] = 'x'; + flats.back()->length = 1; + } + + // Build the tree into 'right', and each so many 'split_limit' edges, + // combine 'left' + 'right' into a new 'left', and start a new 'right'. + // This guarantees we get a reasonable amount of chaos in the tree. + size_t split_count = 0; + size_t split_limit = 3; + auto it = flats.begin(); + CordRepBtree* left = nullptr; + CordRepBtree* right = CordRepBtree::New(*it); + while (++it != flats.end()) { + if (++split_count >= split_limit) { + split_limit += split_limit / 16; + left = left ? CordRepBtree::Append(left, right) : right; + right = CordRepBtree::New(*it); + } else { + right = CordRepBtree::Append(right, *it); + } + } + + // Finalize tree + left = left ? CordRepBtree::Append(left, right) : right; + + // Rebuild + AutoUnref ref; + left = ref.Add(CordRepBtree::Rebuild(ref.RefIf(shared(), left))); + ASSERT_TRUE(CordRepBtree::IsValid(left)); + + // Verify we have the exact same edges in the exact same order. + bool ok = true; + it = flats.begin(); + CordVisitReps(left, [&](CordRep* edge) { + if (edge->tag < FLAT) return; + ok = ok && (it != flats.end() && *it++ == edge); + }); + EXPECT_TRUE(ok && it == flats.end()) << "Rebuild edges mismatch"; + } +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.cc new file mode 100644 index 0000000000..81514543db --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.cc @@ -0,0 +1,129 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_consume.h" + +#include <array> +#include <utility> + +#include "absl/container/inlined_vector.h" +#include "absl/functional/function_ref.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +namespace { + +// Unrefs the provided `substring`, and returns `substring->child` +// Adds or assumes a reference on `substring->child` +CordRep* ClipSubstring(CordRepSubstring* substring) { + CordRep* child = substring->child; + if (substring->refcount.IsOne()) { + delete substring; + } else { + CordRep::Ref(child); + CordRep::Unref(substring); + } + return child; +} + +// Unrefs the provided `concat`, and returns `{concat->left, concat->right}` +// Adds or assumes a reference on `concat->left` and `concat->right`. +// Returns an array of 2 elements containing the left and right nodes. +std::array<CordRep*, 2> ClipConcat(CordRepConcat* concat) { + std::array<CordRep*, 2> result{concat->left, concat->right}; + if (concat->refcount.IsOne()) { + delete concat; + } else { + CordRep::Ref(result[0]); + CordRep::Ref(result[1]); + CordRep::Unref(concat); + } + return result; +} + +void Consume(bool forward, CordRep* rep, ConsumeFn consume_fn) { + size_t offset = 0; + size_t length = rep->length; + struct Entry { + CordRep* rep; + size_t offset; + size_t length; + }; + absl::InlinedVector<Entry, 40> stack; + + for (;;) { + if (rep->tag == CONCAT) { + std::array<CordRep*, 2> res = ClipConcat(rep->concat()); + CordRep* left = res[0]; + CordRep* right = res[1]; + + if (left->length <= offset) { + // Don't need left node + offset -= left->length; + CordRep::Unref(left); + rep = right; + continue; + } + + size_t length_left = left->length - offset; + if (length_left >= length) { + // Don't need right node + CordRep::Unref(right); + rep = left; + continue; + } + + // Need both nodes + size_t length_right = length - length_left; + if (forward) { + stack.push_back({right, 0, length_right}); + rep = left; + length = length_left; + } else { + stack.push_back({left, offset, length_left}); + rep = right; + offset = 0; + length = length_right; + } + } else if (rep->tag == SUBSTRING) { + offset += rep->substring()->start; + rep = ClipSubstring(rep->substring()); + } else { + consume_fn(rep, offset, length); + if (stack.empty()) return; + + rep = stack.back().rep; + offset = stack.back().offset; + length = stack.back().length; + stack.pop_back(); + } + } +} + +} // namespace + +void Consume(CordRep* rep, ConsumeFn consume_fn) { + return Consume(true, rep, std::move(consume_fn)); +} + +void ReverseConsume(CordRep* rep, ConsumeFn consume_fn) { + return Consume(false, rep, std::move(consume_fn)); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.h new file mode 100644 index 0000000000..d46fca2b21 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume.h @@ -0,0 +1,50 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ + +#include <functional> + +#include "absl/functional/function_ref.h" +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Functor for the Consume() and ReverseConsume() functions: +// void ConsumeFunc(CordRep* rep, size_t offset, size_t length); +// See the Consume() and ReverseConsume() function comments for documentation. +using ConsumeFn = FunctionRef<void(CordRep*, size_t, size_t)>; + +// Consume() and ReverseConsume() consume CONCAT based trees and invoke the +// provided functor with the contained nodes in the proper forward or reverse +// order, which is used to convert CONCAT trees into other tree or cord data. +// All CONCAT and SUBSTRING nodes are processed internally. The 'offset` +// parameter of the functor is non-zero for any nodes below SUBSTRING nodes. +// It's up to the caller to form these back into SUBSTRING nodes or otherwise +// store offset / prefix information. These functions are intended to be used +// only for migration / transitional code where due to factors such as ODR +// violations, we can not 100% guarantee that all code respects 'new format' +// settings and flags, so we need to be able to parse old data on the fly until +// all old code is deprecated / no longer the default format. +void Consume(CordRep* rep, ConsumeFn consume_fn); +void ReverseConsume(CordRep* rep, ConsumeFn consume_fn); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume_test.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume_test.cc new file mode 100644 index 0000000000..e507824b4f --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_consume_test.cc @@ -0,0 +1,173 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_consume.h" + +#include <functional> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using testing::InSequence; +using testing::MockFunction; + +// Returns the depth of a node +int Depth(const CordRep* rep) { + return (rep->tag == CONCAT) ? rep->concat()->depth() : 0; +} + +// Creates a concatenation of the specified nodes. +CordRepConcat* CreateConcat(CordRep* left, CordRep* right) { + auto* concat = new CordRepConcat(); + concat->tag = CONCAT; + concat->left = left; + concat->right = right; + concat->length = left->length + right->length; + concat->set_depth(1 + (std::max)(Depth(left), Depth(right))); + return concat; +} + +// Creates a flat with the length set to `length` +CordRepFlat* CreateFlatWithLength(size_t length) { + auto* flat = CordRepFlat::New(length); + flat->length = length; + return flat; +} + +// Creates a substring node on the specified child. +CordRepSubstring* CreateSubstring(CordRep* child, size_t start, size_t length) { + auto* rep = new CordRepSubstring(); + rep->length = length; + rep->tag = SUBSTRING; + rep->start = start; + rep->child = child; + return rep; +} + +// Flats we use in the tests +CordRep* flat[6]; + +// Creates a test tree +CordRep* CreateTestTree() { + flat[0] = CreateFlatWithLength(1); + flat[1] = CreateFlatWithLength(7); + CordRepConcat* left = CreateConcat(flat[0], CreateSubstring(flat[1], 2, 4)); + + flat[2] = CreateFlatWithLength(9); + flat[3] = CreateFlatWithLength(13); + CordRepConcat* right1 = CreateConcat(flat[2], flat[3]); + + flat[4] = CreateFlatWithLength(15); + flat[5] = CreateFlatWithLength(19); + CordRepConcat* right2 = CreateConcat(flat[4], flat[5]); + + CordRepConcat* right = CreateConcat(right1, CreateSubstring(right2, 5, 17)); + return CreateConcat(left, right); +} + +TEST(CordRepConsumeTest, Consume) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction<void(CordRep*, size_t, size_t)> consume; + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + Consume(tree, consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_TRUE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } +} + +TEST(CordRepConsumeTest, ConsumeShared) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction<void(CordRep*, size_t, size_t)> consume; + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + Consume(CordRep::Ref(tree), consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_FALSE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } + CordRep::Unref(tree); +} + +TEST(CordRepConsumeTest, Reverse) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction<void(CordRep*, size_t, size_t)> consume; + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + ReverseConsume(tree, consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_TRUE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } +} + +TEST(CordRepConsumeTest, ReverseShared) { + InSequence in_sequence; + CordRep* tree = CreateTestTree(); + MockFunction<void(CordRep*, size_t, size_t)> consume; + EXPECT_CALL(consume, Call(flat[5], 0, 7)); + EXPECT_CALL(consume, Call(flat[4], 5, 10)); + EXPECT_CALL(consume, Call(flat[3], 0, 13)); + EXPECT_CALL(consume, Call(flat[2], 0, 9)); + EXPECT_CALL(consume, Call(flat[1], 2, 4)); + EXPECT_CALL(consume, Call(flat[0], 0, 1)); + ReverseConsume(CordRep::Ref(tree), consume.AsStdFunction()); + for (CordRep* rep : flat) { + EXPECT_FALSE(rep->refcount.IsOne()); + CordRep::Unref(rep); + } + CordRep::Unref(tree); +} + +TEST(CordRepConsumeTest, UnreachableFlat) { + InSequence in_sequence; + CordRepFlat* flat1 = CreateFlatWithLength(10); + CordRepFlat* flat2 = CreateFlatWithLength(20); + CordRepConcat* concat = CreateConcat(flat1, flat2); + CordRepSubstring* tree = CreateSubstring(concat, 15, 10); + MockFunction<void(CordRep*, size_t, size_t)> consume; + EXPECT_CALL(consume, Call(flat2, 5, 10)); + Consume(tree, consume.AsStdFunction()); + EXPECT_TRUE(flat2->refcount.IsOne()); + CordRep::Unref(flat2); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_flat.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_flat.h new file mode 100644 index 0000000000..4d0f988697 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_flat.h @@ -0,0 +1,146 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> + +#include "absl/strings/internal/cord_internal.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Note: all constants below are never ODR used and internal to cord, we define +// these as static constexpr to avoid 'in struct' definition and usage clutter. + +// Largest and smallest flat node lengths we are willing to allocate +// Flat allocation size is stored in tag, which currently can encode sizes up +// to 4K, encoded as multiple of either 8 or 32 bytes. +// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc. +// kMinFlatSize is bounded by tag needing to be at least FLAT * 8 bytes, and +// ideally a 'nice' size aligning with allocation and cacheline sizes like 32. +// kMaxFlatSize is bounded by the size resulting in a computed tag no greater +// than MAX_FLAT_TAG. MAX_FLAT_TAG provides for additional 'high' tag values. +static constexpr size_t kFlatOverhead = offsetof(CordRep, storage); +static constexpr size_t kMinFlatSize = 32; +static constexpr size_t kMaxFlatSize = 4096; +static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead; +static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead; + +constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) { + return static_cast<uint8_t>((size <= 1024) ? size / 8 + 1 + : 129 + size / 32 - 1024 / 32); +} + +static_assert(kMinFlatSize / 8 + 1 >= FLAT, ""); +static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, ""); + +// Helper functions for rounded div, and rounding to exact sizes. +constexpr size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; } +constexpr size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; } + +// Returns the size to the nearest equal or larger value that can be +// expressed exactly as a tag value. +inline size_t RoundUpForTag(size_t size) { + return RoundUp(size, (size <= 1024) ? 8 : 32); +} + +// Converts the allocated size to a tag, rounding down if the size +// does not exactly match a 'tag expressible' size value. The result is +// undefined if the size exceeds the maximum size that can be encoded in +// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>). +inline uint8_t AllocatedSizeToTag(size_t size) { + const uint8_t tag = AllocatedSizeToTagUnchecked(size); + assert(tag <= MAX_FLAT_TAG); + return tag; +} + +// Converts the provided tag to the corresponding allocated size +constexpr size_t TagToAllocatedSize(uint8_t tag) { + return (tag <= 129) ? ((tag - 1) * 8) : (1024 + (tag - 129) * 32); +} + +// Converts the provided tag to the corresponding available data length +constexpr size_t TagToLength(uint8_t tag) { + return TagToAllocatedSize(tag) - kFlatOverhead; +} + +// Enforce that kMaxFlatSize maps to a well-known exact tag value. +static_assert(TagToAllocatedSize(225) == kMaxFlatSize, "Bad tag logic"); + +struct CordRepFlat : public CordRep { + // Creates a new flat node. + static CordRepFlat* New(size_t len) { + if (len <= kMinFlatLength) { + len = kMinFlatLength; + } else if (len > kMaxFlatLength) { + len = kMaxFlatLength; + } + + // Round size up so it matches a size we can exactly express in a tag. + const size_t size = RoundUpForTag(len + kFlatOverhead); + void* const raw_rep = ::operator new(size); + CordRepFlat* rep = new (raw_rep) CordRepFlat(); + rep->tag = AllocatedSizeToTag(size); + return rep; + } + + // Deletes a CordRepFlat instance created previously through a call to New(). + // Flat CordReps are allocated and constructed with raw ::operator new and + // placement new, and must be destructed and deallocated accordingly. + static void Delete(CordRep*rep) { + assert(rep->tag >= FLAT && rep->tag <= MAX_FLAT_TAG); + +#if defined(__cpp_sized_deallocation) + size_t size = TagToAllocatedSize(rep->tag); + rep->~CordRep(); + ::operator delete(rep, size); +#else + rep->~CordRep(); + ::operator delete(rep); +#endif + } + + // Returns a pointer to the data inside this flat rep. + char* Data() { return reinterpret_cast<char*>(storage); } + const char* Data() const { return reinterpret_cast<const char*>(storage); } + + // Returns the maximum capacity (payload size) of this instance. + size_t Capacity() const { return TagToLength(tag); } + + // Returns the allocated size (payload + overhead) of this instance. + size_t AllocatedSize() const { return TagToAllocatedSize(tag); } +}; + +// Now that CordRepFlat is defined, we can define CordRep's helper casts: +inline CordRepFlat* CordRep::flat() { + assert(tag >= FLAT && tag <= MAX_FLAT_TAG); + return reinterpret_cast<CordRepFlat*>(this); +} + +inline const CordRepFlat* CordRep::flat() const { + assert(tag >= FLAT && tag <= MAX_FLAT_TAG); + return reinterpret_cast<const CordRepFlat*>(this); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_FLAT_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.cc b/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.cc new file mode 100644 index 0000000000..07c77eb3e5 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.cc @@ -0,0 +1,771 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cord_rep_ring.h" + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <iostream> +#include <limits> +#include <memory> +#include <string> + +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/base/macros.h" +#include "absl/container/inlined_vector.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_consume.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +namespace { + +using index_type = CordRepRing::index_type; + +enum class Direction { kForward, kReversed }; + +inline bool IsFlatOrExternal(CordRep* rep) { + return rep->IsFlat() || rep->IsExternal(); +} + +// Verifies that n + extra <= kMaxCapacity: throws std::length_error otherwise. +inline void CheckCapacity(size_t n, size_t extra) { + if (ABSL_PREDICT_FALSE(extra > CordRepRing::kMaxCapacity - n)) { + base_internal::ThrowStdLengthError("Maximum capacity exceeded"); + } +} + +// Creates a flat from the provided string data, allocating up to `extra` +// capacity in the returned flat depending on kMaxFlatLength limitations. +// Requires `len` to be less or equal to `kMaxFlatLength` +CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) { // NOLINT + assert(n <= kMaxFlatLength); + auto* rep = CordRepFlat::New(n + extra); + rep->length = n; + memcpy(rep->Data(), s, n); + return rep; +} + +// Unrefs the entries in `[head, tail)`. +// Requires all entries to be a FLAT or EXTERNAL node. +void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) { + rep->ForEach(head, tail, [rep](index_type ix) { + CordRep* child = rep->entry_child(ix); + if (!child->refcount.Decrement()) { + if (child->tag >= FLAT) { + CordRepFlat::Delete(child->flat()); + } else { + CordRepExternal::Delete(child->external()); + } + } + }); +} + +} // namespace + +std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) { + // Note: 'pos' values are defined as size_t (for overflow reasons), but that + // prints really awkward for small prepended values such as -5. ssize_t is not + // portable (POSIX), so we use ptrdiff_t instead to cast to signed values. + s << " CordRepRing(" << &rep << ", length = " << rep.length + << ", head = " << rep.head_ << ", tail = " << rep.tail_ + << ", cap = " << rep.capacity_ << ", rc = " << rep.refcount.Get() + << ", begin_pos_ = " << static_cast<ptrdiff_t>(rep.begin_pos_) << ") {\n"; + CordRepRing::index_type head = rep.head(); + do { + CordRep* child = rep.entry_child(head); + s << " entry[" << head << "] length = " << rep.entry_length(head) + << ", child " << child << ", clen = " << child->length + << ", tag = " << static_cast<int>(child->tag) + << ", rc = " << child->refcount.Get() + << ", offset = " << rep.entry_data_offset(head) + << ", end_pos = " << static_cast<ptrdiff_t>(rep.entry_end_pos(head)) + << "\n"; + head = rep.advance(head); + } while (head != rep.tail()); + return s << "}\n"; +} + +void CordRepRing::AddDataOffset(index_type index, size_t n) { + entry_data_offset()[index] += static_cast<offset_type>(n); +} + +void CordRepRing::SubLength(index_type index, size_t n) { + entry_end_pos()[index] -= n; +} + +class CordRepRing::Filler { + public: + Filler(CordRepRing* rep, index_type pos) : rep_(rep), head_(pos), pos_(pos) {} + + index_type head() const { return head_; } + index_type pos() const { return pos_; } + + void Add(CordRep* child, size_t offset, pos_type end_pos) { + rep_->entry_end_pos()[pos_] = end_pos; + rep_->entry_child()[pos_] = child; + rep_->entry_data_offset()[pos_] = static_cast<offset_type>(offset); + pos_ = rep_->advance(pos_); + } + + private: + CordRepRing* rep_; + index_type head_; + index_type pos_; +}; + +constexpr size_t CordRepRing::kMaxCapacity; // NOLINT: needed for c++11 + +bool CordRepRing::IsValid(std::ostream& output) const { + if (capacity_ == 0) { + output << "capacity == 0"; + return false; + } + + if (head_ >= capacity_ || tail_ >= capacity_) { + output << "head " << head_ << " and/or tail " << tail_ << "exceed capacity " + << capacity_; + return false; + } + + const index_type back = retreat(tail_); + size_t pos_length = Distance(begin_pos_, entry_end_pos(back)); + if (pos_length != length) { + output << "length " << length << " does not match positional length " + << pos_length << " from begin_pos " << begin_pos_ << " and entry[" + << back << "].end_pos " << entry_end_pos(back); + return false; + } + + index_type head = head_; + pos_type begin_pos = begin_pos_; + do { + pos_type end_pos = entry_end_pos(head); + size_t entry_length = Distance(begin_pos, end_pos); + if (entry_length == 0) { + output << "entry[" << head << "] has an invalid length " << entry_length + << " from begin_pos " << begin_pos << " and end_pos " << end_pos; + return false; + } + + CordRep* child = entry_child(head); + if (child == nullptr) { + output << "entry[" << head << "].child == nullptr"; + return false; + } + if (child->tag < FLAT && child->tag != EXTERNAL) { + output << "entry[" << head << "].child has an invalid tag " + << static_cast<int>(child->tag); + return false; + } + + size_t offset = entry_data_offset(head); + if (offset >= child->length || entry_length > child->length - offset) { + output << "entry[" << head << "] has offset " << offset + << " and entry length " << entry_length + << " which are outside of the child's length of " << child->length; + return false; + } + + begin_pos = end_pos; + head = advance(head); + } while (head != tail_); + + return true; +} + +#ifdef EXTRA_CORD_RING_VALIDATION +CordRepRing* CordRepRing::Validate(CordRepRing* rep, const char* file, + int line) { + if (!rep->IsValid(std::cerr)) { + std::cerr << "\nERROR: CordRepRing corrupted"; + if (line) std::cerr << " at line " << line; + if (file) std::cerr << " in file " << file; + std::cerr << "\nContent = " << *rep; + abort(); + } + return rep; +} +#endif // EXTRA_CORD_RING_VALIDATION + +CordRepRing* CordRepRing::New(size_t capacity, size_t extra) { + CheckCapacity(capacity, extra); + + size_t size = AllocSize(capacity += extra); + void* mem = ::operator new(size); + auto* rep = new (mem) CordRepRing(static_cast<index_type>(capacity)); + rep->tag = RING; + rep->capacity_ = static_cast<index_type>(capacity); + rep->begin_pos_ = 0; + return rep; +} + +void CordRepRing::SetCapacityForTesting(size_t capacity) { + // Adjust for the changed layout + assert(capacity <= capacity_); + assert(head() == 0 || head() < tail()); + memmove(Layout::Partial(capacity).Pointer<1>(data_) + head(), + Layout::Partial(capacity_).Pointer<1>(data_) + head(), + entries() * sizeof(Layout::ElementType<1>)); + memmove(Layout::Partial(capacity, capacity).Pointer<2>(data_) + head(), + Layout::Partial(capacity_, capacity_).Pointer<2>(data_) + head(), + entries() * sizeof(Layout::ElementType<2>)); + capacity_ = static_cast<index_type>(capacity); +} + +void CordRepRing::Delete(CordRepRing* rep) { + assert(rep != nullptr && rep->IsRing()); +#if defined(__cpp_sized_deallocation) + size_t size = AllocSize(rep->capacity_); + rep->~CordRepRing(); + ::operator delete(rep, size); +#else + rep->~CordRepRing(); + ::operator delete(rep); +#endif +} + +void CordRepRing::Destroy(CordRepRing* rep) { + UnrefEntries(rep, rep->head(), rep->tail()); + Delete(rep); +} + +template <bool ref> +void CordRepRing::Fill(const CordRepRing* src, index_type head, + index_type tail) { + this->length = src->length; + head_ = 0; + tail_ = advance(0, src->entries(head, tail)); + begin_pos_ = src->begin_pos_; + + // TODO(mvels): there may be opportunities here for large buffers. + auto* dst_pos = entry_end_pos(); + auto* dst_child = entry_child(); + auto* dst_offset = entry_data_offset(); + src->ForEach(head, tail, [&](index_type index) { + *dst_pos++ = src->entry_end_pos(index); + CordRep* child = src->entry_child(index); + *dst_child++ = ref ? CordRep::Ref(child) : child; + *dst_offset++ = src->entry_data_offset(index); + }); +} + +CordRepRing* CordRepRing::Copy(CordRepRing* rep, index_type head, + index_type tail, size_t extra) { + CordRepRing* newrep = CordRepRing::New(rep->entries(head, tail), extra); + newrep->Fill<true>(rep, head, tail); + CordRep::Unref(rep); + return newrep; +} + +CordRepRing* CordRepRing::Mutable(CordRepRing* rep, size_t extra) { + // Get current number of entries, and check for max capacity. + size_t entries = rep->entries(); + + if (!rep->refcount.IsMutable()) { + return Copy(rep, rep->head(), rep->tail(), extra); + } else if (entries + extra > rep->capacity()) { + const size_t min_grow = rep->capacity() + rep->capacity() / 2; + const size_t min_extra = (std::max)(extra, min_grow - entries); + CordRepRing* newrep = CordRepRing::New(entries, min_extra); + newrep->Fill<false>(rep, rep->head(), rep->tail()); + CordRepRing::Delete(rep); + return newrep; + } else { + return rep; + } +} + +Span<char> CordRepRing::GetAppendBuffer(size_t size) { + assert(refcount.IsMutable()); + index_type back = retreat(tail_); + CordRep* child = entry_child(back); + if (child->tag >= FLAT && child->refcount.IsMutable()) { + size_t capacity = child->flat()->Capacity(); + pos_type end_pos = entry_end_pos(back); + size_t data_offset = entry_data_offset(back); + size_t entry_length = Distance(entry_begin_pos(back), end_pos); + size_t used = data_offset + entry_length; + if (size_t n = (std::min)(capacity - used, size)) { + child->length = data_offset + entry_length + n; + entry_end_pos()[back] = end_pos + n; + this->length += n; + return {child->flat()->Data() + used, n}; + } + } + return {nullptr, 0}; +} + +Span<char> CordRepRing::GetPrependBuffer(size_t size) { + assert(refcount.IsMutable()); + CordRep* child = entry_child(head_); + size_t data_offset = entry_data_offset(head_); + if (data_offset && child->refcount.IsMutable() && child->tag >= FLAT) { + size_t n = (std::min)(data_offset, size); + this->length += n; + begin_pos_ -= n; + data_offset -= n; + entry_data_offset()[head_] = static_cast<offset_type>(data_offset); + return {child->flat()->Data() + data_offset, n}; + } + return {nullptr, 0}; +} + +CordRepRing* CordRepRing::CreateFromLeaf(CordRep* child, size_t offset, + size_t len, size_t extra) { + CordRepRing* rep = CordRepRing::New(1, extra); + rep->head_ = 0; + rep->tail_ = rep->advance(0); + rep->length = len; + rep->entry_end_pos()[0] = len; + rep->entry_child()[0] = child; + rep->entry_data_offset()[0] = static_cast<offset_type>(offset); + return Validate(rep); +} + +CordRepRing* CordRepRing::CreateSlow(CordRep* child, size_t extra) { + CordRepRing* rep = nullptr; + Consume(child, [&](CordRep* child_arg, size_t offset, size_t len) { + if (IsFlatOrExternal(child_arg)) { + rep = rep ? AppendLeaf(rep, child_arg, offset, len) + : CreateFromLeaf(child_arg, offset, len, extra); + } else if (rep) { + rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len); + } else if (offset == 0 && child_arg->length == len) { + rep = Mutable(child_arg->ring(), extra); + } else { + rep = SubRing(child_arg->ring(), offset, len, extra); + } + }); + return Validate(rep, nullptr, __LINE__); +} + +CordRepRing* CordRepRing::Create(CordRep* child, size_t extra) { + size_t length = child->length; + if (IsFlatOrExternal(child)) { + return CreateFromLeaf(child, 0, length, extra); + } + if (child->IsRing()) { + return Mutable(child->ring(), extra); + } + return CreateSlow(child, extra); +} + +template <CordRepRing::AddMode mode> +CordRepRing* CordRepRing::AddRing(CordRepRing* rep, CordRepRing* ring, + size_t offset, size_t len) { + assert(offset < ring->length); + constexpr bool append = mode == AddMode::kAppend; + Position head = ring->Find(offset); + Position tail = ring->FindTail(head.index, offset + len); + const index_type entries = ring->entries(head.index, tail.index); + + rep = Mutable(rep, entries); + + // The delta for making ring[head].end_pos into 'len - offset' + const pos_type delta_length = + (append ? rep->begin_pos_ + rep->length : rep->begin_pos_ - len) - + ring->entry_begin_pos(head.index) - head.offset; + + // Start filling at `tail`, or `entries` before `head` + Filler filler(rep, append ? rep->tail_ : rep->retreat(rep->head_, entries)); + + if (ring->refcount.IsOne()) { + // Copy entries from source stealing the ref and adjusting the end position. + // Commit the filler as this is no-op. + ring->ForEach(head.index, tail.index, [&](index_type ix) { + filler.Add(ring->entry_child(ix), ring->entry_data_offset(ix), + ring->entry_end_pos(ix) + delta_length); + }); + + // Unref entries we did not copy over, and delete source. + if (head.index != ring->head_) UnrefEntries(ring, ring->head_, head.index); + if (tail.index != ring->tail_) UnrefEntries(ring, tail.index, ring->tail_); + CordRepRing::Delete(ring); + } else { + ring->ForEach(head.index, tail.index, [&](index_type ix) { + CordRep* child = ring->entry_child(ix); + filler.Add(child, ring->entry_data_offset(ix), + ring->entry_end_pos(ix) + delta_length); + CordRep::Ref(child); + }); + CordRepRing::Unref(ring); + } + + if (head.offset) { + // Increase offset of first 'source' entry appended or prepended. + // This is always the entry in `filler.head()` + rep->AddDataOffset(filler.head(), head.offset); + } + + if (tail.offset) { + // Reduce length of last 'source' entry appended or prepended. + // This is always the entry tailed by `filler.pos()` + rep->SubLength(rep->retreat(filler.pos()), tail.offset); + } + + // Commit changes + rep->length += len; + if (append) { + rep->tail_ = filler.pos(); + } else { + rep->head_ = filler.head(); + rep->begin_pos_ -= len; + } + + return Validate(rep); +} + +CordRepRing* CordRepRing::AppendSlow(CordRepRing* rep, CordRep* child) { + Consume(child, [&rep](CordRep* child_arg, size_t offset, size_t len) { + if (child_arg->IsRing()) { + rep = AddRing<AddMode::kAppend>(rep, child_arg->ring(), offset, len); + } else { + rep = AppendLeaf(rep, child_arg, offset, len); + } + }); + return rep; +} + +CordRepRing* CordRepRing::AppendLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t len) { + rep = Mutable(rep, 1); + index_type back = rep->tail_; + const pos_type begin_pos = rep->begin_pos_ + rep->length; + rep->tail_ = rep->advance(rep->tail_); + rep->length += len; + rep->entry_end_pos()[back] = begin_pos + len; + rep->entry_child()[back] = child; + rep->entry_data_offset()[back] = static_cast<offset_type>(offset); + return Validate(rep, nullptr, __LINE__); +} + +CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) { + size_t length = child->length; + if (IsFlatOrExternal(child)) { + return AppendLeaf(rep, child, 0, length); + } + if (child->IsRing()) { + return AddRing<AddMode::kAppend>(rep, child->ring(), 0, length); + } + return AppendSlow(rep, child); +} + +CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) { + ReverseConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) { + if (IsFlatOrExternal(child_arg)) { + rep = PrependLeaf(rep, child_arg, offset, len); + } else { + rep = AddRing<AddMode::kPrepend>(rep, child_arg->ring(), offset, len); + } + }); + return Validate(rep); +} + +CordRepRing* CordRepRing::PrependLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t len) { + rep = Mutable(rep, 1); + index_type head = rep->retreat(rep->head_); + pos_type end_pos = rep->begin_pos_; + rep->head_ = head; + rep->length += len; + rep->begin_pos_ -= len; + rep->entry_end_pos()[head] = end_pos; + rep->entry_child()[head] = child; + rep->entry_data_offset()[head] = static_cast<offset_type>(offset); + return Validate(rep); +} + +CordRepRing* CordRepRing::Prepend(CordRepRing* rep, CordRep* child) { + size_t length = child->length; + if (IsFlatOrExternal(child)) { + return PrependLeaf(rep, child, 0, length); + } + if (child->IsRing()) { + return AddRing<AddMode::kPrepend>(rep, child->ring(), 0, length); + } + return PrependSlow(rep, child); +} + +CordRepRing* CordRepRing::Append(CordRepRing* rep, absl::string_view data, + size_t extra) { + if (rep->refcount.IsMutable()) { + Span<char> avail = rep->GetAppendBuffer(data.length()); + if (!avail.empty()) { + memcpy(avail.data(), data.data(), avail.length()); + data.remove_prefix(avail.length()); + } + } + if (data.empty()) return Validate(rep); + + const size_t flats = (data.length() - 1) / kMaxFlatLength + 1; + rep = Mutable(rep, flats); + + Filler filler(rep, rep->tail_); + pos_type pos = rep->begin_pos_ + rep->length; + + while (data.length() >= kMaxFlatLength) { + auto* flat = CreateFlat(data.data(), kMaxFlatLength); + filler.Add(flat, 0, pos += kMaxFlatLength); + data.remove_prefix(kMaxFlatLength); + } + + if (data.length()) { + auto* flat = CreateFlat(data.data(), data.length(), extra); + filler.Add(flat, 0, pos += data.length()); + } + + rep->length = pos - rep->begin_pos_; + rep->tail_ = filler.pos(); + + return Validate(rep); +} + +CordRepRing* CordRepRing::Prepend(CordRepRing* rep, absl::string_view data, + size_t extra) { + if (rep->refcount.IsMutable()) { + Span<char> avail = rep->GetPrependBuffer(data.length()); + if (!avail.empty()) { + const char* tail = data.data() + data.length() - avail.length(); + memcpy(avail.data(), tail, avail.length()); + data.remove_suffix(avail.length()); + } + } + if (data.empty()) return rep; + + const size_t flats = (data.length() - 1) / kMaxFlatLength + 1; + rep = Mutable(rep, flats); + pos_type pos = rep->begin_pos_; + Filler filler(rep, rep->retreat(rep->head_, static_cast<index_type>(flats))); + + size_t first_size = data.size() - (flats - 1) * kMaxFlatLength; + CordRepFlat* flat = CordRepFlat::New(first_size + extra); + flat->length = first_size + extra; + memcpy(flat->Data() + extra, data.data(), first_size); + data.remove_prefix(first_size); + filler.Add(flat, extra, pos); + pos -= first_size; + + while (!data.empty()) { + assert(data.size() >= kMaxFlatLength); + flat = CreateFlat(data.data(), kMaxFlatLength); + filler.Add(flat, 0, pos); + pos -= kMaxFlatLength; + data.remove_prefix(kMaxFlatLength); + } + + rep->head_ = filler.head(); + rep->length += rep->begin_pos_ - pos; + rep->begin_pos_ = pos; + + return Validate(rep); +} + +// 32 entries is 32 * sizeof(pos_type) = 4 cache lines on x86 +static constexpr index_type kBinarySearchThreshold = 32; +static constexpr index_type kBinarySearchEndCount = 8; + +template <bool wrap> +CordRepRing::index_type CordRepRing::FindBinary(index_type head, + index_type tail, + size_t offset) const { + index_type count = tail + (wrap ? capacity_ : 0) - head; + do { + count = (count - 1) / 2; + assert(count < entries(head, tail_)); + index_type mid = wrap ? advance(head, count) : head + count; + index_type after_mid = wrap ? advance(mid) : mid + 1; + bool larger = (offset >= entry_end_offset(mid)); + head = larger ? after_mid : head; + tail = larger ? tail : mid; + assert(head != tail); + } while (ABSL_PREDICT_TRUE(count > kBinarySearchEndCount)); + return head; +} + +CordRepRing::Position CordRepRing::FindSlow(index_type head, + size_t offset) const { + index_type tail = tail_; + + // Binary search until we are good for linear search + // Optimize for branchless / non wrapping ops + if (tail > head) { + index_type count = tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<false>(head, tail, offset); + } + } else { + index_type count = capacity_ + tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<true>(head, tail, offset); + } + } + + pos_type pos = entry_begin_pos(head); + pos_type end_pos = entry_end_pos(head); + while (offset >= Distance(begin_pos_, end_pos)) { + head = advance(head); + pos = end_pos; + end_pos = entry_end_pos(head); + } + + return {head, offset - Distance(begin_pos_, pos)}; +} + +CordRepRing::Position CordRepRing::FindTailSlow(index_type head, + size_t offset) const { + index_type tail = tail_; + const size_t tail_offset = offset - 1; + + // Binary search until we are good for linear search + // Optimize for branchless / non wrapping ops + if (tail > head) { + index_type count = tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<false>(head, tail, tail_offset); + } + } else { + index_type count = capacity_ + tail - head; + if (count > kBinarySearchThreshold) { + head = FindBinary<true>(head, tail, tail_offset); + } + } + + size_t end_offset = entry_end_offset(head); + while (tail_offset >= end_offset) { + head = advance(head); + end_offset = entry_end_offset(head); + } + + return {advance(head), end_offset - offset}; +} + +char CordRepRing::GetCharacter(size_t offset) const { + assert(offset < length); + + Position pos = Find(offset); + size_t data_offset = entry_data_offset(pos.index) + pos.offset; + return GetRepData(entry_child(pos.index))[data_offset]; +} + +CordRepRing* CordRepRing::SubRing(CordRepRing* rep, size_t offset, + size_t len, size_t extra) { + assert(offset <= rep->length); + assert(offset <= rep->length - len); + + if (len == 0) { + CordRep::Unref(rep); + return nullptr; + } + + // Find position of first byte + Position head = rep->Find(offset); + Position tail = rep->FindTail(head.index, offset + len); + const size_t new_entries = rep->entries(head.index, tail.index); + + if (rep->refcount.IsMutable() && extra <= (rep->capacity() - new_entries)) { + // We adopt a privately owned rep and no extra entries needed. + if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); + if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); + rep->head_ = head.index; + rep->tail_ = tail.index; + } else { + // Copy subset to new rep + rep = Copy(rep, head.index, tail.index, extra); + head.index = rep->head_; + tail.index = rep->tail_; + } + + // Adjust begin_pos and length + rep->length = len; + rep->begin_pos_ += offset; + + // Adjust head and tail blocks + if (head.offset) { + rep->AddDataOffset(head.index, head.offset); + } + if (tail.offset) { + rep->SubLength(rep->retreat(tail.index), tail.offset); + } + + return Validate(rep); +} + +CordRepRing* CordRepRing::RemovePrefix(CordRepRing* rep, size_t len, + size_t extra) { + assert(len <= rep->length); + if (len == rep->length) { + CordRep::Unref(rep); + return nullptr; + } + + Position head = rep->Find(len); + if (rep->refcount.IsMutable()) { + if (head.index != rep->head_) UnrefEntries(rep, rep->head_, head.index); + rep->head_ = head.index; + } else { + rep = Copy(rep, head.index, rep->tail_, extra); + head.index = rep->head_; + } + + // Adjust begin_pos and length + rep->length -= len; + rep->begin_pos_ += len; + + // Adjust head block + if (head.offset) { + rep->AddDataOffset(head.index, head.offset); + } + + return Validate(rep); +} + +CordRepRing* CordRepRing::RemoveSuffix(CordRepRing* rep, size_t len, + size_t extra) { + assert(len <= rep->length); + + if (len == rep->length) { + CordRep::Unref(rep); + return nullptr; + } + + Position tail = rep->FindTail(rep->length - len); + if (rep->refcount.IsMutable()) { + // We adopt a privately owned rep, scrub. + if (tail.index != rep->tail_) UnrefEntries(rep, tail.index, rep->tail_); + rep->tail_ = tail.index; + } else { + // Copy subset to new rep + rep = Copy(rep, rep->head_, tail.index, extra); + tail.index = rep->tail_; + } + + // Adjust length + rep->length -= len; + + // Adjust tail block + if (tail.offset) { + rep->SubLength(rep->retreat(tail.index), tail.offset); + } + + return Validate(rep); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.h new file mode 100644 index 0000000000..2000e21ea0 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring.h @@ -0,0 +1,607 @@ +// Copyright 2020 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <iosfwd> +#include <limits> +#include <memory> + +#include "absl/container/internal/layout.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_flat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// All operations modifying a ring buffer are implemented as static methods +// requiring a CordRepRing instance with a reference adopted by the method. +// +// The methods return the modified ring buffer, which may be equal to the input +// if the input was not shared, and having large enough capacity to accommodate +// any newly added node(s). Otherwise, a copy of the input rep with the new +// node(s) added is returned. +// +// Any modification on non shared ring buffers with enough capacity will then +// require minimum atomic operations. Caller should where possible provide +// reasonable `extra` hints for both anticipated extra `flat` byte space, as +// well as anticipated extra nodes required for complex operations. +// +// Example of code creating a ring buffer, adding some data to it, +// and discarding the buffer when done: +// +// void FunWithRings() { +// // Create ring with 3 flats +// CordRep* flat = CreateFlat("Hello"); +// CordRepRing* ring = CordRepRing::Create(flat, 2); +// ring = CordRepRing::Append(ring, CreateFlat(" ")); +// ring = CordRepRing::Append(ring, CreateFlat("world")); +// DoSomethingWithRing(ring); +// CordRep::Unref(ring); +// } +// +// Example of code Copying an existing ring buffer and modifying it: +// +// void MoreFunWithRings(CordRepRing* src) { +// CordRepRing* ring = CordRep::Ref(src)->ring(); +// ring = CordRepRing::Append(ring, CreateFlat("Hello")); +// ring = CordRepRing::Append(ring, CreateFlat(" ")); +// ring = CordRepRing::Append(ring, CreateFlat("world")); +// DoSomethingWithRing(ring); +// CordRep::Unref(ring); +// } +// +class CordRepRing : public CordRep { + public: + // `pos_type` represents a 'logical position'. A CordRepRing instance has a + // `begin_pos` (default 0), and each node inside the buffer will have an + // `end_pos` which is the `end_pos` of the previous node (or `begin_pos`) plus + // this node's length. The purpose is to allow for a binary search on this + // position, while allowing O(1) prepend and append operations. + using pos_type = size_t; + + // `index_type` is the type for the `head`, `tail` and `capacity` indexes. + // Ring buffers are limited to having no more than four billion entries. + using index_type = uint32_t; + + // `offset_type` is the type for the data offset inside a child rep's data. + using offset_type = uint32_t; + + // Position holds the node index and relative offset into the node for + // some physical offset in the contained data as returned by the Find() + // and FindTail() methods. + struct Position { + index_type index; + size_t offset; + }; + + // The maximum # of child nodes that can be hosted inside a CordRepRing. + static constexpr size_t kMaxCapacity = (std::numeric_limits<uint32_t>::max)(); + + // CordRepring can not be default constructed, moved, copied or assigned. + CordRepRing() = delete; + CordRepRing(const CordRepRing&) = delete; + CordRepRing& operator=(const CordRepRing&) = delete; + + // Returns true if this instance is valid, false if some or all of the + // invariants are broken. Intended for debug purposes only. + // `output` receives an explanation of the broken invariants. + bool IsValid(std::ostream& output) const; + + // Returns the size in bytes for a CordRepRing with `capacity' entries. + static constexpr size_t AllocSize(size_t capacity); + + // Returns the distance in bytes from `pos` to `end_pos`. + static constexpr size_t Distance(pos_type pos, pos_type end_pos); + + // Creates a new ring buffer from the provided `rep`. Adopts a reference + // on `rep`. The returned ring buffer has a capacity of at least `extra + 1` + static CordRepRing* Create(CordRep* child, size_t extra = 0); + + // `head`, `tail` and `capacity` indexes defining the ring buffer boundaries. + index_type head() const { return head_; } + index_type tail() const { return tail_; } + index_type capacity() const { return capacity_; } + + // Returns the number of entries in this instance. + index_type entries() const { return entries(head_, tail_); } + + // Returns the logical begin position of this instance. + pos_type begin_pos() const { return begin_pos_; } + + // Returns the number of entries for a given head-tail range. + // Requires `head` and `tail` values to be less than `capacity()`. + index_type entries(index_type head, index_type tail) const { + assert(head < capacity_ && tail < capacity_); + return tail - head + ((tail > head) ? 0 : capacity_); + } + + // Returns the logical end position of entry `index`. + pos_type const& entry_end_pos(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial().Pointer<0>(data_)[index]; + } + + // Returns the child pointer of entry `index`. + CordRep* const& entry_child(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial(capacity()).Pointer<1>(data_)[index]; + } + + // Returns the data offset of entry `index` + offset_type const& entry_data_offset(index_type index) const { + assert(IsValidIndex(index)); + return Layout::Partial(capacity(), capacity()).Pointer<2>(data_)[index]; + } + + // Appends the provided child node to the `rep` instance. + // Adopts a reference from `rep` and `child` which may not be null. + // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node + // containing a FLAT or EXTERNAL node, then flat or external the node is added + // 'as is', with an offset added for the SUBSTRING case. + // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING or + // CONCAT tree, then all child nodes not excluded by any start offset or + // length values are added recursively. + static CordRepRing* Append(CordRepRing* rep, CordRep* child); + + // Appends the provided string data to the `rep` instance. + // This function will attempt to utilize any remaining capacity in the last + // node of the input if that node is not shared (directly or indirectly), and + // of type FLAT. Remaining data will be added as one or more FLAT nodes. + // Any last node added to the ring buffer will be allocated with up to + // `extra` bytes of capacity for (anticipated) subsequent append actions. + static CordRepRing* Append(CordRepRing* rep, string_view data, + size_t extra = 0); + + // Prepends the provided child node to the `rep` instance. + // Adopts a reference from `rep` and `child` which may not be null. + // If the provided child is a FLAT or EXTERNAL node, or a SUBSTRING node + // containing a FLAT or EXTERNAL node, then flat or external the node is + // prepended 'as is', with an optional offset added for the SUBSTRING case. + // If the provided child is a RING or CONCAT tree, or a SUBSTRING of a RING + // or CONCAT tree, then all child nodes not excluded by any start offset or + // length values are added recursively. + static CordRepRing* Prepend(CordRepRing* rep, CordRep* child); + + // Prepends the provided string data to the `rep` instance. + // This function will attempt to utilize any remaining capacity in the first + // node of the input if that node is not shared (directly or indirectly), and + // of type FLAT. Remaining data will be added as one or more FLAT nodes. + // Any first node prepnded to the ring buffer will be allocated with up to + // `extra` bytes of capacity for (anticipated) subsequent prepend actions. + static CordRepRing* Prepend(CordRepRing* rep, string_view data, + size_t extra = 0); + + // Returns a span referencing potentially unused capacity in the last node. + // The returned span may be empty if no such capacity is available, or if the + // current instance is shared. Else, a span of size `n <= size` is returned. + // If non empty, the ring buffer is adjusted to the new length, with the newly + // added capacity left uninitialized. Callers should assign a value to the + // entire span before any other operations on this instance. + Span<char> GetAppendBuffer(size_t size); + + // Returns a span referencing potentially unused capacity in the first node. + // This function is identical to GetAppendBuffer except that it returns a span + // referencing up to `size` capacity directly before the existing data. + Span<char> GetPrependBuffer(size_t size); + + // Returns a cord ring buffer containing `len` bytes of data starting at + // `offset`. If the input is not shared, this function will remove all head + // and tail child nodes outside of the requested range, and adjust the new + // head and tail nodes as required. If the input is shared, this function + // returns a new instance sharing some or all of the nodes from the input. + static CordRepRing* SubRing(CordRepRing* r, size_t offset, size_t len, + size_t extra = 0); + + // Returns a cord ring buffer with the first `len` bytes removed. + // If the input is not shared, this function will remove all head child nodes + // fully inside the first `length` bytes, and adjust the new head as required. + // If the input is shared, this function returns a new instance sharing some + // or all of the nodes from the input. + static CordRepRing* RemoveSuffix(CordRepRing* r, size_t len, + size_t extra = 0); + + // Returns a cord ring buffer with the last `len` bytes removed. + // If the input is not shared, this function will remove all head child nodes + // fully inside the first `length` bytes, and adjust the new head as required. + // If the input is shared, this function returns a new instance sharing some + // or all of the nodes from the input. + static CordRepRing* RemovePrefix(CordRepRing* r, size_t len, + size_t extra = 0); + + // Returns the character at `offset`. Requires that `offset < length`. + char GetCharacter(size_t offset) const; + + // Returns true if this instance manages a single contiguous buffer, in which + // case the (optional) output parameter `fragment` is set. Otherwise, the + // function returns false, and `fragment` is left unchanged. + bool IsFlat(absl::string_view* fragment) const; + + // Returns true if the data starting at `offset` with length `len` is + // managed by this instance inside a single contiguous buffer, in which case + // the (optional) output parameter `fragment` is set to the contiguous memory + // starting at offset `offset` with length `length`. Otherwise, the function + // returns false, and `fragment` is left unchanged. + bool IsFlat(size_t offset, size_t len, absl::string_view* fragment) const; + + // Testing only: set capacity to requested capacity. + void SetCapacityForTesting(size_t capacity); + + // Returns the CordRep data pointer for the provided CordRep. + // Requires that the provided `rep` is either a FLAT or EXTERNAL CordRep. + static const char* GetLeafData(const CordRep* rep); + + // Returns the CordRep data pointer for the provided CordRep. + // Requires that `rep` is either a FLAT, EXTERNAL, or SUBSTRING CordRep. + static const char* GetRepData(const CordRep* rep); + + // Advances the provided position, wrapping around capacity as needed. + // Requires `index` < capacity() + inline index_type advance(index_type index) const; + + // Advances the provided position by 'n`, wrapping around capacity as needed. + // Requires `index` < capacity() and `n` <= capacity. + inline index_type advance(index_type index, index_type n) const; + + // Retreats the provided position, wrapping around 0 as needed. + // Requires `index` < capacity() + inline index_type retreat(index_type index) const; + + // Retreats the provided position by 'n', wrapping around 0 as needed. + // Requires `index` < capacity() + inline index_type retreat(index_type index, index_type n) const; + + // Returns the logical begin position of entry `index` + pos_type const& entry_begin_pos(index_type index) const { + return (index == head_) ? begin_pos_ : entry_end_pos(retreat(index)); + } + + // Returns the physical start offset of entry `index` + size_t entry_start_offset(index_type index) const { + return Distance(begin_pos_, entry_begin_pos(index)); + } + + // Returns the physical end offset of entry `index` + size_t entry_end_offset(index_type index) const { + return Distance(begin_pos_, entry_end_pos(index)); + } + + // Returns the data length for entry `index` + size_t entry_length(index_type index) const { + return Distance(entry_begin_pos(index), entry_end_pos(index)); + } + + // Returns the data for entry `index` + absl::string_view entry_data(index_type index) const; + + // Returns the position for `offset` as {index, prefix}. `index` holds the + // index of the entry at the specified offset and `prefix` holds the relative + // offset inside that entry. + // Requires `offset` < length. + // + // For example we can implement GetCharacter(offset) as: + // char GetCharacter(size_t offset) { + // Position pos = this->Find(offset); + // return this->entry_data(pos.pos)[pos.offset]; + // } + inline Position Find(size_t offset) const; + + // Find starting at `head` + inline Position Find(index_type head, size_t offset) const; + + // Returns the tail position for `offset` as {tail index, suffix}. + // `tail index` holds holds the index of the entry holding the offset directly + // before 'offset` advanced by one. 'suffix` holds the relative offset from + // that relative offset in the entry to the end of the entry. + // For example, FindTail(length) will return {tail(), 0}, FindTail(length - 5) + // will return {retreat(tail), 5)} provided the preceding entry contains at + // least 5 bytes of data. + // Requires offset >= 1 && offset <= length. + // + // This function is very useful in functions that need to clip the end of some + // ring buffer such as 'RemovePrefix'. + // For example, we could implement RemovePrefix for non shared instances as: + // void RemoveSuffix(size_t n) { + // Position pos = FindTail(length - n); + // UnrefEntries(pos.pos, this->tail_); + // this->tail_ = pos.pos; + // entry(retreat(pos.pos)).end_pos -= pos.offset; + // } + inline Position FindTail(size_t offset) const; + + // Find tail starting at `head` + inline Position FindTail(index_type head, size_t offset) const; + + // Invokes f(index_type index) for each entry inside the range [head, tail> + template <typename F> + void ForEach(index_type head, index_type tail, F&& f) const { + index_type n1 = (tail > head) ? tail : capacity_; + for (index_type i = head; i < n1; ++i) f(i); + if (tail <= head) { + for (index_type i = 0; i < tail; ++i) f(i); + } + } + + // Invokes f(index_type index) for each entry inside this instance. + template <typename F> + void ForEach(F&& f) const { + ForEach(head_, tail_, std::forward<F>(f)); + } + + // Dump this instance's data tp stream `s` in human readable format, excluding + // the actual data content itself. Intended for debug purposes only. + friend std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); + + private: + enum class AddMode { kAppend, kPrepend }; + + using Layout = container_internal::Layout<pos_type, CordRep*, offset_type>; + + class Filler; + class Transaction; + class CreateTransaction; + + static constexpr size_t kLayoutAlignment = Layout::Partial().Alignment(); + + // Creates a new CordRepRing. + explicit CordRepRing(index_type capacity) : capacity_(capacity) {} + + // Returns true if `index` is a valid index into this instance. + bool IsValidIndex(index_type index) const; + + // Debug use only: validates the provided CordRepRing invariants. + // Verification of all CordRepRing methods can be enabled by defining + // EXTRA_CORD_RING_VALIDATION, i.e.: `--copts=-DEXTRA_CORD_RING_VALIDATION` + // Verification is VERY expensive, so only do it for debugging purposes. + static CordRepRing* Validate(CordRepRing* rep, const char* file = nullptr, + int line = 0); + + // Allocates a CordRepRing large enough to hold `capacity + extra' entries. + // The returned capacity may be larger if the allocated memory allows for it. + // The maximum capacity of a CordRepRing is capped at kMaxCapacity. + // Throws `std::length_error` if `capacity + extra' exceeds kMaxCapacity. + static CordRepRing* New(size_t capacity, size_t extra); + + // Deallocates (but does not destroy) the provided ring buffer. + static void Delete(CordRepRing* rep); + + // Destroys the provided ring buffer, decrementing the reference count of all + // contained child CordReps. The provided 1\`rep` should have a ref count of + // one (pre decrement destroy call observing `refcount.IsOne()`) or zero + // (post decrement destroy call observing `!refcount.Decrement()`). + static void Destroy(CordRepRing* rep); + + // Returns a mutable reference to the logical end position array. + pos_type* entry_end_pos() { + return Layout::Partial().Pointer<0>(data_); + } + + // Returns a mutable reference to the child pointer array. + CordRep** entry_child() { + return Layout::Partial(capacity()).Pointer<1>(data_); + } + + // Returns a mutable reference to the data offset array. + offset_type* entry_data_offset() { + return Layout::Partial(capacity(), capacity()).Pointer<2>(data_); + } + + // Find implementations for the non fast path 0 / length cases. + Position FindSlow(index_type head, size_t offset) const; + Position FindTailSlow(index_type head, size_t offset) const; + + // Finds the index of the first node that is inside a reasonable distance + // of the node at `offset` from which we can continue with a linear search. + template <bool wrap> + index_type FindBinary(index_type head, index_type tail, size_t offset) const; + + // Fills the current (initialized) instance from the provided source, copying + // entries [head, tail). Adds a reference to copied entries if `ref` is true. + template <bool ref> + void Fill(const CordRepRing* src, index_type head, index_type tail); + + // Create a copy of 'rep', copying all entries [head, tail), allocating room + // for `extra` entries. Adds a reference on all copied entries. + static CordRepRing* Copy(CordRepRing* rep, index_type head, index_type tail, + size_t extra = 0); + + // Returns a Mutable CordRepRing reference from `rep` with room for at least + // `extra` additional nodes. Adopts a reference count from `rep`. + // This function will return `rep` if, and only if: + // - rep.entries + extra <= rep.capacity + // - rep.refcount == 1 + // Otherwise, this function will create a new copy of `rep` with additional + // capacity to satisfy `extra` extra nodes, and unref the old `rep` instance. + // + // If a new CordRepRing can not be allocated, or the new capacity would exceed + // the maxmimum capacity, then the input is consumed only, and an exception is + // thrown. + static CordRepRing* Mutable(CordRepRing* rep, size_t extra); + + // Slow path for Append(CordRepRing* rep, CordRep* child). This function is + // exercised if the provided `child` in Append() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* AppendSlow(CordRepRing* rep, CordRep* child); + + // Appends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. + static CordRepRing* AppendLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t length); + + // Prepends the provided leaf node. Requires `child` to be FLAT or EXTERNAL. + static CordRepRing* PrependLeaf(CordRepRing* rep, CordRep* child, + size_t offset, size_t length); + + // Slow path for Prepend(CordRepRing* rep, CordRep* child). This function is + // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* PrependSlow(CordRepRing* rep, CordRep* child); + + // Slow path for Create(CordRep* child, size_t extra). This function is + // exercised if the provided `child` in Prepend() is not a leaf node, i.e., a + // ring buffer or old (concat) cord tree. + static CordRepRing* CreateSlow(CordRep* child, size_t extra); + + // Creates a new ring buffer from the provided `child` leaf node. Requires + // `child` to be FLAT or EXTERNAL. on `rep`. + // The returned ring buffer has a capacity of at least `1 + extra` + static CordRepRing* CreateFromLeaf(CordRep* child, size_t offset, + size_t length, size_t extra); + + // Appends or prepends (depending on AddMode) the ring buffer in `ring' to + // `rep` starting at `offset` with length `len`. + template <AddMode mode> + static CordRepRing* AddRing(CordRepRing* rep, CordRepRing* ring, + size_t offset, size_t len); + + // Increases the data offset for entry `index` by `n`. + void AddDataOffset(index_type index, size_t n); + + // Descreases the length for entry `index` by `n`. + void SubLength(index_type index, size_t n); + + index_type head_; + index_type tail_; + index_type capacity_; + pos_type begin_pos_; + + alignas(kLayoutAlignment) char data_[kLayoutAlignment]; + + friend struct CordRep; +}; + +constexpr size_t CordRepRing::AllocSize(size_t capacity) { + return sizeof(CordRepRing) - sizeof(data_) + + Layout(capacity, capacity, capacity).AllocSize(); +} + +inline constexpr size_t CordRepRing::Distance(pos_type pos, pos_type end_pos) { + return (end_pos - pos); +} + +inline const char* CordRepRing::GetLeafData(const CordRep* rep) { + return rep->tag != EXTERNAL ? rep->flat()->Data() : rep->external()->base; +} + +inline const char* CordRepRing::GetRepData(const CordRep* rep) { + if (rep->tag >= FLAT) return rep->flat()->Data(); + if (rep->tag == EXTERNAL) return rep->external()->base; + return GetLeafData(rep->substring()->child) + rep->substring()->start; +} + +inline CordRepRing::index_type CordRepRing::advance(index_type index) const { + assert(index < capacity_); + return ++index == capacity_ ? 0 : index; +} + +inline CordRepRing::index_type CordRepRing::advance(index_type index, + index_type n) const { + assert(index < capacity_ && n <= capacity_); + return (index += n) >= capacity_ ? index - capacity_ : index; +} + +inline CordRepRing::index_type CordRepRing::retreat(index_type index) const { + assert(index < capacity_); + return (index > 0 ? index : capacity_) - 1; +} + +inline CordRepRing::index_type CordRepRing::retreat(index_type index, + index_type n) const { + assert(index < capacity_ && n <= capacity_); + return index >= n ? index - n : capacity_ - n + index; +} + +inline absl::string_view CordRepRing::entry_data(index_type index) const { + size_t data_offset = entry_data_offset(index); + return {GetRepData(entry_child(index)) + data_offset, entry_length(index)}; +} + +inline bool CordRepRing::IsValidIndex(index_type index) const { + if (index >= capacity_) return false; + return (tail_ > head_) ? (index >= head_ && index < tail_) + : (index >= head_ || index < tail_); +} + +#ifndef EXTRA_CORD_RING_VALIDATION +inline CordRepRing* CordRepRing::Validate(CordRepRing* rep, + const char* /*file*/, int /*line*/) { + return rep; +} +#endif + +inline CordRepRing::Position CordRepRing::Find(size_t offset) const { + assert(offset < length); + return (offset == 0) ? Position{head_, 0} : FindSlow(head_, offset); +} + +inline CordRepRing::Position CordRepRing::Find(index_type head, + size_t offset) const { + assert(offset < length); + assert(IsValidIndex(head) && offset >= entry_start_offset(head)); + return (offset == 0) ? Position{head_, 0} : FindSlow(head, offset); +} + +inline CordRepRing::Position CordRepRing::FindTail(size_t offset) const { + assert(offset > 0 && offset <= length); + return (offset == length) ? Position{tail_, 0} : FindTailSlow(head_, offset); +} + +inline CordRepRing::Position CordRepRing::FindTail(index_type head, + size_t offset) const { + assert(offset > 0 && offset <= length); + assert(IsValidIndex(head) && offset >= entry_start_offset(head) + 1); + return (offset == length) ? Position{tail_, 0} : FindTailSlow(head, offset); +} + +// Now that CordRepRing is defined, we can define CordRep's helper casts: +inline CordRepRing* CordRep::ring() { + assert(IsRing()); + return static_cast<CordRepRing*>(this); +} + +inline const CordRepRing* CordRep::ring() const { + assert(IsRing()); + return static_cast<const CordRepRing*>(this); +} + +inline bool CordRepRing::IsFlat(absl::string_view* fragment) const { + if (entries() == 1) { + if (fragment) *fragment = entry_data(head()); + return true; + } + return false; +} + +inline bool CordRepRing::IsFlat(size_t offset, size_t len, + absl::string_view* fragment) const { + const Position pos = Find(offset); + const absl::string_view data = entry_data(pos.index); + if (data.length() >= len && data.length() - len >= pos.offset) { + if (fragment) *fragment = data.substr(pos.offset, len); + return true; + } + return false; +} + +std::ostream& operator<<(std::ostream& s, const CordRepRing& rep); + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring_reader.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring_reader.h new file mode 100644 index 0000000000..7ceeaa000e --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_ring_reader.h @@ -0,0 +1,118 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ + +#include <cassert> +#include <cstddef> +#include <cstdint> + +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordRepRingReader provides basic navigation over CordRepRing data. +class CordRepRingReader { + public: + // Returns true if this instance is not empty. + explicit operator bool() const { return ring_ != nullptr; } + + // Returns the ring buffer reference for this instance, or nullptr if empty. + CordRepRing* ring() const { return ring_; } + + // Returns the current node index inside the ring buffer for this instance. + // The returned value is undefined if this instance is empty. + CordRepRing::index_type index() const { return index_; } + + // Returns the current node inside the ring buffer for this instance. + // The returned value is undefined if this instance is empty. + CordRep* node() const { return ring_->entry_child(index_); } + + // Returns the length of the referenced ring buffer. + // Requires the current instance to be non empty. + size_t length() const { + assert(ring_); + return ring_->length; + } + + // Returns the end offset of the last navigated-to chunk, which represents the + // total bytes 'consumed' relative to the start of the ring. The returned + // value is never zero. For example, initializing a reader with a ring buffer + // with a first chunk of 19 bytes will return consumed() = 19. + // Requires the current instance to be non empty. + size_t consumed() const { + assert(ring_); + return ring_->entry_end_offset(index_); + } + + // Returns the number of bytes remaining beyond the last navigated-to chunk. + // Requires the current instance to be non empty. + size_t remaining() const { + assert(ring_); + return length() - consumed(); + } + + // Resets this instance to an empty value + void Reset() { ring_ = nullptr; } + + // Resets this instance to the start of `ring`. `ring` must not be null. + // Returns a reference into the first chunk of the provided ring. + absl::string_view Reset(CordRepRing* ring) { + assert(ring); + ring_ = ring; + index_ = ring_->head(); + return ring_->entry_data(index_); + } + + // Navigates to the next chunk inside the reference ring buffer. + // Returns a reference into the navigated-to chunk. + // Requires remaining() to be non zero. + absl::string_view Next() { + assert(remaining()); + index_ = ring_->advance(index_); + return ring_->entry_data(index_); + } + + // Navigates to the chunk at offset `offset`. + // Returns a reference into the navigated-to chunk, adjusted for the relative + // position of `offset` into that chunk. For example, calling Seek(13) on a + // ring buffer containing 2 chunks of 10 and 20 bytes respectively will return + // a string view into the second chunk starting at offset 3 with a size of 17. + // Requires `offset` to be less than `length()` + absl::string_view Seek(size_t offset) { + assert(offset < length()); + size_t current = ring_->entry_end_offset(index_); + CordRepRing::index_type hint = (offset >= current) ? index_ : ring_->head(); + const CordRepRing::Position head = ring_->Find(hint, offset); + index_ = head.index; + auto data = ring_->entry_data(head.index); + data.remove_prefix(head.offset); + return data; + } + + private: + CordRepRing* ring_ = nullptr; + CordRepRing::index_type index_; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_rep_test_util.h b/third_party/abseil-cpp/absl/strings/internal/cord_rep_test_util.h new file mode 100644 index 0000000000..ad828af2a5 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cord_rep_test_util.h @@ -0,0 +1,220 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ +#define ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ + +#include <cassert> +#include <memory> +#include <random> +#include <string> +#include <vector> + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cordrep_testing { + +inline cord_internal::CordRepSubstring* MakeSubstring( + size_t start, size_t len, cord_internal::CordRep* rep) { + auto* sub = new cord_internal::CordRepSubstring; + sub->tag = cord_internal::SUBSTRING; + sub->start = start; + sub->length = len <= 0 ? rep->length - start + len : len; + sub->child = rep; + return sub; +} + +inline cord_internal::CordRepConcat* MakeConcat(cord_internal::CordRep* left, + cord_internal::CordRep* right, + int depth = 0) { + auto* concat = new cord_internal::CordRepConcat; + concat->tag = cord_internal::CONCAT; + concat->length = left->length + right->length; + concat->left = left; + concat->right = right; + concat->set_depth(depth); + return concat; +} + +inline cord_internal::CordRepFlat* MakeFlat(absl::string_view value) { + assert(value.length() <= cord_internal::kMaxFlatLength); + auto* flat = cord_internal::CordRepFlat::New(value.length()); + flat->length = value.length(); + memcpy(flat->Data(), value.data(), value.length()); + return flat; +} + +// Creates an external node for testing +inline cord_internal::CordRepExternal* MakeExternal(absl::string_view s) { + struct Rep : public cord_internal::CordRepExternal { + std::string s; + explicit Rep(absl::string_view sv) : s(sv) { + this->tag = cord_internal::EXTERNAL; + this->base = s.data(); + this->length = s.length(); + this->releaser_invoker = [](cord_internal::CordRepExternal* self) { + delete static_cast<Rep*>(self); + }; + } + }; + return new Rep(s); +} + +inline std::string CreateRandomString(size_t n) { + absl::string_view data = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789~!@#$%^&*()_+=-<>?:\"{}[]|"; + std::minstd_rand rnd; + std::uniform_int_distribution<size_t> dist(0, data.size() - 1); + std::string s(n, ' '); + for (size_t i = 0; i < n; ++i) { + s[i] = data[dist(rnd)]; + } + return s; +} + +// Creates an array of flats from the provided string, chopping +// the provided string up into flats of size `chunk_size` characters +// resulting in roughly `data.size() / chunk_size` total flats. +inline std::vector<cord_internal::CordRep*> CreateFlatsFromString( + absl::string_view data, size_t chunk_size) { + assert(chunk_size > 0); + std::vector<cord_internal::CordRep*> flats; + for (absl::string_view s = data; !s.empty(); s.remove_prefix(chunk_size)) { + flats.push_back(MakeFlat(s.substr(0, chunk_size))); + } + return flats; +} + +inline cord_internal::CordRepBtree* CordRepBtreeFromFlats( + absl::Span<cord_internal::CordRep* const> flats) { + assert(!flats.empty()); + auto* node = cord_internal::CordRepBtree::Create(flats[0]); + for (size_t i = 1; i < flats.size(); ++i) { + node = cord_internal::CordRepBtree::Append(node, flats[i]); + } + return node; +} + +template <typename Fn> +inline void CordVisitReps(cord_internal::CordRep* rep, Fn&& fn) { + fn(rep); + while (rep->tag == cord_internal::SUBSTRING) { + rep = rep->substring()->child; + fn(rep); + } + if (rep->tag == cord_internal::BTREE) { + for (cord_internal::CordRep* edge : rep->btree()->Edges()) { + CordVisitReps(edge, fn); + } + } else if (rep->tag == cord_internal::CONCAT) { + CordVisitReps(rep->concat()->left, fn); + CordVisitReps(rep->concat()->right, fn); + } +} + +template <typename Predicate> +inline std::vector<cord_internal::CordRep*> CordCollectRepsIf( + Predicate&& predicate, cord_internal::CordRep* rep) { + std::vector<cord_internal::CordRep*> reps; + CordVisitReps(rep, [&reps, &predicate](cord_internal::CordRep* rep) { + if (predicate(rep)) reps.push_back(rep); + }); + return reps; +} + +inline std::vector<cord_internal::CordRep*> CordCollectReps( + cord_internal::CordRep* rep) { + std::vector<cord_internal::CordRep*> reps; + auto fn = [&reps](cord_internal::CordRep* rep) { reps.push_back(rep); }; + CordVisitReps(rep, fn); + return reps; +} + +inline void CordToString(cord_internal::CordRep* rep, std::string& s) { + size_t offset = 0; + size_t length = rep->length; + while (rep->tag == cord_internal::SUBSTRING) { + offset += rep->substring()->start; + rep = rep->substring()->child; + } + if (rep->tag == cord_internal::BTREE) { + for (cord_internal::CordRep* edge : rep->btree()->Edges()) { + CordToString(edge, s); + } + } else if (rep->tag >= cord_internal::FLAT) { + s.append(rep->flat()->Data() + offset, length); + } else if (rep->tag == cord_internal::EXTERNAL) { + s.append(rep->external()->base + offset, length); + } else { + ABSL_RAW_LOG(FATAL, "Unsupported tag %d", rep->tag); + } +} + +inline std::string CordToString(cord_internal::CordRep* rep) { + std::string s; + s.reserve(rep->length); + CordToString(rep, s); + return s; +} + +// RAII Helper class to automatically unref reps on destruction. +class AutoUnref { + public: + ~AutoUnref() { + for (CordRep* rep : unrefs_) CordRep::Unref(rep); + } + + // Adds `rep` to the list of reps to be unreffed at destruction. + template <typename CordRepType> + CordRepType* Add(CordRepType* rep) { + unrefs_.push_back(rep); + return rep; + } + + // Increments the reference count of `rep` by one, and adds it to + // the list of reps to be unreffed at destruction. + template <typename CordRepType> + CordRepType* Ref(CordRepType* rep) { + unrefs_.push_back(CordRep::Ref(rep)); + return rep; + } + + // Increments the reference count of `rep` by one if `condition` is true, + // and adds it to the list of reps to be unreffed at destruction. + template <typename CordRepType> + CordRepType* RefIf(bool condition, CordRepType* rep) { + if (condition) unrefs_.push_back(CordRep::Ref(rep)); + return rep; + } + + private: + using CordRep = absl::cord_internal::CordRep; + + std::vector<CordRep*> unrefs_; +}; + +} // namespace cordrep_testing +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_functions.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_functions.cc new file mode 100644 index 0000000000..20d314f03c --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_functions.cc @@ -0,0 +1,96 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_functions.h" + +#include <atomic> +#include <cmath> +#include <limits> +#include <random> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/profiling/internal/exponential_biased.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +// The average interval until the next sample. A value of 0 disables profiling +// while a value of 1 will profile all Cords. +std::atomic<int> g_cordz_mean_interval(50000); + +} // namespace + +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +// Special negative 'not initialized' per thread value for cordz_next_sample. +static constexpr int64_t kInitCordzNextSample = -1; + +ABSL_CONST_INIT thread_local int64_t cordz_next_sample = kInitCordzNextSample; + +// kIntervalIfDisabled is the number of profile-eligible events need to occur +// before the code will confirm that cordz is still disabled. +constexpr int64_t kIntervalIfDisabled = 1 << 16; + +ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() { + + thread_local absl::profiling_internal::ExponentialBiased + exponential_biased_generator; + int32_t mean_interval = get_cordz_mean_interval(); + + // Check if we disabled profiling. If so, set the next sample to a "large" + // number to minimize the overhead of the should_profile codepath. + if (mean_interval <= 0) { + cordz_next_sample = kIntervalIfDisabled; + return false; + } + + // Check if we're always sampling. + if (mean_interval == 1) { + cordz_next_sample = 1; + return true; + } + + if (cordz_next_sample <= 0) { + // If first check on current thread, check cordz_should_profile() + // again using the created (initial) stride in cordz_next_sample. + const bool initialized = cordz_next_sample != kInitCordzNextSample; + cordz_next_sample = exponential_biased_generator.GetStride(mean_interval); + return initialized || cordz_should_profile(); + } + + --cordz_next_sample; + return false; +} + +void cordz_set_next_sample_for_testing(int64_t next_sample) { + cordz_next_sample = next_sample; +} + +#endif // ABSL_INTERNAL_CORDZ_ENABLED + +int32_t get_cordz_mean_interval() { + return g_cordz_mean_interval.load(std::memory_order_acquire); +} + +void set_cordz_mean_interval(int32_t mean_interval) { + g_cordz_mean_interval.store(mean_interval, std::memory_order_release); +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_functions.h b/third_party/abseil-cpp/absl/strings/internal/cordz_functions.h new file mode 100644 index 0000000000..c9ba14508a --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_functions.h @@ -0,0 +1,85 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_CORDZ_FUNCTIONS_H_ +#define ABSL_STRINGS_CORDZ_FUNCTIONS_H_ + +#include <stdint.h> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Returns the current sample rate. This represents the average interval +// between samples. +int32_t get_cordz_mean_interval(); + +// Sets the sample rate with the average interval between samples. +void set_cordz_mean_interval(int32_t mean_interval); + +// Enable cordz unless any of the following applies: +// - no thread local support +// - MSVC build +// - Android build +// - Apple build +// - DLL build +// Hashtablez is turned off completely in opensource builds. +// MSVC's static atomics are dynamically initialized in debug mode, which breaks +// sampling. +#if defined(ABSL_HAVE_THREAD_LOCAL) && !defined(_MSC_VER) && \ + !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL) && \ + !defined(__ANDROID__) && !defined(__APPLE__) +#define ABSL_INTERNAL_CORDZ_ENABLED 1 +#endif + +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +// cordz_next_sample is the number of events until the next sample event. If +// the value is 1 or less, the code will check on the next event if cordz is +// enabled, and if so, will sample the Cord. cordz is only enabled when we can +// use thread locals. +ABSL_CONST_INIT extern thread_local int64_t cordz_next_sample; + +// Determines if the next sample should be profiled. If it is, the value pointed +// at by next_sample will be set with the interval until the next sample. +bool cordz_should_profile_slow(); + +// Returns true if the next cord should be sampled. +inline bool cordz_should_profile() { + if (ABSL_PREDICT_TRUE(cordz_next_sample > 1)) { + cordz_next_sample--; + return false; + } + return cordz_should_profile_slow(); +} + +// Sets the interval until the next sample (for testing only) +void cordz_set_next_sample_for_testing(int64_t next_sample); + +#else // ABSL_INTERNAL_CORDZ_ENABLED + +inline bool cordz_should_profile() { return false; } +inline void cordz_set_next_sample_for_testing(int64_t) {} + +#endif // ABSL_INTERNAL_CORDZ_ENABLED + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_FUNCTIONS_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_functions_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_functions_test.cc new file mode 100644 index 0000000000..350623c1f3 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_functions_test.cc @@ -0,0 +1,149 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_functions.h" + +#include <thread> // NOLINT we need real clean new threads + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::Eq; +using ::testing::Ge; +using ::testing::Le; + +TEST(CordzFunctionsTest, SampleRate) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + int32_t expected_sample_rate = 123; + set_cordz_mean_interval(expected_sample_rate); + EXPECT_THAT(get_cordz_mean_interval(), Eq(expected_sample_rate)); + set_cordz_mean_interval(orig_sample_rate); +} + +// Cordz is disabled when we don't have thread_local. All calls to +// should_profile will return false when cordz is diabled, so we might want to +// avoid those tests. +#ifdef ABSL_INTERNAL_CORDZ_ENABLED + +TEST(CordzFunctionsTest, ShouldProfileDisable) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(0); + cordz_set_next_sample_for_testing(0); + EXPECT_FALSE(cordz_should_profile()); + // 1 << 16 is from kIntervalIfDisabled in cordz_functions.cc. + EXPECT_THAT(cordz_next_sample, Eq(1 << 16)); + + set_cordz_mean_interval(orig_sample_rate); +} + +TEST(CordzFunctionsTest, ShouldProfileAlways) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(1); + cordz_set_next_sample_for_testing(1); + EXPECT_TRUE(cordz_should_profile()); + EXPECT_THAT(cordz_next_sample, Le(1)); + + set_cordz_mean_interval(orig_sample_rate); +} + +TEST(CordzFunctionsTest, DoesNotAlwaysSampleFirstCord) { + // Set large enough interval such that the chance of 'tons' of threads + // randomly sampling the first call is infinitely small. + set_cordz_mean_interval(10000); + int tries = 0; + bool sampled = false; + do { + ++tries; + ASSERT_THAT(tries, Le(1000)); + std::thread thread([&sampled] { + sampled = cordz_should_profile(); + }); + thread.join(); + } while (sampled); +} + +TEST(CordzFunctionsTest, ShouldProfileRate) { + static constexpr int kDesiredMeanInterval = 1000; + static constexpr int kSamples = 10000; + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(kDesiredMeanInterval); + + int64_t sum_of_intervals = 0; + for (int i = 0; i < kSamples; i++) { + // Setting next_sample to 0 will force cordz_should_profile to generate a + // new value for next_sample each iteration. + cordz_set_next_sample_for_testing(0); + cordz_should_profile(); + sum_of_intervals += cordz_next_sample; + } + + // The sum of independent exponential variables is an Erlang distribution, + // which is a gamma distribution where the shape parameter is equal to the + // number of summands. The distribution used for cordz_should_profile is + // actually floor(Exponential(1/mean)) which introduces bias. However, we can + // apply the squint-really-hard correction factor. That is, when mean is + // large, then if we squint really hard the shape of the distribution between + // N and N+1 looks like a uniform distribution. On average, each value for + // next_sample will be about 0.5 lower than we would expect from an + // exponential distribution. This squint-really-hard correction approach won't + // work when mean is smaller than about 10 but works fine when mean is 1000. + // + // We can use R to calculate a confidence interval. This + // shows how to generate a confidence interval with a false positive rate of + // one in a billion. + // + // $ R -q + // > mean = 1000 + // > kSamples = 10000 + // > errorRate = 1e-9 + // > correction = -kSamples / 2 + // > low = qgamma(errorRate/2, kSamples, 1/mean) + correction + // > high = qgamma(1 - errorRate/2, kSamples, 1/mean) + correction + // > low + // [1] 9396115 + // > high + // [1] 10618100 + EXPECT_THAT(sum_of_intervals, Ge(9396115)); + EXPECT_THAT(sum_of_intervals, Le(10618100)); + + set_cordz_mean_interval(orig_sample_rate); +} + +#else // ABSL_INTERNAL_CORDZ_ENABLED + +TEST(CordzFunctionsTest, ShouldProfileDisabled) { + int32_t orig_sample_rate = get_cordz_mean_interval(); + + set_cordz_mean_interval(1); + cordz_set_next_sample_for_testing(0); + EXPECT_FALSE(cordz_should_profile()); + + set_cordz_mean_interval(orig_sample_rate); +} + +#endif // ABSL_INTERNAL_CORDZ_ENABLED + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc new file mode 100644 index 0000000000..a73fefed59 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc @@ -0,0 +1,139 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_handle.h" + +#include <atomic> + +#include "absl/base/internal/raw_logging.h" // For ABSL_RAW_CHECK +#include "absl/base/internal/spinlock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +using ::absl::base_internal::SpinLockHolder; + +ABSL_CONST_INIT CordzHandle::Queue CordzHandle::global_queue_(absl::kConstInit); + +CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) { + if (is_snapshot) { + SpinLockHolder lock(&queue_->mutex); + CordzHandle* dq_tail = queue_->dq_tail.load(std::memory_order_acquire); + if (dq_tail != nullptr) { + dq_prev_ = dq_tail; + dq_tail->dq_next_ = this; + } + queue_->dq_tail.store(this, std::memory_order_release); + } +} + +CordzHandle::~CordzHandle() { + ODRCheck(); + if (is_snapshot_) { + std::vector<CordzHandle*> to_delete; + { + SpinLockHolder lock(&queue_->mutex); + CordzHandle* next = dq_next_; + if (dq_prev_ == nullptr) { + // We were head of the queue, delete every CordzHandle until we reach + // either the end of the list, or a snapshot handle. + while (next && !next->is_snapshot_) { + to_delete.push_back(next); + next = next->dq_next_; + } + } else { + // Another CordzHandle existed before this one, don't delete anything. + dq_prev_->dq_next_ = next; + } + if (next) { + next->dq_prev_ = dq_prev_; + } else { + queue_->dq_tail.store(dq_prev_, std::memory_order_release); + } + } + for (CordzHandle* handle : to_delete) { + delete handle; + } + } +} + +bool CordzHandle::SafeToDelete() const { + return is_snapshot_ || queue_->IsEmpty(); +} + +void CordzHandle::Delete(CordzHandle* handle) { + assert(handle); + if (handle) { + handle->ODRCheck(); + Queue* const queue = handle->queue_; + if (!handle->SafeToDelete()) { + SpinLockHolder lock(&queue->mutex); + CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire); + if (dq_tail != nullptr) { + handle->dq_prev_ = dq_tail; + dq_tail->dq_next_ = handle; + queue->dq_tail.store(handle, std::memory_order_release); + return; + } + } + delete handle; + } +} + +std::vector<const CordzHandle*> CordzHandle::DiagnosticsGetDeleteQueue() { + std::vector<const CordzHandle*> handles; + SpinLockHolder lock(&global_queue_.mutex); + CordzHandle* dq_tail = global_queue_.dq_tail.load(std::memory_order_acquire); + for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) { + handles.push_back(p); + } + return handles; +} + +bool CordzHandle::DiagnosticsHandleIsSafeToInspect( + const CordzHandle* handle) const { + ODRCheck(); + if (!is_snapshot_) return false; + if (handle == nullptr) return true; + if (handle->is_snapshot_) return false; + bool snapshot_found = false; + SpinLockHolder lock(&queue_->mutex); + for (const CordzHandle* p = queue_->dq_tail; p; p = p->dq_prev_) { + if (p == handle) return !snapshot_found; + if (p == this) snapshot_found = true; + } + ABSL_ASSERT(snapshot_found); // Assert that 'this' is in delete queue. + return true; +} + +std::vector<const CordzHandle*> +CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() { + ODRCheck(); + std::vector<const CordzHandle*> handles; + if (!is_snapshot()) { + return handles; + } + + SpinLockHolder lock(&queue_->mutex); + for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) { + if (!p->is_snapshot()) { + handles.push_back(p); + } + } + return handles; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h new file mode 100644 index 0000000000..5df53c782a --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h @@ -0,0 +1,131 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_CORDZ_HANDLE_H_ +#define ABSL_STRINGS_CORDZ_HANDLE_H_ + +#include <atomic> +#include <vector> + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// This base class allows multiple types of object (CordzInfo and +// CordzSampleToken) to exist simultaneously on the delete queue (pointed to by +// global_dq_tail and traversed using dq_prev_ and dq_next_). The +// delete queue guarantees that once a profiler creates a CordzSampleToken and +// has gained visibility into a CordzInfo object, that CordzInfo object will not +// be deleted prematurely. This allows the profiler to inspect all CordzInfo +// objects that are alive without needing to hold a global lock. +class CordzHandle { + public: + CordzHandle() : CordzHandle(false) {} + + bool is_snapshot() const { return is_snapshot_; } + + // Returns true if this instance is safe to be deleted because it is either a + // snapshot, which is always safe to delete, or not included in the global + // delete queue and thus not included in any snapshot. + // Callers are responsible for making sure this instance can not be newly + // discovered by other threads. For example, CordzInfo instances first de-list + // themselves from the global CordzInfo list before determining if they are + // safe to be deleted directly. + // If SafeToDelete returns false, callers MUST use the Delete() method to + // safely queue CordzHandle instances for deletion. + bool SafeToDelete() const; + + // Deletes the provided instance, or puts it on the delete queue to be deleted + // once there are no more sample tokens (snapshot) instances potentially + // referencing the instance. `handle` should not be null. + static void Delete(CordzHandle* handle); + + // Returns the current entries in the delete queue in LIFO order. + static std::vector<const CordzHandle*> DiagnosticsGetDeleteQueue(); + + // Returns true if the provided handle is nullptr or guarded by this handle. + // Since the CordzSnapshot token is itself a CordzHandle, this method will + // allow tests to check if that token is keeping an arbitrary CordzHandle + // alive. + bool DiagnosticsHandleIsSafeToInspect(const CordzHandle* handle) const; + + // Returns the current entries in the delete queue, in LIFO order, that are + // protected by this. CordzHandle objects are only placed on the delete queue + // after CordzHandle::Delete is called with them as an argument. Only + // CordzHandle objects that are not also CordzSnapshot objects will be + // included in the return vector. For each of the handles in the return + // vector, the earliest that their memory can be freed is when this + // CordzSnapshot object is deleted. + std::vector<const CordzHandle*> DiagnosticsGetSafeToInspectDeletedHandles(); + + protected: + explicit CordzHandle(bool is_snapshot); + virtual ~CordzHandle(); + + private: + // Global queue data. CordzHandle stores a pointer to the global queue + // instance to harden against ODR violations. + struct Queue { + constexpr explicit Queue(absl::ConstInitType) + : mutex(absl::kConstInit, + absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {} + + absl::base_internal::SpinLock mutex; + std::atomic<CordzHandle*> dq_tail ABSL_GUARDED_BY(mutex){nullptr}; + + // Returns true if this delete queue is empty. This method does not acquire + // the lock, but does a 'load acquire' observation on the delete queue tail. + // It is used inside Delete() to check for the presence of a delete queue + // without holding the lock. The assumption is that the caller is in the + // state of 'being deleted', and can not be newly discovered by a concurrent + // 'being constructed' snapshot instance. Practically, this means that any + // such discovery (`find`, 'first' or 'next', etc) must have proper 'happens + // before / after' semantics and atomic fences. + bool IsEmpty() const ABSL_NO_THREAD_SAFETY_ANALYSIS { + return dq_tail.load(std::memory_order_acquire) == nullptr; + } + }; + + void ODRCheck() const { +#ifndef NDEBUG + ABSL_RAW_CHECK(queue_ == &global_queue_, "ODR violation in Cord"); +#endif + } + + ABSL_CONST_INIT static Queue global_queue_; + Queue* const queue_ = &global_queue_; + const bool is_snapshot_; + + // dq_prev_ and dq_next_ require the global queue mutex to be held. + // Unfortunately we can't use thread annotations such that the thread safety + // analysis understands that queue_ and global_queue_ are one and the same. + CordzHandle* dq_prev_ = nullptr; + CordzHandle* dq_next_ = nullptr; +}; + +class CordzSnapshot : public CordzHandle { + public: + CordzSnapshot() : CordzHandle(true) {} +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_HANDLE_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_handle_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_handle_test.cc new file mode 100644 index 0000000000..fd68e06b3e --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_handle_test.cc @@ -0,0 +1,265 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_handle.h" + +#include <random> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::Gt; +using ::testing::IsEmpty; +using ::testing::SizeIs; + +// Local less verbose helper +std::vector<const CordzHandle*> DeleteQueue() { + return CordzHandle::DiagnosticsGetDeleteQueue(); +} + +struct CordzHandleDeleteTracker : public CordzHandle { + bool* deleted; + explicit CordzHandleDeleteTracker(bool* deleted) : deleted(deleted) {} + ~CordzHandleDeleteTracker() override { *deleted = true; } +}; + +TEST(CordzHandleTest, DeleteQueueIsEmpty) { + EXPECT_THAT(DeleteQueue(), SizeIs(0)); +} + +TEST(CordzHandleTest, CordzHandleCreateDelete) { + bool deleted = false; + auto* handle = new CordzHandleDeleteTracker(&deleted); + EXPECT_FALSE(handle->is_snapshot()); + EXPECT_TRUE(handle->SafeToDelete()); + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + + CordzHandle::Delete(handle); + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + EXPECT_TRUE(deleted); +} + +TEST(CordzHandleTest, CordzSnapshotCreateDelete) { + auto* snapshot = new CordzSnapshot(); + EXPECT_TRUE(snapshot->is_snapshot()); + EXPECT_TRUE(snapshot->SafeToDelete()); + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot)); + delete snapshot; + EXPECT_THAT(DeleteQueue(), SizeIs(0)); +} + +TEST(CordzHandleTest, CordzHandleCreateDeleteWithSnapshot) { + bool deleted = false; + auto* snapshot = new CordzSnapshot(); + auto* handle = new CordzHandleDeleteTracker(&deleted); + EXPECT_FALSE(handle->SafeToDelete()); + + CordzHandle::Delete(handle); + EXPECT_THAT(DeleteQueue(), ElementsAre(handle, snapshot)); + EXPECT_FALSE(deleted); + EXPECT_FALSE(handle->SafeToDelete()); + + delete snapshot; + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + EXPECT_TRUE(deleted); +} + +TEST(CordzHandleTest, MultiSnapshot) { + bool deleted[3] = {false, false, false}; + + CordzSnapshot* snapshot[3]; + CordzHandleDeleteTracker* handle[3]; + for (int i = 0; i < 3; ++i) { + snapshot[i] = new CordzSnapshot(); + handle[i] = new CordzHandleDeleteTracker(&deleted[i]); + CordzHandle::Delete(handle[i]); + } + + EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1], + snapshot[1], handle[0], snapshot[0])); + EXPECT_THAT(deleted, ElementsAre(false, false, false)); + + delete snapshot[1]; + EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2], handle[1], + handle[0], snapshot[0])); + EXPECT_THAT(deleted, ElementsAre(false, false, false)); + + delete snapshot[0]; + EXPECT_THAT(DeleteQueue(), ElementsAre(handle[2], snapshot[2])); + EXPECT_THAT(deleted, ElementsAre(true, true, false)); + + delete snapshot[2]; + EXPECT_THAT(DeleteQueue(), SizeIs(0)); + EXPECT_THAT(deleted, ElementsAre(true, true, deleted)); +} + +TEST(CordzHandleTest, DiagnosticsHandleIsSafeToInspect) { + CordzSnapshot snapshot1; + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(nullptr)); + + auto* handle1 = new CordzHandle(); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); + + CordzHandle::Delete(handle1); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); + + CordzSnapshot snapshot2; + auto* handle2 = new CordzHandle(); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle2)); + EXPECT_FALSE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle1)); + EXPECT_TRUE(snapshot2.DiagnosticsHandleIsSafeToInspect(handle2)); + + CordzHandle::Delete(handle2); + EXPECT_TRUE(snapshot1.DiagnosticsHandleIsSafeToInspect(handle1)); +} + +TEST(CordzHandleTest, DiagnosticsGetSafeToInspectDeletedHandles) { + EXPECT_THAT(DeleteQueue(), IsEmpty()); + + auto* handle = new CordzHandle(); + auto* snapshot1 = new CordzSnapshot(); + + // snapshot1 should be able to see handle. + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot1)); + EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle)); + EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(), + IsEmpty()); + + // This handle will be safe to inspect as long as snapshot1 is alive. However, + // since only snapshot1 can prove that it's alive, it will be hidden from + // snapshot2. + CordzHandle::Delete(handle); + + // This snapshot shouldn't be able to see handle because handle was already + // sent to Delete. + auto* snapshot2 = new CordzSnapshot(); + + // DeleteQueue elements are LIFO order. + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2, handle, snapshot1)); + + EXPECT_TRUE(snapshot1->DiagnosticsHandleIsSafeToInspect(handle)); + EXPECT_FALSE(snapshot2->DiagnosticsHandleIsSafeToInspect(handle)); + + EXPECT_THAT(snapshot1->DiagnosticsGetSafeToInspectDeletedHandles(), + ElementsAre(handle)); + EXPECT_THAT(snapshot2->DiagnosticsGetSafeToInspectDeletedHandles(), + IsEmpty()); + + CordzHandle::Delete(snapshot1); + EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot2)); + + CordzHandle::Delete(snapshot2); + EXPECT_THAT(DeleteQueue(), IsEmpty()); +} + +// Create and delete CordzHandle and CordzSnapshot objects in multiple threads +// so that tsan has some time to chew on it and look for memory problems. +TEST(CordzHandleTest, MultiThreaded) { + Notification stop; + static constexpr int kNumThreads = 4; + // Keep the number of handles relatively small so that the test will naturally + // transition to an empty delete queue during the test. If there are, say, 100 + // handles, that will virtually never happen. With 10 handles and around 50k + // iterations in each of 4 threads, the delete queue appears to become empty + // around 200 times. + static constexpr int kNumHandles = 10; + + // Each thread is going to pick a random index and atomically swap its + // CordzHandle with one in handles. This way, each thread can avoid + // manipulating a CordzHandle that might be operated upon in another thread. + std::vector<std::atomic<CordzHandle*>> handles(kNumHandles); + + // global bool which is set when any thread did get some 'safe to inspect' + // handles. On some platforms and OSS tests, we might risk that some pool + // threads are starved, stalled, or just got a few unlikely random 'handle' + // coin tosses, so we satisfy this test with simply observing 'some' thread + // did something meaningful, which should minimize the potential for flakes. + std::atomic<bool> found_safe_to_inspect(false); + + { + absl::synchronization_internal::ThreadPool pool(kNumThreads); + for (int i = 0; i < kNumThreads; ++i) { + pool.Schedule([&stop, &handles, &found_safe_to_inspect]() { + std::minstd_rand gen; + std::uniform_int_distribution<int> dist_type(0, 2); + std::uniform_int_distribution<int> dist_handle(0, kNumHandles - 1); + + while (!stop.HasBeenNotified()) { + CordzHandle* handle; + switch (dist_type(gen)) { + case 0: + handle = new CordzHandle(); + break; + case 1: + handle = new CordzSnapshot(); + break; + default: + handle = nullptr; + break; + } + CordzHandle* old_handle = handles[dist_handle(gen)].exchange(handle); + if (old_handle != nullptr) { + std::vector<const CordzHandle*> safe_to_inspect = + old_handle->DiagnosticsGetSafeToInspectDeletedHandles(); + for (const CordzHandle* handle : safe_to_inspect) { + // We're in a tight loop, so don't generate too many error + // messages. + ASSERT_FALSE(handle->is_snapshot()); + } + if (!safe_to_inspect.empty()) { + found_safe_to_inspect.store(true); + } + CordzHandle::Delete(old_handle); + } + } + + // Have each thread attempt to clean up everything. Some thread will be + // the last to reach this cleanup code, and it will be guaranteed to + // clean up everything because nothing remains to create new handles. + for (auto& h : handles) { + if (CordzHandle* handle = h.exchange(nullptr)) { + CordzHandle::Delete(handle); + } + } + }); + } + + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); + } + + // Confirm that the test did *something*. This check will be satisfied as + // long as any thread has deleted a CordzSnapshot object and a non-snapshot + // CordzHandle was deleted after the CordzSnapshot was created. + // See also comments on `found_safe_to_inspect` + EXPECT_TRUE(found_safe_to_inspect.load()); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc new file mode 100644 index 0000000000..5c18bbc566 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc @@ -0,0 +1,445 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_info.h" + +#include "absl/base/config.h" +#include "absl/base/internal/spinlock.h" +#include "absl/container/inlined_vector.h" +#include "absl/debugging/stacktrace.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +using ::absl::base_internal::SpinLockHolder; + +constexpr int CordzInfo::kMaxStackDepth; + +ABSL_CONST_INIT CordzInfo::List CordzInfo::global_list_{absl::kConstInit}; + +namespace { + +// CordRepAnalyzer performs the analysis of a cord. +// +// It computes absolute node counts and total memory usage, and an 'estimated +// fair share memory usage` statistic. +// Conceptually, it divides the 'memory usage' at each location in the 'cord +// graph' by the cumulative reference count of that location. The cumulative +// reference count is the factored total of all edges leading into that node. +// +// The top level node is treated specially: we assume the current thread +// (typically called from the CordzHandler) to hold a reference purely to +// perform a safe analysis, and not being part of the application. So we +// substract 1 from the reference count of the top node to compute the +// 'application fair share' excluding the reference of the current thread. +// +// An example of fair sharing, and why we multiply reference counts: +// Assume we have 2 CordReps, both being a Substring referencing a Flat: +// CordSubstring A (refcount = 5) --> child Flat C (refcount = 2) +// CordSubstring B (refcount = 9) --> child Flat C (refcount = 2) +// +// Flat C has 2 incoming edges from the 2 substrings (refcount = 2) and is not +// referenced directly anywhere else. Translated into a 'fair share', we then +// attribute 50% of the memory (memory / refcount = 2) to each incoming edge. +// Rep A has a refcount of 5, so we attribute each incoming edge 1 / 5th of the +// memory cost below it, i.e.: the fair share of Rep A of the memory used by C +// is then 'memory C / (refcount C * refcount A) + (memory A / refcount A)'. +// It is also easy to see how all incoming edges add up to 100%. +class CordRepAnalyzer { + public: + // Creates an analyzer instance binding to `statistics`. + explicit CordRepAnalyzer(CordzStatistics& statistics) + : statistics_(statistics) {} + + // Analyzes the memory statistics and node counts for the provided `rep`, and + // adds the results to `statistics`. Note that node counts and memory sizes + // are not initialized, computed values are added to any existing values. + void AnalyzeCordRep(const CordRep* rep) { + // Process all linear nodes. + // As per the class comments, use refcout - 1 on the top level node, as the + // top level node is assumed to be referenced only for analysis purposes. + size_t refcount = rep->refcount.Get(); + RepRef repref{rep, (refcount > 1) ? refcount - 1 : 1}; + + // Process all top level linear nodes (substrings and flats). + repref = CountLinearReps(repref, memory_usage_); + + if (repref.rep != nullptr) { + if (repref.rep->tag == RING) { + AnalyzeRing(repref); + } else if (repref.rep->tag == BTREE) { + AnalyzeBtree(repref); + } else if (repref.rep->tag == CONCAT) { + AnalyzeConcat(repref); + } else { + // We should have either a concat, btree, or ring node if not null. + assert(false); + } + } + + // Adds values to output + statistics_.estimated_memory_usage += memory_usage_.total; + statistics_.estimated_fair_share_memory_usage += + static_cast<size_t>(memory_usage_.fair_share); + } + + private: + // RepRef identifies a CordRep* inside the Cord tree with its cumulative + // refcount including itself. For example, a tree consisting of a substring + // with a refcount of 3 and a child flat with a refcount of 4 will have RepRef + // refcounts of 3 and 12 respectively. + struct RepRef { + const CordRep* rep; + size_t refcount; + + // Returns a 'child' RepRef which contains the cumulative reference count of + // this instance multiplied by the child's reference count. + RepRef Child(const CordRep* child) const { + return RepRef{child, refcount * child->refcount.Get()}; + } + }; + + // Memory usage values + struct MemoryUsage { + size_t total = 0; + double fair_share = 0.0; + + // Adds 'size` memory usage to this class, with a cumulative (recursive) + // reference count of `refcount` + void Add(size_t size, size_t refcount) { + total += size; + fair_share += static_cast<double>(size) / refcount; + } + }; + + // Returns `rr` if `rr.rep` is not null and a CONCAT type. + // Asserts that `rr.rep` is a concat node or null. + static RepRef AssertConcat(RepRef repref) { + const CordRep* rep = repref.rep; + assert(rep == nullptr || rep->tag == CONCAT); + return (rep != nullptr && rep->tag == CONCAT) ? repref : RepRef{nullptr, 0}; + } + + // Counts a flat of the provide allocated size + void CountFlat(size_t size) { + statistics_.node_count++; + statistics_.node_counts.flat++; + if (size <= 64) { + statistics_.node_counts.flat_64++; + } else if (size <= 128) { + statistics_.node_counts.flat_128++; + } else if (size <= 256) { + statistics_.node_counts.flat_256++; + } else if (size <= 512) { + statistics_.node_counts.flat_512++; + } else if (size <= 1024) { + statistics_.node_counts.flat_1k++; + } + } + + // Processes 'linear' reps (substring, flat, external) not requiring iteration + // or recursion. Returns RefRep{null} if all reps were processed, else returns + // the top-most non-linear concat or ring cordrep. + // Node counts are updated into `statistics_`, memory usage is update into + // `memory_usage`, which typically references `memory_usage_` except for ring + // buffers where we count children unrounded. + RepRef CountLinearReps(RepRef rep, MemoryUsage& memory_usage) { + // Consume all substrings + while (rep.rep->tag == SUBSTRING) { + statistics_.node_count++; + statistics_.node_counts.substring++; + memory_usage.Add(sizeof(CordRepSubstring), rep.refcount); + rep = rep.Child(rep.rep->substring()->child); + } + + // Consume possible FLAT + if (rep.rep->tag >= FLAT) { + size_t size = rep.rep->flat()->AllocatedSize(); + CountFlat(size); + memory_usage.Add(size, rep.refcount); + return RepRef{nullptr, 0}; + } + + // Consume possible external + if (rep.rep->tag == EXTERNAL) { + statistics_.node_count++; + statistics_.node_counts.external++; + size_t size = rep.rep->length + sizeof(CordRepExternalImpl<intptr_t>); + memory_usage.Add(size, rep.refcount); + return RepRef{nullptr, 0}; + } + + return rep; + } + + // Analyzes the provided concat node in a flattened recursive way. + void AnalyzeConcat(RepRef rep) { + absl::InlinedVector<RepRef, 47> pending; + + while (rep.rep != nullptr) { + const CordRepConcat* concat = rep.rep->concat(); + RepRef left = rep.Child(concat->left); + RepRef right = rep.Child(concat->right); + + statistics_.node_count++; + statistics_.node_counts.concat++; + memory_usage_.Add(sizeof(CordRepConcat), rep.refcount); + + right = AssertConcat(CountLinearReps(right, memory_usage_)); + rep = AssertConcat(CountLinearReps(left, memory_usage_)); + if (rep.rep != nullptr) { + if (right.rep != nullptr) { + pending.push_back(right); + } + } else if (right.rep != nullptr) { + rep = right; + } else if (!pending.empty()) { + rep = pending.back(); + pending.pop_back(); + } + } + } + + // Analyzes the provided ring. + void AnalyzeRing(RepRef rep) { + statistics_.node_count++; + statistics_.node_counts.ring++; + const CordRepRing* ring = rep.rep->ring(); + memory_usage_.Add(CordRepRing::AllocSize(ring->capacity()), rep.refcount); + ring->ForEach([&](CordRepRing::index_type pos) { + CountLinearReps(rep.Child(ring->entry_child(pos)), memory_usage_); + }); + } + + // Analyzes the provided btree. + void AnalyzeBtree(RepRef rep) { + statistics_.node_count++; + statistics_.node_counts.btree++; + memory_usage_.Add(sizeof(CordRepBtree), rep.refcount); + const CordRepBtree* tree = rep.rep->btree(); + if (tree->height() > 0) { + for (CordRep* edge : tree->Edges()) { + AnalyzeBtree(rep.Child(edge)); + } + } else { + for (CordRep* edge : tree->Edges()) { + CountLinearReps(rep.Child(edge), memory_usage_); + } + } + } + + CordzStatistics& statistics_; + MemoryUsage memory_usage_; +}; + +} // namespace + +CordzInfo* CordzInfo::Head(const CordzSnapshot& snapshot) { + ABSL_ASSERT(snapshot.is_snapshot()); + + // We can do an 'unsafe' load of 'head', as we are guaranteed that the + // instance it points to is kept alive by the provided CordzSnapshot, so we + // can simply return the current value using an acquire load. + // We do enforce in DEBUG builds that the 'head' value is present in the + // delete queue: ODR violations may lead to 'snapshot' and 'global_list_' + // being in different libraries / modules. + CordzInfo* head = global_list_.head.load(std::memory_order_acquire); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(head)); + return head; +} + +CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const { + ABSL_ASSERT(snapshot.is_snapshot()); + + // Similar to the 'Head()' function, we do not need a mutex here. + CordzInfo* next = ci_next_.load(std::memory_order_acquire); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(this)); + ABSL_ASSERT(snapshot.DiagnosticsHandleIsSafeToInspect(next)); + return next; +} + +void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) { + assert(cord.is_tree()); + assert(!cord.is_profiled()); + CordzInfo* cordz_info = new CordzInfo(cord.as_tree(), nullptr, method); + cord.set_cordz_info(cordz_info); + cordz_info->Track(); +} + +void CordzInfo::TrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method) { + assert(cord.is_tree()); + assert(src.is_tree()); + + // Unsample current as we the current cord is being replaced with 'src', + // so any method history is no longer relevant. + CordzInfo* cordz_info = cord.cordz_info(); + if (cordz_info != nullptr) cordz_info->Untrack(); + + // Start new cord sample + cordz_info = new CordzInfo(cord.as_tree(), src.cordz_info(), method); + cord.set_cordz_info(cordz_info); + cordz_info->Track(); +} + +void CordzInfo::MaybeTrackCordImpl(InlineData& cord, const InlineData& src, + MethodIdentifier method) { + if (src.is_profiled()) { + TrackCord(cord, src, method); + } else if (cord.is_profiled()) { + cord.cordz_info()->Untrack(); + cord.clear_cordz_info(); + } +} + +CordzInfo::MethodIdentifier CordzInfo::GetParentMethod(const CordzInfo* src) { + if (src == nullptr) return MethodIdentifier::kUnknown; + return src->parent_method_ != MethodIdentifier::kUnknown ? src->parent_method_ + : src->method_; +} + +int CordzInfo::FillParentStack(const CordzInfo* src, void** stack) { + assert(stack); + if (src == nullptr) return 0; + if (src->parent_stack_depth_) { + memcpy(stack, src->parent_stack_, src->parent_stack_depth_ * sizeof(void*)); + return src->parent_stack_depth_; + } + memcpy(stack, src->stack_, src->stack_depth_ * sizeof(void*)); + return src->stack_depth_; +} + +CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src, + MethodIdentifier method) + : rep_(rep), + stack_depth_(absl::GetStackTrace(stack_, /*max_depth=*/kMaxStackDepth, + /*skip_count=*/1)), + parent_stack_depth_(FillParentStack(src, parent_stack_)), + method_(method), + parent_method_(GetParentMethod(src)), + create_time_(absl::Now()) { + update_tracker_.LossyAdd(method); + if (src) { + // Copy parent counters. + update_tracker_.LossyAdd(src->update_tracker_); + } +} + +CordzInfo::~CordzInfo() { + // `rep_` is potentially kept alive if CordzInfo is included + // in a collection snapshot (which should be rare). + if (ABSL_PREDICT_FALSE(rep_)) { + CordRep::Unref(rep_); + } +} + +void CordzInfo::Track() { + SpinLockHolder l(&list_->mutex); + + CordzInfo* const head = list_->head.load(std::memory_order_acquire); + if (head != nullptr) { + head->ci_prev_.store(this, std::memory_order_release); + } + ci_next_.store(head, std::memory_order_release); + list_->head.store(this, std::memory_order_release); +} + +void CordzInfo::Untrack() { + ODRCheck(); + { + SpinLockHolder l(&list_->mutex); + + CordzInfo* const head = list_->head.load(std::memory_order_acquire); + CordzInfo* const next = ci_next_.load(std::memory_order_acquire); + CordzInfo* const prev = ci_prev_.load(std::memory_order_acquire); + + if (next) { + ABSL_ASSERT(next->ci_prev_.load(std::memory_order_acquire) == this); + next->ci_prev_.store(prev, std::memory_order_release); + } + if (prev) { + ABSL_ASSERT(head != this); + ABSL_ASSERT(prev->ci_next_.load(std::memory_order_acquire) == this); + prev->ci_next_.store(next, std::memory_order_release); + } else { + ABSL_ASSERT(head == this); + list_->head.store(next, std::memory_order_release); + } + } + + // We can no longer be discovered: perform a fast path check if we are not + // listed on any delete queue, so we can directly delete this instance. + if (SafeToDelete()) { + UnsafeSetCordRep(nullptr); + delete this; + return; + } + + // We are likely part of a snapshot, extend the life of the CordRep + { + absl::MutexLock lock(&mutex_); + if (rep_) CordRep::Ref(rep_); + } + CordzHandle::Delete(this); +} + +void CordzInfo::Lock(MethodIdentifier method) + ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_) { + mutex_.Lock(); + update_tracker_.LossyAdd(method); + assert(rep_); +} + +void CordzInfo::Unlock() ABSL_UNLOCK_FUNCTION(mutex_) { + bool tracked = rep_ != nullptr; + mutex_.Unlock(); + if (!tracked) { + Untrack(); + } +} + +absl::Span<void* const> CordzInfo::GetStack() const { + return absl::MakeConstSpan(stack_, stack_depth_); +} + +absl::Span<void* const> CordzInfo::GetParentStack() const { + return absl::MakeConstSpan(parent_stack_, parent_stack_depth_); +} + +CordzStatistics CordzInfo::GetCordzStatistics() const { + CordzStatistics stats; + stats.method = method_; + stats.parent_method = parent_method_; + stats.update_tracker = update_tracker_; + if (CordRep* rep = RefCordRep()) { + stats.size = rep->length; + CordRepAnalyzer analyzer(stats); + analyzer.AnalyzeCordRep(rep); + CordRep::Unref(rep); + } + return stats; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_info.h b/third_party/abseil-cpp/absl/strings/internal/cordz_info.h new file mode 100644 index 0000000000..026d5b9981 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_info.h @@ -0,0 +1,298 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_CORDZ_INFO_H_ +#define ABSL_STRINGS_CORDZ_INFO_H_ + +#include <atomic> +#include <cstdint> +#include <functional> + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_functions.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzInfo tracks a profiled Cord. Each of these objects can be in two places. +// If a Cord is alive, the CordzInfo will be in the global_cordz_infos map, and +// can also be retrieved via the linked list starting with +// global_cordz_infos_head and continued via the cordz_info_next() method. When +// a Cord has reached the end of its lifespan, the CordzInfo object will be +// migrated out of the global_cordz_infos list and the global_cordz_infos_map, +// and will either be deleted or appended to the global_delete_queue. If it is +// placed on the global_delete_queue, the CordzInfo object will be cleaned in +// the destructor of a CordzSampleToken object. +class ABSL_LOCKABLE CordzInfo : public CordzHandle { + public: + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + + // TrackCord creates a CordzInfo instance which tracks important metrics of + // a sampled cord, and stores the created CordzInfo instance into `cord'. All + // CordzInfo instances are placed in a global list which is used to discover + // and snapshot all actively tracked cords. Callers are responsible for + // calling UntrackCord() before the tracked Cord instance is deleted, or to + // stop tracking the sampled Cord. Callers are also responsible for guarding + // changes to the 'tree' value of a Cord (InlineData.tree) through the Lock() + // and Unlock() calls. Any change resulting in a new tree value for the cord + // requires a call to SetCordRep() before the old tree has been unreffed + // and/or deleted. `method` identifies the Cord public API method initiating + // the cord to be sampled. + // Requires `cord` to hold a tree, and `cord.cordz_info()` to be null. + static void TrackCord(InlineData& cord, MethodIdentifier method); + + // Identical to TrackCord(), except that this function fills the + // `parent_stack` and `parent_method` properties of the returned CordzInfo + // instance from the provided `src` instance if `src` is sampled. + // This function should be used for sampling 'copy constructed' and 'copy + // assigned' cords. This function allows 'cord` to be already sampled, in + // which case the CordzInfo will be newly created from `src`. + static void TrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method); + + // Maybe sample the cord identified by 'cord' for method 'method'. + // Uses `cordz_should_profile` to randomly pick cords to be sampled, and if + // so, invokes `TrackCord` to start sampling `cord`. + static void MaybeTrackCord(InlineData& cord, MethodIdentifier method); + + // Maybe sample the cord identified by 'cord' for method 'method'. + // `src` identifies a 'parent' cord which is assigned to `cord`, typically the + // input cord for a copy constructor, or an assign method such as `operator=` + // `cord` will be sampled if (and only if) `src` is sampled. + // If `cord` is currently being sampled and `src` is not being sampled, then + // this function will stop sampling the cord and reset the cord's cordz_info. + // + // Previously this function defined that `cord` will be sampled if either + // `src` is sampled, or if `cord` is randomly picked for sampling. However, + // this can cause issues, as there may be paths where some cord is assigned an + // indirect copy of it's own value. As such a 'string of copies' would then + // remain sampled (`src.is_profiled`), then assigning such a cord back to + // 'itself' creates a cycle where the cord will converge to 'always sampled`. + // + // For example: + // + // Cord x; + // for (...) { + // // Copy ctor --> y.is_profiled := x.is_profiled | random(...) + // Cord y = x; + // ... + // // Assign x = y --> x.is_profiled = y.is_profiled | random(...) + // // ==> x.is_profiled |= random(...) + // // ==> x converges to 'always profiled' + // x = y; + // } + static void MaybeTrackCord(InlineData& cord, const InlineData& src, + MethodIdentifier method); + + // Stops tracking changes for a sampled cord, and deletes the provided info. + // This function must be called before the sampled cord instance is deleted, + // and before the root cordrep of the sampled cord is unreffed. + // This function may extend the lifetime of the cordrep in cases where the + // CordInfo instance is being held by a concurrent collection thread. + void Untrack(); + + // Invokes UntrackCord() on `info` if `info` is not null. + static void MaybeUntrackCord(CordzInfo* info); + + CordzInfo() = delete; + CordzInfo(const CordzInfo&) = delete; + CordzInfo& operator=(const CordzInfo&) = delete; + + // Retrieves the oldest existing CordzInfo. + static CordzInfo* Head(const CordzSnapshot& snapshot) + ABSL_NO_THREAD_SAFETY_ANALYSIS; + + // Retrieves the next oldest existing CordzInfo older than 'this' instance. + CordzInfo* Next(const CordzSnapshot& snapshot) const + ABSL_NO_THREAD_SAFETY_ANALYSIS; + + // Locks this instance for the update identified by `method`. + // Increases the count for `method` in `update_tracker`. + void Lock(MethodIdentifier method) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex_); + + // Unlocks this instance. If the contained `rep` has been set to null + // indicating the Cord has been cleared or is otherwise no longer sampled, + // then this method will delete this CordzInfo instance. + void Unlock() ABSL_UNLOCK_FUNCTION(mutex_); + + // Asserts that this CordzInfo instance is locked. + void AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_); + + // Updates the `rep` property of this instance. This methods is invoked by + // Cord logic each time the root node of a sampled Cord changes, and before + // the old root reference count is deleted. This guarantees that collection + // code can always safely take a reference on the tracked cord. + // Requires a lock to be held through the `Lock()` method. + // TODO(b/117940323): annotate with ABSL_EXCLUSIVE_LOCKS_REQUIRED once all + // Cord code is in a state where this can be proven true by the compiler. + void SetCordRep(CordRep* rep); + + // Returns the current `rep` property of this instance with a reference + // added, or null if this instance represents a cord that has since been + // deleted or untracked. + CordRep* RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns the current value of `rep_` for testing purposes only. + CordRep* GetCordRepForTesting() const ABSL_NO_THREAD_SAFETY_ANALYSIS { + return rep_; + } + + // Sets the current value of `rep_` for testing purposes only. + void SetCordRepForTesting(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS { + rep_ = rep; + } + + // Returns the stack trace for where the cord was first sampled. Cords are + // potentially sampled when they promote from an inlined cord to a tree or + // ring representation, which is not necessarily the location where the cord + // was first created. Some cords are created as inlined cords, and only as + // data is added do they become a non-inlined cord. However, typically the + // location represents reasonably well where the cord is 'created'. + absl::Span<void* const> GetStack() const; + + // Returns the stack trace for a sampled cord's 'parent stack trace'. This + // value may be set if the cord is sampled (promoted) after being created + // from, or being assigned the value of an existing (sampled) cord. + absl::Span<void* const> GetParentStack() const; + + // Retrieves the CordzStatistics associated with this Cord. The statistics + // are only updated when a Cord goes through a mutation, such as an Append + // or RemovePrefix. + CordzStatistics GetCordzStatistics() const; + + private: + using SpinLock = absl::base_internal::SpinLock; + using SpinLockHolder = ::absl::base_internal::SpinLockHolder; + + // Global cordz info list. CordzInfo stores a pointer to the global list + // instance to harden against ODR violations. + struct List { + constexpr explicit List(absl::ConstInitType) + : mutex(absl::kConstInit, + absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {} + + SpinLock mutex; + std::atomic<CordzInfo*> head ABSL_GUARDED_BY(mutex){nullptr}; + }; + + static constexpr int kMaxStackDepth = 64; + + explicit CordzInfo(CordRep* rep, const CordzInfo* src, + MethodIdentifier method); + ~CordzInfo() override; + + // Sets `rep_` without holding a lock. + void UnsafeSetCordRep(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS; + + void Track(); + + // Returns the parent method from `src`, which is either `parent_method_` or + // `method_` depending on `parent_method_` being kUnknown. + // Returns kUnknown if `src` is null. + static MethodIdentifier GetParentMethod(const CordzInfo* src); + + // Fills the provided stack from `src`, copying either `parent_stack_` or + // `stack_` depending on `parent_stack_` being empty, returning the size of + // the parent stack. + // Returns 0 if `src` is null. + static int FillParentStack(const CordzInfo* src, void** stack); + + void ODRCheck() const { +#ifndef NDEBUG + ABSL_RAW_CHECK(list_ == &global_list_, "ODR violation in Cord"); +#endif + } + + // Non-inlined implementation of `MaybeTrackCord`, which is executed if + // either `src` is sampled or `cord` is sampled, and either untracks or + // tracks `cord` as documented per `MaybeTrackCord`. + static void MaybeTrackCordImpl(InlineData& cord, const InlineData& src, + MethodIdentifier method); + + ABSL_CONST_INIT static List global_list_; + List* const list_ = &global_list_; + + // ci_prev_ and ci_next_ require the global list mutex to be held. + // Unfortunately we can't use thread annotations such that the thread safety + // analysis understands that list_ and global_list_ are one and the same. + std::atomic<CordzInfo*> ci_prev_{nullptr}; + std::atomic<CordzInfo*> ci_next_{nullptr}; + + mutable absl::Mutex mutex_; + CordRep* rep_ ABSL_GUARDED_BY(mutex_); + + void* stack_[kMaxStackDepth]; + void* parent_stack_[kMaxStackDepth]; + const int stack_depth_; + const int parent_stack_depth_; + const MethodIdentifier method_; + const MethodIdentifier parent_method_; + CordzUpdateTracker update_tracker_; + const absl::Time create_time_; +}; + +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( + InlineData& cord, MethodIdentifier method) { + if (ABSL_PREDICT_FALSE(cordz_should_profile())) { + TrackCord(cord, method); + } +} + +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord( + InlineData& cord, const InlineData& src, MethodIdentifier method) { + if (ABSL_PREDICT_FALSE(InlineData::is_either_profiled(cord, src))) { + MaybeTrackCordImpl(cord, src, method); + } +} + +inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeUntrackCord( + CordzInfo* info) { + if (ABSL_PREDICT_FALSE(info)) { + info->Untrack(); + } +} + +inline void CordzInfo::AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_) { +#ifndef NDEBUG + mutex_.AssertHeld(); +#endif +} + +inline void CordzInfo::SetCordRep(CordRep* rep) { + AssertHeld(); + rep_ = rep; +} + +inline void CordzInfo::UnsafeSetCordRep(CordRep* rep) { rep_ = rep; } + +inline CordRep* CordzInfo::RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_) { + MutexLock lock(&mutex_); + return rep_ ? CordRep::Ref(rep_) : nullptr; +} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_INFO_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_info_statistics_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_info_statistics_test.cc new file mode 100644 index 0000000000..7430d281ca --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_info_statistics_test.cc @@ -0,0 +1,625 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <iostream> +#include <random> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/cord.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cord_rep_btree.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cord_rep_ring.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_sample_token.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_scope.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/notification.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// Do not print statistics contents, the matcher prints them as needed. +inline void PrintTo(const CordzStatistics& stats, std::ostream* s) { + if (s) *s << "CordzStatistics{...}"; +} + +namespace { + +using ::testing::Ge; + +// Creates a flat of the specified allocated size +CordRepFlat* Flat(size_t size) { + // Round up to a tag size, as we are going to poke an exact tag size back into + // the allocated flat. 'size returning allocators' could grant us more than we + // wanted, but we are ok to poke the 'requested' size in the tag, even in the + // presence of sized deletes, so we need to make sure the size rounds + // perfectly to a tag value. + assert(size >= kMinFlatSize); + size = RoundUpForTag(size); + CordRepFlat* flat = CordRepFlat::New(size - kFlatOverhead); + flat->tag = AllocatedSizeToTag(size); + flat->length = size - kFlatOverhead; + return flat; +} + +// Creates an external of the specified length +CordRepExternal* External(int length = 512) { + return static_cast<CordRepExternal*>( + NewExternalRep(absl::string_view("", length), [](absl::string_view) {})); +} + +// Creates a substring on the provided rep of length - 1 +CordRepSubstring* Substring(CordRep* rep) { + auto* substring = new CordRepSubstring; + substring->length = rep->length - 1; + substring->tag = SUBSTRING; + substring->child = rep; + return substring; +} + +// Creates a concat on the provided reps +CordRepConcat* Concat(CordRep* left, CordRep* right) { + auto* concat = new CordRepConcat; + concat->length = left->length + right->length; + concat->tag = CONCAT; + concat->left = left; + concat->right = right; + return concat; +} + +// Reference count helper +struct RefHelper { + std::vector<CordRep*> refs; + + ~RefHelper() { + for (CordRep* rep : refs) { + CordRep::Unref(rep); + } + } + + // Invokes CordRep::Unref() on `rep` when this instance is destroyed. + template <typename T> + T* NeedsUnref(T* rep) { + refs.push_back(rep); + return rep; + } + + // Adds `n` reference counts to `rep` which will be unreffed when this + // instance is destroyed. + template <typename T> + T* Ref(T* rep, size_t n = 1) { + while (n--) { + NeedsUnref(CordRep::Ref(rep)); + } + return rep; + } +}; + +// Sizeof helper. Returns the allocated size of `p`, excluding any child +// elements for substring, concat and ring cord reps. +template <typename T> +size_t SizeOf(const T* rep) { + return sizeof(T); +} + +template <> +size_t SizeOf(const CordRepFlat* rep) { + return rep->AllocatedSize(); +} + +template <> +size_t SizeOf(const CordRepExternal* rep) { + // See cord.cc + return sizeof(CordRepExternalImpl<intptr_t>) + rep->length; +} + +template <> +size_t SizeOf(const CordRepRing* rep) { + return CordRepRing::AllocSize(rep->capacity()); +} + +// Computes fair share memory used in a naive 'we dare to recurse' way. +double FairShareImpl(CordRep* rep, size_t ref) { + double self = 0.0, children = 0.0; + ref *= rep->refcount.Get(); + if (rep->tag >= FLAT) { + self = SizeOf(rep->flat()); + } else if (rep->tag == EXTERNAL) { + self = SizeOf(rep->external()); + } else if (rep->tag == SUBSTRING) { + self = SizeOf(rep->substring()); + children = FairShareImpl(rep->substring()->child, ref); + } else if (rep->tag == BTREE) { + self = SizeOf(rep->btree()); + for (CordRep*edge : rep->btree()->Edges()) { + children += FairShareImpl(edge, ref); + } + } else if (rep->tag == RING) { + self = SizeOf(rep->ring()); + rep->ring()->ForEach([&](CordRepRing::index_type i) { + self += FairShareImpl(rep->ring()->entry_child(i), 1); + }); + } else if (rep->tag == CONCAT) { + self = SizeOf(rep->concat()); + children = FairShareImpl(rep->concat()->left, ref) + + FairShareImpl(rep->concat()->right, ref); + } else { + assert(false); + } + return self / ref + children; +} + +// Returns the fair share memory size from `ShareFhareImpl()` as a size_t. +size_t FairShare(CordRep* rep, size_t ref = 1) { + return static_cast<size_t>(FairShareImpl(rep, ref)); +} + +// Samples the cord and returns CordzInfo::GetStatistics() +CordzStatistics SampleCord(CordRep* rep) { + InlineData cord(rep); + CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown); + CordzStatistics stats = cord.cordz_info()->GetCordzStatistics(); + cord.cordz_info()->Untrack(); + return stats; +} + +MATCHER_P(EqStatistics, stats, "Statistics equal expected values") { + bool ok = true; + +#define STATS_MATCHER_EXPECT_EQ(member) \ + if (stats.member != arg.member) { \ + *result_listener << "\n stats." << #member \ + << ": actual = " << arg.member << ", expected " \ + << stats.member; \ + ok = false; \ + } + + STATS_MATCHER_EXPECT_EQ(size); + STATS_MATCHER_EXPECT_EQ(node_count); + STATS_MATCHER_EXPECT_EQ(node_counts.flat); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_64); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_128); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_256); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_512); + STATS_MATCHER_EXPECT_EQ(node_counts.flat_1k); + STATS_MATCHER_EXPECT_EQ(node_counts.external); + STATS_MATCHER_EXPECT_EQ(node_counts.concat); + STATS_MATCHER_EXPECT_EQ(node_counts.substring); + STATS_MATCHER_EXPECT_EQ(node_counts.ring); + STATS_MATCHER_EXPECT_EQ(node_counts.btree); + STATS_MATCHER_EXPECT_EQ(estimated_memory_usage); + STATS_MATCHER_EXPECT_EQ(estimated_fair_share_memory_usage); + +#undef STATS_MATCHER_EXPECT_EQ + + return ok; +} + +TEST(CordzInfoStatisticsTest, Flat) { + RefHelper ref; + auto* flat = ref.NeedsUnref(Flat(512)); + + CordzStatistics expected; + expected.size = flat->length; + expected.estimated_memory_usage = SizeOf(flat); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 1; + expected.node_counts.flat = 1; + expected.node_counts.flat_512 = 1; + + EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedFlat) { + RefHelper ref; + auto* flat = ref.Ref(ref.NeedsUnref(Flat(64))); + + CordzStatistics expected; + expected.size = flat->length; + expected.estimated_memory_usage = SizeOf(flat); + expected.estimated_fair_share_memory_usage = SizeOf(flat) / 2; + expected.node_count = 1; + expected.node_counts.flat = 1; + expected.node_counts.flat_64 = 1; + + EXPECT_THAT(SampleCord(flat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, External) { + RefHelper ref; + auto* external = ref.NeedsUnref(External()); + + CordzStatistics expected; + expected.size = external->length; + expected.estimated_memory_usage = SizeOf(external); + expected.estimated_fair_share_memory_usage = SizeOf(external); + expected.node_count = 1; + expected.node_counts.external = 1; + + EXPECT_THAT(SampleCord(external), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedExternal) { + RefHelper ref; + auto* external = ref.Ref(ref.NeedsUnref(External())); + + CordzStatistics expected; + expected.size = external->length; + expected.estimated_memory_usage = SizeOf(external); + expected.estimated_fair_share_memory_usage = SizeOf(external) / 2; + expected.node_count = 1; + expected.node_counts.external = 1; + + EXPECT_THAT(SampleCord(external), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, Substring) { + RefHelper ref; + auto* flat = Flat(1024); + auto* substring = ref.NeedsUnref(Substring(flat)); + + CordzStatistics expected; + expected.size = substring->length; + expected.estimated_memory_usage = SizeOf(substring) + SizeOf(flat); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 2; + expected.node_counts.flat = 1; + expected.node_counts.flat_1k = 1; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedSubstring) { + RefHelper ref; + auto* flat = ref.Ref(Flat(511), 2); + auto* substring = ref.Ref(ref.NeedsUnref(Substring(flat))); + + CordzStatistics expected; + expected.size = substring->length; + expected.estimated_memory_usage = SizeOf(flat) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = + SizeOf(substring) / 2 + SizeOf(flat) / 6; + expected.node_count = 2; + expected.node_counts.flat = 1; + expected.node_counts.flat_512 = 1; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, Concat) { + RefHelper ref; + auto* flat1 = Flat(300); + auto* flat2 = Flat(2000); + auto* concat = ref.NeedsUnref(Concat(flat1, flat2)); + + CordzStatistics expected; + expected.size = concat->length; + expected.estimated_memory_usage = + SizeOf(concat) + SizeOf(flat1) + SizeOf(flat2); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 3; + expected.node_counts.flat = 2; + expected.node_counts.flat_512 = 1; + expected.node_counts.concat = 1; + + EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, DeepConcat) { + RefHelper ref; + auto* flat1 = Flat(300); + auto* flat2 = Flat(2000); + auto* flat3 = Flat(400); + auto* external = External(3000); + auto* substring = Substring(external); + auto* concat1 = Concat(flat1, flat2); + auto* concat2 = Concat(flat3, substring); + auto* concat = ref.NeedsUnref(Concat(concat1, concat2)); + + CordzStatistics expected; + expected.size = concat->length; + expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + + expected.node_count = 8; + expected.node_counts.flat = 3; + expected.node_counts.flat_512 = 2; + expected.node_counts.external = 1; + expected.node_counts.concat = 3; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, DeepSharedConcat) { + RefHelper ref; + auto* flat1 = Flat(40); + auto* flat2 = ref.Ref(Flat(2000), 4); + auto* flat3 = Flat(70); + auto* external = ref.Ref(External(3000)); + auto* substring = ref.Ref(Substring(external), 3); + auto* concat1 = Concat(flat1, flat2); + auto* concat2 = Concat(flat3, substring); + auto* concat = ref.Ref(ref.NeedsUnref(Concat(concat1, concat2))); + + CordzStatistics expected; + expected.size = concat->length; + expected.estimated_memory_usage = SizeOf(concat) * 3 + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = FairShare(concat); + expected.node_count = 8; + expected.node_counts.flat = 3; + expected.node_counts.flat_64 = 1; + expected.node_counts.flat_128 = 1; + expected.node_counts.external = 1; + expected.node_counts.concat = 3; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(concat), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, Ring) { + RefHelper ref; + auto* flat1 = Flat(240); + auto* flat2 = Flat(2000); + auto* flat3 = Flat(70); + auto* external = External(3000); + CordRepRing* ring = CordRepRing::Create(flat1); + ring = CordRepRing::Append(ring, flat2); + ring = CordRepRing::Append(ring, flat3); + ring = ref.NeedsUnref(CordRepRing::Append(ring, external)); + + CordzStatistics expected; + expected.size = ring->length; + expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 5; + expected.node_counts.flat = 3; + expected.node_counts.flat_128 = 1; + expected.node_counts.flat_256 = 1; + expected.node_counts.external = 1; + expected.node_counts.ring = 1; + + EXPECT_THAT(SampleCord(ring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, SharedSubstringRing) { + RefHelper ref; + auto* flat1 = ref.Ref(Flat(240)); + auto* flat2 = Flat(200); + auto* flat3 = Flat(70); + auto* external = ref.Ref(External(3000), 5); + CordRepRing* ring = CordRepRing::Create(flat1); + ring = CordRepRing::Append(ring, flat2); + ring = CordRepRing::Append(ring, flat3); + ring = ref.Ref(CordRepRing::Append(ring, external), 4); + auto* substring = ref.Ref(ref.NeedsUnref(Substring(ring))); + + + CordzStatistics expected; + expected.size = substring->length; + expected.estimated_memory_usage = SizeOf(ring) + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(flat3) + + SizeOf(external) + SizeOf(substring); + expected.estimated_fair_share_memory_usage = FairShare(substring); + expected.node_count = 6; + expected.node_counts.flat = 3; + expected.node_counts.flat_128 = 1; + expected.node_counts.flat_256 = 2; + expected.node_counts.external = 1; + expected.node_counts.ring = 1; + expected.node_counts.substring = 1; + + EXPECT_THAT(SampleCord(substring), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, BtreeLeaf) { + ASSERT_THAT(CordRepBtree::kMaxCapacity, Ge(3)); + RefHelper ref; + auto* flat1 = Flat(2000); + auto* flat2 = Flat(200); + auto* substr = Substring(flat2); + auto* external = External(3000); + + CordRepBtree* tree = CordRepBtree::Create(flat1); + tree = CordRepBtree::Append(tree, substr); + tree = CordRepBtree::Append(tree, external); + size_t flat3_count = CordRepBtree::kMaxCapacity - 3; + size_t flat3_size = 0; + for (size_t i = 0; i < flat3_count; ++i) { + auto* flat3 = Flat(70); + flat3_size += SizeOf(flat3); + tree = CordRepBtree::Append(tree, flat3); + } + ref.NeedsUnref(tree); + + CordzStatistics expected; + expected.size = tree->length; + expected.estimated_memory_usage = SizeOf(tree) + SizeOf(flat1) + + SizeOf(flat2) + SizeOf(substr) + + flat3_size + SizeOf(external); + expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage; + expected.node_count = 1 + 3 + 1 + flat3_count; + expected.node_counts.flat = 2 + flat3_count; + expected.node_counts.flat_128 = flat3_count; + expected.node_counts.flat_256 = 1; + expected.node_counts.external = 1; + expected.node_counts.substring = 1; + expected.node_counts.btree = 1; + + EXPECT_THAT(SampleCord(tree), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, BtreeNodeShared) { + RefHelper ref; + static constexpr int leaf_count = 3; + const size_t flat3_count = CordRepBtree::kMaxCapacity - 3; + ASSERT_THAT(flat3_count, Ge(0)); + + CordRepBtree* tree = nullptr; + size_t mem_size = 0; + for (int i = 0; i < leaf_count; ++i) { + auto* flat1 = ref.Ref(Flat(2000), 9); + mem_size += SizeOf(flat1); + if (i == 0) { + tree = CordRepBtree::Create(flat1); + } else { + tree = CordRepBtree::Append(tree, flat1); + } + + auto* flat2 = Flat(200); + auto* substr = Substring(flat2); + mem_size += SizeOf(flat2) + SizeOf(substr); + tree = CordRepBtree::Append(tree, substr); + + auto* external = External(30); + mem_size += SizeOf(external); + tree = CordRepBtree::Append(tree, external); + + for (size_t i = 0; i < flat3_count; ++i) { + auto* flat3 = Flat(70); + mem_size += SizeOf(flat3); + tree = CordRepBtree::Append(tree, flat3); + } + + if (i == 0) { + mem_size += SizeOf(tree); + } else { + mem_size += SizeOf(tree->Edges().back()->btree()); + } + } + ref.NeedsUnref(tree); + + // Ref count: 2 for top (add 1), 5 for leaf 0 (add 4). + ref.Ref(tree, 1); + ref.Ref(tree->Edges().front(), 4); + + CordzStatistics expected; + expected.size = tree->length; + expected.estimated_memory_usage = SizeOf(tree) + mem_size; + expected.estimated_fair_share_memory_usage = FairShare(tree); + + expected.node_count = 1 + leaf_count * (1 + 3 + 1 + flat3_count); + expected.node_counts.flat = leaf_count * (2 + flat3_count); + expected.node_counts.flat_128 = leaf_count * flat3_count; + expected.node_counts.flat_256 = leaf_count; + expected.node_counts.external = leaf_count; + expected.node_counts.substring = leaf_count; + expected.node_counts.btree = 1 + leaf_count; + + EXPECT_THAT(SampleCord(tree), EqStatistics(expected)); +} + +TEST(CordzInfoStatisticsTest, ThreadSafety) { + Notification stop; + static constexpr int kNumThreads = 8; + int64_t sampled_node_count = 0; + + { + absl::synchronization_internal::ThreadPool pool(kNumThreads); + + // Run analyzer thread emulating a CordzHandler collection. + pool.Schedule([&]() { + while (!stop.HasBeenNotified()) { + // Run every 10us (about 100K total collections). + absl::SleepFor(absl::Microseconds(10)); + CordzSampleToken token; + for (const CordzInfo& cord_info : token) { + CordzStatistics stats = cord_info.GetCordzStatistics(); + sampled_node_count += stats.node_count; + } + } + }); + + // Run 'application threads' + for (int i = 0; i < kNumThreads; ++i) { + pool.Schedule([&]() { + // Track 0 - 2 cordz infos at a time, providing permutations of 0, 1 + // and 2 CordzHandle and CordzInfo queues being active, with plenty of + // 'empty to non empty' transitions. + InlineData cords[2]; + std::minstd_rand gen; + std::uniform_int_distribution<int> coin_toss(0, 1); + + while (!stop.HasBeenNotified()) { + for (InlineData& cord : cords) { + // 50/50 flip the state of the cord + if (coin_toss(gen) != 0) { + if (cord.is_tree()) { + // 50/50 simulate delete (untrack) or 'edit to empty' + if (coin_toss(gen) != 0) { + CordzInfo::MaybeUntrackCord(cord.cordz_info()); + } else { + CordzUpdateScope scope(cord.cordz_info(), + CordzUpdateTracker::kUnknown); + scope.SetCordRep(nullptr); + } + CordRep::Unref(cord.as_tree()); + cord.set_inline_size(0); + } else { + // Coin toss to 25% ring, 25% btree, and 50% flat. + CordRep* rep = Flat(256); + if (coin_toss(gen) != 0) { + if (coin_toss(gen) != 0) { + rep = CordRepRing::Create(rep); + } else { + rep = CordRepBtree::Create(rep); + } + } + cord.make_tree(rep); + + // 50/50 sample + if (coin_toss(gen) != 0) { + CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown); + } + } + } + } + } + for (InlineData& cord : cords) { + if (cord.is_tree()) { + CordzInfo::MaybeUntrackCord(cord.cordz_info()); + CordRep::Unref(cord.as_tree()); + } + } + }); + } + + // Run for 1 second to give memory and thread safety analyzers plenty of + // time to detect any mishaps or undefined behaviors. + absl::SleepFor(absl::Seconds(1)); + stop.Notify(); + } + + std::cout << "Sampled " << sampled_node_count << " nodes\n"; +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_info_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_info_test.cc new file mode 100644 index 0000000000..b98343ae79 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_info_test.cc @@ -0,0 +1,341 @@ +// Copyright 2019 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_info.h" + +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" +#include "absl/strings/cordz_test_helpers.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_statistics.h" +#include "absl/strings/internal/cordz_update_tracker.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Ne; +using ::testing::SizeIs; + +// Used test values +auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown; +auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; +auto constexpr kChildMethod = CordzUpdateTracker::kConstructorCord; +auto constexpr kUpdateMethod = CordzUpdateTracker::kAppendString; + +// Local less verbose helper +std::vector<const CordzHandle*> DeleteQueue() { + return CordzHandle::DiagnosticsGetDeleteQueue(); +} + +std::string FormatStack(absl::Span<void* const> raw_stack) { + static constexpr size_t buf_size = 1 << 14; + std::unique_ptr<char[]> buf(new char[buf_size]); + std::string output; + for (void* stackp : raw_stack) { + if (absl::Symbolize(stackp, buf.get(), buf_size)) { + absl::StrAppend(&output, " ", buf.get(), "\n"); + } + } + return output; +} + +TEST(CordzInfoTest, TrackCord) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + ASSERT_THAT(info, Ne(nullptr)); + EXPECT_FALSE(info->is_snapshot()); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info)); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep)); + info->Untrack(); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithoutSampling) { + CordzSamplingIntervalHelper sample_none(99999); + TestCordData parent, child; + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithSampling) { + CordzSamplingIntervalHelper sample_all(1); + TestCordData parent, child; + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingParentSampled) { + CordzSamplingIntervalHelper sample_none(99999); + TestCordData parent, child; + CordzInfo::TrackCord(parent.data, kTrackCordMethod); + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + CordzInfo* parent_info = parent.data.cordz_info(); + CordzInfo* child_info = child.data.cordz_info(); + ASSERT_THAT(child_info, Ne(nullptr)); + EXPECT_THAT(child_info->GetCordRepForTesting(), Eq(child.rep.rep)); + EXPECT_THAT(child_info->GetParentStack(), parent_info->GetStack()); + parent_info->Untrack(); + child_info->Untrack(); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingChildSampled) { + CordzSamplingIntervalHelper sample_none(99999); + TestCordData parent, child; + CordzInfo::TrackCord(child.data, kTrackCordMethod); + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); +} + +TEST(CordzInfoTest, MaybeTrackChildCordWithSamplingChildSampled) { + CordzSamplingIntervalHelper sample_all(1); + TestCordData parent, child; + CordzInfo::TrackCord(child.data, kTrackCordMethod); + CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod); + EXPECT_THAT(child.data.cordz_info(), Eq(nullptr)); +} + +TEST(CordzInfoTest, UntrackCord) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + + info->Untrack(); + EXPECT_THAT(DeleteQueue(), SizeIs(0)); +} + +TEST(CordzInfoTest, UntrackCordWithSnapshot) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + + CordzSnapshot snapshot; + info->Untrack(); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep)); + EXPECT_THAT(DeleteQueue(), ElementsAre(info, &snapshot)); +} + +TEST(CordzInfoTest, SetCordRep) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + + TestCordRep rep; + info->Lock(CordzUpdateTracker::kAppendCord); + info->SetCordRep(rep.rep); + info->Unlock(); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep.rep)); + + info->Untrack(); +} + +TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + + info->Lock(CordzUpdateTracker::kAppendString); + info->SetCordRep(nullptr); + EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr)); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info)); + + info->Unlock(); + EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr)); +} + +TEST(CordzInfoTest, RefCordRep) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + + size_t refcount = data.rep.rep->refcount.Get(); + EXPECT_THAT(info->RefCordRep(), Eq(data.rep.rep)); + EXPECT_THAT(data.rep.rep->refcount.Get(), Eq(refcount + 1)); + CordRep::Unref(data.rep.rep); + info->Untrack(); +} + +#if GTEST_HAS_DEATH_TEST + +TEST(CordzInfoTest, SetCordRepRequiresMutex) { + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + TestCordRep rep; + EXPECT_DEBUG_DEATH(info->SetCordRep(rep.rep), ".*"); + info->Untrack(); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(CordzInfoTest, TrackUntrackHeadFirstV2) { + CordzSnapshot snapshot; + EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); + + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info1 = data.data.cordz_info(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + TestCordData data2; + CordzInfo::TrackCord(data2.data, kTrackCordMethod); + CordzInfo* info2 = data2.data.cordz_info(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); + EXPECT_THAT(info2->Next(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + info2->Untrack(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + info1->Untrack(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); +} + +TEST(CordzInfoTest, TrackUntrackTailFirstV2) { + CordzSnapshot snapshot; + EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); + + TestCordData data; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info1 = data.data.cordz_info(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + TestCordData data2; + CordzInfo::TrackCord(data2.data, kTrackCordMethod); + CordzInfo* info2 = data2.data.cordz_info(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); + EXPECT_THAT(info2->Next(snapshot), Eq(info1)); + EXPECT_THAT(info1->Next(snapshot), Eq(nullptr)); + + info1->Untrack(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2)); + EXPECT_THAT(info2->Next(snapshot), Eq(nullptr)); + + info2->Untrack(); + ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr)); +} + +TEST(CordzInfoTest, StackV2) { + TestCordData data; + // kMaxStackDepth is intentionally less than 64 (which is the max depth that + // Cordz will record) because if the actual stack depth is over 64 + // (which it is on Apple platforms) then the expected_stack will end up + // catching a few frames at the end that the actual_stack didn't get and + // it will no longer be subset. At the time of this writing 58 is the max + // that will allow this test to pass (with a minimum os version of iOS 9), so + // rounded down to 50 to hopefully not run into this in the future if Apple + // makes small modifications to its testing stack. 50 is sufficient to prove + // that we got a decent stack. + static constexpr int kMaxStackDepth = 50; + CordzInfo::TrackCord(data.data, kTrackCordMethod); + CordzInfo* info = data.data.cordz_info(); + std::vector<void*> local_stack; + local_stack.resize(kMaxStackDepth); + // In some environments we don't get stack traces. For example in Android + // absl::GetStackTrace will return 0 indicating it didn't find any stack. The + // resultant formatted stack will be "", but that still equals the stack + // recorded in CordzInfo, which is also empty. The skip_count is 1 so that the + // line number of the current stack isn't included in the HasSubstr check. + local_stack.resize(absl::GetStackTrace(local_stack.data(), kMaxStackDepth, + /*skip_count=*/1)); + + std::string got_stack = FormatStack(info->GetStack()); + std::string expected_stack = FormatStack(local_stack); + // If TrackCord is inlined, got_stack should match expected_stack. If it isn't + // inlined, got_stack should include an additional frame not present in + // expected_stack. Either way, expected_stack should be a substring of + // got_stack. + EXPECT_THAT(got_stack, HasSubstr(expected_stack)); + + info->Untrack(); +} + +// Local helper functions to get different stacks for child and parent. +CordzInfo* TrackChildCord(InlineData& data, const InlineData& parent) { + CordzInfo::TrackCord(data, parent, kChildMethod); + return data.cordz_info(); +} +CordzInfo* TrackParentCord(InlineData& data) { + CordzInfo::TrackCord(data, kTrackCordMethod); + return data.cordz_info(); +} + +TEST(CordzInfoTest, GetStatistics) { + TestCordData data; + CordzInfo* info = TrackParentCord(data.data); + + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(data.rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kTrackCordMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kTrackCordMethod), Eq(1)); + + info->Untrack(); +} + +TEST(CordzInfoTest, LockCountsMethod) { + TestCordData data; + CordzInfo* info = TrackParentCord(data.data); + + info->Lock(kUpdateMethod); + info->Unlock(); + info->Lock(kUpdateMethod); + info->Unlock(); + + CordzStatistics statistics = info->GetCordzStatistics(); + EXPECT_THAT(statistics.update_tracker.Value(kUpdateMethod), Eq(2)); + + info->Untrack(); +} + +TEST(CordzInfoTest, FromParent) { + TestCordData parent; + TestCordData child; + CordzInfo* info_parent = TrackParentCord(parent.data); + CordzInfo* info_child = TrackChildCord(child.data, parent.data); + + std::string stack = FormatStack(info_parent->GetStack()); + std::string parent_stack = FormatStack(info_child->GetParentStack()); + EXPECT_THAT(stack, Eq(parent_stack)); + + CordzStatistics statistics = info_child->GetCordzStatistics(); + EXPECT_THAT(statistics.size, Eq(child.rep.rep->length)); + EXPECT_THAT(statistics.method, Eq(kChildMethod)); + EXPECT_THAT(statistics.parent_method, Eq(kTrackCordMethod)); + EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1)); + + info_parent->Untrack(); + info_child->Untrack(); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.cc new file mode 100644 index 0000000000..ba1270d8f0 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.cc @@ -0,0 +1,64 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_sample_token.h" + +#include "absl/base/config.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_info.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +CordzSampleToken::Iterator& CordzSampleToken::Iterator::operator++() { + if (current_) { + current_ = current_->Next(*token_); + } + return *this; +} + +CordzSampleToken::Iterator CordzSampleToken::Iterator::operator++(int) { + Iterator it(*this); + operator++(); + return it; +} + +bool operator==(const CordzSampleToken::Iterator& lhs, + const CordzSampleToken::Iterator& rhs) { + return lhs.current_ == rhs.current_ && + (lhs.current_ == nullptr || lhs.token_ == rhs.token_); +} + +bool operator!=(const CordzSampleToken::Iterator& lhs, + const CordzSampleToken::Iterator& rhs) { + return !(lhs == rhs); +} + +CordzSampleToken::Iterator::reference CordzSampleToken::Iterator::operator*() + const { + return *current_; +} + +CordzSampleToken::Iterator::pointer CordzSampleToken::Iterator::operator->() + const { + return current_; +} + +CordzSampleToken::Iterator::Iterator(const CordzSampleToken* token) + : token_(token), current_(CordzInfo::Head(*token)) {} + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.h b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.h new file mode 100644 index 0000000000..28a1d70ccc --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token.h @@ -0,0 +1,97 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/base/config.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_info.h" + +#ifndef ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ +#define ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// The existence of a CordzSampleToken guarantees that a reader can traverse the +// global_cordz_infos_head linked-list without needing to hold a mutex. When a +// CordzSampleToken exists, all CordzInfo objects that would be destroyed are +// instead appended to a deletion queue. When the CordzSampleToken is destroyed, +// it will also clean up any of these CordzInfo objects. +// +// E.g., ST are CordzSampleToken objects and CH are CordzHandle objects. +// ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- global_delete_queue_tail +// +// This list tracks that CH1 and CH2 were created after ST1, so the thread +// holding ST1 might have a referece to CH1, CH2, ST2, and CH3. However, ST2 was +// created later, so the thread holding the ST2 token cannot have a reference to +// ST1, CH1, or CH2. If ST1 is cleaned up first, that thread will delete ST1, +// CH1, and CH2. If instead ST2 is cleaned up first, that thread will only +// delete ST2. +// +// If ST1 is cleaned up first, the new list will be: +// ST2 <- CH3 <- global_delete_queue_tail +// +// If ST2 is cleaned up first, the new list will be: +// ST1 <- CH1 <- CH2 <- CH3 <- global_delete_queue_tail +// +// All new CordzHandle objects are appended to the list, so if a new thread +// comes along before either ST1 or ST2 are cleaned up, the new list will be: +// ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- ST3 <- global_delete_queue_tail +// +// A thread must hold the global_delete_queue_mu mutex whenever it's altering +// this list. +// +// It is safe for thread that holds a CordzSampleToken to read +// global_cordz_infos at any time since the objects it is able to retrieve will +// not be deleted while the CordzSampleToken exists. +class CordzSampleToken : public CordzSnapshot { + public: + class Iterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = const CordzInfo&; + using difference_type = ptrdiff_t; + using pointer = const CordzInfo*; + using reference = value_type; + + Iterator() = default; + + Iterator& operator++(); + Iterator operator++(int); + friend bool operator==(const Iterator& lhs, const Iterator& rhs); + friend bool operator!=(const Iterator& lhs, const Iterator& rhs); + reference operator*() const; + pointer operator->() const; + + private: + friend class CordzSampleToken; + explicit Iterator(const CordzSampleToken* token); + + const CordzSampleToken* token_ = nullptr; + pointer current_ = nullptr; + }; + + CordzSampleToken() = default; + CordzSampleToken(const CordzSampleToken&) = delete; + CordzSampleToken& operator=(const CordzSampleToken&) = delete; + + Iterator begin() { return Iterator(this); } + Iterator end() { return Iterator(); } +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token_test.cc new file mode 100644 index 0000000000..9f54301d68 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_sample_token_test.cc @@ -0,0 +1,208 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_sample_token.h" + +#include <memory> +#include <type_traits> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/memory/memory.h" +#include "absl/random/random.h" +#include "absl/strings/cordz_test_helpers.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cordz_handle.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Ne; + +// Used test values +auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; + +TEST(CordzSampleTokenTest, IteratorTraits) { + static_assert(std::is_copy_constructible<CordzSampleToken::Iterator>::value, + ""); + static_assert(std::is_copy_assignable<CordzSampleToken::Iterator>::value, ""); + static_assert(std::is_move_constructible<CordzSampleToken::Iterator>::value, + ""); + static_assert(std::is_move_assignable<CordzSampleToken::Iterator>::value, ""); + static_assert( + std::is_same< + std::iterator_traits<CordzSampleToken::Iterator>::iterator_category, + std::input_iterator_tag>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::value_type, + const CordzInfo&>::value, + ""); + static_assert( + std::is_same< + std::iterator_traits<CordzSampleToken::Iterator>::difference_type, + ptrdiff_t>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::pointer, + const CordzInfo*>::value, + ""); + static_assert( + std::is_same<std::iterator_traits<CordzSampleToken::Iterator>::reference, + const CordzInfo&>::value, + ""); +} + +TEST(CordzSampleTokenTest, IteratorEmpty) { + CordzSampleToken token; + EXPECT_THAT(token.begin(), Eq(token.end())); +} + +TEST(CordzSampleTokenTest, Iterator) { + TestCordData cord1, cord2, cord3; + CordzInfo::TrackCord(cord1.data, kTrackCordMethod); + CordzInfo* info1 = cord1.data.cordz_info(); + CordzInfo::TrackCord(cord2.data, kTrackCordMethod); + CordzInfo* info2 = cord2.data.cordz_info(); + CordzInfo::TrackCord(cord3.data, kTrackCordMethod); + CordzInfo* info3 = cord3.data.cordz_info(); + + CordzSampleToken token; + std::vector<const CordzInfo*> found; + for (const CordzInfo& cord_info : token) { + found.push_back(&cord_info); + } + + EXPECT_THAT(found, ElementsAre(info3, info2, info1)); + + info1->Untrack(); + info2->Untrack(); + info3->Untrack(); +} + +TEST(CordzSampleTokenTest, IteratorEquality) { + TestCordData cord1; + TestCordData cord2; + TestCordData cord3; + CordzInfo::TrackCord(cord1.data, kTrackCordMethod); + CordzInfo* info1 = cord1.data.cordz_info(); + + CordzSampleToken token1; + // lhs starts with the CordzInfo corresponding to cord1 at the head. + CordzSampleToken::Iterator lhs = token1.begin(); + + CordzInfo::TrackCord(cord2.data, kTrackCordMethod); + CordzInfo* info2 = cord2.data.cordz_info(); + + CordzSampleToken token2; + // rhs starts with the CordzInfo corresponding to cord2 at the head. + CordzSampleToken::Iterator rhs = token2.begin(); + + CordzInfo::TrackCord(cord3.data, kTrackCordMethod); + CordzInfo* info3 = cord3.data.cordz_info(); + + // lhs is on cord1 while rhs is on cord2. + EXPECT_THAT(lhs, Ne(rhs)); + + rhs++; + // lhs and rhs are both on cord1, but they didn't come from the same + // CordzSampleToken. + EXPECT_THAT(lhs, Ne(rhs)); + + lhs++; + rhs++; + // Both lhs and rhs are done, so they are on nullptr. + EXPECT_THAT(lhs, Eq(rhs)); + + info1->Untrack(); + info2->Untrack(); + info3->Untrack(); +} + +TEST(CordzSampleTokenTest, MultiThreaded) { + Notification stop; + static constexpr int kNumThreads = 4; + static constexpr int kNumCords = 3; + static constexpr int kNumTokens = 3; + absl::synchronization_internal::ThreadPool pool(kNumThreads); + + for (int i = 0; i < kNumThreads; ++i) { + pool.Schedule([&stop]() { + absl::BitGen gen; + TestCordData cords[kNumCords]; + std::unique_ptr<CordzSampleToken> tokens[kNumTokens]; + + while (!stop.HasBeenNotified()) { + // Randomly perform one of five actions: + // 1) Untrack + // 2) Track + // 3) Iterate over Cords visible to a token. + // 4) Unsample + // 5) Sample + int index = absl::Uniform(gen, 0, kNumCords); + if (absl::Bernoulli(gen, 0.5)) { + TestCordData& cord = cords[index]; + // Track/untrack. + if (cord.data.is_profiled()) { + // 1) Untrack + cord.data.cordz_info()->Untrack(); + cord.data.clear_cordz_info();; + } else { + // 2) Track + CordzInfo::TrackCord(cord.data, kTrackCordMethod); + } + } else { + std::unique_ptr<CordzSampleToken>& token = tokens[index]; + if (token) { + if (absl::Bernoulli(gen, 0.5)) { + // 3) Iterate over Cords visible to a token. + for (const CordzInfo& info : *token) { + // This is trivial work to allow us to compile the loop. + EXPECT_THAT(info.Next(*token), Ne(&info)); + } + } else { + // 4) Unsample + token = nullptr; + } + } else { + // 5) Sample + token = absl::make_unique<CordzSampleToken>(); + } + } + } + for (TestCordData& cord : cords) { + CordzInfo::MaybeUntrackCord(cord.data.cordz_info()); + } + }); + } + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_statistics.h b/third_party/abseil-cpp/absl/strings/internal/cordz_statistics.h new file mode 100644 index 0000000000..da4c7dbb8c --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_statistics.h @@ -0,0 +1,87 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ + +#include <cstdint> + +#include "absl/base/config.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzStatistics captures some meta information about a Cord's shape. +struct CordzStatistics { + using MethodIdentifier = CordzUpdateTracker::MethodIdentifier; + + // Node counts information + struct NodeCounts { + size_t flat = 0; // #flats + size_t flat_64 = 0; // #flats up to 64 bytes + size_t flat_128 = 0; // #flats up to 128 bytes + size_t flat_256 = 0; // #flats up to 256 bytes + size_t flat_512 = 0; // #flats up to 512 bytes + size_t flat_1k = 0; // #flats up to 1K bytes + size_t external = 0; // #external reps + size_t substring = 0; // #substring reps + size_t concat = 0; // #concat reps + size_t ring = 0; // #ring buffer reps + size_t btree = 0; // #btree reps + }; + + // The size of the cord in bytes. This matches the result of Cord::size(). + int64_t size = 0; + + // The estimated memory used by the sampled cord. This value matches the + // value as reported by Cord::EstimatedMemoryUsage(). + // A value of 0 implies the property has not been recorded. + int64_t estimated_memory_usage = 0; + + // The effective memory used by the sampled cord, inversely weighted by the + // effective indegree of each allocated node. This is a representation of the + // fair share of memory usage that should be attributed to the sampled cord. + // This value is more useful for cases where one or more nodes are referenced + // by multiple Cord instances, and for cases where a Cord includes the same + // node multiple times (either directly or indirectly). + // A value of 0 implies the property has not been recorded. + int64_t estimated_fair_share_memory_usage = 0; + + // The total number of nodes referenced by this cord. + // For ring buffer Cords, this includes the 'ring buffer' node. + // For btree Cords, this includes all 'CordRepBtree' tree nodes as well as all + // the substring, flat and external nodes referenced by the tree. + // A value of 0 implies the property has not been recorded. + int64_t node_count = 0; + + // Detailed node counts per type + NodeCounts node_counts; + + // The cord method responsible for sampling the cord. + MethodIdentifier method = MethodIdentifier::kUnknown; + + // The cord method responsible for sampling the parent cord if applicable. + MethodIdentifier parent_method = MethodIdentifier::kUnknown; + + // Update tracker tracking invocation count per cord method. + CordzUpdateTracker update_tracker; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_STATISTICS_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope.h b/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope.h new file mode 100644 index 0000000000..57ba75de93 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope.h @@ -0,0 +1,71 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ + +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/internal/cord_internal.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzUpdateScope scopes an update to the provided CordzInfo. +// The class invokes `info->Lock(method)` and `info->Unlock()` to guard +// cordrep updates. This class does nothing if `info` is null. +// See also the 'Lock`, `Unlock` and `SetCordRep` methods in `CordzInfo`. +class ABSL_SCOPED_LOCKABLE CordzUpdateScope { + public: + CordzUpdateScope(CordzInfo* info, CordzUpdateTracker::MethodIdentifier method) + ABSL_EXCLUSIVE_LOCK_FUNCTION(info) + : info_(info) { + if (ABSL_PREDICT_FALSE(info_)) { + info->Lock(method); + } + } + + // CordzUpdateScope can not be copied or assigned to. + CordzUpdateScope(CordzUpdateScope&& rhs) = delete; + CordzUpdateScope(const CordzUpdateScope&) = delete; + CordzUpdateScope& operator=(CordzUpdateScope&& rhs) = delete; + CordzUpdateScope& operator=(const CordzUpdateScope&) = delete; + + ~CordzUpdateScope() ABSL_UNLOCK_FUNCTION() { + if (ABSL_PREDICT_FALSE(info_)) { + info_->Unlock(); + } + } + + void SetCordRep(CordRep* rep) const { + if (ABSL_PREDICT_FALSE(info_)) { + info_->SetCordRep(rep); + } + } + + CordzInfo* info() const { return info_; } + + private: + CordzInfo* info_; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_SCOPE_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope_test.cc new file mode 100644 index 0000000000..3d08c622d0 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_update_scope_test.cc @@ -0,0 +1,49 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_update_scope.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/strings/cordz_test_helpers.h" +#include "absl/strings/internal/cord_rep_flat.h" +#include "absl/strings/internal/cordz_info.h" +#include "absl/strings/internal/cordz_update_tracker.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +namespace { + +// Used test values +auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString; + +TEST(CordzUpdateScopeTest, ScopeNullptr) { + CordzUpdateScope scope(nullptr, kTrackCordMethod); +} + +TEST(CordzUpdateScopeTest, ScopeSampledCord) { + TestCordData cord; + CordzInfo::TrackCord(cord.data, kTrackCordMethod); + CordzUpdateScope scope(cord.data.cordz_info(), kTrackCordMethod); + cord.data.cordz_info()->SetCordRep(nullptr); +} + +} // namespace +ABSL_NAMESPACE_END +} // namespace cord_internal + +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h new file mode 100644 index 0000000000..1f764486eb --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h @@ -0,0 +1,121 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ +#define ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ + +#include <atomic> +#include <cstdint> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { + +// CordzUpdateTracker tracks counters for Cord update methods. +// +// The purpose of CordzUpdateTracker is to track the number of calls to methods +// updating Cord data for sampled cords. The class internally uses 'lossy' +// atomic operations: Cord is thread-compatible, so there is no need to +// synchronize updates. However, Cordz collection threads may call 'Value()' at +// any point, so the class needs to provide thread safe access. +// +// This class is thread-safe. But as per above comments, all non-const methods +// should be used single-threaded only: updates are thread-safe but lossy. +class CordzUpdateTracker { + public: + // Tracked update methods. + enum MethodIdentifier { + kUnknown, + kAppendBuffer, + kAppendCord, + kAppendExternalMemory, + kAppendString, + kAssignCord, + kAssignString, + kClear, + kConstructorCord, + kConstructorString, + kCordReader, + kFlatten, + kGetAppendRegion, + kMakeCordFromExternal, + kMoveAppendCord, + kMoveAssignCord, + kMovePrependCord, + kPrependBuffer, + kPrependCord, + kPrependString, + kRemovePrefix, + kRemoveSuffix, + kSubCord, + + // kNumMethods defines the number of entries: must be the last entry. + kNumMethods, + }; + + // Constructs a new instance. All counters are zero-initialized. + constexpr CordzUpdateTracker() noexcept : values_{} {} + + // Copy constructs a new instance. + CordzUpdateTracker(const CordzUpdateTracker& rhs) noexcept { *this = rhs; } + + // Assigns the provided value to this instance. + CordzUpdateTracker& operator=(const CordzUpdateTracker& rhs) noexcept { + for (int i = 0; i < kNumMethods; ++i) { + values_[i].store(rhs.values_[i].load(std::memory_order_relaxed), + std::memory_order_relaxed); + } + return *this; + } + + // Returns the value for the specified method. + int64_t Value(MethodIdentifier method) const { + return values_[method].load(std::memory_order_relaxed); + } + + // Increases the value for the specified method by `n` + void LossyAdd(MethodIdentifier method, int64_t n = 1) { + auto& value = values_[method]; + value.store(value.load(std::memory_order_relaxed) + n, + std::memory_order_relaxed); + } + + // Adds all the values from `src` to this instance + void LossyAdd(const CordzUpdateTracker& src) { + for (int i = 0; i < kNumMethods; ++i) { + MethodIdentifier method = static_cast<MethodIdentifier>(i); + if (int64_t value = src.Value(method)) { + LossyAdd(method, value); + } + } + } + + private: + // Until C++20 std::atomic is not constexpr default-constructible, so we need + // a wrapper for this class to be constexpr constructible. + class Counter : public std::atomic<int64_t> { + public: + constexpr Counter() noexcept : std::atomic<int64_t>(0) {} + }; + + Counter values_[kNumMethods]; +}; + +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CORDZ_UPDATE_TRACKER_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker_test.cc new file mode 100644 index 0000000000..2348a17585 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker_test.cc @@ -0,0 +1,145 @@ +// Copyright 2021 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/cordz_update_tracker.h" + +#include <array> +#include <thread> // NOLINT + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/synchronization/notification.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace cord_internal { +namespace { + +using ::testing::AnyOf; +using ::testing::Eq; + +using Method = CordzUpdateTracker::MethodIdentifier; +using Methods = std::array<Method, Method::kNumMethods>; + +// Returns an array of all methods defined in `MethodIdentifier` +Methods AllMethods() { + return Methods{Method::kUnknown, + Method::kAppendBuffer, + Method::kAppendCord, + Method::kAppendExternalMemory, + Method::kAppendString, + Method::kAssignCord, + Method::kAssignString, + Method::kClear, + Method::kConstructorCord, + Method::kConstructorString, + Method::kCordReader, + Method::kFlatten, + Method::kGetAppendRegion, + Method::kMakeCordFromExternal, + Method::kMoveAppendCord, + Method::kMoveAssignCord, + Method::kMovePrependCord, + Method::kPrependBuffer, + Method::kPrependCord, + Method::kPrependString, + Method::kRemovePrefix, + Method::kRemoveSuffix, + Method::kSubCord}; +} + +TEST(CordzUpdateTracker, IsConstExprAndInitializesToZero) { + constexpr CordzUpdateTracker tracker; + for (Method method : AllMethods()) { + ASSERT_THAT(tracker.Value(method), Eq(0)); + } +} + +TEST(CordzUpdateTracker, LossyAdd) { + int64_t n = 1; + CordzUpdateTracker tracker; + for (Method method : AllMethods()) { + tracker.LossyAdd(method, n); + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } +} + +TEST(CordzUpdateTracker, CopyConstructor) { + int64_t n = 1; + CordzUpdateTracker src; + for (Method method : AllMethods()) { + src.LossyAdd(method, n); + n += 2; + } + + n = 1; + CordzUpdateTracker tracker(src); + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } +} + +TEST(CordzUpdateTracker, OperatorAssign) { + int64_t n = 1; + CordzUpdateTracker src; + CordzUpdateTracker tracker; + for (Method method : AllMethods()) { + src.LossyAdd(method, n); + n += 2; + } + + n = 1; + tracker = src; + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } +} + +TEST(CordzUpdateTracker, ThreadSanitizedValueCheck) { + absl::Notification done; + CordzUpdateTracker tracker; + + std::thread reader([&done, &tracker] { + while (!done.HasBeenNotified()) { + int n = 1; + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), AnyOf(Eq(n), Eq(0))); + n += 2; + } + } + int n = 1; + for (Method method : AllMethods()) { + EXPECT_THAT(tracker.Value(method), Eq(n)); + n += 2; + } + }); + + int64_t n = 1; + for (Method method : AllMethods()) { + tracker.LossyAdd(method, n); + n += 2; + } + done.Notify(); + reader.join(); +} + +} // namespace +} // namespace cord_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/numbers_test_common.h b/third_party/abseil-cpp/absl/strings/internal/numbers_test_common.h index 1a1e50c4d8..eaa88a8897 100644 --- a/third_party/abseil-cpp/absl/strings/internal/numbers_test_common.h +++ b/third_party/abseil-cpp/absl/strings/internal/numbers_test_common.h @@ -170,7 +170,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() { {"0x1234", true, 16, 0x1234}, - // Base-10 std::string version. + // Base-10 string version. {"1234", true, 0, 1234}, {nullptr, false, 0, 0}, }}; diff --git a/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized.h b/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized.h index e42628e394..49859dcc7d 100644 --- a/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized.h +++ b/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized.h @@ -17,6 +17,7 @@ #ifndef ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ #define ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ +#include <algorithm> #include <string> #include <type_traits> #include <utility> @@ -28,8 +29,9 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace strings_internal { -// Is a subclass of true_type or false_type, depending on whether or not -// T has a __resize_default_init member. +// In this type trait, we look for a __resize_default_init member function, and +// we use it if available, otherwise, we use resize. We provide HasMember to +// indicate whether __resize_default_init is present. template <typename string_type, typename = void> struct ResizeUninitializedTraits { using HasMember = std::false_type; @@ -66,6 +68,50 @@ inline void STLStringResizeUninitialized(string_type* s, size_t new_size) { ResizeUninitializedTraits<string_type>::Resize(s, new_size); } +// Used to ensure exponential growth so that the amortized complexity of +// increasing the string size by a small amount is O(1), in contrast to +// O(str->size()) in the case of precise growth. +template <typename string_type> +void STLStringReserveAmortized(string_type* s, size_t new_size) { + const size_t cap = s->capacity(); + if (new_size > cap) { + // Make sure to always grow by at least a factor of 2x. + s->reserve((std::max)(new_size, 2 * cap)); + } +} + +// In this type trait, we look for an __append_default_init member function, and +// we use it if available, otherwise, we use append. +template <typename string_type, typename = void> +struct AppendUninitializedTraits { + static void Append(string_type* s, size_t n) { + s->append(n, typename string_type::value_type()); + } +}; + +template <typename string_type> +struct AppendUninitializedTraits< + string_type, absl::void_t<decltype(std::declval<string_type&>() + .__append_default_init(237))> > { + static void Append(string_type* s, size_t n) { + s->__append_default_init(n); + } +}; + +// Like STLStringResizeUninitialized(str, new_size), except guaranteed to use +// exponential growth so that the amortized complexity of increasing the string +// size by a small amount is O(1), in contrast to O(str->size()) in the case of +// precise growth. +template <typename string_type> +void STLStringResizeUninitializedAmortized(string_type* s, size_t new_size) { + const size_t size = s->size(); + if (new_size > size) { + AppendUninitializedTraits<string_type>::Append(s, new_size - size); + } else { + s->erase(new_size); + } +} + } // namespace strings_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized_test.cc b/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized_test.cc index 0f8b3c2a95..ad1b9c58f3 100644 --- a/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/resize_uninitialized_test.cc @@ -19,64 +19,115 @@ namespace { int resize_call_count = 0; +int append_call_count = 0; // A mock string class whose only purpose is to track how many times its -// resize() method has been called. +// resize()/append() methods have been called. struct resizable_string { + using value_type = char; size_t size() const { return 0; } + size_t capacity() const { return 0; } char& operator[](size_t) { static char c = '\0'; return c; } void resize(size_t) { resize_call_count += 1; } + void append(size_t, value_type) { append_call_count += 1; } + void reserve(size_t) {} + resizable_string& erase(size_t = 0, size_t = 0) { return *this; } }; int resize_default_init_call_count = 0; +int append_default_init_call_count = 0; // A mock string class whose only purpose is to track how many times its -// resize() and __resize_default_init() methods have been called. -struct resize_default_init_string { +// resize()/__resize_default_init()/append()/__append_default_init() methods +// have been called. +struct default_init_string { size_t size() const { return 0; } + size_t capacity() const { return 0; } char& operator[](size_t) { static char c = '\0'; return c; } void resize(size_t) { resize_call_count += 1; } void __resize_default_init(size_t) { resize_default_init_call_count += 1; } + void __append_default_init(size_t) { append_default_init_call_count += 1; } + void reserve(size_t) {} + default_init_string& erase(size_t = 0, size_t = 0) { return *this; } }; TEST(ResizeUninit, WithAndWithout) { resize_call_count = 0; + append_call_count = 0; resize_default_init_call_count = 0; + append_default_init_call_count = 0; { resizable_string rs; EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); EXPECT_FALSE( absl::strings_internal::STLStringSupportsNontrashingResize(&rs)); EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitialized(&rs, 237); EXPECT_EQ(resize_call_count, 1); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); + absl::strings_internal::STLStringResizeUninitializedAmortized(&rs, 1000); + EXPECT_EQ(resize_call_count, 1); + EXPECT_EQ(append_call_count, 1); + EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); } resize_call_count = 0; + append_call_count = 0; resize_default_init_call_count = 0; + append_default_init_call_count = 0; { - resize_default_init_string rus; + default_init_string rus; EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); EXPECT_TRUE( absl::strings_internal::STLStringSupportsNontrashingResize(&rus)); EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 0); + EXPECT_EQ(append_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitialized(&rus, 237); EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 1); + EXPECT_EQ(append_default_init_call_count, 0); + absl::strings_internal::STLStringResizeUninitializedAmortized(&rus, 1000); + EXPECT_EQ(resize_call_count, 0); + EXPECT_EQ(append_call_count, 0); EXPECT_EQ(resize_default_init_call_count, 1); + EXPECT_EQ(append_default_init_call_count, 1); + } +} + +TEST(ResizeUninit, Amortized) { + std::string str; + size_t prev_cap = str.capacity(); + int cap_increase_count = 0; + for (int i = 0; i < 1000; ++i) { + absl::strings_internal::STLStringResizeUninitializedAmortized(&str, i); + size_t new_cap = str.capacity(); + if (new_cap > prev_cap) ++cap_increase_count; + prev_cap = new_cap; } + EXPECT_LT(cap_increase_count, 50); } } // namespace diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc index 4d0604e00c..e28a29b171 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + // // POSIX spec: // http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html @@ -12,14 +26,13 @@ #include "absl/base/port.h" #include "absl/strings/internal/str_format/float_conversion.h" +#include "absl/strings/numbers.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { -const char kDigit[2][32] = { "0123456789abcdef", "0123456789ABCDEF" }; - // Reduce *capacity by s.size(), clipped to a 0 minimum. void ReducePadding(string_view s, size_t *capacity) { *capacity = Excess(s.size(), *capacity); @@ -48,125 +61,179 @@ struct IsSigned<absl::int128> : std::true_type {}; template <> struct IsSigned<absl::uint128> : std::false_type {}; -class ConvertedIntInfo { +// Integral digit printer. +// Call one of the PrintAs* routines after construction once. +// Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results. +class IntDigits { public: + // Print the unsigned integer as octal. + // Supports unsigned integral types and uint128. template <typename T> - ConvertedIntInfo(T v, ConversionChar conv) { - using Unsigned = typename MakeUnsigned<T>::type; - auto u = static_cast<Unsigned>(v); - if (IsNeg(v)) { - is_neg_ = true; - u = Unsigned{} - u; - } else { - is_neg_ = false; + void PrintAsOct(T v) { + static_assert(!IsSigned<T>::value, ""); + char *p = storage_ + sizeof(storage_); + do { + *--p = static_cast<char>('0' + (static_cast<size_t>(v) & 7)); + v >>= 3; + } while (v); + start_ = p; + size_ = storage_ + sizeof(storage_) - p; + } + + // Print the signed or unsigned integer as decimal. + // Supports all integral types. + template <typename T> + void PrintAsDec(T v) { + static_assert(std::is_integral<T>::value, ""); + start_ = storage_; + size_ = numbers_internal::FastIntToBuffer(v, storage_) - storage_; + } + + void PrintAsDec(int128 v) { + auto u = static_cast<uint128>(v); + bool add_neg = false; + if (v < 0) { + add_neg = true; + u = uint128{} - u; } - UnsignedToStringRight(u, conv); + PrintAsDec(u, add_neg); } - string_view digits() const { - return {end() - size_, static_cast<size_t>(size_)}; + void PrintAsDec(uint128 v, bool add_neg = false) { + // This function can be sped up if needed. We can call FastIntToBuffer + // twice, or fix FastIntToBuffer to support uint128. + char *p = storage_ + sizeof(storage_); + do { + p -= 2; + numbers_internal::PutTwoDigits(static_cast<size_t>(v % 100), p); + v /= 100; + } while (v); + if (p[0] == '0') { + // We printed one too many hexits. + ++p; + } + if (add_neg) { + *--p = '-'; + } + size_ = storage_ + sizeof(storage_) - p; + start_ = p; } - bool is_neg() const { return is_neg_; } - private: - template <typename T, bool IsSigned> - struct IsNegImpl { - static bool Eval(T v) { return v < 0; } - }; + // Print the unsigned integer as hex using lowercase. + // Supports unsigned integral types and uint128. template <typename T> - struct IsNegImpl<T, false> { - static bool Eval(T) { - return false; + void PrintAsHexLower(T v) { + static_assert(!IsSigned<T>::value, ""); + char *p = storage_ + sizeof(storage_); + + do { + p -= 2; + constexpr const char* table = numbers_internal::kHexTable; + std::memcpy(p, table + 2 * (static_cast<size_t>(v) & 0xFF), 2); + if (sizeof(T) == 1) break; + v >>= 8; + } while (v); + if (p[0] == '0') { + // We printed one too many digits. + ++p; } - }; + start_ = p; + size_ = storage_ + sizeof(storage_) - p; + } + // Print the unsigned integer as hex using uppercase. + // Supports unsigned integral types and uint128. template <typename T> - bool IsNeg(T v) { - return IsNegImpl<T, IsSigned<T>::value>::Eval(v); + void PrintAsHexUpper(T v) { + static_assert(!IsSigned<T>::value, ""); + char *p = storage_ + sizeof(storage_); + + // kHexTable is only lowercase, so do it manually for uppercase. + do { + *--p = "0123456789ABCDEF"[static_cast<size_t>(v) & 15]; + v >>= 4; + } while (v); + start_ = p; + size_ = storage_ + sizeof(storage_) - p; } - template <typename T> - void UnsignedToStringRight(T u, ConversionChar conv) { - char *p = end(); - switch (FormatConversionCharRadix(conv)) { - default: - case 10: - for (; u; u /= 10) - *--p = static_cast<char>('0' + static_cast<size_t>(u % 10)); - break; - case 8: - for (; u; u /= 8) - *--p = static_cast<char>('0' + static_cast<size_t>(u % 8)); - break; - case 16: { - const char *digits = kDigit[FormatConversionCharIsUpper(conv) ? 1 : 0]; - for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)]; - break; - } - } - size_ = static_cast<int>(end() - p); + // The printed value including the '-' sign if available. + // For inputs of value `0`, this will return "0" + string_view with_neg_and_zero() const { return {start_, size_}; } + + // The printed value not including the '-' sign. + // For inputs of value `0`, this will return "". + string_view without_neg_or_zero() const { + static_assert('-' < '0', "The check below verifies both."); + size_t advance = start_[0] <= '0' ? 1 : 0; + return {start_ + advance, size_ - advance}; } - const char *end() const { return storage_ + sizeof(storage_); } - char *end() { return storage_ + sizeof(storage_); } + bool is_negative() const { return start_[0] == '-'; } - bool is_neg_; - int size_; - // Max size: 128 bit value as octal -> 43 digits - char storage_[128 / 3 + 1]; + private: + const char *start_; + size_t size_; + // Max size: 128 bit value as octal -> 43 digits, plus sign char + char storage_[128 / 3 + 1 + 1]; }; // Note: 'o' conversions do not have a base indicator, it's just that // the '#' flag is specified to modify the precision for 'o' conversions. -string_view BaseIndicator(const ConvertedIntInfo &info, - const ConversionSpec conv) { - bool alt = conv.flags().alt; - int radix = FormatConversionCharRadix(conv.conv()); - if (conv.conv() == ConversionChar::p) alt = true; // always show 0x for %p. +string_view BaseIndicator(const IntDigits &as_digits, + const FormatConversionSpecImpl conv) { + // always show 0x for %p. + bool alt = conv.has_alt_flag() || + conv.conversion_char() == FormatConversionCharInternal::p; + bool hex = (conv.conversion_char() == FormatConversionCharInternal::x || + conv.conversion_char() == FormatConversionCharInternal::X || + conv.conversion_char() == FormatConversionCharInternal::p); // From the POSIX description of '#' flag: // "For x or X conversion specifiers, a non-zero result shall have // 0x (or 0X) prefixed to it." - if (alt && radix == 16 && !info.digits().empty()) { - if (FormatConversionCharIsUpper(conv.conv())) return "0X"; - return "0x"; + if (alt && hex && !as_digits.without_neg_or_zero().empty()) { + return conv.conversion_char() == FormatConversionCharInternal::X ? "0X" + : "0x"; } return {}; } -string_view SignColumn(bool neg, const ConversionSpec conv) { - if (FormatConversionCharIsSigned(conv.conv())) { +string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { + if (conv.conversion_char() == FormatConversionCharInternal::d || + conv.conversion_char() == FormatConversionCharInternal::i) { if (neg) return "-"; - if (conv.flags().show_pos) return "+"; - if (conv.flags().sign_col) return " "; + if (conv.has_show_pos_flag()) return "+"; + if (conv.has_sign_col_flag()) return " "; } return {}; } -bool ConvertCharImpl(unsigned char v, const ConversionSpec conv, +bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { size_t fill = 0; if (conv.width() >= 0) fill = conv.width(); ReducePadding(1, &fill); - if (!conv.flags().left) sink->Append(fill, ' '); + if (!conv.has_left_flag()) sink->Append(fill, ' '); sink->Append(1, v); - if (conv.flags().left) sink->Append(fill, ' '); + if (conv.has_left_flag()) sink->Append(fill, ' '); return true; } -bool ConvertIntImplInner(const ConvertedIntInfo &info, - const ConversionSpec conv, FormatSinkImpl *sink) { +bool ConvertIntImplInnerSlow(const IntDigits &as_digits, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { // Print as a sequence of Substrings: // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] size_t fill = 0; if (conv.width() >= 0) fill = conv.width(); - string_view formatted = info.digits(); + string_view formatted = as_digits.without_neg_or_zero(); ReducePadding(formatted, &fill); - string_view sign = SignColumn(info.is_neg(), conv); + string_view sign = SignColumn(as_digits.is_negative(), conv); ReducePadding(sign, &fill); - string_view base_indicator = BaseIndicator(info, conv); + string_view base_indicator = BaseIndicator(as_digits, conv); ReducePadding(base_indicator, &fill); int precision = conv.precision(); @@ -174,7 +241,8 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, if (!precision_specified) precision = 1; - if (conv.flags().alt && conv.conv() == ConversionChar::o) { + if (conv.has_alt_flag() && + conv.conversion_char() == FormatConversionCharInternal::o) { // From POSIX description of the '#' (alt) flag: // "For o conversion, it increases the precision (if necessary) to // force the first digit of the result to be zero." @@ -187,13 +255,13 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, size_t num_zeroes = Excess(formatted.size(), precision); ReducePadding(num_zeroes, &fill); - size_t num_left_spaces = !conv.flags().left ? fill : 0; - size_t num_right_spaces = conv.flags().left ? fill : 0; + size_t num_left_spaces = !conv.has_left_flag() ? fill : 0; + size_t num_right_spaces = conv.has_left_flag() ? fill : 0; // From POSIX description of the '0' (zero) flag: // "For d, i, o, u, x, and X conversion specifiers, if a precision // is specified, the '0' flag is ignored." - if (!precision_specified && conv.flags().zero) { + if (!precision_specified && conv.has_zero_flag()) { num_zeroes += num_left_spaces; num_left_spaces = 0; } @@ -208,71 +276,97 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info, } template <typename T> -bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - ConvertedIntInfo info(v, conv.conv()); - if (conv.flags().basic && (conv.conv() != ConversionChar::p)) { - if (info.is_neg()) sink->Append(1, '-'); - if (info.digits().empty()) { - sink->Append(1, '0'); - } else { - sink->Append(info.digits()); - } - return true; +bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + using U = typename MakeUnsigned<T>::type; + IntDigits as_digits; + + // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes + // it to complain about a switch/case type mismatch, even though both are + // FormatConverionChar. Likely this is because at this point + // FormatConversionChar is declared, but not defined. + switch (static_cast<uint8_t>(conv.conversion_char())) { + case static_cast<uint8_t>(FormatConversionCharInternal::c): + return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); + + case static_cast<uint8_t>(FormatConversionCharInternal::o): + as_digits.PrintAsOct(static_cast<U>(v)); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::x): + as_digits.PrintAsHexLower(static_cast<U>(v)); + break; + case static_cast<uint8_t>(FormatConversionCharInternal::X): + as_digits.PrintAsHexUpper(static_cast<U>(v)); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::u): + as_digits.PrintAsDec(static_cast<U>(v)); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::d): + case static_cast<uint8_t>(FormatConversionCharInternal::i): + as_digits.PrintAsDec(v); + break; + + case static_cast<uint8_t>(FormatConversionCharInternal::a): + case static_cast<uint8_t>(FormatConversionCharInternal::e): + case static_cast<uint8_t>(FormatConversionCharInternal::f): + case static_cast<uint8_t>(FormatConversionCharInternal::g): + case static_cast<uint8_t>(FormatConversionCharInternal::A): + case static_cast<uint8_t>(FormatConversionCharInternal::E): + case static_cast<uint8_t>(FormatConversionCharInternal::F): + case static_cast<uint8_t>(FormatConversionCharInternal::G): + return ConvertFloatImpl(static_cast<double>(v), conv, sink); + + default: + ABSL_INTERNAL_ASSUME(false); } - return ConvertIntImplInner(info, conv, sink); -} -template <typename T> -bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - if (FormatConversionCharIsFloat(conv.conv())) { - return FormatConvertImpl(static_cast<double>(v), conv, sink).value; - } - if (conv.conv() == ConversionChar::c) - return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); - if (!FormatConversionCharIsIntegral(conv.conv())) return false; - if (!FormatConversionCharIsSigned(conv.conv()) && IsSigned<T>::value) { - using U = typename MakeUnsigned<T>::type; - return FormatConvertImpl(static_cast<U>(v), conv, sink).value; + if (conv.is_basic()) { + sink->Append(as_digits.with_neg_and_zero()); + return true; } - return ConvertIntImplInner(v, conv, sink); + return ConvertIntImplInnerSlow(as_digits, conv, sink); } template <typename T> -bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { - return FormatConversionCharIsFloat(conv.conv()) && +bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + return FormatConversionCharIsFloat(conv.conversion_char()) && ConvertFloatImpl(v, conv, sink); } -inline bool ConvertStringArg(string_view v, const ConversionSpec conv, +inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { - if (conv.conv() != ConversionChar::s) return false; - if (conv.flags().basic) { + if (conv.is_basic()) { sink->Append(v); return true; } return sink->PutPaddedString(v, conv.width(), conv.precision(), - conv.flags().left); + conv.has_left_flag()); } } // namespace // ==================== Strings ==================== -ConvertResult<Conv::s> FormatConvertImpl(const std::string &v, - const ConversionSpec conv, - FormatSinkImpl *sink) { +StringConvertResult FormatConvertImpl(const std::string &v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } -ConvertResult<Conv::s> FormatConvertImpl(string_view v, - const ConversionSpec conv, - FormatSinkImpl *sink) { +StringConvertResult FormatConvertImpl(string_view v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } -ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, - const ConversionSpec conv, - FormatSinkImpl *sink) { - if (conv.conv() == ConversionChar::p) +ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> +FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + if (conv.conversion_char() == FormatConversionCharInternal::p) return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; size_t len; if (v == nullptr) { @@ -287,93 +381,99 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, } // ==================== Raw pointers ==================== -ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, - FormatSinkImpl *sink) { - if (conv.conv() != ConversionChar::p) return {false}; +ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( + VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { if (!v.value) { sink->Append("(nil)"); return {true}; } - return {ConvertIntImplInner(v.value, conv, sink)}; + IntDigits as_digits; + as_digits.PrintAsHexLower(v.value); + return {ConvertIntImplInnerSlow(as_digits, conv, sink)}; } // ==================== Floats ==================== -FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(float v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } -FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(double v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } FloatingConvertResult FormatConvertImpl(long double v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } // ==================== Chars ==================== -IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(char v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(signed char v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned char v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } // ==================== Ints ==================== IntegralConvertResult FormatConvertImpl(short v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(int v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(unsigned v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(absl::int128 v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(absl::uint128 v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h index 7a93756305..3c91be701f 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ @@ -25,16 +39,37 @@ class Cord; class FormatCountCapture; class FormatSink; +template <absl::FormatConversionCharSet C> +struct FormatConvertResult; +class FormatConversionSpec; + namespace str_format_internal { template <typename T, typename = void> struct HasUserDefinedConvert : std::false_type {}; template <typename T> -struct HasUserDefinedConvert< - T, void_t<decltype(AbslFormatConvert( - std::declval<const T&>(), std::declval<ConversionSpec>(), - std::declval<FormatSink*>()))>> : std::true_type {}; +struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert( + std::declval<const T&>(), + std::declval<const FormatConversionSpec&>(), + std::declval<FormatSink*>()))>> + : std::true_type {}; + +void AbslFormatConvert(); // Stops the lexical name lookup +template <typename T> +auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) + -> decltype(AbslFormatConvert(v, + std::declval<const FormatConversionSpec&>(), + std::declval<FormatSink*>())) { + using FormatConversionSpecT = + absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>; + using FormatSinkT = + absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>; + auto fcs = conv.Wrap<FormatConversionSpecT>(); + auto fs = sink->Wrap<FormatSinkT>(); + return AbslFormatConvert(v, fcs, &fs); +} template <typename T> class StreamedWrapper; @@ -43,6 +78,13 @@ class StreamedWrapper; // then convert it, appending to `sink` and return `true`. // Otherwise fail and return `false`. +// AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v' +// as an extension mechanism. These FormatConvertImpl functions are the default +// implementations. +// The ADL search is augmented via the 'Sink*' parameter, which also +// serves as a disambiguator to reject possible unintended 'AbslFormatConvert' +// functions in the namespaces associated with 'v'. + // Raw pointers. struct VoidPtr { VoidPtr() = default; @@ -52,27 +94,53 @@ struct VoidPtr { : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {} uintptr_t value; }; -ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, ConversionSpec conv, - FormatSinkImpl* sink); + +template <FormatConversionCharSet C> +struct ArgConvertResult { + bool value; +}; + +template <FormatConversionCharSet C> +constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) { + return C; +} + +template <FormatConversionCharSet C> +constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { + return C; +} + +using StringConvertResult = + ArgConvertResult<FormatConversionCharSetInternal::s>; +ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( + VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Strings. -ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, - ConversionSpec conv, - FormatSinkImpl* sink); -ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv, - FormatSinkImpl* sink); -ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v, - ConversionSpec conv, - FormatSinkImpl* sink); -template <class AbslCord, - typename std::enable_if< - std::is_same<AbslCord, absl::Cord>::value>::type* = nullptr> -ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, - ConversionSpec conv, - FormatSinkImpl* sink) { - if (conv.conv() != ConversionChar::s) return {false}; - - bool is_left = conv.flags().left; +StringConvertResult FormatConvertImpl(const std::string& v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +StringConvertResult FormatConvertImpl(string_view v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW) +inline StringConvertResult FormatConvertImpl(std::string_view v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink); +} +#endif // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW + +ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> +FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, + FormatSinkImpl* sink); + +template <class AbslCord, typename std::enable_if<std::is_same< + AbslCord, absl::Cord>::value>::type* = nullptr> +StringConvertResult FormatConvertImpl(const AbslCord& value, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + bool is_left = conv.has_left_flag(); size_t space_remaining = 0; int width = conv.width(); @@ -105,55 +173,63 @@ ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, return {true}; } -using IntegralConvertResult = - ConvertResult<Conv::c | Conv::numeric | Conv::star>; -using FloatingConvertResult = ConvertResult<Conv::floating>; +using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::c, + FormatConversionCharSetInternal::kNumeric, + FormatConversionCharSetInternal::kStar)>; +using FloatingConvertResult = + ArgConvertResult<FormatConversionCharSetInternal::kFloating>; // Floats. -FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(double v, ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(long double v, ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(long double v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Chars. -IntegralConvertResult FormatConvertImpl(char v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(signed char v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(signed char v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned char v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(unsigned char v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Ints. IntegralConvertResult FormatConvertImpl(short v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(unsigned v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int128 v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(uint128 v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> -IntegralConvertResult FormatConvertImpl(T v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { return FormatConvertImpl(static_cast<int>(v), conv, sink); } @@ -164,12 +240,12 @@ template <typename T> typename std::enable_if<std::is_enum<T>::value && !HasUserDefinedConvert<T>::value, IntegralConvertResult>::type -FormatConvertImpl(T v, ConversionSpec conv, FormatSinkImpl* sink); +FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); template <typename T> -ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v, - ConversionSpec conv, - FormatSinkImpl* out) { +StringConvertResult FormatConvertImpl(const StreamedWrapper<T>& v, + FormatConversionSpecImpl conv, + FormatSinkImpl* out) { std::ostringstream oss; oss << v.v_; if (!oss) return {false}; @@ -180,21 +256,24 @@ ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v, // until after FormatCountCapture is fully defined. struct FormatCountCaptureHelper { template <class T = int> - static ConvertResult<Conv::n> ConvertHelper(const FormatCountCapture& v, - ConversionSpec conv, - FormatSinkImpl* sink) { + static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper( + const FormatCountCapture& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; - if (conv.conv() != str_format_internal::ConversionChar::n) return {false}; + if (conv.conversion_char() != + str_format_internal::FormatConversionCharInternal::n) { + return {false}; + } *v2.p_ = static_cast<int>(sink->size()); return {true}; } }; template <class T = int> -ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v, - ConversionSpec conv, - FormatSinkImpl* sink) { +ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl( + const FormatCountCapture& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { return FormatCountCaptureHelper::ConvertHelper(v, conv, sink); } @@ -203,13 +282,13 @@ ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v, struct FormatArgImplFriend { template <typename Arg> static bool ToInt(Arg arg, int* out) { - // A value initialized ConversionSpec has a `none` conv, which tells the - // dispatcher to run the `int` conversion. + // A value initialized FormatConversionSpecImpl has a `none` conv, which + // tells the dispatcher to run the `int` conversion. return arg.dispatcher_(arg.data_, {}, out); } template <typename Arg> - static bool Convert(Arg arg, str_format_internal::ConversionSpec conv, + static bool Convert(Arg arg, FormatConversionSpecImpl conv, FormatSinkImpl* out) { return arg.dispatcher_(arg.data_, conv, out); } @@ -220,6 +299,15 @@ struct FormatArgImplFriend { } }; +template <typename Arg> +constexpr FormatConversionCharSet ArgumentToConv() { + return absl::str_format_internal::ExtractCharSet( + decltype(str_format_internal::FormatConvertImpl( + std::declval<const Arg&>(), + std::declval<const FormatConversionSpecImpl&>(), + std::declval<FormatSinkImpl*>())){}); +} + // A type-erased handle to a format argument. class FormatArgImpl { private: @@ -233,7 +321,7 @@ class FormatArgImpl { char buf[kInlinedSpace]; }; - using Dispatcher = bool (*)(Data, ConversionSpec, void* out); + using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out); template <typename T> struct store_by_value @@ -375,15 +463,20 @@ class FormatArgImpl { } template <typename T> - static bool Dispatch(Data arg, ConversionSpec spec, void* out) { + static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) { // A `none` conv indicates that we want the `int` conversion. - if (ABSL_PREDICT_FALSE(spec.conv() == ConversionChar::none)) { + if (ABSL_PREDICT_FALSE(spec.conversion_char() == + FormatConversionCharInternal::kNone)) { return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), std::is_enum<T>()); } - + if (ABSL_PREDICT_FALSE(!Contains(ArgumentToConv<T>(), + spec.conversion_char()))) { + return false; + } return str_format_internal::FormatConvertImpl( - Manager<T>::Value(arg), spec, static_cast<FormatSinkImpl*>(out)) + Manager<T>::Value(arg), spec, + static_cast<FormatSinkImpl*>(out)) .value; } @@ -391,8 +484,9 @@ class FormatArgImpl { Dispatcher dispatcher_; }; -#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ - E template bool FormatArgImpl::Dispatch<T>(Data, ConversionSpec, void*) +#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ + E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \ + void*) #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \ diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/arg_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/arg_test.cc index 8d30d8b8ce..1261937c30 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/arg_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/arg_test.cc @@ -6,6 +6,12 @@ // // https://www.apache.org/licenses/LICENSE-2.0 // +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/str_format/arg.h" #include <ostream> @@ -23,8 +29,17 @@ class FormatArgImplTest : public ::testing::Test { enum Color { kRed, kGreen, kBlue }; static const char *hi() { return "hi"; } + + struct X {}; + + X x_; }; +inline FormatConvertResult<FormatConversionCharSet{}> AbslFormatConvert( + const FormatArgImplTest::X &, const FormatConversionSpec &, FormatSink *) { + return {false}; +} + TEST_F(FormatArgImplTest, ToInt) { int out = 0; EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out)); @@ -59,6 +74,7 @@ TEST_F(FormatArgImplTest, ToInt) { FormatArgImpl(static_cast<int *>(nullptr)), &out)); EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out)); EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out)); + EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(x_), &out)); EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out)); EXPECT_EQ(2, out); } @@ -95,8 +111,9 @@ TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) { TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) { std::string s; FormatSinkImpl sink(&s); - ConversionSpec conv; - FormatConversionSpecImplFriend::SetConversionChar(ConversionChar::s, &conv); + FormatConversionSpecImpl conv; + FormatConversionSpecImplFriend::SetConversionChar( + FormatConversionCharInternal::s, &conv); FormatConversionSpecImplFriend::SetFlags(Flags(), &conv); FormatConversionSpecImplFriend::SetWidth(-1, &conv); FormatConversionSpecImplFriend::SetPrecision(-1, &conv); diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/bind.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/bind.cc index 27522fdb4f..c988ba8fd2 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/bind.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/bind.cc @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/str_format/bind.h" #include <cerrno> @@ -44,7 +58,7 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound, if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false; arg = &pack_[arg_position - 1]; // 1-based - if (!unbound->flags.basic) { + if (unbound->flags != Flags::kBasic) { int width = unbound->width.value(); bool force_left = false; if (unbound->width.is_from_arg()) { @@ -70,9 +84,8 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound, FormatConversionSpecImplFriend::SetPrecision(precision, bound); if (force_left) { - Flags flags = unbound->flags; - flags.left = true; - FormatConversionSpecImplFriend::SetFlags(flags, bound); + FormatConversionSpecImplFriend::SetFlags(unbound->flags | Flags::kLeft, + bound); } else { FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); } @@ -147,7 +160,7 @@ class SummarizingConverter { << FormatConversionSpecImplFriend::FlagsToString(bound); if (bound.width() >= 0) ss << bound.width(); if (bound.precision() >= 0) ss << "." << bound.precision(); - ss << bound.conv() << "}"; + ss << bound.conversion_char() << "}"; Append(ss.str()); return true; } @@ -221,7 +234,7 @@ int FprintF(std::FILE* output, const UntypedFormatSpecImpl format, errno = sink.error(); return -1; } - if (sink.count() > std::numeric_limits<int>::max()) { + if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) { errno = EFBIG; return -1; } diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/bind.h b/third_party/abseil-cpp/absl/strings/internal/str_format/bind.h index cf41b19748..b26cff6648 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/bind.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/bind.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ @@ -19,7 +33,7 @@ class UntypedFormatSpec; namespace str_format_internal { -class BoundConversion : public ConversionSpec { +class BoundConversion : public FormatConversionSpecImpl { public: const FormatArgImpl* arg() const { return arg_; } void set_arg(const FormatArgImpl* a) { arg_ = a; } @@ -60,7 +74,7 @@ class UntypedFormatSpecImpl { size_t size_; }; -template <typename T, typename...> +template <typename T, FormatConversionCharSet...> struct MakeDependent { using type = T; }; @@ -68,7 +82,7 @@ struct MakeDependent { // Implicitly convertible from `const char*`, `string_view`, and the // `ExtendedParsedFormat` type. This abstraction allows all format functions to // operate on any without providing too many overloads. -template <typename... Args> +template <FormatConversionCharSet... Args> class FormatSpecTemplate : public MakeDependent<UntypedFormatSpec, Args...>::type { using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type; @@ -76,17 +90,17 @@ class FormatSpecTemplate public: #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - // Honeypot overload for when the std::string is not constexpr. + // Honeypot overload for when the string is not constexpr. // We use the 'unavailable' attribute to give a better compiler error than // just 'method is deleted'. FormatSpecTemplate(...) // NOLINT - __attribute__((unavailable("Format std::string is not constexpr."))); + __attribute__((unavailable("Format string is not constexpr."))); // Honeypot overload for when the format is constexpr and invalid. // We use the 'unavailable' attribute to give a better compiler error than // just 'method is deleted'. // To avoid checking the format twice, we just check that the format is - // constexpr. If is it valid, then the overload below will kick in. + // constexpr. If it is valid, then the overload below will kick in. // We add the template here to make this overload have lower priority. template <typename = void> FormatSpecTemplate(const char* s) // NOLINT @@ -105,13 +119,11 @@ class FormatSpecTemplate // Good format overload. FormatSpecTemplate(const char* s) // NOLINT - __attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s), - "bad format trap"))) + __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) : Base(s) {} FormatSpecTemplate(string_view s) // NOLINT - __attribute__((enable_if(ValidFormatImpl<ArgumentToConv<Args>()...>(s), - "bad format trap"))) + __attribute__((enable_if(ValidFormatImpl<Args...>(s), "bad format trap"))) : Base(s) {} #else // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER @@ -121,19 +133,15 @@ class FormatSpecTemplate #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - template <Conv... C, typename = typename std::enable_if< - AllOf(sizeof...(C) == sizeof...(Args), - Contains(ArgumentToConv<Args>(), - C)...)>::type> + template < + FormatConversionCharSet... C, + typename = typename std::enable_if<sizeof...(C) == sizeof...(Args)>::type, + typename = typename std::enable_if<AllOf(Contains(Args, + C)...)>::type> FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT : Base(&pc) {} }; -template <typename... Args> -struct FormatSpecDeductionBarrier { - using type = FormatSpecTemplate<Args...>; -}; - class Streamable { public: Streamable(const UntypedFormatSpecImpl& format, @@ -196,9 +204,9 @@ class StreamedWrapper { private: template <typename S> - friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v, - ConversionSpec conv, - FormatSinkImpl* out); + friend ArgConvertResult<FormatConversionCharSetInternal::s> FormatConvertImpl( + const StreamedWrapper<S>& v, FormatConversionSpecImpl conv, + FormatSinkImpl* out); const T& v_; }; diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/bind_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/bind_test.cc index 64790a85fd..1eef9c4326 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/bind_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/bind_test.cc @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/str_format/bind.h" #include <string.h> diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/checker.h b/third_party/abseil-cpp/absl/strings/internal/str_format/checker.h index 8993a79b95..2a2601eccf 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/checker.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/checker.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ @@ -24,13 +38,6 @@ constexpr bool AllOf(bool b, T... t) { return b && AllOf(t...); } -template <typename Arg> -constexpr Conv ArgumentToConv() { - return decltype(str_format_internal::FormatConvertImpl( - std::declval<const Arg&>(), std::declval<const ConversionSpec&>(), - std::declval<FormatSinkImpl*>()))::kConv; -} - #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER constexpr bool ContainsChar(const char* chars, char c) { @@ -39,14 +46,14 @@ constexpr bool ContainsChar(const char* chars, char c) { // A constexpr compatible list of Convs. struct ConvList { - const Conv* array; + const FormatConversionCharSet* array; int count; // We do the bound check here to avoid having to do it on the callers. - // Returning an empty Conv has the same effect as short circuiting because it - // will never match any conversion. - constexpr Conv operator[](int i) const { - return i < count ? array[i] : Conv{}; + // Returning an empty FormatConversionCharSet has the same effect as + // short circuiting because it will never match any conversion. + constexpr FormatConversionCharSet operator[](int i) const { + return i < count ? array[i] : FormatConversionCharSet{}; } constexpr ConvList without_front() const { @@ -57,7 +64,7 @@ struct ConvList { template <size_t count> struct ConvListT { // Make sure the array has size > 0. - Conv list[count ? count : 1]; + FormatConversionCharSet list[count ? count : 1]; }; constexpr char GetChar(string_view str, size_t index) { @@ -310,7 +317,7 @@ class FormatParser { ConvList args_; }; -template <Conv... C> +template <FormatConversionCharSet... C> constexpr bool ValidFormatImpl(string_view format) { return FormatParser(format, {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)}) diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/checker_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/checker_test.cc index ea2a7681a6..7c70f47d68 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/checker_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/checker_test.cc @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <string> #include "gmock/gmock.h" @@ -9,18 +23,22 @@ ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { -std::string ConvToString(Conv conv) { +std::string ConvToString(FormatConversionCharSet conv) { std::string out; -#define CONV_SET_CASE(c) \ - if (Contains(conv, Conv::c)) out += #c; +#define CONV_SET_CASE(c) \ + if (Contains(conv, FormatConversionCharSetInternal::c)) { \ + out += #c; \ + } ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, ) #undef CONV_SET_CASE - if (Contains(conv, Conv::star)) out += "*"; + if (Contains(conv, FormatConversionCharSetInternal::kStar)) { + out += "*"; + } return out; } TEST(StrFormatChecker, ArgumentToConv) { - Conv conv = ArgumentToConv<std::string>(); + FormatConversionCharSet conv = ArgumentToConv<std::string>(); EXPECT_EQ(ConvToString(conv), "s"); conv = ArgumentToConv<const char*>(); diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc index cbcd7caf46..91e0360901 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc @@ -1,20 +1,46 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <errno.h> #include <stdarg.h> #include <stdio.h> + #include <cctype> #include <cmath> +#include <limits> #include <string> +#include <thread> // NOLINT #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/strings/internal/str_format/bind.h" +#include "absl/strings/match.h" +#include "absl/types/optional.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { namespace { +struct NativePrintfTraits { + bool hex_float_has_glibc_rounding; + bool hex_float_prefers_denormal_repr; + bool hex_float_uses_minimal_precision_when_not_specified; + bool hex_float_optimizes_leading_digit_bit_count; +}; + template <typename T, size_t N> size_t ArraySize(T (&)[N]) { return N; @@ -57,7 +83,7 @@ std::string Esc(const T &v) { return oss.str(); } -void StrAppend(std::string *dst, const char *format, va_list ap) { +void StrAppendV(std::string *dst, const char *format, va_list ap) { // First try with a small fixed size buffer static const int kSpaceLength = 1024; char space[kSpaceLength]; @@ -98,15 +124,79 @@ void StrAppend(std::string *dst, const char *format, va_list ap) { delete[] buf; } +void StrAppend(std::string *out, const char *format, ...) { + va_list ap; + va_start(ap, format); + StrAppendV(out, format, ap); + va_end(ap); +} + std::string StrPrint(const char *format, ...) { va_list ap; va_start(ap, format); std::string result; - StrAppend(&result, format, ap); + StrAppendV(&result, format, ap); va_end(ap); return result; } +NativePrintfTraits VerifyNativeImplementationImpl() { + NativePrintfTraits result; + + // >>> hex_float_has_glibc_rounding. To have glibc's rounding behavior we need + // to meet three requirements: + // + // - The threshold for rounding up is 8 (for e.g. MSVC uses 9). + // - If the digits lower than than the 8 are non-zero then we round up. + // - If the digits lower than the 8 are all zero then we round toward even. + // + // The numbers below represent all the cases covering {below,at,above} the + // threshold (8) with both {zero,non-zero} lower bits and both {even,odd} + // preceding digits. + const double d0079 = 65657.0; // 0x1.0079p+16 + const double d0179 = 65913.0; // 0x1.0179p+16 + const double d0080 = 65664.0; // 0x1.0080p+16 + const double d0180 = 65920.0; // 0x1.0180p+16 + const double d0081 = 65665.0; // 0x1.0081p+16 + const double d0181 = 65921.0; // 0x1.0181p+16 + result.hex_float_has_glibc_rounding = + StartsWith(StrPrint("%.2a", d0079), "0x1.00") && + StartsWith(StrPrint("%.2a", d0179), "0x1.01") && + StartsWith(StrPrint("%.2a", d0080), "0x1.00") && + StartsWith(StrPrint("%.2a", d0180), "0x1.02") && + StartsWith(StrPrint("%.2a", d0081), "0x1.01") && + StartsWith(StrPrint("%.2a", d0181), "0x1.02"); + + // >>> hex_float_prefers_denormal_repr. Formatting `denormal` on glibc yields + // "0x0.0000000000001p-1022", whereas on std libs that don't use denormal + // representation it would either be 0x1p-1074 or 0x1.0000000000000-1074. + const double denormal = std::numeric_limits<double>::denorm_min(); + result.hex_float_prefers_denormal_repr = + StartsWith(StrPrint("%a", denormal), "0x0.0000000000001"); + + // >>> hex_float_uses_minimal_precision_when_not_specified. Some (non-glibc) + // libs will format the following as "0x1.0079000000000p+16". + result.hex_float_uses_minimal_precision_when_not_specified = + (StrPrint("%a", d0079) == "0x1.0079p+16"); + + // >>> hex_float_optimizes_leading_digit_bit_count. The number 1.5, when + // formatted by glibc should yield "0x1.8p+0" for `double` and "0xcp-3" for + // `long double`, i.e., number of bits in the leading digit is adapted to the + // number of bits in the mantissa. + const double d_15 = 1.5; + const long double ld_15 = 1.5; + result.hex_float_optimizes_leading_digit_bit_count = + StartsWith(StrPrint("%a", d_15), "0x1.8") && + StartsWith(StrPrint("%La", ld_15), "0xc"); + + return result; +} + +const NativePrintfTraits &VerifyNativeImplementation() { + static NativePrintfTraits native_traits = VerifyNativeImplementationImpl(); + return native_traits; +} + class FormatConvertTest : public ::testing::Test { }; template <typename T> @@ -139,6 +229,9 @@ TEST_F(FormatConvertTest, BasicString) { TestStringConvert(static_cast<const char*>("hello")); TestStringConvert(std::string("hello")); TestStringConvert(string_view("hello")); +#if defined(ABSL_HAVE_STD_STRING_VIEW) + TestStringConvert(std::string_view("hello")); +#endif // ABSL_HAVE_STD_STRING_VIEW } TEST_F(FormatConvertTest, NullString) { @@ -463,17 +556,130 @@ TEST_F(FormatConvertTest, Uint128) { } } -TEST_F(FormatConvertTest, Float) { -#ifdef _MSC_VER - // MSVC has a different rounding policy than us so we can't test our - // implementation against the native one there. - return; -#endif // _MSC_VER +template <typename Floating> +void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats, + const std::set<Floating> &skip_verify) { + const NativePrintfTraits &native_traits = VerifyNativeImplementation(); + // Reserve the space to ensure we don't allocate memory in the output itself. + std::string str_format_result; + str_format_result.reserve(1 << 20); + std::string string_printf_result; + string_printf_result.reserve(1 << 20); const char *const kFormats[] = { - "%", "%.3", "%8.5", "%9", "%.60", "%.30", "%03", "%+", - "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"}; + "%", "%.3", "%8.5", "%500", "%.5000", "%.60", "%.30", "%03", + "%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"}; + + for (const char *fmt : kFormats) { + for (char f : {'f', 'F', // + 'g', 'G', // + 'a', 'A', // + 'e', 'E'}) { + std::string fmt_str = std::string(fmt) + f; + + if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' && + f != 'a' && f != 'A') { + // This particular test takes way too long with snprintf. + // Disable for the case we are not implementing natively. + continue; + } + + if ((f == 'a' || f == 'A') && + !native_traits.hex_float_has_glibc_rounding) { + continue; + } + for (Floating d : floats) { + if (!native_traits.hex_float_prefers_denormal_repr && + (f == 'a' || f == 'A') && std::fpclassify(d) == FP_SUBNORMAL) { + continue; + } + int i = -10; + FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)}; + UntypedFormatSpecImpl format(fmt_str); + + string_printf_result.clear(); + StrAppend(&string_printf_result, fmt_str.c_str(), d, i); + str_format_result.clear(); + + { + AppendPack(&str_format_result, format, absl::MakeSpan(args)); + } + +#ifdef _MSC_VER + // MSVC has a different rounding policy than us so we can't test our + // implementation against the native one there. + continue; +#elif defined(__APPLE__) + // Apple formats NaN differently (+nan) vs. (nan) + if (std::isnan(d)) continue; +#endif + if (string_printf_result != str_format_result && + skip_verify.find(d) == skip_verify.end()) { + // We use ASSERT_EQ here because failures are usually correlated and a + // bug would print way too many failed expectations causing the test + // to time out. + ASSERT_EQ(string_printf_result, str_format_result) + << fmt_str << " " << StrPrint("%.18g", d) << " " + << StrPrint("%a", d) << " " << StrPrint("%.50f", d); + } + } + } + } +} + +TEST_F(FormatConvertTest, Float) { + std::vector<float> floats = {0.0f, + -0.0f, + .9999999f, + 9999999.f, + std::numeric_limits<float>::max(), + -std::numeric_limits<float>::max(), + std::numeric_limits<float>::min(), + -std::numeric_limits<float>::min(), + std::numeric_limits<float>::lowest(), + -std::numeric_limits<float>::lowest(), + std::numeric_limits<float>::epsilon(), + std::numeric_limits<float>::epsilon() + 1.0f, + std::numeric_limits<float>::infinity(), + -std::numeric_limits<float>::infinity(), + std::nanf("")}; + + // Some regression tests. + floats.push_back(0.999999989f); + + if (std::numeric_limits<float>::has_denorm != std::denorm_absent) { + floats.push_back(std::numeric_limits<float>::denorm_min()); + floats.push_back(-std::numeric_limits<float>::denorm_min()); + } + + for (float base : + {1.f, 12.f, 123.f, 1234.f, 12345.f, 123456.f, 1234567.f, 12345678.f, + 123456789.f, 1234567890.f, 12345678901.f, 12345678.f, 12345678.f}) { + for (int exp = -123; exp <= 123; ++exp) { + for (int sign : {1, -1}) { + floats.push_back(sign * std::ldexp(base, exp)); + } + } + } + + for (int exp = -300; exp <= 300; ++exp) { + const float all_ones_mantissa = 0xffffff; + floats.push_back(std::ldexp(all_ones_mantissa, exp)); + } + + // Remove duplicates to speed up the logic below. + std::sort(floats.begin(), floats.end()); + floats.erase(std::unique(floats.begin(), floats.end()), floats.end()); + + TestWithMultipleFormatsHelper(floats, {}); +} + +TEST_F(FormatConvertTest, Double) { + // For values that we know won't match the standard library implementation we + // skip verification, but still run the algorithm to catch asserts/sanitizer + // bugs. + std::set<double> skip_verify; std::vector<double> doubles = {0.0, -0.0, .99999999999999, @@ -487,12 +693,8 @@ TEST_F(FormatConvertTest, Float) { std::numeric_limits<double>::epsilon(), std::numeric_limits<double>::epsilon() + 1, std::numeric_limits<double>::infinity(), - -std::numeric_limits<double>::infinity()}; - -#ifndef __APPLE__ - // Apple formats NaN differently (+nan) vs. (nan) - doubles.push_back(std::nan("")); -#endif + -std::numeric_limits<double>::infinity(), + std::nan("")}; // Some regression tests. doubles.push_back(0.99999999999999989); @@ -512,43 +714,366 @@ TEST_F(FormatConvertTest, Float) { } } - for (const char *fmt : kFormats) { - for (char f : {'f', 'F', // - 'g', 'G', // - 'a', 'A', // - 'e', 'E'}) { - std::string fmt_str = std::string(fmt) + f; - for (double d : doubles) { - int i = -10; - FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)}; - UntypedFormatSpecImpl format(fmt_str); - // We use ASSERT_EQ here because failures are usually correlated and a - // bug would print way too many failed expectations causing the test to - // time out. - ASSERT_EQ(StrPrint(fmt_str.c_str(), d, i), - FormatPack(format, absl::MakeSpan(args))) - << fmt_str << " " << StrPrint("%.18g", d) << " " - << StrPrint("%.999f", d); - } + // Workaround libc bug. + // https://sourceware.org/bugzilla/show_bug.cgi?id=22142 + const bool gcc_bug_22142 = + StrPrint("%f", std::numeric_limits<double>::max()) != + "1797693134862315708145274237317043567980705675258449965989174768031" + "5726078002853876058955863276687817154045895351438246423432132688946" + "4182768467546703537516986049910576551282076245490090389328944075868" + "5084551339423045832369032229481658085593321233482747978262041447231" + "68738177180919299881250404026184124858368.000000"; + + for (int exp = -300; exp <= 300; ++exp) { + const double all_ones_mantissa = 0x1fffffffffffff; + doubles.push_back(std::ldexp(all_ones_mantissa, exp)); + if (gcc_bug_22142) { + skip_verify.insert(doubles.back()); + } + } + + if (gcc_bug_22142) { + using L = std::numeric_limits<double>; + skip_verify.insert(L::max()); + skip_verify.insert(L::min()); // NOLINT + skip_verify.insert(L::denorm_min()); + skip_verify.insert(-L::max()); + skip_verify.insert(-L::min()); // NOLINT + skip_verify.insert(-L::denorm_min()); + } + + // Remove duplicates to speed up the logic below. + std::sort(doubles.begin(), doubles.end()); + doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end()); + + TestWithMultipleFormatsHelper(doubles, skip_verify); +} + +TEST_F(FormatConvertTest, DoubleRound) { + std::string s; + const auto format = [&](const char *fmt, double d) -> std::string & { + s.clear(); + FormatArgImpl args[1] = {FormatArgImpl(d)}; + AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); +#if !defined(_MSC_VER) + // MSVC has a different rounding policy than us so we can't test our + // implementation against the native one there. + EXPECT_EQ(StrPrint(fmt, d), s); +#endif // _MSC_VER + + return s; + }; + // All of these values have to be exactly represented. + // Otherwise we might not be testing what we think we are testing. + + // These values can fit in a 64bit "fast" representation. + const double exact_value = 0.00000000000005684341886080801486968994140625; + assert(exact_value == std::pow(2, -44)); + // Round up at a 5xx. + EXPECT_EQ(format("%.13f", exact_value), "0.0000000000001"); + // Round up at a >5 + EXPECT_EQ(format("%.14f", exact_value), "0.00000000000006"); + // Round down at a <5 + EXPECT_EQ(format("%.16f", exact_value), "0.0000000000000568"); + // Nine handling + EXPECT_EQ(format("%.35f", exact_value), + "0.00000000000005684341886080801486969"); + EXPECT_EQ(format("%.36f", exact_value), + "0.000000000000056843418860808014869690"); + // Round down the last nine. + EXPECT_EQ(format("%.37f", exact_value), + "0.0000000000000568434188608080148696899"); + EXPECT_EQ(format("%.10f", 0.000003814697265625), "0.0000038147"); + // Round up the last nine + EXPECT_EQ(format("%.11f", 0.000003814697265625), "0.00000381470"); + EXPECT_EQ(format("%.12f", 0.000003814697265625), "0.000003814697"); + + // Round to even (down) + EXPECT_EQ(format("%.43f", exact_value), + "0.0000000000000568434188608080148696899414062"); + // Exact + EXPECT_EQ(format("%.44f", exact_value), + "0.00000000000005684341886080801486968994140625"); + // Round to even (up), let make the last digits 75 instead of 25 + EXPECT_EQ(format("%.43f", exact_value + std::pow(2, -43)), + "0.0000000000001705302565824240446090698242188"); + // Exact, just to check. + EXPECT_EQ(format("%.44f", exact_value + std::pow(2, -43)), + "0.00000000000017053025658242404460906982421875"); + + // This value has to be small enough that it won't fit in the uint128 + // representation for printing. + const double small_exact_value = + 0.000000000000000000000000000000000000752316384526264005099991383822237233803945956334136013765601092018187046051025390625; // NOLINT + assert(small_exact_value == std::pow(2, -120)); + // Round up at a 5xx. + EXPECT_EQ(format("%.37f", small_exact_value), + "0.0000000000000000000000000000000000008"); + // Round down at a <5 + EXPECT_EQ(format("%.38f", small_exact_value), + "0.00000000000000000000000000000000000075"); + // Round up at a >5 + EXPECT_EQ(format("%.41f", small_exact_value), + "0.00000000000000000000000000000000000075232"); + // Nine handling + EXPECT_EQ(format("%.55f", small_exact_value), + "0.0000000000000000000000000000000000007523163845262640051"); + EXPECT_EQ(format("%.56f", small_exact_value), + "0.00000000000000000000000000000000000075231638452626400510"); + EXPECT_EQ(format("%.57f", small_exact_value), + "0.000000000000000000000000000000000000752316384526264005100"); + EXPECT_EQ(format("%.58f", small_exact_value), + "0.0000000000000000000000000000000000007523163845262640051000"); + // Round down the last nine + EXPECT_EQ(format("%.59f", small_exact_value), + "0.00000000000000000000000000000000000075231638452626400509999"); + // Round up the last nine + EXPECT_EQ(format("%.79f", small_exact_value), + "0.000000000000000000000000000000000000" + "7523163845262640050999913838222372338039460"); + + // Round to even (down) + EXPECT_EQ(format("%.119f", small_exact_value), + "0.000000000000000000000000000000000000" + "75231638452626400509999138382223723380" + "394595633413601376560109201818704605102539062"); + // Exact + EXPECT_EQ(format("%.120f", small_exact_value), + "0.000000000000000000000000000000000000" + "75231638452626400509999138382223723380" + "3945956334136013765601092018187046051025390625"); + // Round to even (up), let make the last digits 75 instead of 25 + EXPECT_EQ(format("%.119f", small_exact_value + std::pow(2, -119)), + "0.000000000000000000000000000000000002" + "25694915357879201529997415146671170141" + "183786900240804129680327605456113815307617188"); + // Exact, just to check. + EXPECT_EQ(format("%.120f", small_exact_value + std::pow(2, -119)), + "0.000000000000000000000000000000000002" + "25694915357879201529997415146671170141" + "1837869002408041296803276054561138153076171875"); +} + +TEST_F(FormatConvertTest, DoubleRoundA) { + const NativePrintfTraits &native_traits = VerifyNativeImplementation(); + std::string s; + const auto format = [&](const char *fmt, double d) -> std::string & { + s.clear(); + FormatArgImpl args[1] = {FormatArgImpl(d)}; + AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); + if (native_traits.hex_float_has_glibc_rounding) { + EXPECT_EQ(StrPrint(fmt, d), s); + } + return s; + }; + + // 0x1.00018000p+100 + const double on_boundary_odd = 1267679614447900152596896153600.0; + EXPECT_EQ(format("%.0a", on_boundary_odd), "0x1p+100"); + EXPECT_EQ(format("%.1a", on_boundary_odd), "0x1.0p+100"); + EXPECT_EQ(format("%.2a", on_boundary_odd), "0x1.00p+100"); + EXPECT_EQ(format("%.3a", on_boundary_odd), "0x1.000p+100"); + EXPECT_EQ(format("%.4a", on_boundary_odd), "0x1.0002p+100"); // round + EXPECT_EQ(format("%.5a", on_boundary_odd), "0x1.00018p+100"); + EXPECT_EQ(format("%.6a", on_boundary_odd), "0x1.000180p+100"); + + // 0x1.00028000p-2 + const double on_boundary_even = 0.250009536743164062500; + EXPECT_EQ(format("%.0a", on_boundary_even), "0x1p-2"); + EXPECT_EQ(format("%.1a", on_boundary_even), "0x1.0p-2"); + EXPECT_EQ(format("%.2a", on_boundary_even), "0x1.00p-2"); + EXPECT_EQ(format("%.3a", on_boundary_even), "0x1.000p-2"); + EXPECT_EQ(format("%.4a", on_boundary_even), "0x1.0002p-2"); // no round + EXPECT_EQ(format("%.5a", on_boundary_even), "0x1.00028p-2"); + EXPECT_EQ(format("%.6a", on_boundary_even), "0x1.000280p-2"); + + // 0x1.00018001p+1 + const double slightly_over = 2.00004577683284878730773925781250; + EXPECT_EQ(format("%.0a", slightly_over), "0x1p+1"); + EXPECT_EQ(format("%.1a", slightly_over), "0x1.0p+1"); + EXPECT_EQ(format("%.2a", slightly_over), "0x1.00p+1"); + EXPECT_EQ(format("%.3a", slightly_over), "0x1.000p+1"); + EXPECT_EQ(format("%.4a", slightly_over), "0x1.0002p+1"); + EXPECT_EQ(format("%.5a", slightly_over), "0x1.00018p+1"); + EXPECT_EQ(format("%.6a", slightly_over), "0x1.000180p+1"); + + // 0x1.00017fffp+0 + const double slightly_under = 1.000022887950763106346130371093750; + EXPECT_EQ(format("%.0a", slightly_under), "0x1p+0"); + EXPECT_EQ(format("%.1a", slightly_under), "0x1.0p+0"); + EXPECT_EQ(format("%.2a", slightly_under), "0x1.00p+0"); + EXPECT_EQ(format("%.3a", slightly_under), "0x1.000p+0"); + EXPECT_EQ(format("%.4a", slightly_under), "0x1.0001p+0"); + EXPECT_EQ(format("%.5a", slightly_under), "0x1.00018p+0"); + EXPECT_EQ(format("%.6a", slightly_under), "0x1.000180p+0"); + EXPECT_EQ(format("%.7a", slightly_under), "0x1.0001800p+0"); + + // 0x1.1b3829ac28058p+3 + const double hex_value = 8.85060580848964661981881363317370414733886718750; + EXPECT_EQ(format("%.0a", hex_value), "0x1p+3"); + EXPECT_EQ(format("%.1a", hex_value), "0x1.2p+3"); + EXPECT_EQ(format("%.2a", hex_value), "0x1.1bp+3"); + EXPECT_EQ(format("%.3a", hex_value), "0x1.1b4p+3"); + EXPECT_EQ(format("%.4a", hex_value), "0x1.1b38p+3"); + EXPECT_EQ(format("%.5a", hex_value), "0x1.1b383p+3"); + EXPECT_EQ(format("%.6a", hex_value), "0x1.1b382ap+3"); + EXPECT_EQ(format("%.7a", hex_value), "0x1.1b3829bp+3"); + EXPECT_EQ(format("%.8a", hex_value), "0x1.1b3829acp+3"); + EXPECT_EQ(format("%.9a", hex_value), "0x1.1b3829ac3p+3"); + EXPECT_EQ(format("%.10a", hex_value), "0x1.1b3829ac28p+3"); + EXPECT_EQ(format("%.11a", hex_value), "0x1.1b3829ac280p+3"); + EXPECT_EQ(format("%.12a", hex_value), "0x1.1b3829ac2806p+3"); + EXPECT_EQ(format("%.13a", hex_value), "0x1.1b3829ac28058p+3"); + EXPECT_EQ(format("%.14a", hex_value), "0x1.1b3829ac280580p+3"); + EXPECT_EQ(format("%.15a", hex_value), "0x1.1b3829ac2805800p+3"); + EXPECT_EQ(format("%.16a", hex_value), "0x1.1b3829ac28058000p+3"); + EXPECT_EQ(format("%.17a", hex_value), "0x1.1b3829ac280580000p+3"); + EXPECT_EQ(format("%.18a", hex_value), "0x1.1b3829ac2805800000p+3"); + EXPECT_EQ(format("%.19a", hex_value), "0x1.1b3829ac28058000000p+3"); + EXPECT_EQ(format("%.20a", hex_value), "0x1.1b3829ac280580000000p+3"); + EXPECT_EQ(format("%.21a", hex_value), "0x1.1b3829ac2805800000000p+3"); + + // 0x1.0818283848586p+3 + const double hex_value2 = 8.2529488658208371987257123691961169242858886718750; + EXPECT_EQ(format("%.0a", hex_value2), "0x1p+3"); + EXPECT_EQ(format("%.1a", hex_value2), "0x1.1p+3"); + EXPECT_EQ(format("%.2a", hex_value2), "0x1.08p+3"); + EXPECT_EQ(format("%.3a", hex_value2), "0x1.082p+3"); + EXPECT_EQ(format("%.4a", hex_value2), "0x1.0818p+3"); + EXPECT_EQ(format("%.5a", hex_value2), "0x1.08183p+3"); + EXPECT_EQ(format("%.6a", hex_value2), "0x1.081828p+3"); + EXPECT_EQ(format("%.7a", hex_value2), "0x1.0818284p+3"); + EXPECT_EQ(format("%.8a", hex_value2), "0x1.08182838p+3"); + EXPECT_EQ(format("%.9a", hex_value2), "0x1.081828385p+3"); + EXPECT_EQ(format("%.10a", hex_value2), "0x1.0818283848p+3"); + EXPECT_EQ(format("%.11a", hex_value2), "0x1.08182838486p+3"); + EXPECT_EQ(format("%.12a", hex_value2), "0x1.081828384858p+3"); + EXPECT_EQ(format("%.13a", hex_value2), "0x1.0818283848586p+3"); + EXPECT_EQ(format("%.14a", hex_value2), "0x1.08182838485860p+3"); + EXPECT_EQ(format("%.15a", hex_value2), "0x1.081828384858600p+3"); + EXPECT_EQ(format("%.16a", hex_value2), "0x1.0818283848586000p+3"); + EXPECT_EQ(format("%.17a", hex_value2), "0x1.08182838485860000p+3"); + EXPECT_EQ(format("%.18a", hex_value2), "0x1.081828384858600000p+3"); + EXPECT_EQ(format("%.19a", hex_value2), "0x1.0818283848586000000p+3"); + EXPECT_EQ(format("%.20a", hex_value2), "0x1.08182838485860000000p+3"); + EXPECT_EQ(format("%.21a", hex_value2), "0x1.081828384858600000000p+3"); +} + +TEST_F(FormatConvertTest, LongDoubleRoundA) { + if (std::numeric_limits<long double>::digits % 4 != 0) { + // This test doesn't really make sense to run on platforms where a long + // double has a different mantissa size (mod 4) than Prod, since then the + // leading digit will be formatted differently. + return; + } + const NativePrintfTraits &native_traits = VerifyNativeImplementation(); + std::string s; + const auto format = [&](const char *fmt, long double d) -> std::string & { + s.clear(); + FormatArgImpl args[1] = {FormatArgImpl(d)}; + AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); + if (native_traits.hex_float_has_glibc_rounding && + native_traits.hex_float_optimizes_leading_digit_bit_count) { + EXPECT_EQ(StrPrint(fmt, d), s); + } + return s; + }; + + // 0x8.8p+4 + const long double on_boundary_even = 136.0; + EXPECT_EQ(format("%.0La", on_boundary_even), "0x8p+4"); + EXPECT_EQ(format("%.1La", on_boundary_even), "0x8.8p+4"); + EXPECT_EQ(format("%.2La", on_boundary_even), "0x8.80p+4"); + EXPECT_EQ(format("%.3La", on_boundary_even), "0x8.800p+4"); + EXPECT_EQ(format("%.4La", on_boundary_even), "0x8.8000p+4"); + EXPECT_EQ(format("%.5La", on_boundary_even), "0x8.80000p+4"); + EXPECT_EQ(format("%.6La", on_boundary_even), "0x8.800000p+4"); + + // 0x9.8p+4 + const long double on_boundary_odd = 152.0; + EXPECT_EQ(format("%.0La", on_boundary_odd), "0xap+4"); + EXPECT_EQ(format("%.1La", on_boundary_odd), "0x9.8p+4"); + EXPECT_EQ(format("%.2La", on_boundary_odd), "0x9.80p+4"); + EXPECT_EQ(format("%.3La", on_boundary_odd), "0x9.800p+4"); + EXPECT_EQ(format("%.4La", on_boundary_odd), "0x9.8000p+4"); + EXPECT_EQ(format("%.5La", on_boundary_odd), "0x9.80000p+4"); + EXPECT_EQ(format("%.6La", on_boundary_odd), "0x9.800000p+4"); + + // 0x8.80001p+24 + const long double slightly_over = 142606352.0; + EXPECT_EQ(format("%.0La", slightly_over), "0x9p+24"); + EXPECT_EQ(format("%.1La", slightly_over), "0x8.8p+24"); + EXPECT_EQ(format("%.2La", slightly_over), "0x8.80p+24"); + EXPECT_EQ(format("%.3La", slightly_over), "0x8.800p+24"); + EXPECT_EQ(format("%.4La", slightly_over), "0x8.8000p+24"); + EXPECT_EQ(format("%.5La", slightly_over), "0x8.80001p+24"); + EXPECT_EQ(format("%.6La", slightly_over), "0x8.800010p+24"); + + // 0x8.7ffffp+24 + const long double slightly_under = 142606320.0; + EXPECT_EQ(format("%.0La", slightly_under), "0x8p+24"); + EXPECT_EQ(format("%.1La", slightly_under), "0x8.8p+24"); + EXPECT_EQ(format("%.2La", slightly_under), "0x8.80p+24"); + EXPECT_EQ(format("%.3La", slightly_under), "0x8.800p+24"); + EXPECT_EQ(format("%.4La", slightly_under), "0x8.8000p+24"); + EXPECT_EQ(format("%.5La", slightly_under), "0x8.7ffffp+24"); + EXPECT_EQ(format("%.6La", slightly_under), "0x8.7ffff0p+24"); + EXPECT_EQ(format("%.7La", slightly_under), "0x8.7ffff00p+24"); + + // 0xc.0828384858688000p+128 + const long double eights = 4094231060438608800781871108094404067328.0; + EXPECT_EQ(format("%.0La", eights), "0xcp+128"); + EXPECT_EQ(format("%.1La", eights), "0xc.1p+128"); + EXPECT_EQ(format("%.2La", eights), "0xc.08p+128"); + EXPECT_EQ(format("%.3La", eights), "0xc.083p+128"); + EXPECT_EQ(format("%.4La", eights), "0xc.0828p+128"); + EXPECT_EQ(format("%.5La", eights), "0xc.08284p+128"); + EXPECT_EQ(format("%.6La", eights), "0xc.082838p+128"); + EXPECT_EQ(format("%.7La", eights), "0xc.0828385p+128"); + EXPECT_EQ(format("%.8La", eights), "0xc.08283848p+128"); + EXPECT_EQ(format("%.9La", eights), "0xc.082838486p+128"); + EXPECT_EQ(format("%.10La", eights), "0xc.0828384858p+128"); + EXPECT_EQ(format("%.11La", eights), "0xc.08283848587p+128"); + EXPECT_EQ(format("%.12La", eights), "0xc.082838485868p+128"); + EXPECT_EQ(format("%.13La", eights), "0xc.0828384858688p+128"); + EXPECT_EQ(format("%.14La", eights), "0xc.08283848586880p+128"); + EXPECT_EQ(format("%.15La", eights), "0xc.082838485868800p+128"); + EXPECT_EQ(format("%.16La", eights), "0xc.0828384858688000p+128"); +} + +// We don't actually store the results. This is just to exercise the rest of the +// machinery. +struct NullSink { + friend void AbslFormatFlush(NullSink *sink, string_view str) {} +}; + +template <typename... T> +bool FormatWithNullSink(absl::string_view fmt, const T &... a) { + NullSink sink; + FormatArgImpl args[] = {FormatArgImpl(a)...}; + return FormatUntyped(&sink, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); +} + +TEST_F(FormatConvertTest, ExtremeWidthPrecision) { + for (const char *fmt : {"f"}) { + for (double d : {1e-100, 1.0, 1e100}) { + constexpr int max = std::numeric_limits<int>::max(); + EXPECT_TRUE(FormatWithNullSink(std::string("%.*") + fmt, max, d)); + EXPECT_TRUE(FormatWithNullSink(std::string("%1.*") + fmt, max, d)); + EXPECT_TRUE(FormatWithNullSink(std::string("%*") + fmt, max, d)); + EXPECT_TRUE(FormatWithNullSink(std::string("%*.*") + fmt, max, max, d)); } } } TEST_F(FormatConvertTest, LongDouble) { - const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", + const NativePrintfTraits &native_traits = VerifyNativeImplementation(); + const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000", "%.60", "%+", "% ", "%-10"}; - // This value is not representable in double, but it is in long double that - // uses the extended format. - // This is to verify that we are not truncating the value mistakenly through a - // double. - long double very_precise = 10000000000000000.25L; - std::vector<long double> doubles = { 0.0, -0.0, - very_precise, - 1 / very_precise, std::numeric_limits<long double>::max(), -std::numeric_limits<long double>::max(), std::numeric_limits<long double>::min(), @@ -556,28 +1081,73 @@ TEST_F(FormatConvertTest, LongDouble) { std::numeric_limits<long double>::infinity(), -std::numeric_limits<long double>::infinity()}; + for (long double base : {1.L, 12.L, 123.L, 1234.L, 12345.L, 123456.L, + 1234567.L, 12345678.L, 123456789.L, 1234567890.L, + 12345678901.L, 123456789012.L, 1234567890123.L, + // This value is not representable in double, but it + // is in long double that uses the extended format. + // This is to verify that we are not truncating the + // value mistakenly through a double. + 10000000000000000.25L}) { + for (int exp : {-1000, -500, 0, 500, 1000}) { + for (int sign : {1, -1}) { + doubles.push_back(sign * std::ldexp(base, exp)); + doubles.push_back(sign / std::ldexp(base, exp)); + } + } + } + + // Regression tests + // + // Using a string literal because not all platforms support hex literals or it + // might be out of range. + doubles.push_back(std::strtold("-0xf.ffffffb5feafffbp-16324L", nullptr)); + for (const char *fmt : kFormats) { for (char f : {'f', 'F', // 'g', 'G', // 'a', 'A', // 'e', 'E'}) { std::string fmt_str = std::string(fmt) + 'L' + f; + + if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F' && + f != 'a' && f != 'A') { + // This particular test takes way too long with snprintf. + // Disable for the case we are not implementing natively. + continue; + } + + if (f == 'a' || f == 'A') { + if (!native_traits.hex_float_has_glibc_rounding || + !native_traits.hex_float_optimizes_leading_digit_bit_count) { + continue; + } + } + for (auto d : doubles) { FormatArgImpl arg(d); UntypedFormatSpecImpl format(fmt_str); + std::string result = FormatPack(format, {&arg, 1}); + +#ifdef _MSC_VER + // MSVC has a different rounding policy than us so we can't test our + // implementation against the native one there. + continue; +#endif // _MSC_VER + // We use ASSERT_EQ here because failures are usually correlated and a // bug would print way too many failed expectations causing the test to // time out. - ASSERT_EQ(StrPrint(fmt_str.c_str(), d), - FormatPack(format, {&arg, 1})) + ASSERT_EQ(StrPrint(fmt_str.c_str(), d), result) << fmt_str << " " << StrPrint("%.18Lg", d) << " " - << StrPrint("%.999Lf", d); + << StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d); } } } } -TEST_F(FormatConvertTest, IntAsFloat) { +TEST_F(FormatConvertTest, IntAsDouble) { + const NativePrintfTraits &native_traits = VerifyNativeImplementation(); const int kMin = std::numeric_limits<int>::min(); const int kMax = std::numeric_limits<int>::max(); const int ia[] = { @@ -593,14 +1163,17 @@ TEST_F(FormatConvertTest, IntAsFloat) { const char *fmt; }; const double dx = static_cast<double>(fx); - const Expectation kExpect[] = { - { __LINE__, StrPrint("%f", dx), "%f" }, - { __LINE__, StrPrint("%12f", dx), "%12f" }, - { __LINE__, StrPrint("%.12f", dx), "%.12f" }, - { __LINE__, StrPrint("%12a", dx), "%12a" }, - { __LINE__, StrPrint("%.12a", dx), "%.12a" }, + std::vector<Expectation> expect = { + {__LINE__, StrPrint("%f", dx), "%f"}, + {__LINE__, StrPrint("%12f", dx), "%12f"}, + {__LINE__, StrPrint("%.12f", dx), "%.12f"}, + {__LINE__, StrPrint("%.12a", dx), "%.12a"}, }; - for (const Expectation &e : kExpect) { + if (native_traits.hex_float_uses_minimal_precision_when_not_specified) { + Expectation ex = {__LINE__, StrPrint("%12a", dx), "%12a"}; + expect.push_back(ex); + } + for (const Expectation &e : expect) { SCOPED_TRACE(e.line); SCOPED_TRACE(e.fmt); UntypedFormatSpecImpl format(e.fmt); @@ -645,6 +1218,25 @@ TEST_F(FormatConvertTest, ExpectedFailures) { EXPECT_TRUE(FormatFails("%*d", "")); } +// Sanity check to make sure that we are testing what we think we're testing on +// e.g. the x86_64+glibc platform. +TEST_F(FormatConvertTest, GlibcHasCorrectTraits) { +#if !defined(__GLIBC__) || !defined(__x86_64__) + return; +#endif + const NativePrintfTraits &native_traits = VerifyNativeImplementation(); + // If one of the following tests break then it is either because the above PP + // macro guards failed to exclude a new platform (likely) or because something + // has changed in the implemention of glibc sprintf float formatting behavior. + // If the latter, then the code that computes these flags needs to be + // revisited and/or possibly the StrFormat implementation. + EXPECT_TRUE(native_traits.hex_float_has_glibc_rounding); + EXPECT_TRUE(native_traits.hex_float_prefers_denormal_repr); + EXPECT_TRUE( + native_traits.hex_float_uses_minimal_precision_when_not_specified); + EXPECT_TRUE(native_traits.hex_float_optimizes_leading_digit_bit_count); +} + } // namespace } // namespace str_format_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/extension.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/extension.cc index 2e5bc2ce0b..484f6ebfc1 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/extension.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/extension.cc @@ -23,26 +23,50 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -std::string Flags::ToString() const { +std::string FlagsToString(Flags v) { std::string s; - s.append(left ? "-" : ""); - s.append(show_pos ? "+" : ""); - s.append(sign_col ? " " : ""); - s.append(alt ? "#" : ""); - s.append(zero ? "0" : ""); + s.append(FlagsContains(v, Flags::kLeft) ? "-" : ""); + s.append(FlagsContains(v, Flags::kShowPos) ? "+" : ""); + s.append(FlagsContains(v, Flags::kSignCol) ? " " : ""); + s.append(FlagsContains(v, Flags::kAlt) ? "#" : ""); + s.append(FlagsContains(v, Flags::kZero) ? "0" : ""); return s; } -bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) { +#define ABSL_INTERNAL_X_VAL(id) \ + constexpr absl::FormatConversionChar FormatConversionCharInternal::id; +ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) +#undef ABSL_INTERNAL_X_VAL +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr absl::FormatConversionChar FormatConversionCharInternal::kNone; + +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + constexpr FormatConversionCharSet FormatConversionCharSetInternal::c; +ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) +#undef ABSL_INTERNAL_CHAR_SET_CASE + +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric; +// NOLINTNEXTLINE(readability-redundant-declaration) +constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer; + +bool FormatSinkImpl::PutPaddedString(string_view value, int width, + int precision, bool left) { size_t space_remaining = 0; - if (w >= 0) space_remaining = w; - size_t n = v.size(); - if (p >= 0) n = std::min(n, static_cast<size_t>(p)); - string_view shown(v.data(), n); + if (width >= 0) space_remaining = width; + size_t n = value.size(); + if (precision >= 0) n = std::min(n, static_cast<size_t>(precision)); + string_view shown(value.data(), n); space_remaining = Excess(shown.size(), space_remaining); - if (!l) Append(space_remaining, ' '); + if (!left) Append(space_remaining, ' '); Append(shown); - if (l) Append(space_remaining, ' '); + if (left) Append(space_remaining, ' '); return true; } diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/extension.h b/third_party/abseil-cpp/absl/strings/internal/str_format/extension.h index d1665753d1..55cbb56d0a 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/extension.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/extension.h @@ -24,11 +24,16 @@ #include "absl/base/config.h" #include "absl/base/port.h" +#include "absl/meta/type_traits.h" #include "absl/strings/internal/str_format/output.h" #include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN + +enum class FormatConversionChar : uint8_t; +enum class FormatConversionCharSet : uint64_t; + namespace str_format_internal { class FormatRawSinkImpl { @@ -102,7 +107,7 @@ class FormatSinkImpl { size_t size() const { return size_; } // Put 'v' to 'sink' with specified width, precision, and left flag. - bool PutPaddedString(string_view v, int w, int p, bool l); + bool PutPaddedString(string_view v, int width, int precision, bool left); template <typename T> T Wrap() { @@ -123,23 +128,37 @@ class FormatSinkImpl { char buf_[1024]; }; -struct Flags { - bool basic : 1; // fastest conversion: no flags, width, or precision - bool left : 1; // "-" - bool show_pos : 1; // "+" - bool sign_col : 1; // " " - bool alt : 1; // "#" - bool zero : 1; // "0" - std::string ToString() const; - friend std::ostream& operator<<(std::ostream& os, const Flags& v) { - return os << v.ToString(); - } +enum class Flags : uint8_t { + kBasic = 0, + kLeft = 1 << 0, + kShowPos = 1 << 1, + kSignCol = 1 << 2, + kAlt = 1 << 3, + kZero = 1 << 4, + // This is not a real flag. It just exists to turn off kBasic when no other + // flags are set. This is for when width/precision are specified. + kNonBasic = 1 << 5, }; +constexpr Flags operator|(Flags a, Flags b) { + return static_cast<Flags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b)); +} + +constexpr bool FlagsContains(Flags haystack, Flags needle) { + return (static_cast<uint8_t>(haystack) & static_cast<uint8_t>(needle)) == + static_cast<uint8_t>(needle); +} + +std::string FlagsToString(Flags v); + +inline std::ostream& operator<<(std::ostream& os, Flags v) { + return os << FlagsToString(v); +} + // clang-format off #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ /* text */ \ - X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \ + X_VAL(c) X_SEP X_VAL(s) X_SEP \ /* ints */ \ X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \ X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \ @@ -148,14 +167,39 @@ struct Flags { X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \ /* misc */ \ X_VAL(n) X_SEP X_VAL(p) +// clang-format on + +// This type should not be referenced, it exists only to provide labels +// internally that match the values declared in FormatConversionChar in +// str_format.h. This is meant to allow internal libraries to use the same +// declared interface type as the public interface +// (absl::StrFormatConversionChar) while keeping the definition in a public +// header. +// Internal libraries should use the form +// `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for +// comparisons. Use in switch statements is not recommended due to a bug in how +// gcc 4.9 -Wswitch handles declared but undefined enums. +struct FormatConversionCharInternal { + FormatConversionCharInternal() = delete; -enum class FormatConversionChar : uint8_t { - c, C, s, S, // text + private: + // clang-format off + enum class Enum : uint8_t { + c, s, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float n, p, // misc - kNone, - none = kNone + kNone + }; + // clang-format on + public: +#define ABSL_INTERNAL_X_VAL(id) \ + static constexpr FormatConversionChar id = \ + static_cast<FormatConversionChar>(Enum::id); + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) +#undef ABSL_INTERNAL_X_VAL + static constexpr FormatConversionChar kNone = + static_cast<FormatConversionChar>(Enum::kNone); }; // clang-format on @@ -163,95 +207,56 @@ inline FormatConversionChar FormatConversionCharFromChar(char c) { switch (c) { #define ABSL_INTERNAL_X_VAL(id) \ case #id[0]: \ - return FormatConversionChar::id; + return FormatConversionCharInternal::id; ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) #undef ABSL_INTERNAL_X_VAL } - return FormatConversionChar::kNone; -} - -inline int FormatConversionCharRadix(FormatConversionChar c) { - switch (c) { - case FormatConversionChar::x: - case FormatConversionChar::X: - case FormatConversionChar::a: - case FormatConversionChar::A: - case FormatConversionChar::p: - return 16; - case FormatConversionChar::o: - return 8; - default: - return 10; - } + return FormatConversionCharInternal::kNone; } inline bool FormatConversionCharIsUpper(FormatConversionChar c) { - switch (c) { - case FormatConversionChar::X: - case FormatConversionChar::F: - case FormatConversionChar::E: - case FormatConversionChar::G: - case FormatConversionChar::A: - return true; - default: - return false; - } -} - -inline bool FormatConversionCharIsSigned(FormatConversionChar c) { - switch (c) { - case FormatConversionChar::d: - case FormatConversionChar::i: - return true; - default: - return false; - } -} - -inline bool FormatConversionCharIsIntegral(FormatConversionChar c) { - switch (c) { - case FormatConversionChar::d: - case FormatConversionChar::i: - case FormatConversionChar::u: - case FormatConversionChar::o: - case FormatConversionChar::x: - case FormatConversionChar::X: - return true; - default: - return false; + if (c == FormatConversionCharInternal::X || + c == FormatConversionCharInternal::F || + c == FormatConversionCharInternal::E || + c == FormatConversionCharInternal::G || + c == FormatConversionCharInternal::A) { + return true; + } else { + return false; } } inline bool FormatConversionCharIsFloat(FormatConversionChar c) { - switch (c) { - case FormatConversionChar::a: - case FormatConversionChar::e: - case FormatConversionChar::f: - case FormatConversionChar::g: - case FormatConversionChar::A: - case FormatConversionChar::E: - case FormatConversionChar::F: - case FormatConversionChar::G: - return true; - default: - return false; + if (c == FormatConversionCharInternal::a || + c == FormatConversionCharInternal::e || + c == FormatConversionCharInternal::f || + c == FormatConversionCharInternal::g || + c == FormatConversionCharInternal::A || + c == FormatConversionCharInternal::E || + c == FormatConversionCharInternal::F || + c == FormatConversionCharInternal::G) { + return true; + } else { + return false; } } inline char FormatConversionCharToChar(FormatConversionChar c) { - switch (c) { -#define ABSL_INTERNAL_X_VAL(e) \ - case FormatConversionChar::e: \ + if (c == FormatConversionCharInternal::kNone) { + return '\0'; + +#define ABSL_INTERNAL_X_VAL(e) \ + } else if (c == FormatConversionCharInternal::e) { \ return #e[0]; #define ABSL_INTERNAL_X_SEP - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, - ABSL_INTERNAL_X_SEP) - case FormatConversionChar::kNone: - return '\0'; + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, + ABSL_INTERNAL_X_SEP) + } else { + return '\0'; + } + #undef ABSL_INTERNAL_X_VAL #undef ABSL_INTERNAL_X_SEP - } - return '\0'; } // The associated char. @@ -263,20 +268,24 @@ inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) { struct FormatConversionSpecImplFriend; -class FormatConversionSpec { +class FormatConversionSpecImpl { public: // Width and precison are not specified, no flags are set. - bool is_basic() const { return flags_.basic; } - bool has_left_flag() const { return flags_.left; } - bool has_show_pos_flag() const { return flags_.show_pos; } - bool has_sign_col_flag() const { return flags_.sign_col; } - bool has_alt_flag() const { return flags_.alt; } - bool has_zero_flag() const { return flags_.zero; } + bool is_basic() const { return flags_ == Flags::kBasic; } + bool has_left_flag() const { return FlagsContains(flags_, Flags::kLeft); } + bool has_show_pos_flag() const { + return FlagsContains(flags_, Flags::kShowPos); + } + bool has_sign_col_flag() const { + return FlagsContains(flags_, Flags::kSignCol); + } + bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); } + bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); } FormatConversionChar conversion_char() const { // Keep this field first in the struct . It generates better code when // accessing it when ConversionSpec is passed by value in registers. - static_assert(offsetof(FormatConversionSpec, conv_) == 0, ""); + static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, ""); return conv_; } @@ -287,41 +296,65 @@ class FormatConversionSpec { // negative value. int precision() const { return precision_; } - // Deprecated (use has_x_flag() instead). - Flags flags() const { return flags_; } - // Deprecated - FormatConversionChar conv() const { return conversion_char(); } + template <typename T> + T Wrap() { + return T(*this); + } private: friend struct str_format_internal::FormatConversionSpecImplFriend; - FormatConversionChar conv_ = FormatConversionChar::kNone; + FormatConversionChar conv_ = FormatConversionCharInternal::kNone; Flags flags_; int width_; int precision_; }; struct FormatConversionSpecImplFriend final { - static void SetFlags(Flags f, FormatConversionSpec* conv) { + static void SetFlags(Flags f, FormatConversionSpecImpl* conv) { conv->flags_ = f; } static void SetConversionChar(FormatConversionChar c, - FormatConversionSpec* conv) { + FormatConversionSpecImpl* conv) { conv->conv_ = c; } - static void SetWidth(int w, FormatConversionSpec* conv) { conv->width_ = w; } - static void SetPrecision(int p, FormatConversionSpec* conv) { + static void SetWidth(int w, FormatConversionSpecImpl* conv) { + conv->width_ = w; + } + static void SetPrecision(int p, FormatConversionSpecImpl* conv) { conv->precision_ = p; } - static std::string FlagsToString(const FormatConversionSpec& spec) { - return spec.flags_.ToString(); + static std::string FlagsToString(const FormatConversionSpecImpl& spec) { + return str_format_internal::FlagsToString(spec.flags_); } }; -constexpr uint64_t FormatConversionCharToConvValue(char conv) { +// Type safe OR operator. +// We need this for two reasons: +// 1. operator| on enums makes them decay to integers and the result is an +// integer. We need the result to stay as an enum. +// 2. We use "enum class" which would not work even if we accepted the decay. +constexpr FormatConversionCharSet FormatConversionCharSetUnion( + FormatConversionCharSet a) { + return a; +} + +template <typename... CharSet> +constexpr FormatConversionCharSet FormatConversionCharSetUnion( + FormatConversionCharSet a, CharSet... rest) { + return static_cast<FormatConversionCharSet>( + static_cast<uint64_t>(a) | + static_cast<uint64_t>(FormatConversionCharSetUnion(rest...))); +} + +constexpr uint64_t FormatConversionCharToConvInt(FormatConversionChar c) { + return uint64_t{1} << (1 + static_cast<uint8_t>(c)); +} + +constexpr uint64_t FormatConversionCharToConvInt(char conv) { return -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - conv == #c[0] \ - ? (uint64_t{1} << (1 + static_cast<uint8_t>(FormatConversionChar::c))) \ +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + conv == #c[0] \ + ? FormatConversionCharToConvInt(FormatConversionCharInternal::c) \ : ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) #undef ABSL_INTERNAL_CHAR_SET_CASE @@ -330,28 +363,29 @@ constexpr uint64_t FormatConversionCharToConvValue(char conv) { : 0; } -enum class FormatConversionCharSet : uint64_t { -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - c = FormatConversionCharToConvValue(#c[0]), +constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) { + return static_cast<FormatConversionCharSet>( + FormatConversionCharToConvInt(conv)); +} + +struct FormatConversionCharSetInternal { +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + static constexpr FormatConversionCharSet c = \ + FormatConversionCharToConvValue(#c[0]); ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) #undef ABSL_INTERNAL_CHAR_SET_CASE // Used for width/precision '*' specification. - kStar = FormatConversionCharToConvValue('*'), - // Some predefined values: - kIntegral = d | i | u | o | x | X, - kFloating = a | e | f | g | A | E | F | G, - kNumeric = kIntegral | kFloating, - kString = s, - kPointer = p, - - // The following are deprecated - star = kStar, - integral = kIntegral, - floating = kFloating, - numeric = kNumeric, - string = kString, - pointer = kPointer + static constexpr FormatConversionCharSet kStar = + FormatConversionCharToConvValue('*'); + + static constexpr FormatConversionCharSet kIntegral = + FormatConversionCharSetUnion(d, i, u, o, x, X); + static constexpr FormatConversionCharSet kFloating = + FormatConversionCharSetUnion(a, e, f, g, A, E, F, G); + static constexpr FormatConversionCharSet kNumeric = + FormatConversionCharSetUnion(kIntegral, kFloating); + static constexpr FormatConversionCharSet kPointer = p; }; // Type safe OR operator. @@ -361,18 +395,29 @@ enum class FormatConversionCharSet : uint64_t { // 2. We use "enum class" which would not work even if we accepted the decay. constexpr FormatConversionCharSet operator|(FormatConversionCharSet a, FormatConversionCharSet b) { - return FormatConversionCharSet(static_cast<uint64_t>(a) | - static_cast<uint64_t>(b)); + return FormatConversionCharSetUnion(a, b); } +// Overloaded conversion functions to support absl::ParsedFormat. // Get a conversion with a single character in it. -constexpr FormatConversionCharSet ConversionCharToConv(char c) { - return FormatConversionCharSet(FormatConversionCharToConvValue(c)); +constexpr FormatConversionCharSet ToFormatConversionCharSet(char c) { + return static_cast<FormatConversionCharSet>( + FormatConversionCharToConvValue(c)); } +// Get a conversion with a single character in it. +constexpr FormatConversionCharSet ToFormatConversionCharSet( + FormatConversionCharSet c) { + return c; +} + +template <typename T> +void ToFormatConversionCharSet(T) = delete; + // Checks whether `c` exists in `set`. constexpr bool Contains(FormatConversionCharSet set, char c) { - return (static_cast<uint64_t>(set) & FormatConversionCharToConvValue(c)) != 0; + return (static_cast<uint64_t>(set) & + static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0; } // Checks whether all the characters in `c` are contained in `set` @@ -382,31 +427,16 @@ constexpr bool Contains(FormatConversionCharSet set, static_cast<uint64_t>(c); } -// Return type of the AbslFormatConvert() functions. -// The FormatConversionCharSet template parameter is used to inform the -// framework of what conversion characters are supported by that -// AbslFormatConvert routine. -template <FormatConversionCharSet C> -struct FormatConvertResult { - static constexpr FormatConversionCharSet kConv = C; - bool value; -}; - -template <FormatConversionCharSet C> -constexpr FormatConversionCharSet FormatConvertResult<C>::kConv; +// Checks whether all the characters in `c` are contained in `set` +constexpr bool Contains(FormatConversionCharSet set, FormatConversionChar c) { + return (static_cast<uint64_t>(set) & FormatConversionCharToConvInt(c)) != 0; +} // Return capacity - used, clipped to a minimum of 0. inline size_t Excess(size_t used, size_t capacity) { return used < capacity ? capacity - used : 0; } -// Type alias for use during migration. -using ConversionChar = FormatConversionChar; -using ConversionSpec = FormatConversionSpec; -using Conv = FormatConversionCharSet; -template <FormatConversionCharSet C> -using ConvertResult = FormatConvertResult<C>; - } // namespace str_format_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/extension_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/extension_test.cc index 4e23fefbd5..1c93fdb1c7 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/extension_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/extension_test.cc @@ -19,9 +19,26 @@ #include <random> #include <string> +#include "gtest/gtest.h" #include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" -#include "gtest/gtest.h" +namespace my_namespace { +class UserDefinedType { + public: + UserDefinedType() = default; + + void Append(absl::string_view str) { value_.append(str.data(), str.size()); } + const std::string& Value() const { return value_; } + + friend void AbslFormatFlush(UserDefinedType* x, absl::string_view str) { + x->Append(str); + } + + private: + std::string value_; +}; +} // namespace my_namespace namespace { @@ -63,4 +80,19 @@ TEST(FormatExtensionTest, SinkAppendChars) { EXPECT_EQ(actual, expected); } } + +TEST(FormatExtensionTest, VerifyEnumEquality) { +#define X_VAL(id) \ + EXPECT_EQ(absl::FormatConversionChar::id, \ + absl::str_format_internal::FormatConversionCharInternal::id); + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, ); +#undef X_VAL + +#define X_VAL(id) \ + EXPECT_EQ(absl::FormatConversionCharSet::id, \ + absl::str_format_internal::FormatConversionCharSetInternal::id); + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, ); +#undef X_VAL +} + } // namespace diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc index d4c647c3ed..b1c4068475 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.cc @@ -1,12 +1,38 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/str_format/float_conversion.h" #include <string.h> + #include <algorithm> #include <cassert> #include <cmath> +#include <limits> #include <string> +#include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/functional/function_ref.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/bits.h" +#include "absl/numeric/int128.h" +#include "absl/numeric/internal/representation.h" +#include "absl/strings/numbers.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -14,13 +40,905 @@ namespace str_format_internal { namespace { -char *CopyStringTo(string_view v, char *out) { +using ::absl::numeric_internal::IsDoubleDouble; + +// The code below wants to avoid heap allocations. +// To do so it needs to allocate memory on the stack. +// `StackArray` will allocate memory on the stack in the form of a uint32_t +// array and call the provided callback with said memory. +// It will allocate memory in increments of 512 bytes. We could allocate the +// largest needed unconditionally, but that is more than we need in most of +// cases. This way we use less stack in the common cases. +class StackArray { + using Func = absl::FunctionRef<void(absl::Span<uint32_t>)>; + static constexpr size_t kStep = 512 / sizeof(uint32_t); + // 5 steps is 2560 bytes, which is enough to hold a long double with the + // largest/smallest exponents. + // The operations below will static_assert their particular maximum. + static constexpr size_t kNumSteps = 5; + + // We do not want this function to be inlined. + // Otherwise the caller will allocate the stack space unnecessarily for all + // the variants even though it only calls one. + template <size_t steps> + ABSL_ATTRIBUTE_NOINLINE static void RunWithCapacityImpl(Func f) { + uint32_t values[steps * kStep]{}; + f(absl::MakeSpan(values)); + } + + public: + static constexpr size_t kMaxCapacity = kStep * kNumSteps; + + static void RunWithCapacity(size_t capacity, Func f) { + assert(capacity <= kMaxCapacity); + const size_t step = (capacity + kStep - 1) / kStep; + assert(step <= kNumSteps); + switch (step) { + case 1: + return RunWithCapacityImpl<1>(f); + case 2: + return RunWithCapacityImpl<2>(f); + case 3: + return RunWithCapacityImpl<3>(f); + case 4: + return RunWithCapacityImpl<4>(f); + case 5: + return RunWithCapacityImpl<5>(f); + } + + assert(false && "Invalid capacity"); + } +}; + +// Calculates `10 * (*v) + carry` and stores the result in `*v` and returns +// the carry. +template <typename Int> +inline Int MultiplyBy10WithCarry(Int *v, Int carry) { + using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; + BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry; + *v = static_cast<Int>(tmp); + return static_cast<Int>(tmp >> (sizeof(Int) * 8)); +} + +// Calculates `(2^64 * carry + *v) / 10`. +// Stores the quotient in `*v` and returns the remainder. +// Requires: `0 <= carry <= 9` +inline uint64_t DivideBy10WithCarry(uint64_t *v, uint64_t carry) { + constexpr uint64_t divisor = 10; + // 2^64 / divisor = chunk_quotient + chunk_remainder / divisor + constexpr uint64_t chunk_quotient = (uint64_t{1} << 63) / (divisor / 2); + constexpr uint64_t chunk_remainder = uint64_t{} - chunk_quotient * divisor; + + const uint64_t mod = *v % divisor; + const uint64_t next_carry = chunk_remainder * carry + mod; + *v = *v / divisor + carry * chunk_quotient + next_carry / divisor; + return next_carry % divisor; +} + +using MaxFloatType = + typename std::conditional<IsDoubleDouble(), double, long double>::type; + +// Generates the decimal representation for an integer of the form `v * 2^exp`, +// where `v` and `exp` are both positive integers. +// It generates the digits from the left (ie the most significant digit first) +// to allow for direct printing into the sink. +// +// Requires `0 <= exp` and `exp <= numeric_limits<MaxFloatType>::max_exponent`. +class BinaryToDecimal { + static constexpr int ChunksNeeded(int exp) { + // We will left shift a uint128 by `exp` bits, so we need `128+exp` total + // bits. Round up to 32. + // See constructor for details about adding `10%` to the value. + return (128 + exp + 31) / 32 * 11 / 10; + } + + public: + // Run the conversion for `v * 2^exp` and call `f(binary_to_decimal)`. + // This function will allocate enough stack space to perform the conversion. + static void RunConversion(uint128 v, int exp, + absl::FunctionRef<void(BinaryToDecimal)> f) { + assert(exp > 0); + assert(exp <= std::numeric_limits<MaxFloatType>::max_exponent); + static_assert( + static_cast<int>(StackArray::kMaxCapacity) >= + ChunksNeeded(std::numeric_limits<MaxFloatType>::max_exponent), + ""); + + StackArray::RunWithCapacity( + ChunksNeeded(exp), + [=](absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); }); + } + + int TotalDigits() const { + return static_cast<int>((decimal_end_ - decimal_start_) * kDigitsPerChunk + + CurrentDigits().size()); + } + + // See the current block of digits. + absl::string_view CurrentDigits() const { + return absl::string_view(digits_ + kDigitsPerChunk - size_, size_); + } + + // Advance the current view of digits. + // Returns `false` when no more digits are available. + bool AdvanceDigits() { + if (decimal_start_ >= decimal_end_) return false; + + uint32_t w = data_[decimal_start_++]; + for (size_ = 0; size_ < kDigitsPerChunk; w /= 10) { + digits_[kDigitsPerChunk - ++size_] = w % 10 + '0'; + } + return true; + } + + private: + BinaryToDecimal(absl::Span<uint32_t> data, uint128 v, int exp) : data_(data) { + // We need to print the digits directly into the sink object without + // buffering them all first. To do this we need two things: + // - to know the total number of digits to do padding when necessary + // - to generate the decimal digits from the left. + // + // In order to do this, we do a two pass conversion. + // On the first pass we convert the binary representation of the value into + // a decimal representation in which each uint32_t chunk holds up to 9 + // decimal digits. In the second pass we take each decimal-holding-uint32_t + // value and generate the ascii decimal digits into `digits_`. + // + // The binary and decimal representations actually share the same memory + // region. As we go converting the chunks from binary to decimal we free + // them up and reuse them for the decimal representation. One caveat is that + // the decimal representation is around 7% less efficient in space than the + // binary one. We allocate an extra 10% memory to account for this. See + // ChunksNeeded for this calculation. + int chunk_index = exp / 32; + decimal_start_ = decimal_end_ = ChunksNeeded(exp); + const int offset = exp % 32; + // Left shift v by exp bits. + data_[chunk_index] = static_cast<uint32_t>(v << offset); + for (v >>= (32 - offset); v; v >>= 32) + data_[++chunk_index] = static_cast<uint32_t>(v); + + while (chunk_index >= 0) { + // While we have more than one chunk available, go in steps of 1e9. + // `data_[chunk_index]` holds the highest non-zero binary chunk, so keep + // the variable updated. + uint32_t carry = 0; + for (int i = chunk_index; i >= 0; --i) { + uint64_t tmp = uint64_t{data_[i]} + (uint64_t{carry} << 32); + data_[i] = static_cast<uint32_t>(tmp / uint64_t{1000000000}); + carry = static_cast<uint32_t>(tmp % uint64_t{1000000000}); + } + + // If the highest chunk is now empty, remove it from view. + if (data_[chunk_index] == 0) --chunk_index; + + --decimal_start_; + assert(decimal_start_ != chunk_index); + data_[decimal_start_] = carry; + } + + // Fill the first set of digits. The first chunk might not be complete, so + // handle differently. + for (uint32_t first = data_[decimal_start_++]; first != 0; first /= 10) { + digits_[kDigitsPerChunk - ++size_] = first % 10 + '0'; + } + } + + private: + static constexpr int kDigitsPerChunk = 9; + + int decimal_start_; + int decimal_end_; + + char digits_[kDigitsPerChunk]; + int size_ = 0; + + absl::Span<uint32_t> data_; +}; + +// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits. +// Requires `-exp < 0` and +// `-exp >= limits<MaxFloatType>::min_exponent - limits<MaxFloatType>::digits`. +class FractionalDigitGenerator { + public: + // Run the conversion for `v * 2^exp` and call `f(generator)`. + // This function will allocate enough stack space to perform the conversion. + static void RunConversion( + uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) { + using Limits = std::numeric_limits<MaxFloatType>; + assert(-exp < 0); + assert(-exp >= Limits::min_exponent - 128); + static_assert(StackArray::kMaxCapacity >= + (Limits::digits + 128 - Limits::min_exponent + 31) / 32, + ""); + StackArray::RunWithCapacity((Limits::digits + exp + 31) / 32, + [=](absl::Span<uint32_t> input) { + f(FractionalDigitGenerator(input, v, exp)); + }); + } + + // Returns true if there are any more non-zero digits left. + bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; } + + // Returns true if the remainder digits are greater than 5000... + bool IsGreaterThanHalf() const { + return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0); + } + // Returns true if the remainder digits are exactly 5000... + bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; } + + struct Digits { + int digit_before_nine; + int num_nines; + }; + + // Get the next set of digits. + // They are composed by a non-9 digit followed by a runs of zero or more 9s. + Digits GetDigits() { + Digits digits{next_digit_, 0}; + + next_digit_ = GetOneDigit(); + while (next_digit_ == 9) { + ++digits.num_nines; + next_digit_ = GetOneDigit(); + } + + return digits; + } + + private: + // Return the next digit. + int GetOneDigit() { + if (chunk_index_ < 0) return 0; + + uint32_t carry = 0; + for (int i = chunk_index_; i >= 0; --i) { + carry = MultiplyBy10WithCarry(&data_[i], carry); + } + // If the lowest chunk is now empty, remove it from view. + if (data_[chunk_index_] == 0) --chunk_index_; + return carry; + } + + FractionalDigitGenerator(absl::Span<uint32_t> data, uint128 v, int exp) + : chunk_index_(exp / 32), data_(data) { + const int offset = exp % 32; + // Right shift `v` by `exp` bits. + data_[chunk_index_] = static_cast<uint32_t>(v << (32 - offset)); + v >>= offset; + // Make sure we don't overflow the data. We already calculated that + // non-zero bits fit, so we might not have space for leading zero bits. + for (int pos = chunk_index_; v; v >>= 32) + data_[--pos] = static_cast<uint32_t>(v); + + // Fill next_digit_, as GetDigits expects it to be populated always. + next_digit_ = GetOneDigit(); + } + + int next_digit_; + int chunk_index_; + absl::Span<uint32_t> data_; +}; + +// Count the number of leading zero bits. +int LeadingZeros(uint64_t v) { return countl_zero(v); } +int LeadingZeros(uint128 v) { + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + return high != 0 ? countl_zero(high) : 64 + countl_zero(low); +} + +// Round up the text digits starting at `p`. +// The buffer must have an extra digit that is known to not need rounding. +// This is done below by having an extra '0' digit on the left. +void RoundUp(char *p) { + while (*p == '9' || *p == '.') { + if (*p == '9') *p = '0'; + --p; + } + ++*p; +} + +// Check the previous digit and round up or down to follow the round-to-even +// policy. +void RoundToEven(char *p) { + if (*p == '.') --p; + if (*p % 2 == 1) RoundUp(p); +} + +// Simple integral decimal digit printing for values that fit in 64-bits. +// Returns the pointer to the last written digit. +char *PrintIntegralDigitsFromRightFast(uint64_t v, char *p) { + do { + *--p = DivideBy10WithCarry(&v, 0) + '0'; + } while (v != 0); + return p; +} + +// Simple integral decimal digit printing for values that fit in 128-bits. +// Returns the pointer to the last written digit. +char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) { + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + + while (high != 0) { + uint64_t carry = DivideBy10WithCarry(&high, 0); + carry = DivideBy10WithCarry(&low, carry); + *--p = carry + '0'; + } + return PrintIntegralDigitsFromRightFast(low, p); +} + +// Simple fractional decimal digit printing for values that fir in 64-bits after +// shifting. +// Performs rounding if necessary to fit within `precision`. +// Returns the pointer to one after the last character written. +char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp, + int precision) { + char *p = start; + v <<= (64 - exp); + while (precision > 0) { + if (!v) return p; + *p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0'; + --precision; + } + + // We need to round. + if (v < 0x8000000000000000) { + // We round down, so nothing to do. + } else if (v > 0x8000000000000000) { + // We round up. + RoundUp(p - 1); + } else { + RoundToEven(p - 1); + } + + assert(precision == 0); + // Precision can only be zero here. + return p; +} + +// Simple fractional decimal digit printing for values that fir in 128-bits +// after shifting. +// Performs rounding if necessary to fit within `precision`. +// Returns the pointer to one after the last character written. +char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, + int precision) { + char *p = start; + v <<= (128 - exp); + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + + // While we have digits to print and `low` is not empty, do the long + // multiplication. + while (precision > 0 && low != 0) { + uint64_t carry = MultiplyBy10WithCarry(&low, uint64_t{0}); + carry = MultiplyBy10WithCarry(&high, carry); + + *p++ = carry + '0'; + --precision; + } + + // Now `low` is empty, so use a faster approach for the rest of the digits. + // This block is pretty much the same as the main loop for the 64-bit case + // above. + while (precision > 0) { + if (!high) return p; + *p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0'; + --precision; + } + + // We need to round. + if (high < 0x8000000000000000) { + // We round down, so nothing to do. + } else if (high > 0x8000000000000000 || low != 0) { + // We round up. + RoundUp(p - 1); + } else { + RoundToEven(p - 1); + } + + assert(precision == 0); + // Precision can only be zero here. + return p; +} + +struct FormatState { + char sign_char; + int precision; + const FormatConversionSpecImpl &conv; + FormatSinkImpl *sink; + + // In `alt` mode (flag #) we keep the `.` even if there are no fractional + // digits. In non-alt mode, we strip it. + bool ShouldPrintDot() const { return precision != 0 || conv.has_alt_flag(); } +}; + +struct Padding { + int left_spaces; + int zeros; + int right_spaces; +}; + +Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { + if (state.conv.width() < 0 || + static_cast<size_t>(state.conv.width()) <= total_size) { + return {0, 0, 0}; + } + int missing_chars = state.conv.width() - total_size; + if (state.conv.has_left_flag()) { + return {0, 0, missing_chars}; + } else if (state.conv.has_zero_flag()) { + return {0, missing_chars, 0}; + } else { + return {missing_chars, 0, 0}; + } +} + +void FinalPrint(const FormatState &state, absl::string_view data, + int padding_offset, int trailing_zeros, + absl::string_view data_postfix) { + if (state.conv.width() < 0) { + // No width specified. Fast-path. + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(data); + state.sink->Append(trailing_zeros, '0'); + state.sink->Append(data_postfix); + return; + } + + auto padding = ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + + data.size() + data_postfix.size() + + static_cast<size_t>(trailing_zeros), + state); + + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + // Padding in general needs to be inserted somewhere in the middle of `data`. + state.sink->Append(data.substr(0, padding_offset)); + state.sink->Append(padding.zeros, '0'); + state.sink->Append(data.substr(padding_offset)); + state.sink->Append(trailing_zeros, '0'); + state.sink->Append(data_postfix); + state.sink->Append(padding.right_spaces, ' '); +} + +// Fastpath %f formatter for when the shifted value fits in a simple integral +// type. +// Prints `v*2^exp` with the options from `state`. +template <typename Int> +void FormatFFast(Int v, int exp, const FormatState &state) { + constexpr int input_bits = sizeof(Int) * 8; + + static constexpr size_t integral_size = + /* in case we need to round up an extra digit */ 1 + + /* decimal digits for uint128 */ 40 + 1; + char buffer[integral_size + /* . */ 1 + /* max digits uint128 */ 128]; + buffer[integral_size] = '.'; + char *const integral_digits_end = buffer + integral_size; + char *integral_digits_start; + char *const fractional_digits_start = buffer + integral_size + 1; + char *fractional_digits_end = fractional_digits_start; + + if (exp >= 0) { + const int total_bits = input_bits - LeadingZeros(v) + exp; + integral_digits_start = + total_bits <= 64 + ? PrintIntegralDigitsFromRightFast(static_cast<uint64_t>(v) << exp, + integral_digits_end) + : PrintIntegralDigitsFromRightFast(static_cast<uint128>(v) << exp, + integral_digits_end); + } else { + exp = -exp; + + integral_digits_start = PrintIntegralDigitsFromRightFast( + exp < input_bits ? v >> exp : 0, integral_digits_end); + // PrintFractionalDigits may pull a carried 1 all the way up through the + // integral portion. + integral_digits_start[-1] = '0'; + + fractional_digits_end = + exp <= 64 ? PrintFractionalDigitsFast(v, fractional_digits_start, exp, + state.precision) + : PrintFractionalDigitsFast(static_cast<uint128>(v), + fractional_digits_start, exp, + state.precision); + // There was a carry, so include the first digit too. + if (integral_digits_start[-1] != '0') --integral_digits_start; + } + + size_t size = fractional_digits_end - integral_digits_start; + + // In `alt` mode (flag #) we keep the `.` even if there are no fractional + // digits. In non-alt mode, we strip it. + if (!state.ShouldPrintDot()) --size; + FinalPrint(state, absl::string_view(integral_digits_start, size), + /*padding_offset=*/0, + static_cast<int>(state.precision - (fractional_digits_end - + fractional_digits_start)), + /*data_postfix=*/""); +} + +// Slow %f formatter for when the shifted value does not fit in a uint128, and +// `exp > 0`. +// Prints `v*2^exp` with the options from `state`. +// This one is guaranteed to not have fractional digits, so we don't have to +// worry about anything after the `.`. +void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) { + BinaryToDecimal::RunConversion(v, exp, [&](BinaryToDecimal btd) { + const size_t total_digits = + btd.TotalDigits() + + (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); + + const auto padding = ExtraWidthToPadding( + total_digits + (state.sign_char != '\0' ? 1 : 0), state); + + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(padding.zeros, '0'); + + do { + state.sink->Append(btd.CurrentDigits()); + } while (btd.AdvanceDigits()); + + if (state.ShouldPrintDot()) state.sink->Append(1, '.'); + state.sink->Append(state.precision, '0'); + state.sink->Append(padding.right_spaces, ' '); + }); +} + +// Slow %f formatter for when the shifted value does not fit in a uint128, and +// `exp < 0`. +// Prints `v*2^exp` with the options from `state`. +// This one is guaranteed to be < 1.0, so we don't have to worry about integral +// digits. +void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) { + const size_t total_digits = + /* 0 */ 1 + + (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); + auto padding = + ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state); + padding.zeros += 1; + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(padding.zeros, '0'); + + if (state.ShouldPrintDot()) state.sink->Append(1, '.'); + + // Print digits + int digits_to_go = state.precision; + + FractionalDigitGenerator::RunConversion( + v, exp, [&](FractionalDigitGenerator digit_gen) { + // There are no digits to print here. + if (state.precision == 0) return; + + // We go one digit at a time, while keeping track of runs of nines. + // The runs of nines are used to perform rounding when necessary. + + while (digits_to_go > 0 && digit_gen.HasMoreDigits()) { + auto digits = digit_gen.GetDigits(); + + // Now we have a digit and a run of nines. + // See if we can print them all. + if (digits.num_nines + 1 < digits_to_go) { + // We don't have to round yet, so print them. + state.sink->Append(1, digits.digit_before_nine + '0'); + state.sink->Append(digits.num_nines, '9'); + digits_to_go -= digits.num_nines + 1; + + } else { + // We can't print all the nines, see where we have to truncate. + + bool round_up = false; + if (digits.num_nines + 1 > digits_to_go) { + // We round up at a nine. No need to print them. + round_up = true; + } else { + // We can fit all the nines, but truncate just after it. + if (digit_gen.IsGreaterThanHalf()) { + round_up = true; + } else if (digit_gen.IsExactlyHalf()) { + // Round to even + round_up = + digits.num_nines != 0 || digits.digit_before_nine % 2 == 1; + } + } + + if (round_up) { + state.sink->Append(1, digits.digit_before_nine + '1'); + --digits_to_go; + // The rest will be zeros. + } else { + state.sink->Append(1, digits.digit_before_nine + '0'); + state.sink->Append(digits_to_go - 1, '9'); + digits_to_go = 0; + } + return; + } + } + }); + + state.sink->Append(digits_to_go, '0'); + state.sink->Append(padding.right_spaces, ' '); +} + +template <typename Int> +void FormatF(Int mantissa, int exp, const FormatState &state) { + if (exp >= 0) { + const int total_bits = sizeof(Int) * 8 - LeadingZeros(mantissa) + exp; + + // Fallback to the slow stack-based approach if we can't do it in a 64 or + // 128 bit state. + if (ABSL_PREDICT_FALSE(total_bits > 128)) { + return FormatFPositiveExpSlow(mantissa, exp, state); + } + } else { + // Fallback to the slow stack-based approach if we can't do it in a 64 or + // 128 bit state. + if (ABSL_PREDICT_FALSE(exp < -128)) { + return FormatFNegativeExpSlow(mantissa, -exp, state); + } + } + return FormatFFast(mantissa, exp, state); +} + +// Grab the group of four bits (nibble) from `n`. E.g., nibble 1 corresponds to +// bits 4-7. +template <typename Int> +uint8_t GetNibble(Int n, int nibble_index) { + constexpr Int mask_low_nibble = Int{0xf}; + int shift = nibble_index * 4; + n &= mask_low_nibble << shift; + return static_cast<uint8_t>((n >> shift) & 0xf); +} + +// Add one to the given nibble, applying carry to higher nibbles. Returns true +// if overflow, false otherwise. +template <typename Int> +bool IncrementNibble(int nibble_index, Int *n) { + constexpr int kShift = sizeof(Int) * 8 - 1; + constexpr int kNumNibbles = sizeof(Int) * 8 / 4; + Int before = *n >> kShift; + // Here we essentially want to take the number 1 and move it into the requsted + // nibble, then add it to *n to effectively increment the nibble. However, + // ASan will complain if we try to shift the 1 beyond the limits of the Int, + // i.e., if the nibble_index is out of range. So therefore we check for this + // and if we are out of range we just add 0 which leaves *n unchanged, which + // seems like the reasonable thing to do in that case. + *n += ((nibble_index >= kNumNibbles) ? 0 : (Int{1} << (nibble_index * 4))); + Int after = *n >> kShift; + return (before && !after) || (nibble_index >= kNumNibbles); +} + +// Return a mask with 1's in the given nibble and all lower nibbles. +template <typename Int> +Int MaskUpToNibbleInclusive(int nibble_index) { + constexpr int kNumNibbles = sizeof(Int) * 8 / 4; + static const Int ones = ~Int{0}; + return ones >> std::max(0, 4 * (kNumNibbles - nibble_index - 1)); +} + +// Return a mask with 1's below the given nibble. +template <typename Int> +Int MaskUpToNibbleExclusive(int nibble_index) { + return nibble_index <= 0 ? 0 : MaskUpToNibbleInclusive<Int>(nibble_index - 1); +} + +template <typename Int> +Int MoveToNibble(uint8_t nibble, int nibble_index) { + return Int{nibble} << (4 * nibble_index); +} + +// Given mantissa size, find optimal # of mantissa bits to put in initial digit. +// +// In the hex representation we keep a single hex digit to the left of the dot. +// However, the question as to how many bits of the mantissa should be put into +// that hex digit in theory is arbitrary, but in practice it is optimal to +// choose based on the size of the mantissa. E.g., for a `double`, there are 53 +// mantissa bits, so that means that we should put 1 bit to the left of the dot, +// thereby leaving 52 bits to the right, which is evenly divisible by four and +// thus all fractional digits represent actual precision. For a `long double`, +// on the other hand, there are 64 bits of mantissa, thus we can use all four +// bits for the initial hex digit and still have a number left over (60) that is +// a multiple of four. Once again, the goal is to have all fractional digits +// represent real precision. +template <typename Float> +constexpr int HexFloatLeadingDigitSizeInBits() { + return std::numeric_limits<Float>::digits % 4 > 0 + ? std::numeric_limits<Float>::digits % 4 + : 4; +} + +// This function captures the rounding behavior of glibc for hex float +// representations. E.g. when rounding 0x1.ab800000 to a precision of .2 +// ("%.2a") glibc will round up because it rounds toward the even number (since +// 0xb is an odd number, it will round up to 0xc). However, when rounding at a +// point that is not followed by 800000..., it disregards the parity and rounds +// up if > 8 and rounds down if < 8. +template <typename Int> +bool HexFloatNeedsRoundUp(Int mantissa, int final_nibble_displayed, + uint8_t leading) { + // If the last nibble (hex digit) to be displayed is the lowest on in the + // mantissa then that means that we don't have any further nibbles to inform + // rounding, so don't round. + if (final_nibble_displayed <= 0) { + return false; + } + int rounding_nibble_idx = final_nibble_displayed - 1; + constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; + assert(final_nibble_displayed <= kTotalNibbles); + Int mantissa_up_to_rounding_nibble_inclusive = + mantissa & MaskUpToNibbleInclusive<Int>(rounding_nibble_idx); + Int eight = MoveToNibble<Int>(8, rounding_nibble_idx); + if (mantissa_up_to_rounding_nibble_inclusive != eight) { + return mantissa_up_to_rounding_nibble_inclusive > eight; + } + // Nibble in question == 8. + uint8_t round_if_odd = (final_nibble_displayed == kTotalNibbles) + ? leading + : GetNibble(mantissa, final_nibble_displayed); + return round_if_odd % 2 == 1; +} + +// Stores values associated with a Float type needed by the FormatA +// implementation in order to avoid templatizing that function by the Float +// type. +struct HexFloatTypeParams { + template <typename Float> + explicit HexFloatTypeParams(Float) + : min_exponent(std::numeric_limits<Float>::min_exponent - 1), + leading_digit_size_bits(HexFloatLeadingDigitSizeInBits<Float>()) { + assert(leading_digit_size_bits >= 1 && leading_digit_size_bits <= 4); + } + + int min_exponent; + int leading_digit_size_bits; +}; + +// Hex Float Rounding. First check if we need to round; if so, then we do that +// by manipulating (incrementing) the mantissa, that way we can later print the +// mantissa digits by iterating through them in the same way regardless of +// whether a rounding happened. +template <typename Int> +void FormatARound(bool precision_specified, const FormatState &state, + uint8_t *leading, Int *mantissa, int *exp) { + constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; + // Index of the last nibble that we could display given precision. + int final_nibble_displayed = + precision_specified ? std::max(0, (kTotalNibbles - state.precision)) : 0; + if (HexFloatNeedsRoundUp(*mantissa, final_nibble_displayed, *leading)) { + // Need to round up. + bool overflow = IncrementNibble(final_nibble_displayed, mantissa); + *leading += (overflow ? 1 : 0); + if (ABSL_PREDICT_FALSE(*leading > 15)) { + // We have overflowed the leading digit. This would mean that we would + // need two hex digits to the left of the dot, which is not allowed. So + // adjust the mantissa and exponent so that the result is always 1.0eXXX. + *leading = 1; + *mantissa = 0; + *exp += 4; + } + } + // Now that we have handled a possible round-up we can go ahead and zero out + // all the nibbles of the mantissa that we won't need. + if (precision_specified) { + *mantissa &= ~MaskUpToNibbleExclusive<Int>(final_nibble_displayed); + } +} + +template <typename Int> +void FormatANormalize(const HexFloatTypeParams float_traits, uint8_t *leading, + Int *mantissa, int *exp) { + constexpr int kIntBits = sizeof(Int) * 8; + static const Int kHighIntBit = Int{1} << (kIntBits - 1); + const int kLeadDigitBitsCount = float_traits.leading_digit_size_bits; + // Normalize mantissa so that highest bit set is in MSB position, unless we + // get interrupted by the exponent threshold. + while (*mantissa && !(*mantissa & kHighIntBit)) { + if (ABSL_PREDICT_FALSE(*exp - 1 < float_traits.min_exponent)) { + *mantissa >>= (float_traits.min_exponent - *exp); + *exp = float_traits.min_exponent; + return; + } + *mantissa <<= 1; + --*exp; + } + // Extract bits for leading digit then shift them away leaving the + // fractional part. + *leading = + static_cast<uint8_t>(*mantissa >> (kIntBits - kLeadDigitBitsCount)); + *exp -= (*mantissa != 0) ? kLeadDigitBitsCount : *exp; + *mantissa <<= kLeadDigitBitsCount; +} + +template <typename Int> +void FormatA(const HexFloatTypeParams float_traits, Int mantissa, int exp, + bool uppercase, const FormatState &state) { + // Int properties. + constexpr int kIntBits = sizeof(Int) * 8; + constexpr int kTotalNibbles = sizeof(Int) * 8 / 4; + // Did the user specify a precision explicitly? + const bool precision_specified = state.conv.precision() >= 0; + + // ========== Normalize/Denormalize ========== + exp += kIntBits; // make all digits fractional digits. + // This holds the (up to four) bits of leading digit, i.e., the '1' in the + // number 0x1.e6fp+2. It's always > 0 unless number is zero or denormal. + uint8_t leading = 0; + FormatANormalize(float_traits, &leading, &mantissa, &exp); + + // =============== Rounding ================== + // Check if we need to round; if so, then we do that by manipulating + // (incrementing) the mantissa before beginning to print characters. + FormatARound(precision_specified, state, &leading, &mantissa, &exp); + + // ============= Format Result =============== + // This buffer holds the "0x1.ab1de3" portion of "0x1.ab1de3pe+2". Compute the + // size with long double which is the largest of the floats. + constexpr size_t kBufSizeForHexFloatRepr = + 2 // 0x + + std::numeric_limits<MaxFloatType>::digits / 4 // number of hex digits + + 1 // round up + + 1; // "." (dot) + char digits_buffer[kBufSizeForHexFloatRepr]; + char *digits_iter = digits_buffer; + const char *const digits = + static_cast<const char *>("0123456789ABCDEF0123456789abcdef") + + (uppercase ? 0 : 16); + + // =============== Hex Prefix ================ + *digits_iter++ = '0'; + *digits_iter++ = uppercase ? 'X' : 'x'; + + // ========== Non-Fractional Digit =========== + *digits_iter++ = digits[leading]; + + // ================== Dot ==================== + // There are three reasons we might need a dot. Keep in mind that, at this + // point, the mantissa holds only the fractional part. + if ((precision_specified && state.precision > 0) || + (!precision_specified && mantissa > 0) || state.conv.has_alt_flag()) { + *digits_iter++ = '.'; + } + + // ============ Fractional Digits ============ + int digits_emitted = 0; + while (mantissa > 0) { + *digits_iter++ = digits[GetNibble(mantissa, kTotalNibbles - 1)]; + mantissa <<= 4; + ++digits_emitted; + } + int trailing_zeros = + precision_specified ? state.precision - digits_emitted : 0; + assert(trailing_zeros >= 0); + auto digits_result = string_view(digits_buffer, digits_iter - digits_buffer); + + // =============== Exponent ================== + constexpr size_t kBufSizeForExpDecRepr = + numbers_internal::kFastToBufferSize // requred for FastIntToBuffer + + 1 // 'p' or 'P' + + 1; // '+' or '-' + char exp_buffer[kBufSizeForExpDecRepr]; + exp_buffer[0] = uppercase ? 'P' : 'p'; + exp_buffer[1] = exp >= 0 ? '+' : '-'; + numbers_internal::FastIntToBuffer(exp < 0 ? -exp : exp, exp_buffer + 2); + + // ============ Assemble Result ============== + FinalPrint(state, // + digits_result, // 0xN.NNN... + 2, // offset in `data` to start padding if needed. + trailing_zeros, // num remaining mantissa padding zeros + exp_buffer); // exponent +} + +char *CopyStringTo(absl::string_view v, char *out) { std::memcpy(out, v.data(), v.size()); return out + v.size(); } template <typename Float> -bool FallbackToSnprintf(const Float v, const ConversionSpec &conv, +bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { int w = conv.width() >= 0 ? conv.width() : 0; int p = conv.precision() >= 0 ? conv.precision() : -1; @@ -33,17 +951,17 @@ bool FallbackToSnprintf(const Float v, const ConversionSpec &conv, if (std::is_same<long double, Float>()) { *fp++ = 'L'; } - *fp++ = FormatConversionCharToChar(conv.conv()); + *fp++ = FormatConversionCharToChar(conv.conversion_char()); *fp = 0; assert(fp < fmt + sizeof(fmt)); } std::string space(512, '\0'); - string_view result; + absl::string_view result; while (true) { int n = snprintf(&space[0], space.size(), fmt, w, p, v); if (n < 0) return false; if (static_cast<size_t>(n) < space.size()) { - result = string_view(space.data(), n); + result = absl::string_view(space.data(), n); break; } space.resize(n + 1); @@ -96,21 +1014,24 @@ enum class FormatStyle { Fixed, Precision }; // Otherwise, return false. template <typename Float> bool ConvertNonNumericFloats(char sign_char, Float v, - const ConversionSpec &conv, FormatSinkImpl *sink) { + const FormatConversionSpecImpl &conv, + FormatSinkImpl *sink) { char text[4], *ptr = text; - if (sign_char) *ptr++ = sign_char; + if (sign_char != '\0') *ptr++ = sign_char; if (std::isnan(v)) { - ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan", - 3, ptr); + ptr = std::copy_n( + FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, + ptr); } else if (std::isinf(v)) { - ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf", - 3, ptr); + ptr = std::copy_n( + FormatConversionCharIsUpper(conv.conversion_char()) ? "INF" : "inf", 3, + ptr); } else { return false; } return sink->PutPaddedString(string_view(text, ptr - text), conv.width(), -1, - conv.flags().left); + conv.has_left_flag()); } // Round up the last digit of the value. @@ -170,7 +1091,12 @@ constexpr bool CanFitMantissa() { template <typename Float> struct Decomposed { - Float mantissa; + using MantissaType = + absl::conditional_t<std::is_same<long double, Float>::value, uint128, + uint64_t>; + static_assert(std::numeric_limits<Float>::digits <= sizeof(MantissaType) * 8, + ""); + MantissaType mantissa; int exponent; }; @@ -181,7 +1107,8 @@ Decomposed<Float> Decompose(Float v) { Float m = std::frexp(v, &exp); m = std::ldexp(m, std::numeric_limits<Float>::digits); exp -= std::numeric_limits<Float>::digits; - return {m, exp}; + + return {static_cast<typename Decomposed<Float>::MantissaType>(m), exp}; } // Print 'digits' as decimal. @@ -350,31 +1277,32 @@ bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out, return false; } -void WriteBufferToSink(char sign_char, string_view str, - const ConversionSpec &conv, FormatSinkImpl *sink) { +void WriteBufferToSink(char sign_char, absl::string_view str, + const FormatConversionSpecImpl &conv, + FormatSinkImpl *sink) { int left_spaces = 0, zeros = 0, right_spaces = 0; int missing_chars = conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) - static_cast<int>(sign_char != 0), 0) : 0; - if (conv.flags().left) { + if (conv.has_left_flag()) { right_spaces = missing_chars; - } else if (conv.flags().zero) { + } else if (conv.has_zero_flag()) { zeros = missing_chars; } else { left_spaces = missing_chars; } sink->Append(left_spaces, ' '); - if (sign_char) sink->Append(1, sign_char); + if (sign_char != '\0') sink->Append(1, sign_char); sink->Append(zeros, '0'); sink->Append(str); sink->Append(right_spaces, ' '); } template <typename Float> -bool FloatToSink(const Float v, const ConversionSpec &conv, +bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { // Print the sign or the sign column. Float abs_v = v; @@ -382,9 +1310,9 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, if (std::signbit(abs_v)) { sign_char = '-'; abs_v = -abs_v; - } else if (conv.flags().show_pos) { + } else if (conv.has_show_pos_flag()) { sign_char = '+'; - } else if (conv.flags().sign_col) { + } else if (conv.has_sign_col_flag()) { sign_char = ' '; } @@ -401,89 +1329,91 @@ bool FloatToSink(const Float v, const ConversionSpec &conv, Buffer buffer; - switch (conv.conv()) { - case ConversionChar::f: - case ConversionChar::F: - if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer, - nullptr)) { - return FallbackToSnprintf(v, conv, sink); - } - if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); - break; - - case ConversionChar::e: - case ConversionChar::E: - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); - } - if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back(); - PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', - &buffer); - break; - - case ConversionChar::g: - case ConversionChar::G: - precision = std::max(0, precision - 1); - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); - } - if (precision + 1 > exp && exp >= -4) { - if (exp < 0) { - // Have 1.23456, needs 0.00123456 - // Move the first digit - buffer.begin[1] = *buffer.begin; - // Add some zeros - for (; exp < -1; ++exp) *buffer.begin-- = '0'; - *buffer.begin-- = '.'; - *buffer.begin = '0'; - } else if (exp > 0) { - // Have 1.23456, needs 1234.56 - // Move the '.' exp positions to the right. - std::rotate(buffer.begin + 1, buffer.begin + 2, - buffer.begin + exp + 2); - } - exp = 0; - } - if (!conv.flags().alt) { - while (buffer.back() == '0') buffer.pop_back(); - if (buffer.back() == '.') buffer.pop_back(); - } - if (exp) { - PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e', - &buffer); - } - break; + FormatConversionChar c = conv.conversion_char(); - case ConversionChar::a: - case ConversionChar::A: + if (c == FormatConversionCharInternal::f || + c == FormatConversionCharInternal::F) { + FormatF(decomposed.mantissa, decomposed.exponent, + {sign_char, precision, conv, sink}); + return true; + } else if (c == FormatConversionCharInternal::e || + c == FormatConversionCharInternal::E) { + if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, + &exp)) { return FallbackToSnprintf(v, conv, sink); - - default: - return false; + } + if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); + PrintExponent( + exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', + &buffer); + } else if (c == FormatConversionCharInternal::g || + c == FormatConversionCharInternal::G) { + precision = std::max(0, precision - 1); + if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, + &exp)) { + return FallbackToSnprintf(v, conv, sink); + } + if (precision + 1 > exp && exp >= -4) { + if (exp < 0) { + // Have 1.23456, needs 0.00123456 + // Move the first digit + buffer.begin[1] = *buffer.begin; + // Add some zeros + for (; exp < -1; ++exp) *buffer.begin-- = '0'; + *buffer.begin-- = '.'; + *buffer.begin = '0'; + } else if (exp > 0) { + // Have 1.23456, needs 1234.56 + // Move the '.' exp positions to the right. + std::rotate(buffer.begin + 1, buffer.begin + 2, buffer.begin + exp + 2); + } + exp = 0; + } + if (!conv.has_alt_flag()) { + while (buffer.back() == '0') buffer.pop_back(); + if (buffer.back() == '.') buffer.pop_back(); + } + if (exp) { + PrintExponent( + exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', + &buffer); + } + } else if (c == FormatConversionCharInternal::a || + c == FormatConversionCharInternal::A) { + bool uppercase = (c == FormatConversionCharInternal::A); + FormatA(HexFloatTypeParams(Float{}), decomposed.mantissa, + decomposed.exponent, uppercase, {sign_char, precision, conv, sink}); + return true; + } else { + return false; } WriteBufferToSink(sign_char, - string_view(buffer.begin, buffer.end - buffer.begin), conv, - sink); + absl::string_view(buffer.begin, buffer.end - buffer.begin), + conv, sink); return true; } } // namespace -bool ConvertFloatImpl(long double v, const ConversionSpec &conv, +bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { + if (IsDoubleDouble()) { + // This is the `double-double` representation of `long double`. We do not + // handle it natively. Fallback to snprintf. + return FallbackToSnprintf(v, conv, sink); + } + return FloatToSink(v, conv, sink); } -bool ConvertFloatImpl(float v, const ConversionSpec &conv, +bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { - return FloatToSink(v, conv, sink); + return FloatToSink(static_cast<double>(v), conv, sink); } -bool ConvertFloatImpl(double v, const ConversionSpec &conv, +bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { return FloatToSink(v, conv, sink); } diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.h b/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.h index 49a6a63630..71100e7142 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/float_conversion.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ @@ -7,13 +21,13 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -bool ConvertFloatImpl(float v, const ConversionSpec &conv, +bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); -bool ConvertFloatImpl(double v, const ConversionSpec &conv, +bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); -bool ConvertFloatImpl(long double v, const ConversionSpec &conv, +bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); } // namespace str_format_internal diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/output.h b/third_party/abseil-cpp/absl/strings/internal/str_format/output.h index 28b288b7dd..8030dae00f 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/output.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/output.h @@ -30,9 +30,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN - -class Cord; - namespace str_format_internal { // RawSink implementation that writes into a char* buffer. @@ -77,12 +74,6 @@ inline void AbslFormatFlush(std::ostream* out, string_view s) { out->write(s.data(), s.size()); } -template <class AbslCord, typename = typename std::enable_if< - std::is_same<AbslCord, absl::Cord>::value>::type> -inline void AbslFormatFlush(AbslCord* out, string_view s) { - out->Append(s); -} - inline void AbslFormatFlush(FILERawSink* sink, string_view v) { sink->Write(v); } @@ -91,10 +82,11 @@ inline void AbslFormatFlush(BufferRawSink* sink, string_view v) { sink->Write(v); } +// This is a SFINAE to get a better compiler error message when the type +// is not supported. template <typename T> -auto InvokeFlush(T* out, string_view s) - -> decltype(str_format_internal::AbslFormatFlush(out, s)) { - str_format_internal::AbslFormatFlush(out, s); +auto InvokeFlush(T* out, string_view s) -> decltype(AbslFormatFlush(out, s)) { + AbslFormatFlush(out, s); } } // namespace str_format_internal diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/output_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/output_test.cc index e54e6f70a5..ce2e91a0bb 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/output_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/output_test.cc @@ -19,6 +19,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/strings/cord.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -37,6 +38,12 @@ TEST(InvokeFlush, Stream) { EXPECT_EQ(str.str(), "ABCDEF"); } +TEST(InvokeFlush, Cord) { + absl::Cord str("ABC"); + str_format_internal::InvokeFlush(&str, "DEF"); + EXPECT_EQ(str, "ABCDEF"); +} + TEST(BufferRawSink, Limits) { char buf[16]; { @@ -70,4 +77,3 @@ TEST(BufferRawSink, Limits) { } // namespace ABSL_NAMESPACE_END } // namespace absl - diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/parser.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/parser.cc index aab68db94b..2c9c07dacc 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/parser.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/parser.cc @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/str_format/parser.h" #include <assert.h> @@ -17,63 +31,70 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -using CC = ConversionChar; +using CC = FormatConversionCharInternal; using LM = LengthMod; +// Abbreviations to fit in the table below. +constexpr auto f_sign = Flags::kSignCol; +constexpr auto f_alt = Flags::kAlt; +constexpr auto f_pos = Flags::kShowPos; +constexpr auto f_left = Flags::kLeft; +constexpr auto f_zero = Flags::kZero; + ABSL_CONST_INIT const ConvTag kTags[256] = { - {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 - {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f - {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 - {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f - {}, {}, {}, {}, {}, {}, {}, {}, // 20-27 - {}, {}, {}, {}, {}, {}, {}, {}, // 28-2f - {}, {}, {}, {}, {}, {}, {}, {}, // 30-37 - {}, {}, {}, {}, {}, {}, {}, {}, // 38-3f - {}, CC::A, {}, CC::C, {}, CC::E, CC::F, CC::G, // @ABCDEFG - {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO - {}, {}, {}, CC::S, {}, {}, {}, {}, // PQRSTUVW - CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ - {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg - LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno - CC::p, LM::q, {}, CC::s, LM::t, CC::u, {}, {}, // pqrstuvw - CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! - {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 - {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f - {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 - {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f - {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 - {}, {}, {}, {}, {}, {}, {}, {}, // a8-af - {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 - {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf - {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 - {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf - {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 - {}, {}, {}, {}, {}, {}, {}, {}, // d8-df - {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 - {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef - {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 - {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff + {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 + {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f + {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 + {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f + f_sign, {}, {}, f_alt, {}, {}, {}, {}, // !"#$%&' + {}, {}, {}, f_pos, {}, f_left, {}, {}, // ()*+,-./ + f_zero, {}, {}, {}, {}, {}, {}, {}, // 01234567 + {}, {}, {}, {}, {}, {}, {}, {}, // 89:;<=>? + {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG + {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO + {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW + CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ + {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg + LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno + CC::p, LM::q, {}, CC::s, LM::t, CC::u, {}, {}, // pqrstuvw + CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! + {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 + {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f + {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 + {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f + {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 + {}, {}, {}, {}, {}, {}, {}, {}, // a8-af + {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 + {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf + {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 + {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf + {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 + {}, {}, {}, {}, {}, {}, {}, {}, // d8-df + {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 + {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef + {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 + {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff }; namespace { bool CheckFastPathSetting(const UnboundConversion& conv) { - bool should_be_basic = !conv.flags.left && // - !conv.flags.show_pos && // - !conv.flags.sign_col && // - !conv.flags.alt && // - !conv.flags.zero && // - (conv.width.value() == -1) && - (conv.precision.value() == -1); - if (should_be_basic != conv.flags.basic) { + bool width_precision_needed = + conv.width.value() >= 0 || conv.precision.value() >= 0; + if (width_precision_needed && conv.flags == Flags::kBasic) { fprintf(stderr, "basic=%d left=%d show_pos=%d sign_col=%d alt=%d zero=%d " "width=%d precision=%d\n", - conv.flags.basic, conv.flags.left, conv.flags.show_pos, - conv.flags.sign_col, conv.flags.alt, conv.flags.zero, - conv.width.value(), conv.precision.value()); + conv.flags == Flags::kBasic ? 1 : 0, + FlagsContains(conv.flags, Flags::kLeft) ? 1 : 0, + FlagsContains(conv.flags, Flags::kShowPos) ? 1 : 0, + FlagsContains(conv.flags, Flags::kSignCol) ? 1 : 0, + FlagsContains(conv.flags, Flags::kAlt) ? 1 : 0, + FlagsContains(conv.flags, Flags::kZero) ? 1 : 0, conv.width.value(), + conv.precision.value()); + return false; } - return should_be_basic == conv.flags.basic; + return true; } template <bool is_positional> @@ -117,40 +138,21 @@ const char *ConsumeConversion(const char *pos, const char *const end, ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); // We should start with the basic flag on. - assert(conv->flags.basic); + assert(conv->flags == Flags::kBasic); // Any non alpha character makes this conversion not basic. // This includes flags (-+ #0), width (1-9, *) or precision (.). // All conversion characters and length modifiers are alpha characters. if (c < 'A') { - conv->flags.basic = false; - - for (; c <= '0';) { - // FIXME: We might be able to speed this up reusing the lookup table from - // above. It might require changing Flags to be a plain integer where we - // can |= a value. - switch (c) { - case '-': - conv->flags.left = true; - break; - case '+': - conv->flags.show_pos = true; - break; - case ' ': - conv->flags.sign_col = true; - break; - case '#': - conv->flags.alt = true; - break; - case '0': - conv->flags.zero = true; - break; - default: - goto flags_done; + while (c <= '0') { + auto tag = GetTagForChar(c); + if (tag.is_flags()) { + conv->flags = conv->flags | tag.as_flags(); + ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); + } else { + break; } - ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); } -flags_done: if (c <= '9') { if (c >= '0') { @@ -159,12 +161,12 @@ flags_done: if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr; // Positional conversion. *next_arg = -1; - conv->flags = Flags(); - conv->flags.basic = true; return ConsumeConversion<true>(original_pos, end, conv, next_arg); } + conv->flags = conv->flags | Flags::kNonBasic; conv->width.set_value(maybe_width); } else if (c == '*') { + conv->flags = conv->flags | Flags::kNonBasic; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (is_positional) { if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; @@ -178,6 +180,7 @@ flags_done: } if (c == '.') { + conv->flags = conv->flags | Flags::kNonBasic; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (std::isdigit(c)) { conv->precision.set_value(parse_digits()); @@ -296,15 +299,17 @@ struct ParsedFormatBase::ParsedFormatConsumer { char* data_pos; }; -ParsedFormatBase::ParsedFormatBase(string_view format, bool allow_ignored, - std::initializer_list<Conv> convs) +ParsedFormatBase::ParsedFormatBase( + string_view format, bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) : data_(format.empty() ? nullptr : new char[format.size()]) { has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) || !MatchesConversions(allow_ignored, convs); } bool ParsedFormatBase::MatchesConversions( - bool allow_ignored, std::initializer_list<Conv> convs) const { + bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) const { std::unordered_set<int> used; auto add_if_valid_conv = [&](int pos, char c) { if (static_cast<size_t>(pos) > convs.size() || diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/parser.h b/third_party/abseil-cpp/absl/strings/internal/str_format/parser.h index 45c90d1df0..ad8646edff 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/parser.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/parser.h @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ @@ -27,10 +41,7 @@ std::string LengthModToString(LengthMod v); // The analyzed properties of a single specified conversion. struct UnboundConversion { - UnboundConversion() - : flags() /* This is required to zero all the fields of flags. */ { - flags.basic = true; - } + UnboundConversion() {} class InputValue { public: @@ -65,9 +76,9 @@ struct UnboundConversion { InputValue width; InputValue precision; - Flags flags; + Flags flags = Flags::kBasic; LengthMod length_mod = LengthMod::none; - ConversionChar conv = FormatConversionChar::kNone; + FormatConversionChar conv = FormatConversionCharInternal::kNone; }; // Consume conversion spec prefix (not including '%') of [p, end) if valid. @@ -79,32 +90,43 @@ const char* ConsumeUnboundConversion(const char* p, const char* end, UnboundConversion* conv, int* next_arg); // Helper tag class for the table below. -// It allows fast `char -> ConversionChar/LengthMod` checking and +// It allows fast `char -> ConversionChar/LengthMod/Flags` checking and // conversions. class ConvTag { public: - constexpr ConvTag(ConversionChar conversion_char) // NOLINT - : tag_(static_cast<int8_t>(conversion_char)) {} - // We invert the length modifiers to make them negative so that we can easily - // test for them. + constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT + : tag_(static_cast<uint8_t>(conversion_char)) {} constexpr ConvTag(LengthMod length_mod) // NOLINT - : tag_(~static_cast<std::int8_t>(length_mod)) {} - // Everything else is -128, which is negative to make is_conv() simpler. - constexpr ConvTag() : tag_(-128) {} + : tag_(0x80 | static_cast<uint8_t>(length_mod)) {} + constexpr ConvTag(Flags flags) // NOLINT + : tag_(0xc0 | static_cast<uint8_t>(flags)) {} + constexpr ConvTag() : tag_(0xFF) {} + + bool is_conv() const { return (tag_ & 0x80) == 0; } + bool is_length() const { return (tag_ & 0xC0) == 0x80; } + bool is_flags() const { return (tag_ & 0xE0) == 0xC0; } - bool is_conv() const { return tag_ >= 0; } - bool is_length() const { return tag_ < 0 && tag_ != -128; } - ConversionChar as_conv() const { + FormatConversionChar as_conv() const { assert(is_conv()); - return static_cast<ConversionChar>(tag_); + assert(!is_length()); + assert(!is_flags()); + return static_cast<FormatConversionChar>(tag_); } LengthMod as_length() const { + assert(!is_conv()); assert(is_length()); - return static_cast<LengthMod>(~tag_); + assert(!is_flags()); + return static_cast<LengthMod>(tag_ & 0x3F); + } + Flags as_flags() const { + assert(!is_conv()); + assert(!is_length()); + assert(is_flags()); + return static_cast<Flags>(tag_ & 0x1F); } private: - std::int8_t tag_; + uint8_t tag_; }; extern const ConvTag kTags[256]; @@ -143,7 +165,7 @@ bool ParseFormatString(string_view src, Consumer consumer) { auto tag = GetTagForChar(percent[1]); if (tag.is_conv()) { if (ABSL_PREDICT_FALSE(next_arg < 0)) { - // This indicates an error in the format std::string. + // This indicates an error in the format string. // The only way to get `next_arg < 0` here is to have a positional // argument first which sets next_arg to -1 and then a non-positional // argument. @@ -186,8 +208,9 @@ constexpr bool EnsureConstexpr(string_view s) { class ParsedFormatBase { public: - explicit ParsedFormatBase(string_view format, bool allow_ignored, - std::initializer_list<Conv> convs); + explicit ParsedFormatBase( + string_view format, bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs); ParsedFormatBase(const ParsedFormatBase& other) { *this = other; } @@ -234,8 +257,9 @@ class ParsedFormatBase { private: // Returns whether the conversions match and if !allow_ignored it verifies // that all conversions are used by the format. - bool MatchesConversions(bool allow_ignored, - std::initializer_list<Conv> convs) const; + bool MatchesConversions( + bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) const; struct ParsedFormatConsumer; @@ -280,14 +304,14 @@ class ParsedFormatBase { // This is the only API that allows the user to pass a runtime specified format // string. These factory functions will return NULL if the format does not match // the conversions requested by the user. -template <str_format_internal::Conv... C> +template <FormatConversionCharSet... C> class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { public: explicit ExtendedParsedFormat(string_view format) #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER __attribute__(( enable_if(str_format_internal::EnsureConstexpr(format), - "Format std::string is not constexpr."), + "Format string is not constexpr."), enable_if(str_format_internal::ValidFormatImpl<C...>(format), "Format specified does not match the template arguments."))) #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/parser_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/parser_test.cc index 1b1ee030f1..fe0d296360 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_format/parser_test.cc +++ b/third_party/abseil-cpp/absl/strings/internal/str_format/parser_test.cc @@ -1,3 +1,17 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/str_format/parser.h" #include <string.h> @@ -41,23 +55,23 @@ TEST(LengthModTest, Names) { TEST(ConversionCharTest, Names) { struct Expectation { - ConversionChar id; + FormatConversionChar id; char name; }; // clang-format off const Expectation kExpect[] = { -#define X(c) {ConversionChar::c, #c[0]} - X(c), X(C), X(s), X(S), // text +#define X(c) {FormatConversionCharInternal::c, #c[0]} + X(c), X(s), // text X(d), X(i), X(o), X(u), X(x), X(X), // int X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A), // float X(n), X(p), // misc #undef X - {ConversionChar::none, '\0'}, + {FormatConversionCharInternal::kNone, '\0'}, }; // clang-format on for (auto e : kExpect) { SCOPED_TRACE(e.name); - ConversionChar v = e.id; + FormatConversionChar v = e.id; EXPECT_EQ(e.name, FormatConversionCharToChar(v)); } } @@ -256,15 +270,22 @@ TEST_F(ConsumeUnboundConversionTest, Flags) { for (int k = 0; k < kNumFlags; ++k) if ((i >> k) & 1) fmt += kAllFlags[k]; // flag order shouldn't matter - if (rev == 1) { std::reverse(fmt.begin(), fmt.end()); } + if (rev == 1) { + std::reverse(fmt.begin(), fmt.end()); + } fmt += 'd'; SCOPED_TRACE(fmt); EXPECT_TRUE(Run(fmt.c_str())); - EXPECT_EQ(fmt.find('-') == std::string::npos, !o.flags.left); - EXPECT_EQ(fmt.find('+') == std::string::npos, !o.flags.show_pos); - EXPECT_EQ(fmt.find(' ') == std::string::npos, !o.flags.sign_col); - EXPECT_EQ(fmt.find('#') == std::string::npos, !o.flags.alt); - EXPECT_EQ(fmt.find('0') == std::string::npos, !o.flags.zero); + EXPECT_EQ(fmt.find('-') == std::string::npos, + !FlagsContains(o.flags, Flags::kLeft)); + EXPECT_EQ(fmt.find('+') == std::string::npos, + !FlagsContains(o.flags, Flags::kShowPos)); + EXPECT_EQ(fmt.find(' ') == std::string::npos, + !FlagsContains(o.flags, Flags::kSignCol)); + EXPECT_EQ(fmt.find('#') == std::string::npos, + !FlagsContains(o.flags, Flags::kAlt)); + EXPECT_EQ(fmt.find('0') == std::string::npos, + !FlagsContains(o.flags, Flags::kZero)); } } } @@ -274,14 +295,14 @@ TEST_F(ConsumeUnboundConversionTest, BasicFlag) { for (const char* fmt : {"d", "llx", "G", "1$X"}) { SCOPED_TRACE(fmt); EXPECT_TRUE(Run(fmt)); - EXPECT_TRUE(o.flags.basic); + EXPECT_EQ(o.flags, Flags::kBasic); } // Flag is off for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) { SCOPED_TRACE(fmt); EXPECT_TRUE(Run(fmt)); - EXPECT_FALSE(o.flags.basic); + EXPECT_NE(o.flags, Flags::kBasic); } } @@ -349,7 +370,8 @@ TEST_F(ParsedFormatTest, ValueSemantics) { ParsedFormatBase p2 = p1; // copy construct (empty) EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2)); - p1 = ParsedFormatBase("hello%s", true, {Conv::s}); // move assign + p1 = ParsedFormatBase("hello%s", true, + {FormatConversionCharSetInternal::s}); // move assign EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1)); ParsedFormatBase p3 = p1; // copy construct (nonempty) @@ -367,7 +389,7 @@ TEST_F(ParsedFormatTest, ValueSemantics) { struct ExpectParse { const char* in; - std::initializer_list<Conv> conv_set; + std::initializer_list<FormatConversionCharSet> conv_set; const char* out; }; @@ -377,9 +399,9 @@ TEST_F(ParsedFormatTest, Parsing) { const ExpectParse kExpect[] = { {"", {}, ""}, {"ab", {}, "[ab]"}, - {"a%d", {Conv::d}, "[a]{d:1$d}"}, - {"a%+d", {Conv::d}, "[a]{+d:1$d}"}, - {"a% d", {Conv::d}, "[a]{ d:1$d}"}, + {"a%d", {FormatConversionCharSetInternal::d}, "[a]{d:1$d}"}, + {"a%+d", {FormatConversionCharSetInternal::d}, "[a]{+d:1$d}"}, + {"a% d", {FormatConversionCharSetInternal::d}, "[a]{ d:1$d}"}, {"a%b %d", {}, "[a]!"}, // stop after error }; for (const auto& e : kExpect) { @@ -391,13 +413,13 @@ TEST_F(ParsedFormatTest, Parsing) { TEST_F(ParsedFormatTest, ParsingFlagOrder) { const ExpectParse kExpect[] = { - {"a%+ 0d", {Conv::d}, "[a]{+ 0d:1$d}"}, - {"a%+0 d", {Conv::d}, "[a]{+0 d:1$d}"}, - {"a%0+ d", {Conv::d}, "[a]{0+ d:1$d}"}, - {"a% +0d", {Conv::d}, "[a]{ +0d:1$d}"}, - {"a%0 +d", {Conv::d}, "[a]{0 +d:1$d}"}, - {"a% 0+d", {Conv::d}, "[a]{ 0+d:1$d}"}, - {"a%+ 0+d", {Conv::d}, "[a]{+ 0+d:1$d}"}, + {"a%+ 0d", {FormatConversionCharSetInternal::d}, "[a]{+ 0d:1$d}"}, + {"a%+0 d", {FormatConversionCharSetInternal::d}, "[a]{+0 d:1$d}"}, + {"a%0+ d", {FormatConversionCharSetInternal::d}, "[a]{0+ d:1$d}"}, + {"a% +0d", {FormatConversionCharSetInternal::d}, "[a]{ +0d:1$d}"}, + {"a%0 +d", {FormatConversionCharSetInternal::d}, "[a]{0 +d:1$d}"}, + {"a% 0+d", {FormatConversionCharSetInternal::d}, "[a]{ 0+d:1$d}"}, + {"a%+ 0+d", {FormatConversionCharSetInternal::d}, "[a]{+ 0+d:1$d}"}, }; for (const auto& e : kExpect) { SCOPED_TRACE(e.in); diff --git a/third_party/abseil-cpp/absl/strings/internal/str_split_internal.h b/third_party/abseil-cpp/absl/strings/internal/str_split_internal.h index b54f6ebe09..e766421617 100644 --- a/third_party/abseil-cpp/absl/strings/internal/str_split_internal.h +++ b/third_party/abseil-cpp/absl/strings/internal/str_split_internal.h @@ -32,7 +32,7 @@ #include <array> #include <initializer_list> #include <iterator> -#include <map> +#include <tuple> #include <type_traits> #include <utility> #include <vector> @@ -51,9 +51,9 @@ ABSL_NAMESPACE_BEGIN namespace strings_internal { // This class is implicitly constructible from everything that absl::string_view -// is implicitly constructible from. If it's constructed from a temporary -// string, the data is moved into a data member so its lifetime matches that of -// the ConvertibleToStringView instance. +// is implicitly constructible from, except for rvalue strings. This means it +// can be used as a function parameter in places where passing a temporary +// string might cause memory lifetime issues. class ConvertibleToStringView { public: ConvertibleToStringView(const char* s) // NOLINT(runtime/explicit) @@ -64,42 +64,13 @@ class ConvertibleToStringView { ConvertibleToStringView(const std::string& s) // NOLINT(runtime/explicit) : value_(s) {} - // Matches rvalue strings and moves their data to a member. -ConvertibleToStringView(std::string&& s) // NOLINT(runtime/explicit) - : copy_(std::move(s)), value_(copy_) {} - - ConvertibleToStringView(const ConvertibleToStringView& other) - : copy_(other.copy_), - value_(other.IsSelfReferential() ? copy_ : other.value_) {} - - ConvertibleToStringView(ConvertibleToStringView&& other) { - StealMembers(std::move(other)); - } - - ConvertibleToStringView& operator=(ConvertibleToStringView other) { - StealMembers(std::move(other)); - return *this; - } + // Disable conversion from rvalue strings. + ConvertibleToStringView(std::string&& s) = delete; + ConvertibleToStringView(const std::string&& s) = delete; absl::string_view value() const { return value_; } private: - // Returns true if ctsp's value refers to its internal copy_ member. - bool IsSelfReferential() const { return value_.data() == copy_.data(); } - - void StealMembers(ConvertibleToStringView&& other) { - if (other.IsSelfReferential()) { - copy_ = std::move(other.copy_); - value_ = copy_; - other.value_ = other.copy_; - } else { - value_ = other.value_; - } - } - - // Holds the data moved from temporary std::string arguments. Declared first - // so that 'value' can refer to 'copy_'. - std::string copy_; absl::string_view value_; }; @@ -211,6 +182,13 @@ template <typename T> struct HasConstIterator<T, absl::void_t<typename T::const_iterator>> : std::true_type {}; +// HasEmplace<T>::value is true iff there exists a method T::emplace(). +template <typename T, typename = void> +struct HasEmplace : std::false_type {}; +template <typename T> +struct HasEmplace<T, absl::void_t<decltype(std::declval<T>().emplace())>> + : std::true_type {}; + // IsInitializerList<T>::value is true iff T is an std::initializer_list. More // details below in Splitter<> where this is used. std::false_type IsInitializerListDispatch(...); // default: No @@ -273,7 +251,11 @@ struct SplitterIsConvertibleTo // the split strings: only strings for which the predicate returns true will be // kept. A Predicate object is any unary functor that takes an absl::string_view // and returns bool. -template <typename Delimiter, typename Predicate> +// +// The StringType parameter can be either string_view or string, depending on +// whether the Splitter refers to a string stored elsewhere, or if the string +// resides inside the Splitter itself. +template <typename Delimiter, typename Predicate, typename StringType> class Splitter { public: using DelimiterType = Delimiter; @@ -281,12 +263,12 @@ class Splitter { using const_iterator = strings_internal::SplitIterator<Splitter>; using value_type = typename std::iterator_traits<const_iterator>::value_type; - Splitter(ConvertibleToStringView input_text, Delimiter d, Predicate p) + Splitter(StringType input_text, Delimiter d, Predicate p) : text_(std::move(input_text)), delimiter_(std::move(d)), predicate_(std::move(p)) {} - absl::string_view text() const { return text_.value(); } + absl::string_view text() const { return text_; } const Delimiter& delimiter() const { return delimiter_; } const Predicate& predicate() const { return predicate_; } @@ -336,7 +318,7 @@ class Splitter { Container operator()(const Splitter& splitter) const { Container c; auto it = std::inserter(c, c.end()); - for (const auto sp : splitter) { + for (const auto& sp : splitter) { *it++ = ValueType(sp); } return c; @@ -397,53 +379,46 @@ class Splitter { // value. template <typename Container, typename First, typename Second> struct ConvertToContainer<Container, std::pair<const First, Second>, true> { + using iterator = typename Container::iterator; + Container operator()(const Splitter& splitter) const { Container m; - typename Container::iterator it; + iterator it; bool insert = true; - for (const auto sp : splitter) { + for (const absl::string_view sv : splitter) { if (insert) { - it = Inserter<Container>::Insert(&m, First(sp), Second()); + it = InsertOrEmplace(&m, sv); } else { - it->second = Second(sp); + it->second = Second(sv); } insert = !insert; } return m; } - // Inserts the key and value into the given map, returning an iterator to - // the inserted item. Specialized for std::map and std::multimap to use - // emplace() and adapt emplace()'s return value. - template <typename Map> - struct Inserter { - using M = Map; - template <typename... Args> - static typename M::iterator Insert(M* m, Args&&... args) { - return m->insert(std::make_pair(std::forward<Args>(args)...)).first; - } - }; - - template <typename... Ts> - struct Inserter<std::map<Ts...>> { - using M = std::map<Ts...>; - template <typename... Args> - static typename M::iterator Insert(M* m, Args&&... args) { - return m->emplace(std::make_pair(std::forward<Args>(args)...)).first; - } - }; - - template <typename... Ts> - struct Inserter<std::multimap<Ts...>> { - using M = std::multimap<Ts...>; - template <typename... Args> - static typename M::iterator Insert(M* m, Args&&... args) { - return m->emplace(std::make_pair(std::forward<Args>(args)...)); - } - }; + // Inserts the key and an empty value into the map, returning an iterator to + // the inserted item. We use emplace() if available, otherwise insert(). + template <typename M> + static absl::enable_if_t<HasEmplace<M>::value, iterator> InsertOrEmplace( + M* m, absl::string_view key) { + // Use piecewise_construct to support old versions of gcc in which pair + // constructor can't otherwise construct string from string_view. + return ToIter(m->emplace(std::piecewise_construct, std::make_tuple(key), + std::tuple<>())); + } + template <typename M> + static absl::enable_if_t<!HasEmplace<M>::value, iterator> InsertOrEmplace( + M* m, absl::string_view key) { + return ToIter(m->insert(std::make_pair(First(key), Second("")))); + } + + static iterator ToIter(std::pair<iterator, bool> pair) { + return pair.first; + } + static iterator ToIter(iterator iter) { return iter; } }; - ConvertibleToStringView text_; + StringType text_; Delimiter delimiter_; Predicate predicate_; }; diff --git a/third_party/abseil-cpp/absl/strings/internal/string_constant.h b/third_party/abseil-cpp/absl/strings/internal/string_constant.h new file mode 100644 index 0000000000..a11336b7f0 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/string_constant.h @@ -0,0 +1,64 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ +#define ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ + +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace strings_internal { + +// StringConstant<T> represents a compile time string constant. +// It can be accessed via its `absl::string_view value` static member. +// It is guaranteed that the `string_view` returned has constant `.data()`, +// constant `.size()` and constant `value[i]` for all `0 <= i < .size()` +// +// The `T` is an opaque type. It is guaranteed that different string constants +// will have different values of `T`. This allows users to associate the string +// constant with other static state at compile time. +// +// Instances should be made using the `MakeStringConstant()` factory function +// below. +template <typename T> +struct StringConstant { + static constexpr absl::string_view value = T{}(); + constexpr absl::string_view operator()() const { return value; } + + // Check to be sure `view` points to constant data. + // Otherwise, it can't be constant evaluated. + static_assert(value.empty() || 2 * value[0] != 1, + "The input string_view must point to constant data."); +}; + +template <typename T> +constexpr absl::string_view StringConstant<T>::value; // NOLINT + +// Factory function for `StringConstant` instances. +// It supports callables that have a constexpr default constructor and a +// constexpr operator(). +// It must return an `absl::string_view` or `const char*` pointing to constant +// data. This is validated at compile time. +template <typename T> +constexpr StringConstant<T> MakeStringConstant(T) { + return {}; +} + +} // namespace strings_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_STRING_CONSTANT_H_ diff --git a/third_party/abseil-cpp/absl/strings/internal/string_constant_test.cc b/third_party/abseil-cpp/absl/strings/internal/string_constant_test.cc new file mode 100644 index 0000000000..392833cf15 --- /dev/null +++ b/third_party/abseil-cpp/absl/strings/internal/string_constant_test.cc @@ -0,0 +1,60 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/internal/string_constant.h" + +#include "absl/meta/type_traits.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace { + +using absl::strings_internal::MakeStringConstant; + +struct Callable { + constexpr absl::string_view operator()() const { + return absl::string_view("Callable", 8); + } +}; + +TEST(StringConstant, Traits) { + constexpr auto str = MakeStringConstant(Callable{}); + using T = decltype(str); + + EXPECT_TRUE(std::is_empty<T>::value); + EXPECT_TRUE(std::is_trivial<T>::value); + EXPECT_TRUE(absl::is_trivially_default_constructible<T>::value); + EXPECT_TRUE(absl::is_trivially_copy_constructible<T>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<T>::value); + EXPECT_TRUE(absl::is_trivially_destructible<T>::value); +} + +TEST(StringConstant, MakeFromCallable) { + constexpr auto str = MakeStringConstant(Callable{}); + using T = decltype(str); + EXPECT_EQ(Callable{}(), T::value); + EXPECT_EQ(Callable{}(), str()); +} + +TEST(StringConstant, MakeFromStringConstant) { + // We want to make sure the StringConstant itself is a valid input to the + // factory function. + constexpr auto str = MakeStringConstant(Callable{}); + constexpr auto str2 = MakeStringConstant(str); + using T = decltype(str2); + EXPECT_EQ(Callable{}(), T::value); + EXPECT_EQ(Callable{}(), str2()); +} + +} // namespace diff --git a/third_party/abseil-cpp/absl/strings/match.cc b/third_party/abseil-cpp/absl/strings/match.cc index 8127cb0c5e..2d67250970 100644 --- a/third_party/abseil-cpp/absl/strings/match.cc +++ b/third_party/abseil-cpp/absl/strings/match.cc @@ -19,19 +19,22 @@ namespace absl { ABSL_NAMESPACE_BEGIN -bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) { +bool EqualsIgnoreCase(absl::string_view piece1, + absl::string_view piece2) noexcept { return (piece1.size() == piece2.size() && 0 == absl::strings_internal::memcasecmp(piece1.data(), piece2.data(), piece1.size())); // memcasecmp uses absl::ascii_tolower(). } -bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix) { +bool StartsWithIgnoreCase(absl::string_view text, + absl::string_view prefix) noexcept { return (text.size() >= prefix.size()) && EqualsIgnoreCase(text.substr(0, prefix.size()), prefix); } -bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) { +bool EndsWithIgnoreCase(absl::string_view text, + absl::string_view suffix) noexcept { return (text.size() >= suffix.size()) && EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix); } diff --git a/third_party/abseil-cpp/absl/strings/match.h b/third_party/abseil-cpp/absl/strings/match.h index 90fca98ad2..038cbb3fa8 100644 --- a/third_party/abseil-cpp/absl/strings/match.h +++ b/third_party/abseil-cpp/absl/strings/match.h @@ -43,14 +43,20 @@ ABSL_NAMESPACE_BEGIN // StrContains() // // Returns whether a given string `haystack` contains the substring `needle`. -inline bool StrContains(absl::string_view haystack, absl::string_view needle) { +inline bool StrContains(absl::string_view haystack, + absl::string_view needle) noexcept { return haystack.find(needle, 0) != haystack.npos; } +inline bool StrContains(absl::string_view haystack, char needle) noexcept { + return haystack.find(needle) != haystack.npos; +} + // StartsWith() // // Returns whether a given string `text` begins with `prefix`. -inline bool StartsWith(absl::string_view text, absl::string_view prefix) { +inline bool StartsWith(absl::string_view text, + absl::string_view prefix) noexcept { return prefix.empty() || (text.size() >= prefix.size() && memcmp(text.data(), prefix.data(), prefix.size()) == 0); @@ -59,7 +65,8 @@ inline bool StartsWith(absl::string_view text, absl::string_view prefix) { // EndsWith() // // Returns whether a given string `text` ends with `suffix`. -inline bool EndsWith(absl::string_view text, absl::string_view suffix) { +inline bool EndsWith(absl::string_view text, + absl::string_view suffix) noexcept { return suffix.empty() || (text.size() >= suffix.size() && memcmp(text.data() + (text.size() - suffix.size()), suffix.data(), @@ -70,19 +77,22 @@ inline bool EndsWith(absl::string_view text, absl::string_view suffix) { // // Returns whether given ASCII strings `piece1` and `piece2` are equal, ignoring // case in the comparison. -bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2); +bool EqualsIgnoreCase(absl::string_view piece1, + absl::string_view piece2) noexcept; // StartsWithIgnoreCase() // // Returns whether a given ASCII string `text` starts with `prefix`, // ignoring case in the comparison. -bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix); +bool StartsWithIgnoreCase(absl::string_view text, + absl::string_view prefix) noexcept; // EndsWithIgnoreCase() // // Returns whether a given ASCII string `text` ends with `suffix`, ignoring // case in the comparison. -bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix); +bool EndsWithIgnoreCase(absl::string_view text, + absl::string_view suffix) noexcept; ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/match_test.cc b/third_party/abseil-cpp/absl/strings/match_test.cc index 4c313dda14..5841bc1b48 100644 --- a/third_party/abseil-cpp/absl/strings/match_test.cc +++ b/third_party/abseil-cpp/absl/strings/match_test.cc @@ -66,6 +66,23 @@ TEST(MatchTest, Contains) { EXPECT_FALSE(absl::StrContains("", "a")); } +TEST(MatchTest, ContainsChar) { + absl::string_view a("abcdefg"); + absl::string_view b("abcd"); + EXPECT_TRUE(absl::StrContains(a, 'a')); + EXPECT_TRUE(absl::StrContains(a, 'b')); + EXPECT_TRUE(absl::StrContains(a, 'e')); + EXPECT_FALSE(absl::StrContains(a, 'h')); + + EXPECT_TRUE(absl::StrContains(b, 'a')); + EXPECT_TRUE(absl::StrContains(b, 'b')); + EXPECT_FALSE(absl::StrContains(b, 'e')); + EXPECT_FALSE(absl::StrContains(b, 'h')); + + EXPECT_FALSE(absl::StrContains("", 'a')); + EXPECT_FALSE(absl::StrContains("", 'a')); +} + TEST(MatchTest, ContainsNull) { const std::string s = "foo"; const char* cs = "foo"; diff --git a/third_party/abseil-cpp/absl/strings/numbers.cc b/third_party/abseil-cpp/absl/strings/numbers.cc index 68c26dd6f8..cbd84c918b 100644 --- a/third_party/abseil-cpp/absl/strings/numbers.cc +++ b/third_party/abseil-cpp/absl/strings/numbers.cc @@ -31,8 +31,8 @@ #include <utility> #include "absl/base/attributes.h" -#include "absl/base/internal/bits.h" #include "absl/base/internal/raw_logging.h" +#include "absl/numeric/bits.h" #include "absl/strings/ascii.h" #include "absl/strings/charconv.h" #include "absl/strings/escaping.h" @@ -46,8 +46,13 @@ ABSL_NAMESPACE_BEGIN bool SimpleAtof(absl::string_view str, float* out) { *out = 0.0; str = StripAsciiWhitespace(str); + // std::from_chars doesn't accept an initial +, but SimpleAtof does, so if one + // is present, skip it, while avoiding accepting "+-0" as valid. if (!str.empty() && str[0] == '+') { str.remove_prefix(1); + if (!str.empty() && str[0] == '-') { + return false; + } } auto result = absl::from_chars(str.data(), str.data() + str.size(), *out); if (result.ec == std::errc::invalid_argument) { @@ -72,8 +77,13 @@ bool SimpleAtof(absl::string_view str, float* out) { bool SimpleAtod(absl::string_view str, double* out) { *out = 0.0; str = StripAsciiWhitespace(str); + // std::from_chars doesn't accept an initial +, but SimpleAtod does, so if one + // is present, skip it, while avoiding accepting "+-0" as valid. if (!str.empty() && str[0] == '+') { str.remove_prefix(1); + if (!str.empty() && str[0] == '-') { + return false; + } } auto result = absl::from_chars(str.data(), str.data() + str.size(), *out); if (result.ec == std::errc::invalid_argument) { @@ -303,7 +313,7 @@ static std::pair<uint64_t, uint64_t> Mul32(std::pair<uint64_t, uint64_t> num, uint64_t bits128_up = (bits96_127 >> 32) + (bits64_127 < bits64_95); if (bits128_up == 0) return {bits64_127, bits0_63}; - int shift = 64 - base_internal::CountLeadingZeros64(bits128_up); + auto shift = static_cast<unsigned>(bit_width(bits128_up)); uint64_t lo = (bits0_63 >> shift) + (bits64_127 << (64 - shift)); uint64_t hi = (bits64_127 >> shift) + (bits128_up << (64 - shift)); return {hi, lo}; @@ -334,7 +344,7 @@ static std::pair<uint64_t, uint64_t> PowFive(uint64_t num, int expfive) { 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5}; result = Mul32(result, powers_of_five[expfive & 15]); - int shift = base_internal::CountLeadingZeros64(result.first); + int shift = countl_zero(result.first); if (shift != 0) { result.first = (result.first << shift) + (result.second >> (64 - shift)); result.second = (result.second << shift); @@ -495,7 +505,7 @@ size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { *out++ = '-'; d = -d; } - if (std::isinf(d)) { + if (d > std::numeric_limits<double>::max()) { strcpy(out, "inf"); // NOLINT(runtime/printf) return out + 3 - buffer; } @@ -736,9 +746,18 @@ struct LookupTables { X / 35, X / 36, \ } +// This kVmaxOverBase is generated with +// for (int base = 2; base < 37; ++base) { +// absl::uint128 max = std::numeric_limits<absl::uint128>::max(); +// auto result = max / base; +// std::cout << " MakeUint128(" << absl::Uint128High64(result) << "u, " +// << absl::Uint128Low64(result) << "u),\n"; +// } +// See https://godbolt.org/z/aneYsb +// // uint128& operator/=(uint128) is not constexpr, so hardcode the resulting // array to avoid a static initializer. -template <> +template<> const uint128 LookupTables<uint128>::kVmaxOverBase[] = { 0, 0, @@ -779,6 +798,111 @@ const uint128 LookupTables<uint128>::kVmaxOverBase[] = { MakeUint128(512409557603043100u, 8198552921648689607u), }; +// This kVmaxOverBase generated with +// for (int base = 2; base < 37; ++base) { +// absl::int128 max = std::numeric_limits<absl::int128>::max(); +// auto result = max / base; +// std::cout << "\tMakeInt128(" << absl::Int128High64(result) << ", " +// << absl::Int128Low64(result) << "u),\n"; +// } +// See https://godbolt.org/z/7djYWz +// +// int128& operator/=(int128) is not constexpr, so hardcode the resulting array +// to avoid a static initializer. +template<> +const int128 LookupTables<int128>::kVmaxOverBase[] = { + 0, + 0, + MakeInt128(4611686018427387903, 18446744073709551615u), + MakeInt128(3074457345618258602, 12297829382473034410u), + MakeInt128(2305843009213693951, 18446744073709551615u), + MakeInt128(1844674407370955161, 11068046444225730969u), + MakeInt128(1537228672809129301, 6148914691236517205u), + MakeInt128(1317624576693539401, 2635249153387078802u), + MakeInt128(1152921504606846975, 18446744073709551615u), + MakeInt128(1024819115206086200, 16397105843297379214u), + MakeInt128(922337203685477580, 14757395258967641292u), + MakeInt128(838488366986797800, 13415813871788764811u), + MakeInt128(768614336404564650, 12297829382473034410u), + MakeInt128(709490156681136600, 11351842506898185609u), + MakeInt128(658812288346769700, 10540996613548315209u), + MakeInt128(614891469123651720, 9838263505978427528u), + MakeInt128(576460752303423487, 18446744073709551615u), + MakeInt128(542551296285575047, 9765923333140350855u), + MakeInt128(512409557603043100, 8198552921648689607u), + MakeInt128(485440633518672410, 17475862806672206794u), + MakeInt128(461168601842738790, 7378697629483820646u), + MakeInt128(439208192231179800, 7027331075698876806u), + MakeInt128(419244183493398900, 6707906935894382405u), + MakeInt128(401016175515425035, 2406097053092550210u), + MakeInt128(384307168202282325, 6148914691236517205u), + MakeInt128(368934881474191032, 5902958103587056517u), + MakeInt128(354745078340568300, 5675921253449092804u), + MakeInt128(341606371735362066, 17763531330238827482u), + MakeInt128(329406144173384850, 5270498306774157604u), + MakeInt128(318047311615681924, 7633135478776366185u), + MakeInt128(307445734561825860, 4919131752989213764u), + MakeInt128(297528130221121800, 4760450083537948804u), + MakeInt128(288230376151711743, 18446744073709551615u), + MakeInt128(279496122328932600, 4471937957262921603u), + MakeInt128(271275648142787523, 14106333703424951235u), + MakeInt128(263524915338707880, 4216398645419326083u), + MakeInt128(256204778801521550, 4099276460824344803u), +}; + +// This kVminOverBase generated with +// for (int base = 2; base < 37; ++base) { +// absl::int128 min = std::numeric_limits<absl::int128>::min(); +// auto result = min / base; +// std::cout << "\tMakeInt128(" << absl::Int128High64(result) << ", " +// << absl::Int128Low64(result) << "u),\n"; +// } +// +// See https://godbolt.org/z/7djYWz +// +// int128& operator/=(int128) is not constexpr, so hardcode the resulting array +// to avoid a static initializer. +template<> +const int128 LookupTables<int128>::kVminOverBase[] = { + 0, + 0, + MakeInt128(-4611686018427387904, 0u), + MakeInt128(-3074457345618258603, 6148914691236517206u), + MakeInt128(-2305843009213693952, 0u), + MakeInt128(-1844674407370955162, 7378697629483820647u), + MakeInt128(-1537228672809129302, 12297829382473034411u), + MakeInt128(-1317624576693539402, 15811494920322472814u), + MakeInt128(-1152921504606846976, 0u), + MakeInt128(-1024819115206086201, 2049638230412172402u), + MakeInt128(-922337203685477581, 3689348814741910324u), + MakeInt128(-838488366986797801, 5030930201920786805u), + MakeInt128(-768614336404564651, 6148914691236517206u), + MakeInt128(-709490156681136601, 7094901566811366007u), + MakeInt128(-658812288346769701, 7905747460161236407u), + MakeInt128(-614891469123651721, 8608480567731124088u), + MakeInt128(-576460752303423488, 0u), + MakeInt128(-542551296285575048, 8680820740569200761u), + MakeInt128(-512409557603043101, 10248191152060862009u), + MakeInt128(-485440633518672411, 970881267037344822u), + MakeInt128(-461168601842738791, 11068046444225730970u), + MakeInt128(-439208192231179801, 11419412998010674810u), + MakeInt128(-419244183493398901, 11738837137815169211u), + MakeInt128(-401016175515425036, 16040647020617001406u), + MakeInt128(-384307168202282326, 12297829382473034411u), + MakeInt128(-368934881474191033, 12543785970122495099u), + MakeInt128(-354745078340568301, 12770822820260458812u), + MakeInt128(-341606371735362067, 683212743470724134u), + MakeInt128(-329406144173384851, 13176245766935394012u), + MakeInt128(-318047311615681925, 10813608594933185431u), + MakeInt128(-307445734561825861, 13527612320720337852u), + MakeInt128(-297528130221121801, 13686293990171602812u), + MakeInt128(-288230376151711744, 0u), + MakeInt128(-279496122328932601, 13974806116446630013u), + MakeInt128(-271275648142787524, 4340410370284600381u), + MakeInt128(-263524915338707881, 14230345428290225533u), + MakeInt128(-256204778801521551, 14347467612885206813u), +}; + template <typename IntType> const IntType LookupTables<IntType>::kVmaxOverBase[] = X_OVER_BASE_INITIALIZER(std::numeric_limits<IntType>::max()); @@ -948,6 +1072,10 @@ bool safe_strto64_base(absl::string_view text, int64_t* value, int base) { return safe_int_internal<int64_t>(text, value, base); } +bool safe_strto128_base(absl::string_view text, int128* value, int base) { + return safe_int_internal<absl::int128>(text, value, base); +} + bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base) { return safe_uint_internal<uint32_t>(text, value, base); } diff --git a/third_party/abseil-cpp/absl/strings/numbers.h b/third_party/abseil-cpp/absl/strings/numbers.h index d872cca5dc..4ae07c2d60 100644 --- a/third_party/abseil-cpp/absl/strings/numbers.h +++ b/third_party/abseil-cpp/absl/strings/numbers.h @@ -1,4 +1,3 @@ -// // Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,7 +36,6 @@ #include <type_traits> #include "absl/base/config.h" -#include "absl/base/internal/bits.h" #ifdef __SSE4_2__ // TODO(jorg): Remove this when we figure out the right way // to swap bytes on SSE 4.2 that works with the compilers @@ -48,6 +46,7 @@ #endif #include "absl/base/macros.h" #include "absl/base/port.h" +#include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/strings/string_view.h" @@ -97,6 +96,25 @@ ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); // unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out); +// SimpleHexAtoi() +// +// Converts a hexadecimal string (optionally followed or preceded by ASCII +// whitespace) to an integer, returning `true` if successful. Only valid base-16 +// hexadecimal integers whose value falls within the range of the integer type +// (optionally preceded by a `+` or `-`) can be converted. A valid hexadecimal +// value may include both upper and lowercase character symbols, and may +// optionally include a leading "0x" (or "0X") number prefix, which is ignored +// by this function. If any errors are encountered, this function returns +// `false`, leaving `out` in an unspecified state. +template <typename int_type> +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out); + +// Overloads of SimpleHexAtoi() for 128 bit integers. +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::int128* out); +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::uint128* out); + ABSL_NAMESPACE_END } // namespace absl @@ -125,8 +143,11 @@ inline void PutTwoDigits(size_t i, char* buf) { } // safe_strto?() functions for implementing SimpleAtoi() + bool safe_strto32_base(absl::string_view text, int32_t* value, int base); bool safe_strto64_base(absl::string_view text, int64_t* value, int base); +bool safe_strto128_base(absl::string_view text, absl::int128* value, + int base); bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base); bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base); bool safe_strtou128_base(absl::string_view text, absl::uint128* value, @@ -238,28 +259,41 @@ inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { } #endif // | 0x1 so that even 0 has 1 digit. - return 16 - absl::base_internal::CountLeadingZeros64(val | 0x1) / 4; + return 16 - countl_zero(val | 0x1) / 4; } } // namespace numbers_internal -// SimpleAtoi() -// -// Converts a string to an integer, using `safe_strto?()` functions for actual -// parsing, returning `true` if successful. The `safe_strto?()` functions apply -// strict checking; the string must be a base-10 integer, optionally followed or -// preceded by ASCII whitespace, with a value in the range of the corresponding -// integer type. template <typename int_type> ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) { return numbers_internal::safe_strtoi_base(str, out, 10); } ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, + absl::int128* out) { + return numbers_internal::safe_strto128_base(str, out, 10); +} + +ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, absl::uint128* out) { return numbers_internal::safe_strtou128_base(str, out, 10); } +template <typename int_type> +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out) { + return numbers_internal::safe_strtoi_base(str, out, 16); +} + +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::int128* out) { + return numbers_internal::safe_strto128_base(str, out, 16); +} + +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, + absl::uint128* out) { + return numbers_internal::safe_strtou128_base(str, out, 16); +} + ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/numbers_test.cc b/third_party/abseil-cpp/absl/strings/numbers_test.cc index 68229b15a4..498c210d3b 100644 --- a/third_party/abseil-cpp/absl/strings/numbers_test.cc +++ b/third_party/abseil-cpp/absl/strings/numbers_test.cc @@ -40,11 +40,14 @@ #include "absl/random/distributions.h" #include "absl/random/random.h" #include "absl/strings/internal/numbers_test_common.h" +#include "absl/strings/internal/ostringstream.h" #include "absl/strings/internal/pow10_helper.h" #include "absl/strings/str_cat.h" namespace { +using absl::SimpleAtoi; +using absl::SimpleHexAtoi; using absl::numbers_internal::kSixDigitsToBufferSize; using absl::numbers_internal::safe_strto32_base; using absl::numbers_internal::safe_strto64_base; @@ -54,7 +57,6 @@ using absl::numbers_internal::SixDigitsToBuffer; using absl::strings_internal::Itoa; using absl::strings_internal::strtouint32_test_cases; using absl::strings_internal::strtouint64_test_cases; -using absl::SimpleAtoi; using testing::Eq; using testing::MatchesRegex; @@ -250,7 +252,7 @@ TEST(Numbers, TestFastPrints) { template <typename int_type, typename in_val_type> void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) { std::string s; - // uint128 can be streamed but not StrCat'd + // (u)int128 can be streamed but not StrCat'd. absl::strings_internal::OStringStream(&s) << in_value; int_type x = static_cast<int_type>(~exp_value); EXPECT_TRUE(SimpleAtoi(s, &x)) @@ -263,7 +265,9 @@ void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) { template <typename int_type, typename in_val_type> void VerifySimpleAtoiBad(in_val_type in_value) { - std::string s = absl::StrCat(in_value); + std::string s; + // (u)int128 can be streamed but not StrCat'd. + absl::strings_internal::OStringStream(&s) << in_value; int_type x; EXPECT_FALSE(SimpleAtoi(s, &x)); EXPECT_FALSE(SimpleAtoi(s.c_str(), &x)); @@ -346,18 +350,71 @@ TEST(NumbersTest, Atoi) { std::numeric_limits<absl::uint128>::max(), std::numeric_limits<absl::uint128>::max()); + // SimpleAtoi(absl::string_view, absl::int128) + VerifySimpleAtoiGood<absl::int128>(0, 0); + VerifySimpleAtoiGood<absl::int128>(42, 42); + VerifySimpleAtoiGood<absl::int128>(-42, -42); + + VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int32_t>::min(), + std::numeric_limits<int32_t>::min()); + VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int32_t>::max(), + std::numeric_limits<int32_t>::max()); + VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint32_t>::max()); + VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::min()); + VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<int64_t>::max(), + std::numeric_limits<int64_t>::max()); + VerifySimpleAtoiGood<absl::int128>(std::numeric_limits<uint64_t>::max(), + std::numeric_limits<uint64_t>::max()); + VerifySimpleAtoiGood<absl::int128>( + std::numeric_limits<absl::int128>::min(), + std::numeric_limits<absl::int128>::min()); + VerifySimpleAtoiGood<absl::int128>( + std::numeric_limits<absl::int128>::max(), + std::numeric_limits<absl::int128>::max()); + VerifySimpleAtoiBad<absl::int128>(std::numeric_limits<absl::uint128>::max()); + // Some other types VerifySimpleAtoiGood<int>(-42, -42); VerifySimpleAtoiGood<int32_t>(-42, -42); VerifySimpleAtoiGood<uint32_t>(42, 42); VerifySimpleAtoiGood<unsigned int>(42, 42); VerifySimpleAtoiGood<int64_t>(-42, -42); - VerifySimpleAtoiGood<long>(-42, -42); // NOLINT(runtime/int) + VerifySimpleAtoiGood<long>(-42, -42); // NOLINT: runtime-int VerifySimpleAtoiGood<uint64_t>(42, 42); VerifySimpleAtoiGood<size_t>(42, 42); VerifySimpleAtoiGood<std::string::size_type>(42, 42); } +TEST(NumbersTest, Atod) { + double d; + EXPECT_TRUE(absl::SimpleAtod("nan", &d)); + EXPECT_TRUE(std::isnan(d)); +} + +TEST(NumbersTest, Prefixes) { + double d; + EXPECT_FALSE(absl::SimpleAtod("++1", &d)); + EXPECT_FALSE(absl::SimpleAtod("+-1", &d)); + EXPECT_FALSE(absl::SimpleAtod("-+1", &d)); + EXPECT_FALSE(absl::SimpleAtod("--1", &d)); + EXPECT_TRUE(absl::SimpleAtod("-1", &d)); + EXPECT_EQ(d, -1.); + EXPECT_TRUE(absl::SimpleAtod("+1", &d)); + EXPECT_EQ(d, +1.); + + float f; + EXPECT_FALSE(absl::SimpleAtof("++1", &f)); + EXPECT_FALSE(absl::SimpleAtof("+-1", &f)); + EXPECT_FALSE(absl::SimpleAtof("-+1", &f)); + EXPECT_FALSE(absl::SimpleAtof("--1", &f)); + EXPECT_TRUE(absl::SimpleAtof("-1", &f)); + EXPECT_EQ(f, -1.f); + EXPECT_TRUE(absl::SimpleAtof("+1", &f)); + EXPECT_EQ(f, +1.f); +} + TEST(NumbersTest, Atoenum) { enum E01 { E01_zero = 0, @@ -412,6 +469,148 @@ TEST(NumbersTest, Atoenum) { VerifySimpleAtoiGood<E_biguint>(E_biguint_max32, E_biguint_max32); } +template <typename int_type, typename in_val_type> +void VerifySimpleHexAtoiGood(in_val_type in_value, int_type exp_value) { + std::string s; + // uint128 can be streamed but not StrCat'd + absl::strings_internal::OStringStream strm(&s); + if (in_value >= 0) { + strm << std::hex << in_value; + } else { + // Inefficient for small integers, but works with all integral types. + strm << "-" << std::hex << -absl::uint128(in_value); + } + int_type x = static_cast<int_type>(~exp_value); + EXPECT_TRUE(SimpleHexAtoi(s, &x)) + << "in_value=" << std::hex << in_value << " s=" << s << " x=" << x; + EXPECT_EQ(exp_value, x); + x = static_cast<int_type>(~exp_value); + EXPECT_TRUE(SimpleHexAtoi( + s.c_str(), &x)); // NOLINT: readability-redundant-string-conversions + EXPECT_EQ(exp_value, x); +} + +template <typename int_type, typename in_val_type> +void VerifySimpleHexAtoiBad(in_val_type in_value) { + std::string s; + // uint128 can be streamed but not StrCat'd + absl::strings_internal::OStringStream strm(&s); + if (in_value >= 0) { + strm << std::hex << in_value; + } else { + // Inefficient for small integers, but works with all integral types. + strm << "-" << std::hex << -absl::uint128(in_value); + } + int_type x; + EXPECT_FALSE(SimpleHexAtoi(s, &x)); + EXPECT_FALSE(SimpleHexAtoi( + s.c_str(), &x)); // NOLINT: readability-redundant-string-conversions +} + +TEST(NumbersTest, HexAtoi) { + // SimpleHexAtoi(absl::string_view, int32_t) + VerifySimpleHexAtoiGood<int32_t>(0, 0); + VerifySimpleHexAtoiGood<int32_t>(0x42, 0x42); + VerifySimpleHexAtoiGood<int32_t>(-0x42, -0x42); + + VerifySimpleHexAtoiGood<int32_t>(std::numeric_limits<int32_t>::min(), + std::numeric_limits<int32_t>::min()); + VerifySimpleHexAtoiGood<int32_t>(std::numeric_limits<int32_t>::max(), + std::numeric_limits<int32_t>::max()); + + // SimpleHexAtoi(absl::string_view, uint32_t) + VerifySimpleHexAtoiGood<uint32_t>(0, 0); + VerifySimpleHexAtoiGood<uint32_t>(0x42, 0x42); + VerifySimpleHexAtoiBad<uint32_t>(-0x42); + + VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int32_t>::min()); + VerifySimpleHexAtoiGood<uint32_t>(std::numeric_limits<int32_t>::max(), + std::numeric_limits<int32_t>::max()); + VerifySimpleHexAtoiGood<uint32_t>(std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint32_t>::max()); + VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int64_t>::min()); + VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<int64_t>::max()); + VerifySimpleHexAtoiBad<uint32_t>(std::numeric_limits<uint64_t>::max()); + + // SimpleHexAtoi(absl::string_view, int64_t) + VerifySimpleHexAtoiGood<int64_t>(0, 0); + VerifySimpleHexAtoiGood<int64_t>(0x42, 0x42); + VerifySimpleHexAtoiGood<int64_t>(-0x42, -0x42); + + VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int32_t>::min(), + std::numeric_limits<int32_t>::min()); + VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int32_t>::max(), + std::numeric_limits<int32_t>::max()); + VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint32_t>::max()); + VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::min()); + VerifySimpleHexAtoiGood<int64_t>(std::numeric_limits<int64_t>::max(), + std::numeric_limits<int64_t>::max()); + VerifySimpleHexAtoiBad<int64_t>(std::numeric_limits<uint64_t>::max()); + + // SimpleHexAtoi(absl::string_view, uint64_t) + VerifySimpleHexAtoiGood<uint64_t>(0, 0); + VerifySimpleHexAtoiGood<uint64_t>(0x42, 0x42); + VerifySimpleHexAtoiBad<uint64_t>(-0x42); + + VerifySimpleHexAtoiBad<uint64_t>(std::numeric_limits<int32_t>::min()); + VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<int32_t>::max(), + std::numeric_limits<int32_t>::max()); + VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint32_t>::max()); + VerifySimpleHexAtoiBad<uint64_t>(std::numeric_limits<int64_t>::min()); + VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<int64_t>::max(), + std::numeric_limits<int64_t>::max()); + VerifySimpleHexAtoiGood<uint64_t>(std::numeric_limits<uint64_t>::max(), + std::numeric_limits<uint64_t>::max()); + + // SimpleHexAtoi(absl::string_view, absl::uint128) + VerifySimpleHexAtoiGood<absl::uint128>(0, 0); + VerifySimpleHexAtoiGood<absl::uint128>(0x42, 0x42); + VerifySimpleHexAtoiBad<absl::uint128>(-0x42); + + VerifySimpleHexAtoiBad<absl::uint128>(std::numeric_limits<int32_t>::min()); + VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<int32_t>::max(), + std::numeric_limits<int32_t>::max()); + VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint32_t>::max()); + VerifySimpleHexAtoiBad<absl::uint128>(std::numeric_limits<int64_t>::min()); + VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<int64_t>::max(), + std::numeric_limits<int64_t>::max()); + VerifySimpleHexAtoiGood<absl::uint128>(std::numeric_limits<uint64_t>::max(), + std::numeric_limits<uint64_t>::max()); + VerifySimpleHexAtoiGood<absl::uint128>( + std::numeric_limits<absl::uint128>::max(), + std::numeric_limits<absl::uint128>::max()); + + // Some other types + VerifySimpleHexAtoiGood<int>(-0x42, -0x42); + VerifySimpleHexAtoiGood<int32_t>(-0x42, -0x42); + VerifySimpleHexAtoiGood<uint32_t>(0x42, 0x42); + VerifySimpleHexAtoiGood<unsigned int>(0x42, 0x42); + VerifySimpleHexAtoiGood<int64_t>(-0x42, -0x42); + VerifySimpleHexAtoiGood<long>(-0x42, -0x42); // NOLINT: runtime-int + VerifySimpleHexAtoiGood<uint64_t>(0x42, 0x42); + VerifySimpleHexAtoiGood<size_t>(0x42, 0x42); + VerifySimpleHexAtoiGood<std::string::size_type>(0x42, 0x42); + + // Number prefix + int32_t value; + EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16)); + EXPECT_EQ(0x34234324, value); + + EXPECT_TRUE(safe_strto32_base("0X34234324", &value, 16)); + EXPECT_EQ(0x34234324, value); + + // ASCII whitespace + EXPECT_TRUE(safe_strto32_base(" \t\n 34234324", &value, 16)); + EXPECT_EQ(0x34234324, value); + + EXPECT_TRUE(safe_strto32_base("34234324 \t\n ", &value, 16)); + EXPECT_EQ(0x34234324, value); +} + TEST(stringtest, safe_strto32_base) { int32_t value; EXPECT_TRUE(safe_strto32_base("0x34234324", &value, 16)); @@ -481,7 +680,7 @@ TEST(stringtest, safe_strto32_base) { EXPECT_TRUE(safe_strto32_base(std::string("0x1234"), &value, 16)); EXPECT_EQ(0x1234, value); - // Base-10 std::string version. + // Base-10 string version. EXPECT_TRUE(safe_strto32_base("1234", &value, 10)); EXPECT_EQ(1234, value); } @@ -622,7 +821,7 @@ TEST(stringtest, safe_strto64_base) { EXPECT_TRUE(safe_strto64_base(std::string("0x1234"), &value, 16)); EXPECT_EQ(0x1234, value); - // Base-10 std::string version. + // Base-10 string version. EXPECT_TRUE(safe_strto64_base("1234", &value, 10)); EXPECT_EQ(1234, value); } @@ -718,6 +917,51 @@ TEST(stringtest, safe_strtou128_random) { EXPECT_FALSE(parse_func(s, &parsed_value, base)); } } +TEST(stringtest, safe_strto128_random) { + // random number generators don't work for int128, and + // int128 can be streamed but not StrCat'd, so this code must be custom + // implemented for int128, but is generally the same as what's above. + // test_random_integer_parse_base<absl::int128>( + // &absl::numbers_internal::safe_strto128_base); + using RandomEngine = std::minstd_rand0; + using IntType = absl::int128; + constexpr auto parse_func = &absl::numbers_internal::safe_strto128_base; + + std::random_device rd; + RandomEngine rng(rd()); + std::uniform_int_distribution<int64_t> random_int64( + std::numeric_limits<int64_t>::min()); + std::uniform_int_distribution<uint64_t> random_uint64( + std::numeric_limits<uint64_t>::min()); + std::uniform_int_distribution<int> random_base(2, 35); + + for (size_t i = 0; i < kNumRandomTests; ++i) { + int64_t high = random_int64(rng); + uint64_t low = random_uint64(rng); + IntType value = absl::MakeInt128(high, low); + + int base = random_base(rng); + std::string str_value; + EXPECT_TRUE(Itoa<IntType>(value, base, &str_value)); + IntType parsed_value; + + // Test successful parse + EXPECT_TRUE(parse_func(str_value, &parsed_value, base)); + EXPECT_EQ(parsed_value, value); + + // Test overflow + std::string s; + absl::strings_internal::OStringStream(&s) + << std::numeric_limits<IntType>::max() << value; + EXPECT_FALSE(parse_func(s, &parsed_value, base)); + + // Test underflow + s.clear(); + absl::strings_internal::OStringStream(&s) + << std::numeric_limits<IntType>::min() << value; + EXPECT_FALSE(parse_func(s, &parsed_value, base)); + } +} TEST(stringtest, safe_strtou32_base) { for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) { diff --git a/third_party/abseil-cpp/absl/strings/str_cat.cc b/third_party/abseil-cpp/absl/strings/str_cat.cc index d9afe2f385..f4a77493a4 100644 --- a/third_party/abseil-cpp/absl/strings/str_cat.cc +++ b/third_party/abseil-cpp/absl/strings/str_cat.cc @@ -141,12 +141,12 @@ namespace strings_internal { std::string CatPieces(std::initializer_list<absl::string_view> pieces) { std::string result; size_t total_size = 0; - for (const absl::string_view piece : pieces) total_size += piece.size(); + for (const absl::string_view& piece : pieces) total_size += piece.size(); strings_internal::STLStringResizeUninitialized(&result, total_size); char* const begin = &result[0]; char* out = begin; - for (const absl::string_view piece : pieces) { + for (const absl::string_view& piece : pieces) { const size_t this_size = piece.size(); if (this_size != 0) { memcpy(out, piece.data(), this_size); @@ -170,15 +170,15 @@ void AppendPieces(std::string* dest, std::initializer_list<absl::string_view> pieces) { size_t old_size = dest->size(); size_t total_size = old_size; - for (const absl::string_view piece : pieces) { + for (const absl::string_view& piece : pieces) { ASSERT_NO_OVERLAP(*dest, piece); total_size += piece.size(); } - strings_internal::STLStringResizeUninitialized(dest, total_size); + strings_internal::STLStringResizeUninitializedAmortized(dest, total_size); char* const begin = &(*dest)[0]; char* out = begin + old_size; - for (const absl::string_view piece : pieces) { + for (const absl::string_view& piece : pieces) { const size_t this_size = piece.size(); if (this_size != 0) { memcpy(out, piece.data(), this_size); @@ -199,7 +199,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) { ASSERT_NO_OVERLAP(*dest, a); ASSERT_NO_OVERLAP(*dest, b); std::string::size_type old_size = dest->size(); - strings_internal::STLStringResizeUninitialized( + strings_internal::STLStringResizeUninitializedAmortized( dest, old_size + a.size() + b.size()); char* const begin = &(*dest)[0]; char* out = begin + old_size; @@ -214,7 +214,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, ASSERT_NO_OVERLAP(*dest, b); ASSERT_NO_OVERLAP(*dest, c); std::string::size_type old_size = dest->size(); - strings_internal::STLStringResizeUninitialized( + strings_internal::STLStringResizeUninitializedAmortized( dest, old_size + a.size() + b.size() + c.size()); char* const begin = &(*dest)[0]; char* out = begin + old_size; @@ -231,7 +231,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, ASSERT_NO_OVERLAP(*dest, c); ASSERT_NO_OVERLAP(*dest, d); std::string::size_type old_size = dest->size(); - strings_internal::STLStringResizeUninitialized( + strings_internal::STLStringResizeUninitializedAmortized( dest, old_size + a.size() + b.size() + c.size() + d.size()); char* const begin = &(*dest)[0]; char* out = begin + old_size; diff --git a/third_party/abseil-cpp/absl/strings/str_cat.h b/third_party/abseil-cpp/absl/strings/str_cat.h index 292fa23597..a8a85c7322 100644 --- a/third_party/abseil-cpp/absl/strings/str_cat.h +++ b/third_party/abseil-cpp/absl/strings/str_cat.h @@ -253,7 +253,7 @@ class AlphaNum { const std::basic_string<char, std::char_traits<char>, Allocator>& str) : piece_(str) {} - // Use std::string literals ":" instead of character literals ':'. + // Use string literals ":" instead of character literals ':'. AlphaNum(char c) = delete; // NOLINT(runtime/explicit) AlphaNum(const AlphaNum&) = delete; diff --git a/third_party/abseil-cpp/absl/strings/str_cat_benchmark.cc b/third_party/abseil-cpp/absl/strings/str_cat_benchmark.cc index 14c63b3fc9..02c4dbe6d8 100644 --- a/third_party/abseil-cpp/absl/strings/str_cat_benchmark.cc +++ b/third_party/abseil-cpp/absl/strings/str_cat_benchmark.cc @@ -23,7 +23,7 @@ namespace { const char kStringOne[] = "Once Upon A Time, "; -const char kStringTwo[] = "There was a std::string benchmark"; +const char kStringTwo[] = "There was a string benchmark"; // We want to include negative numbers in the benchmark, so this function // is used to count 0, 1, -1, 2, -2, 3, -3, ... @@ -137,4 +137,51 @@ void BM_DoubleToString_By_SixDigits(benchmark::State& state) { } BENCHMARK(BM_DoubleToString_By_SixDigits); +template <typename... Chunks> +void BM_StrAppendImpl(benchmark::State& state, size_t total_bytes, + Chunks... chunks) { + for (auto s : state) { + std::string result; + while (result.size() < total_bytes) { + absl::StrAppend(&result, chunks...); + benchmark::DoNotOptimize(result); + } + } +} + +void BM_StrAppend(benchmark::State& state) { + const int total_bytes = state.range(0); + const int chunks_at_a_time = state.range(1); + const absl::string_view kChunk = "0123456789"; + + switch (chunks_at_a_time) { + case 1: + return BM_StrAppendImpl(state, total_bytes, kChunk); + case 2: + return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk); + case 4: + return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk, + kChunk); + case 8: + return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk, + kChunk, kChunk, kChunk, kChunk, kChunk); + default: + std::abort(); + } +} + +template <typename B> +void StrAppendConfig(B* benchmark) { + for (int bytes : {10, 100, 1000, 10000}) { + for (int chunks : {1, 2, 4, 8}) { + // Only add the ones that divide properly. Otherwise we are over counting. + if (bytes % (10 * chunks) == 0) { + benchmark->Args({bytes, chunks}); + } + } + } +} + +BENCHMARK(BM_StrAppend)->Apply(StrAppendConfig); + } // namespace diff --git a/third_party/abseil-cpp/absl/strings/str_cat_test.cc b/third_party/abseil-cpp/absl/strings/str_cat_test.cc index be39880be7..f3770dc076 100644 --- a/third_party/abseil-cpp/absl/strings/str_cat_test.cc +++ b/third_party/abseil-cpp/absl/strings/str_cat_test.cc @@ -162,7 +162,7 @@ TEST(StrCat, Basics) { EXPECT_EQ(result, "12345678910, 10987654321!"); std::string one = - "1"; // Actually, it's the size of this std::string that we want; a + "1"; // Actually, it's the size of this string that we want; a // 64-bit build distinguishes between size_t and uint64_t, // even though they're both unsigned 64-bit values. result = absl::StrCat("And a ", one.size(), " and a ", @@ -375,7 +375,7 @@ TEST(StrAppend, Basics) { EXPECT_EQ(result.substr(old_size), "12345678910, 10987654321!"); std::string one = - "1"; // Actually, it's the size of this std::string that we want; a + "1"; // Actually, it's the size of this string that we want; a // 64-bit build distinguishes between size_t and uint64_t, // even though they're both unsigned 64-bit values. old_size = result.size(); @@ -463,7 +463,7 @@ TEST(StrAppend, CornerCases) { } TEST(StrAppend, CornerCasesNonEmptyAppend) { - for (std::string result : {"hello", "a std::string too long to fit in the SSO"}) { + for (std::string result : {"hello", "a string too long to fit in the SSO"}) { const std::string expected = result; absl::StrAppend(&result, ""); EXPECT_EQ(result, expected); diff --git a/third_party/abseil-cpp/absl/strings/str_format.h b/third_party/abseil-cpp/absl/strings/str_format.h index 2f9b4b2786..4b05c70c23 100644 --- a/third_party/abseil-cpp/absl/strings/str_format.h +++ b/third_party/abseil-cpp/absl/strings/str_format.h @@ -19,7 +19,7 @@ // // The `str_format` library is a typesafe replacement for the family of // `printf()` string formatting routines within the `<cstdio>` standard library -// header. Like the `printf` family, the `str_format` uses a "format string" to +// header. Like the `printf` family, `str_format` uses a "format string" to // perform argument substitutions based on types. See the `FormatSpec` section // below for format string documentation. // @@ -57,8 +57,7 @@ // arbitrary sink types: // // * A generic `Format()` function to write outputs to arbitrary sink types, -// which must implement a `RawSinkFormat` interface. (See -// `str_format_sink.h` for more information.) +// which must implement a `FormatRawSink` interface. // // * A `FormatUntyped()` function that is similar to `Format()` except it is // loosely typed. `FormatUntyped()` is not a template and does not perform @@ -66,8 +65,7 @@ // boolean from a runtime check. // // In addition, the `str_format` library provides extension points for -// augmenting formatting to new types. These extensions are fully documented -// within the `str_format_extension.h` header file. +// augmenting formatting to new types. See "StrFormat Extensions" below. #ifndef ABSL_STRINGS_STR_FORMAT_H_ #define ABSL_STRINGS_STR_FORMAT_H_ @@ -255,8 +253,8 @@ class FormatCountCapture { // argument, etc. template <typename... Args> -using FormatSpec = - typename str_format_internal::FormatSpecDeductionBarrier<Args...>::type; +using FormatSpec = str_format_internal::FormatSpecTemplate< + str_format_internal::ArgumentToConv<Args>()...>; // ParsedFormat // @@ -283,9 +281,36 @@ using FormatSpec = // } else { // ... error case ... // } + +#if defined(__cpp_nontype_template_parameter_auto) +// If C++17 is available, an 'extended' format is also allowed that can specify +// multiple conversion characters per format argument, using a combination of +// `absl::FormatConversionCharSet` enum values (logically a set union) +// via the `|` operator. (Single character-based arguments are still accepted, +// but cannot be combined). Some common conversions also have predefined enum +// values, such as `absl::FormatConversionCharSet::kIntegral`. +// +// Example: +// // Extended format supports multiple conversion characters per argument, +// // specified via a combination of `FormatConversionCharSet` enums. +// using MyFormat = absl::ParsedFormat<absl::FormatConversionCharSet::d | +// absl::FormatConversionCharSet::x>; +// MyFormat GetFormat(bool use_hex) { +// if (use_hex) return MyFormat("foo %x bar"); +// return MyFormat("foo %d bar"); +// } +// // `format` can be used with any value that supports 'd' and 'x', +// // like `int`. +// auto format = GetFormat(use_hex); +// value = StringF(format, i); +template <auto... Conv> +using ParsedFormat = absl::str_format_internal::ExtendedParsedFormat< + absl::str_format_internal::ToFormatConversionCharSet(Conv)...>; +#else template <char... Conv> using ParsedFormat = str_format_internal::ExtendedParsedFormat< - str_format_internal::ConversionCharToConv(Conv)...>; + absl::str_format_internal::ToFormatConversionCharSet(Conv)...>; +#endif // defined(__cpp_nontype_template_parameter_auto) // StrFormat() // @@ -432,6 +457,16 @@ int SNPrintF(char* output, std::size_t size, const FormatSpec<Args...>& format, // // FormatRawSink is a type erased wrapper around arbitrary sink objects // specifically used as an argument to `Format()`. +// +// All the object has to do define an overload of `AbslFormatFlush()` for the +// sink, usually by adding a ADL-based free function in the same namespace as +// the sink: +// +// void AbslFormatFlush(MySink* dest, absl::string_view part); +// +// where `dest` is the pointer passed to `absl::Format()`. The function should +// append `part` to `dest`. +// // FormatRawSink does not own the passed sink object. The passed object must // outlive the FormatRawSink. class FormatRawSink { @@ -455,12 +490,13 @@ class FormatRawSink { // `absl::FormatRawSink` interface), using a format string and zero or more // additional arguments. // -// By default, `std::string` and `std::ostream` are supported as destination -// objects. If a `std::string` is used the formatted string is appended to it. +// By default, `std::string`, `std::ostream`, and `absl::Cord` are supported as +// destination objects. If a `std::string` is used the formatted string is +// appended to it. // -// `absl::Format()` is a generic version of `absl::StrFormat(), for custom -// sinks. The format string, like format strings for `StrFormat()`, is checked -// at compile-time. +// `absl::Format()` is a generic version of `absl::StrAppendFormat()`, for +// custom sinks. The format string, like format strings for `StrFormat()`, is +// checked at compile-time. // // On failure, this function returns `false` and the state of the sink is // unspecified. @@ -500,8 +536,7 @@ using FormatArg = str_format_internal::FormatArgImpl; // The arguments are provided in an `absl::Span<const absl::FormatArg>`. // Each `absl::FormatArg` object binds to a single argument and keeps a // reference to it. The values used to create the `FormatArg` objects must -// outlive this function call. (See `str_format_arg.h` for information on -// the `FormatArg` class.)_ +// outlive this function call. // // Example: // @@ -531,6 +566,246 @@ ABSL_MUST_USE_RESULT inline bool FormatUntyped( str_format_internal::UntypedFormatSpecImpl::Extract(format), args); } +//------------------------------------------------------------------------------ +// StrFormat Extensions +//------------------------------------------------------------------------------ +// +// AbslFormatConvert() +// +// The StrFormat library provides a customization API for formatting +// user-defined types using absl::StrFormat(). The API relies on detecting an +// overload in the user-defined type's namespace of a free (non-member) +// `AbslFormatConvert()` function, usually as a friend definition with the +// following signature: +// +// absl::FormatConvertResult<...> AbslFormatConvert( +// const X& value, +// const absl::FormatConversionSpec& spec, +// absl::FormatSink *sink); +// +// An `AbslFormatConvert()` overload for a type should only be declared in the +// same file and namespace as said type. +// +// The abstractions within this definition include: +// +// * An `absl::FormatConversionSpec` to specify the fields to pull from a +// user-defined type's format string +// * An `absl::FormatSink` to hold the converted string data during the +// conversion process. +// * An `absl::FormatConvertResult` to hold the status of the returned +// formatting operation +// +// The return type encodes all the conversion characters that your +// AbslFormatConvert() routine accepts. The return value should be {true}. +// A return value of {false} will result in `StrFormat()` returning +// an empty string. This result will be propagated to the result of +// `FormatUntyped`. +// +// Example: +// +// struct Point { +// // To add formatting support to `Point`, we simply need to add a free +// // (non-member) function `AbslFormatConvert()`. This method interprets +// // `spec` to print in the request format. The allowed conversion characters +// // can be restricted via the type of the result, in this example +// // string and integral formatting are allowed (but not, for instance +// // floating point characters like "%f"). You can add such a free function +// // using a friend declaration within the body of the class: +// friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString | +// absl::FormatConversionCharSet::kIntegral> +// AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec, +// absl::FormatSink* s) { +// if (spec.conversion_char() == absl::FormatConversionChar::s) { +// s->Append(absl::StrCat("x=", p.x, " y=", p.y)); +// } else { +// s->Append(absl::StrCat(p.x, ",", p.y)); +// } +// return {true}; +// } +// +// int x; +// int y; +// }; + +// clang-format off + +// FormatConversionChar +// +// Specifies the formatting character provided in the format string +// passed to `StrFormat()`. +enum class FormatConversionChar : uint8_t { + c, s, // text + d, i, o, u, x, X, // int + f, F, e, E, g, G, a, A, // float + n, p // misc +}; +// clang-format on + +// FormatConversionSpec +// +// Specifies modifications to the conversion of the format string, through use +// of one or more format flags in the source format string. +class FormatConversionSpec { + public: + // FormatConversionSpec::is_basic() + // + // Indicates that width and precision are not specified, and no additional + // flags are set for this conversion character in the format string. + bool is_basic() const { return impl_.is_basic(); } + + // FormatConversionSpec::has_left_flag() + // + // Indicates whether the result should be left justified for this conversion + // character in the format string. This flag is set through use of a '-' + // character in the format string. E.g. "%-s" + bool has_left_flag() const { return impl_.has_left_flag(); } + + // FormatConversionSpec::has_show_pos_flag() + // + // Indicates whether a sign column is prepended to the result for this + // conversion character in the format string, even if the result is positive. + // This flag is set through use of a '+' character in the format string. + // E.g. "%+d" + bool has_show_pos_flag() const { return impl_.has_show_pos_flag(); } + + // FormatConversionSpec::has_sign_col_flag() + // + // Indicates whether a mandatory sign column is added to the result for this + // conversion character. This flag is set through use of a space character + // (' ') in the format string. E.g. "% i" + bool has_sign_col_flag() const { return impl_.has_sign_col_flag(); } + + // FormatConversionSpec::has_alt_flag() + // + // Indicates whether an "alternate" format is applied to the result for this + // conversion character. Alternative forms depend on the type of conversion + // character, and unallowed alternatives are undefined. This flag is set + // through use of a '#' character in the format string. E.g. "%#h" + bool has_alt_flag() const { return impl_.has_alt_flag(); } + + // FormatConversionSpec::has_zero_flag() + // + // Indicates whether zeroes should be prepended to the result for this + // conversion character instead of spaces. This flag is set through use of the + // '0' character in the format string. E.g. "%0f" + bool has_zero_flag() const { return impl_.has_zero_flag(); } + + // FormatConversionSpec::conversion_char() + // + // Returns the underlying conversion character. + FormatConversionChar conversion_char() const { + return impl_.conversion_char(); + } + + // FormatConversionSpec::width() + // + // Returns the specified width (indicated through use of a non-zero integer + // value or '*' character) of the conversion character. If width is + // unspecified, it returns a negative value. + int width() const { return impl_.width(); } + + // FormatConversionSpec::precision() + // + // Returns the specified precision (through use of the '.' character followed + // by a non-zero integer value or '*' character) of the conversion character. + // If precision is unspecified, it returns a negative value. + int precision() const { return impl_.precision(); } + + private: + explicit FormatConversionSpec( + str_format_internal::FormatConversionSpecImpl impl) + : impl_(impl) {} + + friend str_format_internal::FormatConversionSpecImpl; + + absl::str_format_internal::FormatConversionSpecImpl impl_; +}; + +// Type safe OR operator for FormatConversionCharSet to allow accepting multiple +// conversion chars in custom format converters. +constexpr FormatConversionCharSet operator|(FormatConversionCharSet a, + FormatConversionCharSet b) { + return static_cast<FormatConversionCharSet>(static_cast<uint64_t>(a) | + static_cast<uint64_t>(b)); +} + +// FormatConversionCharSet +// +// Specifies the _accepted_ conversion types as a template parameter to +// FormatConvertResult for custom implementations of `AbslFormatConvert`. +// Note the helper predefined alias definitions (kIntegral, etc.) below. +enum class FormatConversionCharSet : uint64_t { + // text + c = str_format_internal::FormatConversionCharToConvInt('c'), + s = str_format_internal::FormatConversionCharToConvInt('s'), + // integer + d = str_format_internal::FormatConversionCharToConvInt('d'), + i = str_format_internal::FormatConversionCharToConvInt('i'), + o = str_format_internal::FormatConversionCharToConvInt('o'), + u = str_format_internal::FormatConversionCharToConvInt('u'), + x = str_format_internal::FormatConversionCharToConvInt('x'), + X = str_format_internal::FormatConversionCharToConvInt('X'), + // Float + f = str_format_internal::FormatConversionCharToConvInt('f'), + F = str_format_internal::FormatConversionCharToConvInt('F'), + e = str_format_internal::FormatConversionCharToConvInt('e'), + E = str_format_internal::FormatConversionCharToConvInt('E'), + g = str_format_internal::FormatConversionCharToConvInt('g'), + G = str_format_internal::FormatConversionCharToConvInt('G'), + a = str_format_internal::FormatConversionCharToConvInt('a'), + A = str_format_internal::FormatConversionCharToConvInt('A'), + // misc + n = str_format_internal::FormatConversionCharToConvInt('n'), + p = str_format_internal::FormatConversionCharToConvInt('p'), + + // Used for width/precision '*' specification. + kStar = static_cast<uint64_t>( + absl::str_format_internal::FormatConversionCharSetInternal::kStar), + // Some predefined values: + kIntegral = d | i | u | o | x | X, + kFloating = a | e | f | g | A | E | F | G, + kNumeric = kIntegral | kFloating, + kString = s, + kPointer = p, +}; + +// FormatSink +// +// An abstraction to which conversions write their string data. +// +class FormatSink { + public: + // Appends `count` copies of `ch`. + void Append(size_t count, char ch) { sink_->Append(count, ch); } + + void Append(string_view v) { sink_->Append(v); } + + // Appends the first `precision` bytes of `v`. If this is less than + // `width`, spaces will be appended first (if `left` is false), or + // after (if `left` is true) to ensure the total amount appended is + // at least `width`. + bool PutPaddedString(string_view v, int width, int precision, bool left) { + return sink_->PutPaddedString(v, width, precision, left); + } + + private: + friend str_format_internal::FormatSinkImpl; + explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {} + str_format_internal::FormatSinkImpl* sink_; +}; + +// FormatConvertResult +// +// Indicates whether a call to AbslFormatConvert() was successful. +// This return type informs the StrFormat extension framework (through +// ADL but using the return type) of what conversion characters are supported. +// It is strongly discouraged to return {false}, as this will result in an +// empty string in StrFormat. +template <FormatConversionCharSet C> +struct FormatConvertResult { + bool value; +}; + ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/strings/str_format_test.cc b/third_party/abseil-cpp/absl/strings/str_format_test.cc index acbdbf4a23..c60027ad29 100644 --- a/third_party/abseil-cpp/absl/strings/str_format_test.cc +++ b/third_party/abseil-cpp/absl/strings/str_format_test.cc @@ -1,3 +1,18 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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/strings/str_format.h" #include <cstdarg> #include <cstdint> @@ -6,7 +21,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "absl/strings/str_format.h" +#include "absl/strings/cord.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" namespace absl { @@ -242,7 +258,7 @@ class TempFile { std::FILE* file() const { return file_; } - // Read the file into a std::string. + // Read the file into a string. std::string ReadFile() { std::fseek(file_, 0, SEEK_END); int size = std::ftell(file_); @@ -345,11 +361,12 @@ TEST(StrFormat, BehavesAsDocumented) { EXPECT_EQ(StrFormat("%c", int{'a'}), "a"); EXPECT_EQ(StrFormat("%c", long{'a'}), "a"); // NOLINT EXPECT_EQ(StrFormat("%c", uint64_t{'a'}), "a"); - // "s" - std::string Eg: "C" -> "C", std::string("C++") -> "C++" + // "s" - string Eg: "C" -> "C", std::string("C++") -> "C++" // Formats std::string, char*, string_view, and Cord. EXPECT_EQ(StrFormat("%s", "C"), "C"); EXPECT_EQ(StrFormat("%s", std::string("C++")), "C++"); EXPECT_EQ(StrFormat("%s", string_view("view")), "view"); + EXPECT_EQ(StrFormat("%s", absl::Cord("cord")), "cord"); // Integral Conversion // These format integral types: char, int, long, uint64_t, etc. EXPECT_EQ(StrFormat("%d", char{10}), "10"); @@ -450,7 +467,7 @@ struct SummarizeConsumer { if (conv.precision.is_from_arg()) { *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*"; } - *out += FormatConversionCharToChar(conv.conv); + *out += str_format_internal::FormatConversionCharToChar(conv.conv); *out += "}"; return true; } @@ -532,76 +549,152 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) { EXPECT_FALSE((ParsedFormat<'s', 'd', 'g'>::New(format))); } -using str_format_internal::Conv; +#if defined(__cpp_nontype_template_parameter_auto) + +template <auto T> +std::true_type IsValidParsedFormatArgTest(ParsedFormat<T>*); + +template <auto T> +std::false_type IsValidParsedFormatArgTest(...); + +template <auto T> +using IsValidParsedFormatArg = decltype(IsValidParsedFormatArgTest<T>(nullptr)); + +TEST_F(ParsedFormatTest, OnlyValidTypesAllowed) { + ASSERT_TRUE(IsValidParsedFormatArg<'c'>::value); + + ASSERT_TRUE(IsValidParsedFormatArg<FormatConversionCharSet::d>::value); + + ASSERT_TRUE(IsValidParsedFormatArg<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::x>::value); + ASSERT_TRUE( + IsValidParsedFormatArg<absl::FormatConversionCharSet::kIntegral>::value); + + // This is an easy mistake to make, however, this will reduce to an integer + // which has no meaning, so we need to ensure it doesn't compile. + ASSERT_FALSE(IsValidParsedFormatArg<'x' | 'd'>::value); + + // For now, we disallow construction based on ConversionChar (rather than + // CharSet) + ASSERT_FALSE(IsValidParsedFormatArg<absl::FormatConversionChar::d>::value); +} + +TEST_F(ParsedFormatTest, ExtendedTyping) { + EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::d>::New("")); + ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::d>::New("%d")); + auto v1 = ParsedFormat<'d', absl::FormatConversionCharSet::s>::New("%d%s"); + ASSERT_TRUE(v1); + auto v2 = ParsedFormat<absl::FormatConversionCharSet::d, 's'>::New("%d%s"); + ASSERT_TRUE(v2); + auto v3 = ParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::s, + 's'>::New("%d%s"); + ASSERT_TRUE(v3); + auto v4 = ParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::s, + 's'>::New("%s%s"); + ASSERT_TRUE(v4); +} +#endif TEST_F(ParsedFormatTest, UncheckedCorrect) { - auto f = ExtendedParsedFormat<Conv::d>::New("ABC%dDEF"); + auto f = + ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("ABC%dDEF"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f)); std::string format = "%sFFF%dZZZ%f"; - auto f2 = - ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New(format); + auto f2 = ExtendedParsedFormat< + absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::kFloating>::New(format); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2)); - f2 = ExtendedParsedFormat<Conv::string, Conv::d, Conv::floating>::New( - "%s %d %f"); + f2 = ExtendedParsedFormat< + absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::kFloating>::New("%s %d %f"); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2)); - auto star = ExtendedParsedFormat<Conv::star, Conv::d>::New("%*d"); + auto star = + ExtendedParsedFormat<absl::FormatConversionCharSet::kStar, + absl::FormatConversionCharSet::d>::New("%*d"); ASSERT_TRUE(star); EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star)); - auto dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d"); + auto dollar = + ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("%2$s %1$d"); ASSERT_TRUE(dollar); EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar)); // with reuse - dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d %1$d"); + dollar = ExtendedParsedFormat< + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("%2$s %1$d %1$d"); ASSERT_TRUE(dollar); EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar)); } TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) { - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC"))); - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("%dABC"))); - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC%2$s"))); - auto f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC"); + EXPECT_FALSE( + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("ABC"))); + EXPECT_FALSE( + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("%dABC"))); + EXPECT_FALSE( + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::New("ABC%2$s"))); + auto f = ExtendedParsedFormat< + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f)); - f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("%dABC"); + f = ExtendedParsedFormat< + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::NewAllowIgnored("%dABC"); ASSERT_TRUE(f); EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f)); - f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC%2$s"); + f = ExtendedParsedFormat< + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC%2$s"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f)); } TEST_F(ParsedFormatTest, UncheckedMultipleTypes) { - auto dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d %1$x"); + auto dx = + ExtendedParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::x>::New("%1$d %1$x"); EXPECT_TRUE(dx); EXPECT_EQ("{1$d:1$d}[ ]{1$x:1$x}", SummarizeParsedFormat(*dx)); - dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d"); + dx = ExtendedParsedFormat<absl::FormatConversionCharSet::d | + absl::FormatConversionCharSet::x>::New("%1$d"); EXPECT_TRUE(dx); EXPECT_EQ("{1$d:1$d}", SummarizeParsedFormat(*dx)); } TEST_F(ParsedFormatTest, UncheckedIncorrect) { - EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New("")); + EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("")); - EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New("ABC%dDEF%d")); + EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New( + "ABC%dDEF%d")); std::string format = "%sFFF%dZZZ%f"; - EXPECT_FALSE((ExtendedParsedFormat<Conv::s, Conv::d, Conv::g>::New(format))); + EXPECT_FALSE( + (ExtendedParsedFormat<absl::FormatConversionCharSet::s, + absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::g>::New(format))); } TEST_F(ParsedFormatTest, RegressionMixPositional) { - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::o>::New("%1$d %o"))); + EXPECT_FALSE( + (ExtendedParsedFormat<absl::FormatConversionCharSet::d, + absl::FormatConversionCharSet::o>::New("%1$d %o"))); } using FormatWrapperTest = ::testing::Test; @@ -626,6 +719,38 @@ TEST_F(FormatWrapperTest, ParsedFormat) { ABSL_NAMESPACE_END } // namespace absl +using FormatExtensionTest = ::testing::Test; + +struct Point { + friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString | + absl::FormatConversionCharSet::kIntegral> + AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec, + absl::FormatSink* s) { + if (spec.conversion_char() == absl::FormatConversionChar::s) { + s->Append(absl::StrCat("x=", p.x, " y=", p.y)); + } else { + s->Append(absl::StrCat(p.x, ",", p.y)); + } + return {true}; + } + + int x = 10; + int y = 20; +}; + +TEST_F(FormatExtensionTest, AbslFormatConvertExample) { + Point p; + EXPECT_EQ(absl::StrFormat("a %s z", p), "a x=10 y=20 z"); + EXPECT_EQ(absl::StrFormat("a %d z", p), "a 10,20 z"); + + // Typed formatting will fail to compile an invalid format. + // StrFormat("%f", p); // Does not compile. + std::string actual; + absl::UntypedFormatSpec f1("%f"); + // FormatUntyped will return false for bad character. + EXPECT_FALSE(absl::FormatUntyped(&actual, f1, {absl::FormatArg(p)})); +} + // Some codegen thunks that we can use to easily dump the generated assembly for // different StrFormat calls. diff --git a/third_party/abseil-cpp/absl/strings/str_join.h b/third_party/abseil-cpp/absl/strings/str_join.h index ae5731a42b..33534536cf 100644 --- a/third_party/abseil-cpp/absl/strings/str_join.h +++ b/third_party/abseil-cpp/absl/strings/str_join.h @@ -144,7 +144,7 @@ strings_internal::DereferenceFormatterImpl<Formatter> DereferenceFormatter( std::forward<Formatter>(f)); } -// Function overload of `DererefenceFormatter()` for using a default +// Function overload of `DereferenceFormatter()` for using a default // `AlphaNumFormatter()`. inline strings_internal::DereferenceFormatterImpl< strings_internal::AlphaNumFormatterImpl> diff --git a/third_party/abseil-cpp/absl/strings/str_join_test.cc b/third_party/abseil-cpp/absl/strings/str_join_test.cc index 921d9c2bfa..2be6256e43 100644 --- a/third_party/abseil-cpp/absl/strings/str_join_test.cc +++ b/third_party/abseil-cpp/absl/strings/str_join_test.cc @@ -134,26 +134,26 @@ TEST(StrJoin, APIExamples) { // { - // Empty range yields an empty std::string. + // Empty range yields an empty string. std::vector<std::string> v; EXPECT_EQ("", absl::StrJoin(v, "-")); } { - // A range of 1 element gives a std::string with that element but no + // A range of 1 element gives a string with that element but no // separator. std::vector<std::string> v = {"foo"}; EXPECT_EQ("foo", absl::StrJoin(v, "-")); } { - // A range with a single empty std::string element + // A range with a single empty string element std::vector<std::string> v = {""}; EXPECT_EQ("", absl::StrJoin(v, "-")); } { - // A range with 2 elements, one of which is an empty std::string + // A range with 2 elements, one of which is an empty string std::vector<std::string> v = {"a", ""}; EXPECT_EQ("a-", absl::StrJoin(v, "-")); } diff --git a/third_party/abseil-cpp/absl/strings/str_replace_benchmark.cc b/third_party/abseil-cpp/absl/strings/str_replace_benchmark.cc index 95b2dc105e..01331da29f 100644 --- a/third_party/abseil-cpp/absl/strings/str_replace_benchmark.cc +++ b/third_party/abseil-cpp/absl/strings/str_replace_benchmark.cc @@ -62,7 +62,7 @@ void SetUpStrings() { } } // big_string->resize(50); - // OK, we've set up the std::string, now let's set up expectations - first by + // OK, we've set up the string, now let's set up expectations - first by // just replacing "the" with "box" after_replacing_the = new std::string(*big_string); for (size_t pos = 0; diff --git a/third_party/abseil-cpp/absl/strings/str_replace_test.cc b/third_party/abseil-cpp/absl/strings/str_replace_test.cc index 1ca23aff55..9d8c7f75b5 100644 --- a/third_party/abseil-cpp/absl/strings/str_replace_test.cc +++ b/third_party/abseil-cpp/absl/strings/str_replace_test.cc @@ -25,7 +25,7 @@ TEST(StrReplaceAll, OneReplacement) { std::string s; - // Empty std::string. + // Empty string. s = absl::StrReplaceAll(s, {{"", ""}}); EXPECT_EQ(s, ""); s = absl::StrReplaceAll(s, {{"x", ""}}); @@ -47,7 +47,7 @@ TEST(StrReplaceAll, OneReplacement) { s = absl::StrReplaceAll("abc", {{"xyz", "123"}}); EXPECT_EQ(s, "abc"); - // Replace entire std::string. + // Replace entire string. s = absl::StrReplaceAll("abc", {{"abc", "xyz"}}); EXPECT_EQ(s, "xyz"); @@ -88,7 +88,7 @@ TEST(StrReplaceAll, OneReplacement) { TEST(StrReplaceAll, ManyReplacements) { std::string s; - // Empty std::string. + // Empty string. s = absl::StrReplaceAll("", {{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}}); EXPECT_EQ(s, ""); @@ -96,7 +96,7 @@ TEST(StrReplaceAll, ManyReplacements) { s = absl::StrReplaceAll("abc", {{"", ""}, {"", "y"}, {"x", ""}}); EXPECT_EQ(s, "abc"); - // Replace entire std::string, one char at a time + // Replace entire string, one char at a time s = absl::StrReplaceAll("abc", {{"a", "x"}, {"b", "y"}, {"c", "z"}}); EXPECT_EQ(s, "xyz"); s = absl::StrReplaceAll("zxy", {{"z", "x"}, {"x", "y"}, {"y", "z"}}); @@ -264,7 +264,7 @@ TEST(StrReplaceAll, Inplace) { std::string s; int reps; - // Empty std::string. + // Empty string. s = ""; reps = absl::StrReplaceAll({{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}}, &s); EXPECT_EQ(reps, 0); @@ -276,7 +276,7 @@ TEST(StrReplaceAll, Inplace) { EXPECT_EQ(reps, 0); EXPECT_EQ(s, "abc"); - // Replace entire std::string, one char at a time + // Replace entire string, one char at a time s = "abc"; reps = absl::StrReplaceAll({{"a", "x"}, {"b", "y"}, {"c", "z"}}, &s); EXPECT_EQ(reps, 3); diff --git a/third_party/abseil-cpp/absl/strings/str_split.cc b/third_party/abseil-cpp/absl/strings/str_split.cc index d0f8666910..e08c26b6bb 100644 --- a/third_party/abseil-cpp/absl/strings/str_split.cc +++ b/third_party/abseil-cpp/absl/strings/str_split.cc @@ -42,7 +42,7 @@ absl::string_view GenericFind(absl::string_view text, absl::string_view delimiter, size_t pos, FindPolicy find_policy) { if (delimiter.empty() && text.length() > 0) { - // Special case for empty std::string delimiters: always return a zero-length + // Special case for empty string delimiters: always return a zero-length // absl::string_view referring to the item at position 1 past pos. return absl::string_view(text.data() + pos + 1, 0); } @@ -127,7 +127,7 @@ absl::string_view ByLength::Find(absl::string_view text, size_t pos) const { pos = std::min(pos, text.size()); // truncate `pos` absl::string_view substr = text.substr(pos); - // If the std::string is shorter than the chunk size we say we + // If the string is shorter than the chunk size we say we // "can't find the delimiter" so this will be the last chunk. if (substr.length() <= static_cast<size_t>(length_)) return absl::string_view(text.data() + text.size(), 0); diff --git a/third_party/abseil-cpp/absl/strings/str_split.h b/third_party/abseil-cpp/absl/strings/str_split.h index a79cd4a0ce..bfbca422a8 100644 --- a/third_party/abseil-cpp/absl/strings/str_split.h +++ b/third_party/abseil-cpp/absl/strings/str_split.h @@ -44,6 +44,7 @@ #include <vector> #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" #include "absl/strings/internal/str_split_internal.h" #include "absl/strings/string_view.h" #include "absl/strings/strip.h" @@ -368,6 +369,12 @@ struct SkipWhitespace { } }; +template <typename T> +using EnableSplitIfString = + typename std::enable_if<std::is_same<T, std::string>::value || + std::is_same<T, const std::string>::value, + int>::type; + //------------------------------------------------------------------------------ // StrSplit() //------------------------------------------------------------------------------ @@ -488,22 +495,50 @@ struct SkipWhitespace { // Try not to depend on this distinction because the bug may one day be fixed. template <typename Delimiter> strings_internal::Splitter< - typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty> + typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty, + absl::string_view> StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d) { using DelimiterType = typename strings_internal::SelectDelimiter<Delimiter>::type; - return strings_internal::Splitter<DelimiterType, AllowEmpty>( + return strings_internal::Splitter<DelimiterType, AllowEmpty, + absl::string_view>( + text.value(), DelimiterType(d), AllowEmpty()); +} + +template <typename Delimiter, typename StringType, + EnableSplitIfString<StringType> = 0> +strings_internal::Splitter< + typename strings_internal::SelectDelimiter<Delimiter>::type, AllowEmpty, + std::string> +StrSplit(StringType&& text, Delimiter d) { + using DelimiterType = + typename strings_internal::SelectDelimiter<Delimiter>::type; + return strings_internal::Splitter<DelimiterType, AllowEmpty, std::string>( std::move(text), DelimiterType(d), AllowEmpty()); } template <typename Delimiter, typename Predicate> strings_internal::Splitter< - typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate> + typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate, + absl::string_view> StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d, Predicate p) { using DelimiterType = typename strings_internal::SelectDelimiter<Delimiter>::type; - return strings_internal::Splitter<DelimiterType, Predicate>( + return strings_internal::Splitter<DelimiterType, Predicate, + absl::string_view>( + text.value(), DelimiterType(d), std::move(p)); +} + +template <typename Delimiter, typename Predicate, typename StringType, + EnableSplitIfString<StringType> = 0> +strings_internal::Splitter< + typename strings_internal::SelectDelimiter<Delimiter>::type, Predicate, + std::string> +StrSplit(StringType&& text, Delimiter d, Predicate p) { + using DelimiterType = + typename strings_internal::SelectDelimiter<Delimiter>::type; + return strings_internal::Splitter<DelimiterType, Predicate, std::string>( std::move(text), DelimiterType(d), std::move(p)); } diff --git a/third_party/abseil-cpp/absl/strings/str_split_test.cc b/third_party/abseil-cpp/absl/strings/str_split_test.cc index 02f27bc414..1b4427b849 100644 --- a/third_party/abseil-cpp/absl/strings/str_split_test.cc +++ b/third_party/abseil-cpp/absl/strings/str_split_test.cc @@ -27,8 +27,12 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "absl/base/dynamic_annotations.h" // for RunningOnValgrind +#include "absl/base/dynamic_annotations.h" #include "absl/base/macros.h" +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/node_hash_map.h" #include "absl/strings/numbers.h" namespace { @@ -71,7 +75,7 @@ TEST(Split, TraitsTest) { // namespaces just like callers will need to use. TEST(Split, APIExamples) { { - // Passes std::string delimiter. Assumes the default of ByString. + // Passes string delimiter. Assumes the default of ByString. std::vector<std::string> v = absl::StrSplit("a,b,c", ","); // NOLINT EXPECT_THAT(v, ElementsAre("a", "b", "c")); @@ -97,7 +101,7 @@ TEST(Split, APIExamples) { } { - // Uses the Literal std::string "=>" as the delimiter. + // Uses the Literal string "=>" as the delimiter. const std::vector<std::string> v = absl::StrSplit("a=>b=>c", "=>"); EXPECT_THAT(v, ElementsAre("a", "b", "c")); } @@ -121,17 +125,17 @@ TEST(Split, APIExamples) { } { - // Splits the input std::string into individual characters by using an empty - // std::string as the delimiter. + // Splits the input string into individual characters by using an empty + // string as the delimiter. std::vector<std::string> v = absl::StrSplit("abc", ""); EXPECT_THAT(v, ElementsAre("a", "b", "c")); } { - // Splits std::string data with embedded NUL characters, using NUL as the + // Splits string data with embedded NUL characters, using NUL as the // delimiter. A simple delimiter of "\0" doesn't work because strlen() will - // say that's the empty std::string when constructing the absl::string_view - // delimiter. Instead, a non-empty std::string containing NUL can be used as the + // say that's the empty string when constructing the absl::string_view + // delimiter. Instead, a non-empty string containing NUL can be used as the // delimiter. std::string embedded_nulls("a\0b\0c", 5); std::string null_delim("\0", 1); @@ -365,7 +369,7 @@ TEST(SplitIterator, EqualityAsEndCondition) { TEST(Splitter, RangeIterators) { auto splitter = absl::StrSplit("a,b,c", ','); std::vector<absl::string_view> output; - for (const absl::string_view p : splitter) { + for (const absl::string_view& p : splitter) { output.push_back(p); } EXPECT_THAT(output, ElementsAre("a", "b", "c")); @@ -403,6 +407,10 @@ TEST(Splitter, ConversionOperator) { TestConversionOperator<std::set<std::string>>(splitter); TestConversionOperator<std::multiset<absl::string_view>>(splitter); TestConversionOperator<std::multiset<std::string>>(splitter); + TestConversionOperator<absl::btree_set<absl::string_view>>(splitter); + TestConversionOperator<absl::btree_set<std::string>>(splitter); + TestConversionOperator<absl::btree_multiset<absl::string_view>>(splitter); + TestConversionOperator<absl::btree_multiset<std::string>>(splitter); TestConversionOperator<std::unordered_set<std::string>>(splitter); // Tests conversion to map-like objects. @@ -419,8 +427,36 @@ TEST(Splitter, ConversionOperator) { TestMapConversionOperator<std::multimap<std::string, absl::string_view>>( splitter); TestMapConversionOperator<std::multimap<std::string, std::string>>(splitter); + TestMapConversionOperator< + absl::btree_map<absl::string_view, absl::string_view>>(splitter); + TestMapConversionOperator<absl::btree_map<absl::string_view, std::string>>( + splitter); + TestMapConversionOperator<absl::btree_map<std::string, absl::string_view>>( + splitter); + TestMapConversionOperator<absl::btree_map<std::string, std::string>>( + splitter); + TestMapConversionOperator< + absl::btree_multimap<absl::string_view, absl::string_view>>(splitter); + TestMapConversionOperator< + absl::btree_multimap<absl::string_view, std::string>>(splitter); + TestMapConversionOperator< + absl::btree_multimap<std::string, absl::string_view>>(splitter); + TestMapConversionOperator<absl::btree_multimap<std::string, std::string>>( + splitter); TestMapConversionOperator<std::unordered_map<std::string, std::string>>( splitter); + TestMapConversionOperator< + absl::node_hash_map<absl::string_view, absl::string_view>>(splitter); + TestMapConversionOperator< + absl::node_hash_map<absl::string_view, std::string>>(splitter); + TestMapConversionOperator< + absl::node_hash_map<std::string, absl::string_view>>(splitter); + TestMapConversionOperator< + absl::flat_hash_map<absl::string_view, absl::string_view>>(splitter); + TestMapConversionOperator< + absl::flat_hash_map<absl::string_view, std::string>>(splitter); + TestMapConversionOperator< + absl::flat_hash_map<std::string, absl::string_view>>(splitter); // Tests conversion to std::pair @@ -436,7 +472,7 @@ TEST(Splitter, ConversionOperator) { // less-than, equal-to, and more-than 2 strings. TEST(Splitter, ToPair) { { - // Empty std::string + // Empty string std::pair<std::string, std::string> p = absl::StrSplit("", ','); EXPECT_EQ("", p.first); EXPECT_EQ("", p.second); @@ -565,7 +601,7 @@ TEST(Split, AcceptsCertainTemporaries) { TEST(Split, Temporary) { // Use a std::string longer than the SSO length, so that when the temporary is - // destroyed, if the splitter keeps a reference to the std::string's contents, + // destroyed, if the splitter keeps a reference to the string's contents, // it'll reference freed memory instead of just dead on-stack memory. const char input[] = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u"; EXPECT_LT(sizeof(std::string), ABSL_ARRAYSIZE(input)) @@ -651,14 +687,14 @@ TEST(Split, UTF8) { // Tests splitting utf8 strings and utf8 delimiters. std::string utf8_string = u8"\u03BA\u1F79\u03C3\u03BC\u03B5"; { - // A utf8 input std::string with an ascii delimiter. + // A utf8 input string with an ascii delimiter. std::string to_split = "a," + utf8_string; std::vector<absl::string_view> v = absl::StrSplit(to_split, ','); EXPECT_THAT(v, ElementsAre("a", utf8_string)); } { - // A utf8 input std::string and a utf8 delimiter. + // A utf8 input string and a utf8 delimiter. std::string to_split = "a," + utf8_string + ",b"; std::string unicode_delimiter = "," + utf8_string + ","; std::vector<absl::string_view> v = @@ -667,7 +703,7 @@ TEST(Split, UTF8) { } { - // A utf8 input std::string and ByAnyChar with ascii chars. + // A utf8 input string and ByAnyChar with ascii chars. std::vector<absl::string_view> v = absl::StrSplit(u8"Foo h\u00E4llo th\u4E1Ere", absl::ByAnyChar(" \t")); EXPECT_THAT(v, ElementsAre("Foo", u8"h\u00E4llo", u8"th\u4E1Ere")); @@ -814,10 +850,10 @@ TEST(Delimiter, ByString) { ByString comma_string(","); TestComma(comma_string); - // The first occurrence of empty std::string ("") in a std::string is at position 0. + // The first occurrence of empty string ("") in a string is at position 0. // There is a test below that demonstrates this for absl::string_view::find(). // If the ByString delimiter returned position 0 for this, there would - // be an infinite loop in the SplitIterator code. To avoid this, empty std::string + // be an infinite loop in the SplitIterator code. To avoid this, empty string // is a special case in that it always returns the item at position 1. absl::string_view abc("abc"); EXPECT_EQ(0, abc.find("")); // "" is found at position 0 @@ -876,7 +912,7 @@ TEST(Delimiter, ByAnyChar) { EXPECT_FALSE(IsFoundAt("=", two_delims, -1)); // ByAnyChar behaves just like ByString when given a delimiter of empty - // std::string. That is, it always returns a zero-length absl::string_view + // string. That is, it always returns a zero-length absl::string_view // referring to the item at position 1, not position 0. ByAnyChar empty(""); EXPECT_FALSE(IsFoundAt("", empty, 0)); @@ -907,13 +943,19 @@ TEST(Delimiter, ByLength) { } TEST(Split, WorksWithLargeStrings) { +#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ + defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER) + constexpr size_t kSize = (uint32_t{1} << 26) + 1; // 64M + 1 byte +#else + constexpr size_t kSize = (uint32_t{1} << 31) + 1; // 2G + 1 byte +#endif if (sizeof(size_t) > 4) { - std::string s((uint32_t{1} << 31) + 1, 'x'); // 2G + 1 byte + std::string s(kSize, 'x'); s.back() = '-'; std::vector<absl::string_view> v = absl::StrSplit(s, '-'); EXPECT_EQ(2, v.size()); // The first element will contain 2G of 'x's. - // testing::StartsWith is too slow with a 2G std::string. + // testing::StartsWith is too slow with a 2G string. EXPECT_EQ('x', v[0][0]); EXPECT_EQ('x', v[0][1]); EXPECT_EQ('x', v[0][3]); diff --git a/third_party/abseil-cpp/absl/strings/string_view.cc b/third_party/abseil-cpp/absl/strings/string_view.cc index c5f5de936d..d596e08cde 100644 --- a/third_party/abseil-cpp/absl/strings/string_view.cc +++ b/third_party/abseil-cpp/absl/strings/string_view.cc @@ -78,8 +78,8 @@ std::ostream& operator<<(std::ostream& o, string_view piece) { return o; } -string_view::size_type string_view::find(string_view s, size_type pos) const - noexcept { +string_view::size_type string_view::find(string_view s, + size_type pos) const noexcept { if (empty() || pos > length_) { if (empty() && pos == 0 && s.empty()) return 0; return npos; @@ -98,8 +98,8 @@ string_view::size_type string_view::find(char c, size_type pos) const noexcept { return result != nullptr ? result - ptr_ : npos; } -string_view::size_type string_view::rfind(string_view s, size_type pos) const - noexcept { +string_view::size_type string_view::rfind(string_view s, + size_type pos) const noexcept { if (length_ < s.length_) return npos; if (s.empty()) return std::min(length_, pos); const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_; @@ -108,8 +108,8 @@ string_view::size_type string_view::rfind(string_view s, size_type pos) const } // Search range is [0..pos] inclusive. If pos == npos, search everything. -string_view::size_type string_view::rfind(char c, size_type pos) const - noexcept { +string_view::size_type string_view::rfind(char c, + size_type pos) const noexcept { // Note: memrchr() is not available on Windows. if (empty()) return npos; for (size_type i = std::min(pos, length_ - 1);; --i) { @@ -121,9 +121,8 @@ string_view::size_type string_view::rfind(char c, size_type pos) const return npos; } -string_view::size_type string_view::find_first_of(string_view s, - size_type pos) const - noexcept { +string_view::size_type string_view::find_first_of( + string_view s, size_type pos) const noexcept { if (empty() || s.empty()) { return npos; } @@ -138,9 +137,8 @@ string_view::size_type string_view::find_first_of(string_view s, return npos; } -string_view::size_type string_view::find_first_not_of(string_view s, - size_type pos) const - noexcept { +string_view::size_type string_view::find_first_not_of( + string_view s, size_type pos) const noexcept { if (empty()) return npos; // Avoid the cost of LookupTable() for a single-character search. if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos); @@ -153,9 +151,8 @@ string_view::size_type string_view::find_first_not_of(string_view s, return npos; } -string_view::size_type string_view::find_first_not_of(char c, - size_type pos) const - noexcept { +string_view::size_type string_view::find_first_not_of( + char c, size_type pos) const noexcept { if (empty()) return npos; for (; pos < length_; ++pos) { if (ptr_[pos] != c) { @@ -180,9 +177,8 @@ string_view::size_type string_view::find_last_of(string_view s, return npos; } -string_view::size_type string_view::find_last_not_of(string_view s, - size_type pos) const - noexcept { +string_view::size_type string_view::find_last_not_of( + string_view s, size_type pos) const noexcept { if (empty()) return npos; size_type i = std::min(pos, length_ - 1); if (s.empty()) return i; @@ -198,9 +194,8 @@ string_view::size_type string_view::find_last_not_of(string_view s, return npos; } -string_view::size_type string_view::find_last_not_of(char c, - size_type pos) const - noexcept { +string_view::size_type string_view::find_last_not_of( + char c, size_type pos) const noexcept { if (empty()) return npos; size_type i = std::min(pos, length_ - 1); for (;; --i) { diff --git a/third_party/abseil-cpp/absl/strings/string_view.h b/third_party/abseil-cpp/absl/strings/string_view.h index 1861ea62a9..a4c9a6526c 100644 --- a/third_party/abseil-cpp/absl/strings/string_view.h +++ b/third_party/abseil-cpp/absl/strings/string_view.h @@ -36,6 +36,7 @@ #include <limits> #include <string> +#include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" @@ -48,7 +49,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN -using std::string_view; +using string_view = std::string_view; ABSL_NAMESPACE_END } // namespace absl @@ -61,6 +62,12 @@ ABSL_NAMESPACE_END #define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp #endif // ABSL_HAVE_BUILTIN(__builtin_memcmp) +#if defined(__cplusplus) && __cplusplus >= 201402L +#define ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR constexpr +#else +#define ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR +#endif + namespace absl { ABSL_NAMESPACE_BEGIN @@ -111,6 +118,11 @@ ABSL_NAMESPACE_BEGIN // example, when splitting a string, `std::vector<absl::string_view>` is a // natural data type for the output. // +// For another example, a Cord is a non-contiguous, potentially very +// long string-like object. The Cord class has an interface that iteratively +// provides string_view objects that point to the successive pieces of a Cord +// object. +// // When constructed from a source which is NUL-terminated, the `string_view` // itself will not include the NUL-terminator unless a specific size (including // the NUL) is passed to the constructor. As a result, common idioms that work @@ -175,18 +187,20 @@ class string_view { template <typename Allocator> string_view( // NOLINT(runtime/explicit) - const std::basic_string<char, std::char_traits<char>, Allocator>& - str) noexcept + const std::basic_string<char, std::char_traits<char>, Allocator>& str + ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // This is implemented in terms of `string_view(p, n)` so `str.size()` // doesn't need to be reevaluated after `ptr_` is set. - : string_view(str.data(), str.size()) {} + // The length check is also skipped since it is unnecessary and causes + // code bloat. + : string_view(str.data(), str.size(), SkipCheckLengthTag{}) {} // Implicit constructor of a `string_view` from NUL-terminated `str`. When // accepting possibly null strings, use `absl::NullSafeStringView(str)` // instead (see below). + // The length check is skipped since it is unnecessary and causes code bloat. constexpr string_view(const char* str) // NOLINT(runtime/explicit) - : ptr_(str), - length_(str ? CheckLengthInternal(StrlenInternal(str)) : 0) {} + : ptr_(str), length_(str ? StrlenInternal(str) : 0) {} // Implicit constructor of a `string_view` from a `const char*` and length. constexpr string_view(const char* data, size_type len) @@ -259,9 +273,7 @@ class string_view { // string_view::size() // // Returns the number of characters in the `string_view`. - constexpr size_type size() const noexcept { - return length_; - } + constexpr size_type size() const noexcept { return length_; } // string_view::length() // @@ -283,7 +295,7 @@ class string_view { // Returns the ith element of the `string_view` using the array operator. // Note that this operator does not perform any bounds checking. constexpr const_reference operator[](size_type i) const { - return ABSL_ASSERT(i < size()), ptr_[i]; + return ABSL_HARDENING_ASSERT(i < size()), ptr_[i]; } // string_view::at() @@ -303,14 +315,14 @@ class string_view { // // Returns the first element of a `string_view`. constexpr const_reference front() const { - return ABSL_ASSERT(!empty()), ptr_[0]; + return ABSL_HARDENING_ASSERT(!empty()), ptr_[0]; } // string_view::back() // // Returns the last element of a `string_view`. constexpr const_reference back() const { - return ABSL_ASSERT(!empty()), ptr_[size() - 1]; + return ABSL_HARDENING_ASSERT(!empty()), ptr_[size() - 1]; } // string_view::data() @@ -319,7 +331,7 @@ class string_view { // stored elsewhere). Note that `string_view::data()` may contain embedded nul // characters, but the returned buffer may or may not be NUL-terminated; // therefore, do not pass `data()` to a routine that expects a NUL-terminated - // std::string. + // string. constexpr const_pointer data() const noexcept { return ptr_; } // Modifiers @@ -327,9 +339,9 @@ class string_view { // string_view::remove_prefix() // // Removes the first `n` characters from the `string_view`. Note that the - // underlying std::string is not changed, only the view. - void remove_prefix(size_type n) { - assert(n <= length_); + // underlying string is not changed, only the view. + ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_prefix(size_type n) { + ABSL_HARDENING_ASSERT(n <= length_); ptr_ += n; length_ -= n; } @@ -337,16 +349,16 @@ class string_view { // string_view::remove_suffix() // // Removes the last `n` characters from the `string_view`. Note that the - // underlying std::string is not changed, only the view. - void remove_suffix(size_type n) { - assert(n <= length_); + // underlying string is not changed, only the view. + ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void remove_suffix(size_type n) { + ABSL_HARDENING_ASSERT(n <= length_); length_ -= n; } // string_view::swap() // // Swaps this `string_view` with another `string_view`. - void swap(string_view& s) noexcept { + ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR void swap(string_view& s) noexcept { auto t = *this; *this = s; s = t; @@ -382,21 +394,21 @@ class string_view { // Returns a "substring" of the `string_view` (at offset `pos` and length // `n`) as another string_view. This function throws `std::out_of_bounds` if // `pos > size`. - string_view substr(size_type pos, size_type n = npos) const { - if (ABSL_PREDICT_FALSE(pos > length_)) - base_internal::ThrowStdOutOfRange("absl::string_view::substr"); - n = (std::min)(n, length_ - pos); - return string_view(ptr_ + pos, n); + // Use absl::ClippedSubstr if you need a truncating substr operation. + constexpr string_view substr(size_type pos = 0, size_type n = npos) const { + return ABSL_PREDICT_FALSE(pos > length_) + ? (base_internal::ThrowStdOutOfRange( + "absl::string_view::substr"), + string_view()) + : string_view(ptr_ + pos, Min(n, length_ - pos)); } // string_view::compare() // - // Performs a lexicographical comparison between the `string_view` and - // another `absl::string_view`, returning -1 if `this` is less than, 0 if - // `this` is equal to, and 1 if `this` is greater than the passed std::string - // view. Note that in the case of data equality, a further comparison is made - // on the respective sizes of the two `string_view`s to determine which is - // smaller, equal, or greater. + // Performs a lexicographical comparison between this `string_view` and + // another `string_view` `x`, returning a negative value if `*this` is less + // than `x`, 0 if `*this` is equal to `x`, and a positive value if `*this` + // is greater than `x`. constexpr int compare(string_view x) const noexcept { return CompareImpl(length_, x.length_, Min(length_, x.length_) == 0 @@ -407,31 +419,31 @@ class string_view { // Overload of `string_view::compare()` for comparing a substring of the // 'string_view` and another `absl::string_view`. - int compare(size_type pos1, size_type count1, string_view v) const { + constexpr int compare(size_type pos1, size_type count1, string_view v) const { return substr(pos1, count1).compare(v); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a substring of another `absl::string_view`. - int compare(size_type pos1, size_type count1, string_view v, size_type pos2, - size_type count2) const { + constexpr int compare(size_type pos1, size_type count1, string_view v, + size_type pos2, size_type count2) const { return substr(pos1, count1).compare(v.substr(pos2, count2)); } // Overload of `string_view::compare()` for comparing a `string_view` and a - // a different C-style std::string `s`. - int compare(const char* s) const { return compare(string_view(s)); } + // a different C-style string `s`. + constexpr int compare(const char* s) const { return compare(string_view(s)); } // Overload of `string_view::compare()` for comparing a substring of the - // `string_view` and a different std::string C-style std::string `s`. - int compare(size_type pos1, size_type count1, const char* s) const { + // `string_view` and a different string C-style string `s`. + constexpr int compare(size_type pos1, size_type count1, const char* s) const { return substr(pos1, count1).compare(string_view(s)); } // Overload of `string_view::compare()` for comparing a substring of the - // `string_view` and a substring of a different C-style std::string `s`. - int compare(size_type pos1, size_type count1, const char* s, - size_type count2) const { + // `string_view` and a substring of a different C-style string `s`. + constexpr int compare(size_type pos1, size_type count1, const char* s, + size_type count2) const { return substr(pos1, count1).compare(string_view(s, count2)); } @@ -448,48 +460,92 @@ class string_view { // within the `string_view`. size_type find(char c, size_type pos = 0) const noexcept; + // Overload of `string_view::find()` for finding a substring of a different + // C-style string `s` within the `string_view`. + size_type find(const char* s, size_type pos, size_type count) const { + return find(string_view(s, count), pos); + } + + // Overload of `string_view::find()` for finding a different C-style string + // `s` within the `string_view`. + size_type find(const char* s, size_type pos = 0) const { + return find(string_view(s), pos); + } + // string_view::rfind() // // Finds the last occurrence of a substring `s` within the `string_view`, // returning the position of the first character's match, or `npos` if no // match was found. - size_type rfind(string_view s, size_type pos = npos) const - noexcept; + size_type rfind(string_view s, size_type pos = npos) const noexcept; // Overload of `string_view::rfind()` for finding the last given character `c` // within the `string_view`. size_type rfind(char c, size_type pos = npos) const noexcept; + // Overload of `string_view::rfind()` for finding a substring of a different + // C-style string `s` within the `string_view`. + size_type rfind(const char* s, size_type pos, size_type count) const { + return rfind(string_view(s, count), pos); + } + + // Overload of `string_view::rfind()` for finding a different C-style string + // `s` within the `string_view`. + size_type rfind(const char* s, size_type pos = npos) const { + return rfind(string_view(s), pos); + } + // string_view::find_first_of() // // Finds the first occurrence of any of the characters in `s` within the // `string_view`, returning the start position of the match, or `npos` if no // match was found. - size_type find_first_of(string_view s, size_type pos = 0) const - noexcept; + size_type find_first_of(string_view s, size_type pos = 0) const noexcept; // Overload of `string_view::find_first_of()` for finding a character `c` // within the `string_view`. - size_type find_first_of(char c, size_type pos = 0) const - noexcept { + size_type find_first_of(char c, size_type pos = 0) const noexcept { return find(c, pos); } + // Overload of `string_view::find_first_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_first_of(const char* s, size_type pos, + size_type count) const { + return find_first_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_first_of()` for finding a different C-style + // string `s` within the `string_view`. + size_type find_first_of(const char* s, size_type pos = 0) const { + return find_first_of(string_view(s), pos); + } + // string_view::find_last_of() // // Finds the last occurrence of any of the characters in `s` within the // `string_view`, returning the start position of the match, or `npos` if no // match was found. - size_type find_last_of(string_view s, size_type pos = npos) const - noexcept; + size_type find_last_of(string_view s, size_type pos = npos) const noexcept; // Overload of `string_view::find_last_of()` for finding a character `c` // within the `string_view`. - size_type find_last_of(char c, size_type pos = npos) const - noexcept { + size_type find_last_of(char c, size_type pos = npos) const noexcept { return rfind(c, pos); } + // Overload of `string_view::find_last_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_last_of(const char* s, size_type pos, size_type count) const { + return find_last_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_last_of()` for finding a different C-style + // string `s` within the `string_view`. + size_type find_last_of(const char* s, size_type pos = npos) const { + return find_last_of(string_view(s), pos); + } + // string_view::find_first_not_of() // // Finds the first occurrence of any of the characters not in `s` within the @@ -501,25 +557,56 @@ class string_view { // that is not `c` within the `string_view`. size_type find_first_not_of(char c, size_type pos = 0) const noexcept; + // Overload of `string_view::find_first_not_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_first_not_of(const char* s, size_type pos, + size_type count) const { + return find_first_not_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_first_not_of()` for finding a different + // C-style string `s` within the `string_view`. + size_type find_first_not_of(const char* s, size_type pos = 0) const { + return find_first_not_of(string_view(s), pos); + } + // string_view::find_last_not_of() // // Finds the last occurrence of any of the characters not in `s` within the // `string_view`, returning the start position of the last non-match, or // `npos` if no non-match was found. size_type find_last_not_of(string_view s, - size_type pos = npos) const noexcept; + size_type pos = npos) const noexcept; // Overload of `string_view::find_last_not_of()` for finding a character // that is not `c` within the `string_view`. - size_type find_last_not_of(char c, size_type pos = npos) const - noexcept; + size_type find_last_not_of(char c, size_type pos = npos) const noexcept; + + // Overload of `string_view::find_last_not_of()` for finding a substring of a + // different C-style string `s` within the `string_view`. + size_type find_last_not_of(const char* s, size_type pos, + size_type count) const { + return find_last_not_of(string_view(s, count), pos); + } + + // Overload of `string_view::find_last_not_of()` for finding a different + // C-style string `s` within the `string_view`. + size_type find_last_not_of(const char* s, size_type pos = npos) const { + return find_last_not_of(string_view(s), pos); + } private: + // The constructor from std::string delegates to this constructor. + // See the comment on that constructor for the rationale. + struct SkipCheckLengthTag {}; + string_view(const char* data, size_type len, SkipCheckLengthTag) noexcept + : ptr_(data), length_(len) {} + static constexpr size_type kMaxSize = (std::numeric_limits<difference_type>::max)(); static constexpr size_type CheckLengthInternal(size_type len) { - return (void)ABSL_ASSERT(len <= kMaxSize), len; + return ABSL_HARDENING_ASSERT(len <= kMaxSize), len; } static constexpr size_type StrlenInternal(const char* str) { @@ -590,6 +677,7 @@ std::ostream& operator<<(std::ostream& o, string_view piece); ABSL_NAMESPACE_END } // namespace absl +#undef ABSL_INTERNAL_STRING_VIEW_CXX14_CONSTEXPR #undef ABSL_INTERNAL_STRING_VIEW_MEMCMP #endif // ABSL_USES_STD_STRING_VIEW diff --git a/third_party/abseil-cpp/absl/strings/string_view_test.cc b/third_party/abseil-cpp/absl/strings/string_view_test.cc index 7b1d56fac7..2c13dd1c14 100644 --- a/third_party/abseil-cpp/absl/strings/string_view_test.cc +++ b/third_party/abseil-cpp/absl/strings/string_view_test.cc @@ -28,6 +28,7 @@ #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" +#include "absl/base/options.h" #if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__) // We don't control the death messaging when using std::string_view. @@ -410,7 +411,7 @@ TEST(StringViewTest, STL2) { EXPECT_EQ(a.find(e, 17), 17); absl::string_view g("xx not found bb"); EXPECT_EQ(a.find(g), absl::string_view::npos); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(d.find(b), absl::string_view::npos); EXPECT_EQ(e.find(b), absl::string_view::npos); EXPECT_EQ(d.find(b, 4), absl::string_view::npos); @@ -438,7 +439,7 @@ TEST(StringViewTest, STL2) { EXPECT_EQ(g.find('o', 4), 4); EXPECT_EQ(g.find('o', 5), 8); EXPECT_EQ(a.find('b', 5), absl::string_view::npos); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(d.find('\0'), absl::string_view::npos); EXPECT_EQ(e.find('\0'), absl::string_view::npos); EXPECT_EQ(d.find('\0', 4), absl::string_view::npos); @@ -448,6 +449,24 @@ TEST(StringViewTest, STL2) { EXPECT_EQ(d.find('x', 4), absl::string_view::npos); EXPECT_EQ(e.find('x', 7), absl::string_view::npos); + EXPECT_EQ(a.find(b.data(), 1, 0), 1); + EXPECT_EQ(a.find(c.data(), 9, 0), 9); + EXPECT_EQ(a.find(c.data(), absl::string_view::npos, 0), + absl::string_view::npos); + EXPECT_EQ(b.find(c.data(), absl::string_view::npos, 0), + absl::string_view::npos); + // empty string nonsense + EXPECT_EQ(d.find(b.data(), 4, 0), absl::string_view::npos); + EXPECT_EQ(e.find(b.data(), 7, 0), absl::string_view::npos); + + EXPECT_EQ(a.find(b.data(), 1), absl::string_view::npos); + EXPECT_EQ(a.find(c.data(), 9), 23); + EXPECT_EQ(a.find(c.data(), absl::string_view::npos), absl::string_view::npos); + EXPECT_EQ(b.find(c.data(), absl::string_view::npos), absl::string_view::npos); + // empty string nonsense + EXPECT_EQ(d.find(b.data(), 4), absl::string_view::npos); + EXPECT_EQ(e.find(b.data(), 7), absl::string_view::npos); + EXPECT_EQ(a.rfind(b), 0); EXPECT_EQ(a.rfind(b, 1), 0); EXPECT_EQ(a.rfind(c), 23); @@ -465,7 +484,7 @@ TEST(StringViewTest, STL2) { EXPECT_EQ(e.rfind(b), absl::string_view::npos); EXPECT_EQ(d.rfind(b, 4), absl::string_view::npos); EXPECT_EQ(e.rfind(b, 7), absl::string_view::npos); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(d.rfind(d, 4), std::string().rfind(std::string())); EXPECT_EQ(e.rfind(d, 7), std::string().rfind(std::string())); EXPECT_EQ(d.rfind(e, 4), std::string().rfind(std::string())); @@ -484,11 +503,19 @@ TEST(StringViewTest, STL2) { EXPECT_EQ(f.rfind('\0', 12), 3); EXPECT_EQ(f.rfind('3'), 2); EXPECT_EQ(f.rfind('5'), 5); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(d.rfind('o'), absl::string_view::npos); EXPECT_EQ(e.rfind('o'), absl::string_view::npos); EXPECT_EQ(d.rfind('o', 4), absl::string_view::npos); EXPECT_EQ(e.rfind('o', 7), absl::string_view::npos); + + EXPECT_EQ(a.rfind(b.data(), 1, 0), 1); + EXPECT_EQ(a.rfind(c.data(), 22, 0), 22); + EXPECT_EQ(a.rfind(c.data(), 1, 0), 1); + EXPECT_EQ(a.rfind(c.data(), 0, 0), 0); + EXPECT_EQ(b.rfind(c.data(), 0, 0), 0); + EXPECT_EQ(d.rfind(b.data(), 4, 0), 0); + EXPECT_EQ(e.rfind(b.data(), 7, 0), 0); } // Continued from STL2 @@ -520,7 +547,7 @@ TEST(StringViewTest, STL2FindFirst) { EXPECT_EQ(g.find_first_of(c), 0); EXPECT_EQ(a.find_first_of(f), absl::string_view::npos); EXPECT_EQ(f.find_first_of(a), absl::string_view::npos); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(a.find_first_of(d), absl::string_view::npos); EXPECT_EQ(a.find_first_of(e), absl::string_view::npos); EXPECT_EQ(d.find_first_of(b), absl::string_view::npos); @@ -538,7 +565,7 @@ TEST(StringViewTest, STL2FindFirst) { EXPECT_EQ(a.find_first_not_of(f), 0); EXPECT_EQ(a.find_first_not_of(d), 0); EXPECT_EQ(a.find_first_not_of(e), 0); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(a.find_first_not_of(d), 0); EXPECT_EQ(a.find_first_not_of(e), 0); EXPECT_EQ(a.find_first_not_of(d, 1), 1); @@ -566,7 +593,7 @@ TEST(StringViewTest, STL2FindFirst) { EXPECT_EQ(f.find_first_not_of('\0'), 0); EXPECT_EQ(f.find_first_not_of('\0', 3), 4); EXPECT_EQ(f.find_first_not_of('\0', 2), 2); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(d.find_first_not_of('x'), absl::string_view::npos); EXPECT_EQ(e.find_first_not_of('x'), absl::string_view::npos); EXPECT_EQ(d.find_first_not_of('\0'), absl::string_view::npos); @@ -606,7 +633,7 @@ TEST(StringViewTest, STL2FindLast) { EXPECT_EQ(f.find_last_of(i, 5), 5); EXPECT_EQ(f.find_last_of(i, 6), 6); EXPECT_EQ(f.find_last_of(a, 4), absl::string_view::npos); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(f.find_last_of(d), absl::string_view::npos); EXPECT_EQ(f.find_last_of(e), absl::string_view::npos); EXPECT_EQ(f.find_last_of(d, 4), absl::string_view::npos); @@ -632,7 +659,7 @@ TEST(StringViewTest, STL2FindLast) { EXPECT_EQ(a.find_last_not_of(c, 24), 22); EXPECT_EQ(a.find_last_not_of(b, 3), 3); EXPECT_EQ(a.find_last_not_of(b, 2), absl::string_view::npos); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(f.find_last_not_of(d), f.size()-1); EXPECT_EQ(f.find_last_not_of(e), f.size()-1); EXPECT_EQ(f.find_last_not_of(d, 4), 4); @@ -656,7 +683,7 @@ TEST(StringViewTest, STL2FindLast) { EXPECT_EQ(h.find_last_not_of('x', 2), 2); EXPECT_EQ(h.find_last_not_of('=', 2), absl::string_view::npos); EXPECT_EQ(b.find_last_not_of('b', 1), 0); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(d.find_last_not_of('x'), absl::string_view::npos); EXPECT_EQ(e.find_last_not_of('x'), absl::string_view::npos); EXPECT_EQ(d.find_last_not_of('\0'), absl::string_view::npos); @@ -677,8 +704,9 @@ TEST(StringViewTest, STL2Substr) { EXPECT_EQ(a.substr(23, 3), c); EXPECT_EQ(a.substr(23, 99), c); EXPECT_EQ(a.substr(0), a); + EXPECT_EQ(a.substr(), a); EXPECT_EQ(a.substr(3, 2), "de"); - // empty std::string nonsense + // empty string nonsense EXPECT_EQ(d.substr(0, 99), e); // use of npos EXPECT_EQ(a.substr(0, absl::string_view::npos), a); @@ -820,7 +848,7 @@ TEST(StringViewTest, FrontBackSingleChar) { TEST(StringViewTest, FrontBackEmpty) { #ifndef ABSL_USES_STD_STRING_VIEW -#ifndef NDEBUG +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED // Abseil's string_view implementation has debug assertions that check that // front() and back() are not called on an empty string_view. absl::string_view sv; @@ -859,7 +887,7 @@ TEST(StringViewTest, NULLInput) { EXPECT_EQ(s.size(), 0); // .ToString() on a absl::string_view with nullptr should produce the empty - // std::string. + // string. EXPECT_EQ("", std::string(s)); #endif // ABSL_HAVE_STRING_VIEW_FROM_NULLPTR } @@ -914,9 +942,9 @@ TEST(StringViewTest, At) { EXPECT_EQ(abc.at(1), 'b'); EXPECT_EQ(abc.at(2), 'c'); #ifdef ABSL_HAVE_EXCEPTIONS - EXPECT_THROW(abc.at(3), std::out_of_range); + EXPECT_THROW((void)abc.at(3), std::out_of_range); #else - ABSL_EXPECT_DEATH_IF_SUPPORTED(abc.at(3), "absl::string_view::at"); + ABSL_EXPECT_DEATH_IF_SUPPORTED((void)abc.at(3), "absl::string_view::at"); #endif } @@ -977,7 +1005,7 @@ TEST(StringViewTest, ConstexprCompiles) { #if defined(ABSL_USES_STD_STRING_VIEW) // In libstdc++ (as of 7.2), `std::string_view::string_view(const char*)` - // calls `std::char_traits<char>::length(const char*)` to get the std::string + // calls `std::char_traits<char>::length(const char*)` to get the string // length, but it is not marked constexpr yet. See GCC bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78156 // Also, there is a LWG issue that adds constexpr to length() which was just @@ -1086,6 +1114,31 @@ TEST(StringViewTest, ConstexprCompiles) { EXPECT_EQ(sp_npos, -1); } +constexpr char ConstexprMethodsHelper() { +#if defined(__cplusplus) && __cplusplus >= 201402L + absl::string_view str("123", 3); + str.remove_prefix(1); + str.remove_suffix(1); + absl::string_view bar; + str.swap(bar); + return bar.front(); +#else + return '2'; +#endif +} + +TEST(StringViewTest, ConstexprMethods) { + // remove_prefix, remove_suffix, swap + static_assert(ConstexprMethodsHelper() == '2', ""); + + // substr + constexpr absl::string_view foobar("foobar", 6); + constexpr absl::string_view foo = foobar.substr(0, 3); + constexpr absl::string_view bar = foobar.substr(3); + EXPECT_EQ(foo, "foo"); + EXPECT_EQ(bar, "bar"); +} + TEST(StringViewTest, Noexcept) { EXPECT_TRUE((std::is_nothrow_constructible<absl::string_view, const std::string&>::value)); @@ -1122,7 +1175,7 @@ TEST(StringViewTest, Noexcept) { TEST(StringViewTest, BoundsCheck) { #ifndef ABSL_USES_STD_STRING_VIEW -#ifndef NDEBUG +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED // Abseil's string_view implementation has bounds-checking in debug mode. absl::string_view h = "hello"; ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], ""); @@ -1168,11 +1221,11 @@ TEST(FindOneCharTest, EdgeCases) { EXPECT_EQ(absl::string_view::npos, a.rfind('x')); } -#ifndef THREAD_SANITIZER // Allocates too much memory for tsan. +#ifndef ABSL_HAVE_THREAD_SANITIZER // Allocates too much memory for tsan. TEST(HugeStringView, TwoPointTwoGB) { - if (sizeof(size_t) <= 4 || RunningOnValgrind()) + if (sizeof(size_t) <= 4) return; - // Try a huge std::string piece. + // Try a huge string piece. const size_t size = size_t{2200} * 1000 * 1000; std::string s(size, 'a'); absl::string_view sp(s); @@ -1182,7 +1235,7 @@ TEST(HugeStringView, TwoPointTwoGB) { sp.remove_suffix(2); EXPECT_EQ(size - 1 - 2, sp.length()); } -#endif // THREAD_SANITIZER +#endif // ABSL_HAVE_THREAD_SANITIZER #if !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW) TEST(NonNegativeLenTest, NonNegativeLen) { diff --git a/third_party/abseil-cpp/absl/strings/substitute.cc b/third_party/abseil-cpp/absl/strings/substitute.cc index 5b69a3ef00..8980b198c2 100644 --- a/third_party/abseil-cpp/absl/strings/substitute.cc +++ b/third_party/abseil-cpp/absl/strings/substitute.cc @@ -36,7 +36,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, if (i + 1 >= format.size()) { #ifndef NDEBUG ABSL_RAW_LOG(FATAL, - "Invalid absl::Substitute() format std::string: \"%s\".", + "Invalid absl::Substitute() format string: \"%s\".", absl::CEscape(format).c_str()); #endif return; @@ -46,8 +46,8 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, #ifndef NDEBUG ABSL_RAW_LOG( FATAL, - "Invalid absl::Substitute() format std::string: asked for \"$" - "%d\", but only %d args were given. Full format std::string was: " + "Invalid absl::Substitute() format string: asked for \"$" + "%d\", but only %d args were given. Full format string was: " "\"%s\".", index, static_cast<int>(num_args), absl::CEscape(format).c_str()); #endif @@ -61,7 +61,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, } else { #ifndef NDEBUG ABSL_RAW_LOG(FATAL, - "Invalid absl::Substitute() format std::string: \"%s\".", + "Invalid absl::Substitute() format string: \"%s\".", absl::CEscape(format).c_str()); #endif return; @@ -73,9 +73,10 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, if (size == 0) return; - // Build the std::string. + // Build the string. size_t original_size = output->size(); - strings_internal::STLStringResizeUninitialized(output, original_size + size); + strings_internal::STLStringResizeUninitializedAmortized(output, + original_size + size); char* target = &(*output)[original_size]; for (size_t i = 0; i < format.size(); i++) { if (format[i] == '$') { diff --git a/third_party/abseil-cpp/absl/strings/substitute.h b/third_party/abseil-cpp/absl/strings/substitute.h index 4d0984d3d1..151c56f543 100644 --- a/third_party/abseil-cpp/absl/strings/substitute.h +++ b/third_party/abseil-cpp/absl/strings/substitute.h @@ -50,7 +50,7 @@ // // Supported types: // * absl::string_view, std::string, const char* (null is equivalent to "") -// * int32_t, int64_t, uint32_t, uint64 +// * int32_t, int64_t, uint32_t, uint64_t // * float, double // * bool (Printed as "true" or "false") // * pointer types other than char* (Printed as "0x<lower case hex string>", @@ -99,7 +99,7 @@ namespace substitute_internal { // This class has implicit constructors. class Arg { public: - // Overloads for std::string-y things + // Overloads for string-y things // // Explicitly overload `const char*` so the compiler doesn't cast to `bool`. Arg(const char* value) // NOLINT(runtime/explicit) @@ -120,7 +120,9 @@ class Arg { // representation. However, we can't really know, so we make the caller decide // what to do. Arg(char value) // NOLINT(runtime/explicit) - : piece_(scratch_, 1) { scratch_[0] = value; } + : piece_(scratch_, 1) { + scratch_[0] = value; + } Arg(short value) // NOLINT(*) : piece_(scratch_, numbers_internal::FastIntToBuffer(value, scratch_) - scratch_) {} @@ -203,10 +205,11 @@ constexpr const char* SkipNumber(const char* format) { } constexpr int PlaceholderBitmask(const char* format) { - return !*format ? 0 : *format != '$' - ? PlaceholderBitmask(format + 1) - : (CalculateOneBit(format + 1) | - PlaceholderBitmask(SkipNumber(format + 1))); + return !*format + ? 0 + : *format != '$' ? PlaceholderBitmask(format + 1) + : (CalculateOneBit(format + 1) | + PlaceholderBitmask(SkipNumber(format + 1))); } #endif // ABSL_BAD_CALL_IF @@ -358,43 +361,49 @@ inline void SubstituteAndAppend( // This body of functions catches cases where the number of placeholders // doesn't match the number of data arguments. void SubstituteAndAppend(std::string* output, const char* format) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0, - "There were no substitution arguments " - "but this format std::string has a $[0-9] in it"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 0, + "There were no substitution arguments " + "but this format string either has a $[0-9] in it or contains " + "an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1, "There was 1 substitution argument given, but " - "this format std::string is either missing its $0, or " - "contains one of $1-$9"); + "this format string is missing its $0, contains " + "one of $1-$9, or contains an unescaped $ character (use " + "$$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3, - "There were 2 substitution arguments given, but " - "this format std::string is either missing its $0/$1, or " - "contains one of $2-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 3, + "There were 2 substitution arguments given, but this format string is " + "missing its $0/$1, contains one of $2-$9, or contains an " + "unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7, - "There were 3 substitution arguments given, but " - "this format std::string is either missing its $0/$1/$2, or " - "contains one of $3-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 7, + "There were 3 substitution arguments given, but " + "this format string is missing its $0/$1/$2, contains one of " + "$3-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15, - "There were 4 substitution arguments given, but " - "this format std::string is either missing its $0-$3, or " - "contains one of $4-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 15, + "There were 4 substitution arguments given, but " + "this format string is missing its $0-$3, contains one of " + "$4-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -402,10 +411,11 @@ void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31, - "There were 5 substitution arguments given, but " - "this format std::string is either missing its $0-$4, or " - "contains one of $5-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 31, + "There were 5 substitution arguments given, but " + "this format string is missing its $0-$4, contains one of " + "$5-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -414,20 +424,22 @@ void SubstituteAndAppend(std::string* output, const char* format, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63, - "There were 6 substitution arguments given, but " - "this format std::string is either missing its $0-$5, or " - "contains one of $6-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 63, + "There were 6 substitution arguments given, but " + "this format string is missing its $0-$5, contains one of " + "$6-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127, - "There were 7 substitution arguments given, but " - "this format std::string is either missing its $0-$6, or " - "contains one of $7-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 127, + "There were 7 substitution arguments given, but " + "this format string is missing its $0-$6, contains one of " + "$7-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -435,10 +447,11 @@ void SubstituteAndAppend( const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255, - "There were 8 substitution arguments given, but " - "this format std::string is either missing its $0-$7, or " - "contains one of $8-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 255, + "There were 8 substitution arguments given, but " + "this format string is missing its $0-$7, contains one of " + "$8-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -449,7 +462,8 @@ void SubstituteAndAppend( ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 511, "There were 9 substitution arguments given, but " - "this format std::string is either missing its $0-$8, or contains a $9"); + "this format string is missing its $0-$8, contains a $9, or " + "contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( std::string* output, const char* format, const substitute_internal::Arg& a0, @@ -458,9 +472,11 @@ void SubstituteAndAppend( const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023, - "There were 10 substitution arguments given, but this " - "format std::string doesn't contain all of $0 through $9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1023, + "There were 10 substitution arguments given, but this " + "format string either doesn't contain all of $0 through $9 or " + "contains an unescaped $ character (use $$ instead)"); #endif // ABSL_BAD_CALL_IF // Substitute() @@ -586,47 +602,53 @@ ABSL_MUST_USE_RESULT inline std::string Substitute( std::string Substitute(const char* format) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0, "There were no substitution arguments " - "but this format std::string has a $[0-9] in it"); + "but this format string either has a $[0-9] in it or " + "contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1, - "There was 1 substitution argument given, but " - "this format std::string is either missing its $0, or " - "contains one of $1-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1, + "There was 1 substitution argument given, but " + "this format string is missing its $0, contains one of $1-$9, " + "or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3, - "There were 2 substitution arguments given, but " - "this format std::string is either missing its $0/$1, or " - "contains one of $2-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 3, + "There were 2 substitution arguments given, but " + "this format string is missing its $0/$1, contains one of " + "$2-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7, - "There were 3 substitution arguments given, but " - "this format std::string is either missing its $0/$1/$2, or " - "contains one of $3-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 7, + "There were 3 substitution arguments given, but " + "this format string is missing its $0/$1/$2, contains one of " + "$3-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15, - "There were 4 substitution arguments given, but " - "this format std::string is either missing its $0-$3, or " - "contains one of $4-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 15, + "There were 4 substitution arguments given, but " + "this format string is missing its $0-$3, contains one of " + "$4-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31, - "There were 5 substitution arguments given, but " - "this format std::string is either missing its $0-$4, or " - "contains one of $5-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 31, + "There were 5 substitution arguments given, but " + "this format string is missing its $0-$4, contains one of " + "$5-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -634,10 +656,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63, - "There were 6 substitution arguments given, but " - "this format std::string is either missing its $0-$5, or " - "contains one of $6-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 63, + "There were 6 substitution arguments given, but " + "this format string is missing its $0-$5, contains one of " + "$6-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -646,10 +669,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127, - "There were 7 substitution arguments given, but " - "this format std::string is either missing its $0-$6, or " - "contains one of $7-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 127, + "There were 7 substitution arguments given, but " + "this format string is missing its $0-$6, contains one of " + "$7-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, @@ -659,10 +683,11 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255, - "There were 8 substitution arguments given, but " - "this format std::string is either missing its $0-$7, or " - "contains one of $8-$9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 255, + "There were 8 substitution arguments given, but " + "this format string is missing its $0-$7, contains one of " + "$8-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute( const char* format, const substitute_internal::Arg& a0, @@ -673,7 +698,8 @@ std::string Substitute( ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 511, "There were 9 substitution arguments given, but " - "this format std::string is either missing its $0-$8, or contains a $9"); + "this format string is missing its $0-$8, contains a $9, or " + "contains an unescaped $ character (use $$ instead)"); std::string Substitute( const char* format, const substitute_internal::Arg& a0, @@ -682,9 +708,11 @@ std::string Substitute( const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) - ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1023, - "There were 10 substitution arguments given, but this " - "format std::string doesn't contain all of $0 through $9"); + ABSL_BAD_CALL_IF( + substitute_internal::PlaceholderBitmask(format) != 1023, + "There were 10 substitution arguments given, but this " + "format string either doesn't contain all of $0 through $9 or " + "contains an unescaped $ character (use $$ instead)"); #endif // ABSL_BAD_CALL_IF ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/strings/substitute_test.cc b/third_party/abseil-cpp/absl/strings/substitute_test.cc index 450cd2bcff..442c921528 100644 --- a/third_party/abseil-cpp/absl/strings/substitute_test.cc +++ b/third_party/abseil-cpp/absl/strings/substitute_test.cc @@ -89,7 +89,7 @@ TEST(SubstituteTest, Substitute) { str = absl::Substitute("$0", char_buf); EXPECT_EQ("print me too", str); - // null char* is "doubly" special. Represented as the empty std::string. + // null char* is "doubly" special. Represented as the empty string. char_p = nullptr; str = absl::Substitute("$0", char_p); EXPECT_EQ("", str); @@ -189,14 +189,14 @@ TEST(SubstituteTest, VectorBoolRef) { TEST(SubstituteDeathTest, SubstituteDeath) { EXPECT_DEBUG_DEATH( static_cast<void>(absl::Substitute(absl::string_view("-$2"), "a", "b")), - "Invalid absl::Substitute\\(\\) format std::string: asked for \"\\$2\", " + "Invalid absl::Substitute\\(\\) format string: asked for \"\\$2\", " "but only 2 args were given."); EXPECT_DEBUG_DEATH( static_cast<void>(absl::Substitute(absl::string_view("-$z-"))), - "Invalid absl::Substitute\\(\\) format std::string: \"-\\$z-\""); + "Invalid absl::Substitute\\(\\) format string: \"-\\$z-\""); EXPECT_DEBUG_DEATH( static_cast<void>(absl::Substitute(absl::string_view("-$"))), - "Invalid absl::Substitute\\(\\) format std::string: \"-\\$\""); + "Invalid absl::Substitute\\(\\) format string: \"-\\$\""); } #endif // GTEST_HAS_DEATH_TEST diff --git a/third_party/abseil-cpp/absl/strings/testdata/getline-1.txt b/third_party/abseil-cpp/absl/strings/testdata/getline-1.txt deleted file mode 100644 index 19b909733a..0000000000 --- a/third_party/abseil-cpp/absl/strings/testdata/getline-1.txt +++ /dev/null @@ -1,3 +0,0 @@ -alpha - -beta gamma diff --git a/third_party/abseil-cpp/absl/strings/testdata/getline-2.txt b/third_party/abseil-cpp/absl/strings/testdata/getline-2.txt deleted file mode 100644 index d6842d8ee3..0000000000 --- a/third_party/abseil-cpp/absl/strings/testdata/getline-2.txt +++ /dev/null @@ -1 +0,0 @@ -one.two.three diff --git a/third_party/abseil-cpp/absl/synchronization/BUILD.bazel b/third_party/abseil-cpp/absl/synchronization/BUILD.bazel index 3f876b9fdc..d71954735a 100644 --- a/third_party/abseil-cpp/absl/synchronization/BUILD.bazel +++ b/third_party/abseil-cpp/absl/synchronization/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) # Internal data structure for efficiently detecting mutex dependency cycles cc_library( @@ -73,15 +72,14 @@ cc_library( "internal/create_thread_identity.cc", "internal/per_thread_sem.cc", "internal/waiter.cc", + "mutex.cc", "notification.cc", - ] + select({ - "//conditions:default": ["mutex.cc"], - }), + ], hdrs = [ "barrier.h", "blocking_counter.h", "internal/create_thread_identity.h", - "internal/mutex_nonprod.inc", + "internal/futex.h", "internal/per_thread_sem.h", "internal/waiter.h", "mutex.h", @@ -89,7 +87,9 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = select({ - "//absl:windows": [], + "//absl:msvc_compiler": [], + "//absl:clang-cl_compiler": [], + "//absl:wasm": [], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, deps = [ @@ -135,6 +135,21 @@ cc_test( ], ) +cc_binary( + name = "blocking_counter_benchmark", + testonly = 1, + srcs = ["blocking_counter_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":synchronization", + ":thread_pool", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_test( name = "graphcycles_test", size = "medium", @@ -189,6 +204,7 @@ cc_test( ":synchronization", ":thread_pool", "//absl/base", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/memory", @@ -210,6 +226,7 @@ cc_library( ":synchronization", ":thread_pool", "//absl/base", + "//absl/base:config", "@com_github_google_benchmark//:benchmark_main", ], alwayslink = 1, @@ -248,6 +265,7 @@ cc_library( deps = [ ":synchronization", "//absl/base", + "//absl/base:config", "//absl/strings", "//absl/time", "@com_google_googletest//:gtest", diff --git a/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt b/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt index dfe5d05df2..605efe2d02 100644 --- a/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/synchronization/CMakeLists.txt @@ -52,7 +52,7 @@ absl_cc_library( "barrier.h" "blocking_counter.h" "internal/create_thread_identity.h" - "internal/mutex_nonprod.inc" + "internal/futex.h" "internal/per_thread_sem.h" "internal/waiter.h" "mutex.h" @@ -95,7 +95,7 @@ absl_cc_test( DEPS absl::synchronization absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -108,7 +108,7 @@ absl_cc_test( DEPS absl::synchronization absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -122,7 +122,7 @@ absl_cc_test( absl::graphcycles_internal absl::core_headers absl::raw_logging_internal - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -149,11 +149,12 @@ absl_cc_test( absl::synchronization absl::thread_pool absl::base + absl::config absl::core_headers absl::memory absl::raw_logging_internal absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -166,7 +167,7 @@ absl_cc_test( DEPS absl::synchronization absl::time - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -179,9 +180,10 @@ absl_cc_library( DEPS absl::synchronization absl::base + absl::config absl::strings absl::time - gmock + GTest::gmock TESTONLY ) @@ -197,7 +199,7 @@ absl_cc_test( absl::synchronization absl::strings absl::time - gmock_main + GTest::gmock_main ) absl_cc_test( diff --git a/third_party/abseil-cpp/absl/synchronization/blocking_counter.cc b/third_party/abseil-cpp/absl/synchronization/blocking_counter.cc index 3cea7aed24..d2f82da3bb 100644 --- a/third_party/abseil-cpp/absl/synchronization/blocking_counter.cc +++ b/third_party/abseil-cpp/absl/synchronization/blocking_counter.cc @@ -14,41 +14,51 @@ #include "absl/synchronization/blocking_counter.h" +#include <atomic> + #include "absl/base/internal/raw_logging.h" namespace absl { ABSL_NAMESPACE_BEGIN -// Return whether int *arg is zero. -static bool IsZero(void *arg) { - return 0 == *reinterpret_cast<int *>(arg); +namespace { + +// Return whether int *arg is true. +bool IsDone(void *arg) { return *reinterpret_cast<bool *>(arg); } + +} // namespace + +BlockingCounter::BlockingCounter(int initial_count) + : count_(initial_count), + num_waiting_(0), + done_{initial_count == 0 ? true : false} { + ABSL_RAW_CHECK(initial_count >= 0, "BlockingCounter initial_count negative"); } bool BlockingCounter::DecrementCount() { - MutexLock l(&lock_); - count_--; - if (count_ < 0) { - ABSL_RAW_LOG( - FATAL, - "BlockingCounter::DecrementCount() called too many times. count=%d", - count_); + int count = count_.fetch_sub(1, std::memory_order_acq_rel) - 1; + ABSL_RAW_CHECK(count >= 0, + "BlockingCounter::DecrementCount() called too many times"); + if (count == 0) { + MutexLock l(&lock_); + done_ = true; + return true; } - return count_ == 0; + return false; } void BlockingCounter::Wait() { MutexLock l(&this->lock_); - ABSL_RAW_CHECK(count_ >= 0, "BlockingCounter underflow"); // only one thread may call Wait(). To support more than one thread, // implement a counter num_to_exit, like in the Barrier class. ABSL_RAW_CHECK(num_waiting_ == 0, "multiple threads called Wait()"); num_waiting_++; - this->lock_.Await(Condition(IsZero, &this->count_)); + this->lock_.Await(Condition(IsDone, &this->done_)); - // At this point, We know that all threads executing DecrementCount have - // released the lock, and so will not touch this object again. + // At this point, we know that all threads executing DecrementCount + // will not touch this object again. // Therefore, the thread calling this method is free to delete the object // after we return from this method. } diff --git a/third_party/abseil-cpp/absl/synchronization/blocking_counter.h b/third_party/abseil-cpp/absl/synchronization/blocking_counter.h index 1f53f9f240..1908fdb1d9 100644 --- a/third_party/abseil-cpp/absl/synchronization/blocking_counter.h +++ b/third_party/abseil-cpp/absl/synchronization/blocking_counter.h @@ -20,6 +20,8 @@ #ifndef ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ #define ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ +#include <atomic> + #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" @@ -60,8 +62,7 @@ ABSL_NAMESPACE_BEGIN // class BlockingCounter { public: - explicit BlockingCounter(int initial_count) - : count_(initial_count), num_waiting_(0) {} + explicit BlockingCounter(int initial_count); BlockingCounter(const BlockingCounter&) = delete; BlockingCounter& operator=(const BlockingCounter&) = delete; @@ -89,8 +90,9 @@ class BlockingCounter { private: Mutex lock_; - int count_ ABSL_GUARDED_BY(lock_); + std::atomic<int> count_; int num_waiting_ ABSL_GUARDED_BY(lock_); + bool done_ ABSL_GUARDED_BY(lock_); }; ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/synchronization/blocking_counter_benchmark.cc b/third_party/abseil-cpp/absl/synchronization/blocking_counter_benchmark.cc new file mode 100644 index 0000000000..b504d1a57c --- /dev/null +++ b/third_party/abseil-cpp/absl/synchronization/blocking_counter_benchmark.cc @@ -0,0 +1,83 @@ +// Copyright 2021 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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 <limits> + +#include "absl/synchronization/blocking_counter.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "benchmark/benchmark.h" + +namespace { + +void BM_BlockingCounter_SingleThread(benchmark::State& state) { + for (auto _ : state) { + int iterations = state.range(0); + absl::BlockingCounter counter{iterations}; + for (int i = 0; i < iterations; ++i) { + counter.DecrementCount(); + } + counter.Wait(); + } +} +BENCHMARK(BM_BlockingCounter_SingleThread) + ->ArgName("iterations") + ->Arg(2) + ->Arg(4) + ->Arg(16) + ->Arg(64) + ->Arg(256); + +void BM_BlockingCounter_DecrementCount(benchmark::State& state) { + static absl::BlockingCounter* counter = + new absl::BlockingCounter{std::numeric_limits<int>::max()}; + for (auto _ : state) { + counter->DecrementCount(); + } +} +BENCHMARK(BM_BlockingCounter_DecrementCount) + ->Threads(2) + ->Threads(4) + ->Threads(6) + ->Threads(8) + ->Threads(10) + ->Threads(12) + ->Threads(16) + ->Threads(32) + ->Threads(64) + ->Threads(128); + +void BM_BlockingCounter_Wait(benchmark::State& state) { + int num_threads = state.range(0); + absl::synchronization_internal::ThreadPool pool(num_threads); + for (auto _ : state) { + absl::BlockingCounter counter{num_threads}; + pool.Schedule([num_threads, &counter, &pool]() { + for (int i = 0; i < num_threads; ++i) { + pool.Schedule([&counter]() { counter.DecrementCount(); }); + } + }); + counter.Wait(); + } +} +BENCHMARK(BM_BlockingCounter_Wait) + ->ArgName("threads") + ->Arg(2) + ->Arg(4) + ->Arg(8) + ->Arg(16) + ->Arg(32) + ->Arg(64) + ->Arg(128); + +} // namespace diff --git a/third_party/abseil-cpp/absl/synchronization/blocking_counter_test.cc b/third_party/abseil-cpp/absl/synchronization/blocking_counter_test.cc index 2926224af7..06885f5759 100644 --- a/third_party/abseil-cpp/absl/synchronization/blocking_counter_test.cc +++ b/third_party/abseil-cpp/absl/synchronization/blocking_counter_test.cc @@ -63,6 +63,18 @@ TEST(BlockingCounterTest, BasicFunctionality) { } } +TEST(BlockingCounterTest, WaitZeroInitialCount) { + BlockingCounter counter(0); + counter.Wait(); +} + +#if GTEST_HAS_DEATH_TEST +TEST(BlockingCounterTest, WaitNegativeInitialCount) { + EXPECT_DEATH(BlockingCounter counter(-1), + "BlockingCounter initial_count negative"); +} +#endif + } // namespace ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc b/third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc index fa0070a9fc..53a71b342b 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc +++ b/third_party/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc @@ -32,9 +32,9 @@ namespace synchronization_internal { // ThreadIdentity storage is persistent, we maintain a free-list of previously // released ThreadIdentity objects. -static base_internal::SpinLock freelist_lock( - base_internal::kLinkerInitialized); -static base_internal::ThreadIdentity* thread_identity_freelist; +ABSL_CONST_INIT static base_internal::SpinLock freelist_lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static base_internal::ThreadIdentity* thread_identity_freelist; // A per-thread destructor for reclaiming associated ThreadIdentity objects. // Since we must preserve their storage we cache them for re-use. diff --git a/third_party/abseil-cpp/absl/synchronization/internal/futex.h b/third_party/abseil-cpp/absl/synchronization/internal/futex.h new file mode 100644 index 0000000000..06fbd6d072 --- /dev/null +++ b/third_party/abseil-cpp/absl/synchronization/internal/futex.h @@ -0,0 +1,154 @@ +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +#ifndef ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_ +#define ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_ + +#include "absl/base/config.h" + +#ifdef _WIN32 +#include <windows.h> +#else +#include <sys/time.h> +#include <unistd.h> +#endif + +#ifdef __linux__ +#include <linux/futex.h> +#include <sys/syscall.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <time.h> + +#include <atomic> +#include <cstdint> + +#include "absl/base/optimization.h" +#include "absl/synchronization/internal/kernel_timeout.h" + +#ifdef ABSL_INTERNAL_HAVE_FUTEX +#error ABSL_INTERNAL_HAVE_FUTEX may not be set on the command line +#elif defined(__BIONIC__) +// Bionic supports all the futex operations we need even when some of the futex +// definitions are missing. +#define ABSL_INTERNAL_HAVE_FUTEX +#elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME) +// FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28. +#define ABSL_INTERNAL_HAVE_FUTEX +#endif + +#ifdef ABSL_INTERNAL_HAVE_FUTEX + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace synchronization_internal { + +// Some Android headers are missing these definitions even though they +// support these futex operations. +#ifdef __BIONIC__ +#ifndef SYS_futex +#define SYS_futex __NR_futex +#endif +#ifndef FUTEX_WAIT_BITSET +#define FUTEX_WAIT_BITSET 9 +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#ifndef FUTEX_CLOCK_REALTIME +#define FUTEX_CLOCK_REALTIME 256 +#endif +#ifndef FUTEX_BITSET_MATCH_ANY +#define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF +#endif +#endif + +#if defined(__NR_futex_time64) && !defined(SYS_futex_time64) +#define SYS_futex_time64 __NR_futex_time64 +#endif + +#if defined(SYS_futex_time64) && !defined(SYS_futex) +#define SYS_futex SYS_futex_time64 +#endif + +class FutexImpl { + public: + static int WaitUntil(std::atomic<int32_t> *v, int32_t val, + KernelTimeout t) { + int err = 0; + if (t.has_timeout()) { + // https://locklessinc.com/articles/futex_cheat_sheet/ + // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. + struct timespec abs_timeout = t.MakeAbsTimespec(); + // Atomically check that the futex value is still 0, and if it + // is, sleep until abs_timeout or until woken by FUTEX_WAKE. + err = syscall( + SYS_futex, reinterpret_cast<int32_t *>(v), + FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val, + &abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY); + } else { + // Atomically check that the futex value is still 0, and if it + // is, sleep until woken by FUTEX_WAKE. + err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), + FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, nullptr); + } + if (ABSL_PREDICT_FALSE(err != 0)) { + err = -errno; + } + return err; + } + + static int WaitBitsetAbsoluteTimeout(std::atomic<int32_t> *v, int32_t val, + int32_t bits, + const struct timespec *abstime) { + int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), + FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, val, abstime, + nullptr, bits); + if (ABSL_PREDICT_FALSE(err != 0)) { + err = -errno; + } + return err; + } + + static int Wake(std::atomic<int32_t> *v, int32_t count) { + int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), + FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count); + if (ABSL_PREDICT_FALSE(err < 0)) { + err = -errno; + } + return err; + } + + // FUTEX_WAKE_BITSET + static int WakeBitset(std::atomic<int32_t> *v, int32_t count, int32_t bits) { + int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), + FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, count, nullptr, + nullptr, bits); + if (ABSL_PREDICT_FALSE(err < 0)) { + err = -errno; + } + return err; + } +}; + +class Futex : public FutexImpl {}; + +} // namespace synchronization_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_HAVE_FUTEX + +#endif // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_ diff --git a/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc b/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc index 6a2bcdf681..27fec21681 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc +++ b/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc @@ -37,6 +37,7 @@ #include <algorithm> #include <array> +#include <limits> #include "absl/base/internal/hide_ptr.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/spinlock.h" @@ -51,9 +52,9 @@ namespace { // Avoid LowLevelAlloc's default arena since it calls malloc hooks in // which people are doing things like acquiring Mutexes. -static absl::base_internal::SpinLock arena_mu( - absl::base_internal::kLinkerInitialized); -static base_internal::LowLevelAlloc::Arena* arena; +ABSL_CONST_INIT static absl::base_internal::SpinLock arena_mu( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static base_internal::LowLevelAlloc::Arena* arena; static void InitArenaIfNecessary() { arena_mu.Lock(); diff --git a/third_party/abseil-cpp/absl/synchronization/internal/kernel_timeout.h b/third_party/abseil-cpp/absl/synchronization/internal/kernel_timeout.h index d6ac5db0af..bbd4d2d70f 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/kernel_timeout.h +++ b/third_party/abseil-cpp/absl/synchronization/internal/kernel_timeout.h @@ -26,6 +26,7 @@ #define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ #include <time.h> + #include <algorithm> #include <limits> @@ -57,6 +58,10 @@ class KernelTimeout { bool has_timeout() const { return ns_ != 0; } + // Convert to parameter for sem_timedwait/futex/similar. Only for approved + // users. Do not call if !has_timeout. + struct timespec MakeAbsTimespec(); + private: // internal rep, not user visible: ns after unix epoch. // zero = no timeout. @@ -82,34 +87,6 @@ class KernelTimeout { return x; } - // Convert to parameter for sem_timedwait/futex/similar. Only for approved - // users. Do not call if !has_timeout. - struct timespec MakeAbsTimespec() { - int64_t n = ns_; - static const int64_t kNanosPerSecond = 1000 * 1000 * 1000; - if (n == 0) { - ABSL_RAW_LOG( - ERROR, - "Tried to create a timespec from a non-timeout; never do this."); - // But we'll try to continue sanely. no-timeout ~= saturated timeout. - n = (std::numeric_limits<int64_t>::max)(); - } - - // Kernel APIs validate timespecs as being at or after the epoch, - // despite the kernel time type being signed. However, no one can - // tell the difference between a timeout at or before the epoch (since - // all such timeouts have expired!) - if (n < 0) n = 0; - - struct timespec abstime; - int64_t seconds = (std::min)(n / kNanosPerSecond, - int64_t{(std::numeric_limits<time_t>::max)()}); - abstime.tv_sec = static_cast<time_t>(seconds); - abstime.tv_nsec = - static_cast<decltype(abstime.tv_nsec)>(n % kNanosPerSecond); - return abstime; - } - #ifdef _WIN32 // Converts to milliseconds from now, or INFINITE when // !has_timeout(). For use by SleepConditionVariableSRW on @@ -148,6 +125,30 @@ class KernelTimeout { friend class Waiter; }; +inline struct timespec KernelTimeout::MakeAbsTimespec() { + int64_t n = ns_; + static const int64_t kNanosPerSecond = 1000 * 1000 * 1000; + if (n == 0) { + ABSL_RAW_LOG( + ERROR, "Tried to create a timespec from a non-timeout; never do this."); + // But we'll try to continue sanely. no-timeout ~= saturated timeout. + n = (std::numeric_limits<int64_t>::max)(); + } + + // Kernel APIs validate timespecs as being at or after the epoch, + // despite the kernel time type being signed. However, no one can + // tell the difference between a timeout at or before the epoch (since + // all such timeouts have expired!) + if (n < 0) n = 0; + + struct timespec abstime; + int64_t seconds = (std::min)(n / kNanosPerSecond, + int64_t{(std::numeric_limits<time_t>::max)()}); + abstime.tv_sec = static_cast<time_t>(seconds); + abstime.tv_nsec = static_cast<decltype(abstime.tv_nsec)>(n % kNanosPerSecond); + return abstime; +} + } // namespace synchronization_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/synchronization/internal/mutex_nonprod.cc b/third_party/abseil-cpp/absl/synchronization/internal/mutex_nonprod.cc deleted file mode 100644 index 4590b98dbb..0000000000 --- a/third_party/abseil-cpp/absl/synchronization/internal/mutex_nonprod.cc +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// 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. - -// Implementation of a small subset of Mutex and CondVar functionality -// for platforms where the production implementation hasn't been fully -// ported yet. - -#include "absl/synchronization/mutex.h" - -#if defined(_WIN32) -#include <chrono> // NOLINT(build/c++11) -#else -#include <sys/time.h> -#include <time.h> -#endif - -#include <algorithm> - -#include "absl/base/internal/raw_logging.h" -#include "absl/time/time.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace synchronization_internal { - -namespace { - -// Return the current time plus the timeout. -absl::Time DeadlineFromTimeout(absl::Duration timeout) { - return absl::Now() + timeout; -} - -// Limit the deadline to a positive, 32-bit time_t value to accommodate -// implementation restrictions. This also deals with InfinitePast and -// InfiniteFuture. -absl::Time LimitedDeadline(absl::Time deadline) { - deadline = std::max(absl::FromTimeT(0), deadline); - deadline = std::min(deadline, absl::FromTimeT(0x7fffffff)); - return deadline; -} - -} // namespace - -#if defined(_WIN32) - -MutexImpl::MutexImpl() {} - -MutexImpl::~MutexImpl() { - if (locked_) { - std_mutex_.unlock(); - } -} - -void MutexImpl::Lock() { - std_mutex_.lock(); - locked_ = true; -} - -bool MutexImpl::TryLock() { - bool locked = std_mutex_.try_lock(); - if (locked) locked_ = true; - return locked; -} - -void MutexImpl::Unlock() { - locked_ = false; - released_.SignalAll(); - std_mutex_.unlock(); -} - -CondVarImpl::CondVarImpl() {} - -CondVarImpl::~CondVarImpl() {} - -void CondVarImpl::Signal() { std_cv_.notify_one(); } - -void CondVarImpl::SignalAll() { std_cv_.notify_all(); } - -void CondVarImpl::Wait(MutexImpl* mu) { - mu->released_.SignalAll(); - std_cv_.wait(mu->std_mutex_); -} - -bool CondVarImpl::WaitWithDeadline(MutexImpl* mu, absl::Time deadline) { - mu->released_.SignalAll(); - time_t when = ToTimeT(deadline); - int64_t nanos = ToInt64Nanoseconds(deadline - absl::FromTimeT(when)); - std::chrono::system_clock::time_point deadline_tp = - std::chrono::system_clock::from_time_t(when) + - std::chrono::duration_cast<std::chrono::system_clock::duration>( - std::chrono::nanoseconds(nanos)); - auto deadline_since_epoch = - std::chrono::duration_cast<std::chrono::duration<double>>( - deadline_tp - std::chrono::system_clock::from_time_t(0)); - return std_cv_.wait_until(mu->std_mutex_, deadline_tp) == - std::cv_status::timeout; -} - -#else // ! _WIN32 - -MutexImpl::MutexImpl() { - ABSL_RAW_CHECK(pthread_mutex_init(&pthread_mutex_, nullptr) == 0, - "pthread error"); -} - -MutexImpl::~MutexImpl() { - if (locked_) { - ABSL_RAW_CHECK(pthread_mutex_unlock(&pthread_mutex_) == 0, "pthread error"); - } - ABSL_RAW_CHECK(pthread_mutex_destroy(&pthread_mutex_) == 0, "pthread error"); -} - -void MutexImpl::Lock() { - ABSL_RAW_CHECK(pthread_mutex_lock(&pthread_mutex_) == 0, "pthread error"); - locked_ = true; -} - -bool MutexImpl::TryLock() { - bool locked = (0 == pthread_mutex_trylock(&pthread_mutex_)); - if (locked) locked_ = true; - return locked; -} - -void MutexImpl::Unlock() { - locked_ = false; - released_.SignalAll(); - ABSL_RAW_CHECK(pthread_mutex_unlock(&pthread_mutex_) == 0, "pthread error"); -} - -CondVarImpl::CondVarImpl() { - ABSL_RAW_CHECK(pthread_cond_init(&pthread_cv_, nullptr) == 0, - "pthread error"); -} - -CondVarImpl::~CondVarImpl() { - ABSL_RAW_CHECK(pthread_cond_destroy(&pthread_cv_) == 0, "pthread error"); -} - -void CondVarImpl::Signal() { - ABSL_RAW_CHECK(pthread_cond_signal(&pthread_cv_) == 0, "pthread error"); -} - -void CondVarImpl::SignalAll() { - ABSL_RAW_CHECK(pthread_cond_broadcast(&pthread_cv_) == 0, "pthread error"); -} - -void CondVarImpl::Wait(MutexImpl* mu) { - mu->released_.SignalAll(); - ABSL_RAW_CHECK(pthread_cond_wait(&pthread_cv_, &mu->pthread_mutex_) == 0, - "pthread error"); -} - -bool CondVarImpl::WaitWithDeadline(MutexImpl* mu, absl::Time deadline) { - mu->released_.SignalAll(); - struct timespec ts = ToTimespec(deadline); - int rc = pthread_cond_timedwait(&pthread_cv_, &mu->pthread_mutex_, &ts); - if (rc == ETIMEDOUT) return true; - ABSL_RAW_CHECK(rc == 0, "pthread error"); - return false; -} - -#endif // ! _WIN32 - -void MutexImpl::Await(const Condition& cond) { - if (cond.Eval()) return; - released_.SignalAll(); - do { - released_.Wait(this); - } while (!cond.Eval()); -} - -bool MutexImpl::AwaitWithDeadline(const Condition& cond, absl::Time deadline) { - if (cond.Eval()) return true; - released_.SignalAll(); - while (true) { - if (released_.WaitWithDeadline(this, deadline)) return false; - if (cond.Eval()) return true; - } -} - -} // namespace synchronization_internal - -Mutex::Mutex() {} - -Mutex::~Mutex() {} - -void Mutex::Lock() { impl()->Lock(); } - -void Mutex::Unlock() { impl()->Unlock(); } - -bool Mutex::TryLock() { return impl()->TryLock(); } - -void Mutex::ReaderLock() { Lock(); } - -void Mutex::ReaderUnlock() { Unlock(); } - -void Mutex::Await(const Condition& cond) { impl()->Await(cond); } - -void Mutex::LockWhen(const Condition& cond) { - Lock(); - Await(cond); -} - -bool Mutex::AwaitWithDeadline(const Condition& cond, absl::Time deadline) { - return impl()->AwaitWithDeadline( - cond, synchronization_internal::LimitedDeadline(deadline)); -} - -bool Mutex::AwaitWithTimeout(const Condition& cond, absl::Duration timeout) { - return AwaitWithDeadline( - cond, synchronization_internal::DeadlineFromTimeout(timeout)); -} - -bool Mutex::LockWhenWithDeadline(const Condition& cond, absl::Time deadline) { - Lock(); - return AwaitWithDeadline(cond, deadline); -} - -bool Mutex::LockWhenWithTimeout(const Condition& cond, absl::Duration timeout) { - return LockWhenWithDeadline( - cond, synchronization_internal::DeadlineFromTimeout(timeout)); -} - -void Mutex::ReaderLockWhen(const Condition& cond) { - ReaderLock(); - Await(cond); -} - -bool Mutex::ReaderLockWhenWithTimeout(const Condition& cond, - absl::Duration timeout) { - return LockWhenWithTimeout(cond, timeout); -} -bool Mutex::ReaderLockWhenWithDeadline(const Condition& cond, - absl::Time deadline) { - return LockWhenWithDeadline(cond, deadline); -} - -void Mutex::EnableDebugLog(const char*) {} -void Mutex::EnableInvariantDebugging(void (*)(void*), void*) {} -void Mutex::ForgetDeadlockInfo() {} -void Mutex::AssertHeld() const {} -void Mutex::AssertReaderHeld() const {} -void Mutex::AssertNotHeld() const {} - -CondVar::CondVar() {} - -CondVar::~CondVar() {} - -void CondVar::Signal() { impl()->Signal(); } - -void CondVar::SignalAll() { impl()->SignalAll(); } - -void CondVar::Wait(Mutex* mu) { return impl()->Wait(mu->impl()); } - -bool CondVar::WaitWithDeadline(Mutex* mu, absl::Time deadline) { - return impl()->WaitWithDeadline( - mu->impl(), synchronization_internal::LimitedDeadline(deadline)); -} - -bool CondVar::WaitWithTimeout(Mutex* mu, absl::Duration timeout) { - return WaitWithDeadline(mu, absl::Now() + timeout); -} - -void CondVar::EnableDebugLog(const char*) {} - -#ifdef THREAD_SANITIZER -extern "C" void __tsan_read1(void *addr); -#else -#define __tsan_read1(addr) // do nothing if TSan not enabled -#endif - -// A function that just returns its argument, dereferenced -static bool Dereference(void *arg) { - // ThreadSanitizer does not instrument this file for memory accesses. - // This function dereferences a user variable that can participate - // in a data race, so we need to manually tell TSan about this memory access. - __tsan_read1(arg); - return *(static_cast<bool *>(arg)); -} - -Condition::Condition() {} // null constructor, used for kTrue only -const Condition Condition::kTrue; - -Condition::Condition(bool (*func)(void *), void *arg) - : eval_(&CallVoidPtrFunction), - function_(func), - method_(nullptr), - arg_(arg) {} - -bool Condition::CallVoidPtrFunction(const Condition *c) { - return (*c->function_)(c->arg_); -} - -Condition::Condition(const bool *cond) - : eval_(CallVoidPtrFunction), - function_(Dereference), - method_(nullptr), - // const_cast is safe since Dereference does not modify arg - arg_(const_cast<bool *>(cond)) {} - -bool Condition::Eval() const { - // eval_ == null for kTrue - return (this->eval_ == nullptr) || (*this->eval_)(this); -} - -void RegisterSymbolizer(bool (*)(const void*, char*, int)) {} - -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil-cpp/absl/synchronization/internal/mutex_nonprod.inc b/third_party/abseil-cpp/absl/synchronization/internal/mutex_nonprod.inc deleted file mode 100644 index a1502e727d..0000000000 --- a/third_party/abseil-cpp/absl/synchronization/internal/mutex_nonprod.inc +++ /dev/null @@ -1,261 +0,0 @@ -// Do not include. This is an implementation detail of base/mutex.h. -// -// Declares three classes: -// -// base::internal::MutexImpl - implementation helper for Mutex -// base::internal::CondVarImpl - implementation helper for CondVar -// base::internal::SynchronizationStorage<T> - implementation helper for -// Mutex, CondVar - -#include <type_traits> - -#if defined(_WIN32) -#include <condition_variable> -#include <mutex> -#else -#include <pthread.h> -#endif - -#include "absl/base/call_once.h" -#include "absl/time/time.h" - -// Declare that Mutex::ReaderLock is actually Lock(). Intended primarily -// for tests, and even then as a last resort. -#ifdef ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE -#error ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE cannot be directly set -#else -#define ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE 1 -#endif - -// Declare that Mutex::EnableInvariantDebugging is not implemented. -// Intended primarily for tests, and even then as a last resort. -#ifdef ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED -#error ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED cannot be directly set -#else -#define ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED 1 -#endif - -namespace absl { -ABSL_NAMESPACE_BEGIN -class Condition; - -namespace synchronization_internal { - -class MutexImpl; - -// Do not use this implementation detail of CondVar. Provides most of the -// implementation, but should not be placed directly in static storage -// because it will not linker initialize properly. See -// SynchronizationStorage<T> below for what we mean by linker -// initialization. -class CondVarImpl { - public: - CondVarImpl(); - CondVarImpl(const CondVarImpl&) = delete; - CondVarImpl& operator=(const CondVarImpl&) = delete; - ~CondVarImpl(); - - void Signal(); - void SignalAll(); - void Wait(MutexImpl* mutex); - bool WaitWithDeadline(MutexImpl* mutex, absl::Time deadline); - - private: -#if defined(_WIN32) - std::condition_variable_any std_cv_; -#else - pthread_cond_t pthread_cv_; -#endif -}; - -// Do not use this implementation detail of Mutex. Provides most of the -// implementation, but should not be placed directly in static storage -// because it will not linker initialize properly. See -// SynchronizationStorage<T> below for what we mean by linker -// initialization. -class MutexImpl { - public: - MutexImpl(); - MutexImpl(const MutexImpl&) = delete; - MutexImpl& operator=(const MutexImpl&) = delete; - ~MutexImpl(); - - void Lock(); - bool TryLock(); - void Unlock(); - void Await(const Condition& cond); - bool AwaitWithDeadline(const Condition& cond, absl::Time deadline); - - private: - friend class CondVarImpl; - -#if defined(_WIN32) - std::mutex std_mutex_; -#else - pthread_mutex_t pthread_mutex_; -#endif - - // True if the underlying mutex is locked. If the destructor is entered - // while locked_, the underlying mutex is unlocked. Mutex supports - // destruction while locked, but the same is undefined behavior for both - // pthread_mutex_t and std::mutex. - bool locked_ = false; - - // Signaled before releasing the lock, in support of Await. - CondVarImpl released_; -}; - -// Do not use this implementation detail of CondVar and Mutex. A storage -// space for T that supports a LinkerInitialized constructor. T must -// have a default constructor, which is called by the first call to -// get(). T's destructor is never called if the LinkerInitialized -// constructor is called. -// -// Objects constructed with the default constructor are constructed and -// destructed like any other object, and should never be allocated in -// static storage. -// -// Objects constructed with the LinkerInitialized constructor should -// always be in static storage. For such objects, calls to get() are always -// valid, except from signal handlers. -// -// Note that this implementation relies on undefined language behavior that -// are known to hold for the set of supported compilers. An analysis -// follows. -// -// From the C++11 standard: -// -// [basic.life] says an object has non-trivial initialization if it is of -// class type and it is initialized by a constructor other than a trivial -// default constructor. (the LinkerInitialized constructor is -// non-trivial) -// -// [basic.life] says the lifetime of an object with a non-trivial -// constructor begins when the call to the constructor is complete. -// -// [basic.life] says the lifetime of an object with non-trivial destructor -// ends when the call to the destructor begins. -// -// [basic.life] p5 specifies undefined behavior when accessing non-static -// members of an instance outside its -// lifetime. (SynchronizationStorage::get() access non-static members) -// -// So, LinkerInitialized object of SynchronizationStorage uses a -// non-trivial constructor, which is called at some point during dynamic -// initialization, and is therefore subject to order of dynamic -// initialization bugs, where get() is called before the object's -// constructor is, resulting in undefined behavior. -// -// Similarly, a LinkerInitialized SynchronizationStorage object has a -// non-trivial destructor, and so its lifetime ends at some point during -// destruction of objects with static storage duration [basic.start.term] -// p4. There is a window where other exit code could call get() after this -// occurs, resulting in undefined behavior. -// -// Combined, these statements imply that LinkerInitialized instances -// of SynchronizationStorage<T> rely on undefined behavior. -// -// However, in practice, the implementation works on all supported -// compilers. Specifically, we rely on: -// -// a) zero-initialization being sufficient to initialize -// LinkerInitialized instances for the purposes of calling -// get(), regardless of when the constructor is called. This is -// because the is_dynamic_ boolean is correctly zero-initialized to -// false. -// -// b) the LinkerInitialized constructor is a NOP, and immaterial to -// even to concurrent calls to get(). -// -// c) the destructor being a NOP for LinkerInitialized objects -// (guaranteed by a check for !is_dynamic_), and so any concurrent and -// subsequent calls to get() functioning as if the destructor were not -// called, by virtue of the instances' storage remaining valid after the -// destructor runs. -// -// d) That a-c apply transitively when SynchronizationStorage<T> is the -// only member of a class allocated in static storage. -// -// Nothing in the language standard guarantees that a-d hold. In practice, -// these hold in all supported compilers. -// -// Future direction: -// -// Ideally, we would simply use std::mutex or a similar class, which when -// allocated statically would support use immediately after static -// initialization up until static storage is reclaimed (i.e. the properties -// we require of all "linker initialized" instances). -// -// Regarding construction in static storage, std::mutex is required to -// provide a constexpr default constructor [thread.mutex.class], which -// ensures the instance's lifetime begins with static initialization -// [basic.start.init], and so is immune to any problems caused by the order -// of dynamic initialization. However, as of this writing Microsoft's -// Visual Studio does not provide a constexpr constructor for std::mutex. -// See -// https://blogs.msdn.microsoft.com/vcblog/2015/06/02/constexpr-complete-for-vs-2015-rtm-c11-compiler-c17-stl/ -// -// Regarding destruction of instances in static storage, [basic.life] does -// say an object ends when storage in which the occupies is released, in -// the case of non-trivial destructor. However, std::mutex is not specified -// to have a trivial destructor. -// -// So, we would need a class with a constexpr default constructor and a -// trivial destructor. Today, we can achieve neither desired property using -// std::mutex directly. -template <typename T> -class SynchronizationStorage { - public: - // Instances allocated on the heap or on the stack should use the default - // constructor. - SynchronizationStorage() - : is_dynamic_(true), once_() {} - - // Instances allocated in static storage (not on the heap, not on the - // stack) should use this constructor. - explicit SynchronizationStorage(base_internal::LinkerInitialized) {} - - constexpr explicit SynchronizationStorage(absl::ConstInitType) - : is_dynamic_(false), once_(), space_{{0}} {} - - SynchronizationStorage(SynchronizationStorage&) = delete; - SynchronizationStorage& operator=(SynchronizationStorage&) = delete; - - ~SynchronizationStorage() { - if (is_dynamic_) { - get()->~T(); - } - } - - // Retrieve the object in storage. This is fast and thread safe, but does - // incur the cost of absl::call_once(). - // - // For instances in static storage constructed with the - // LinkerInitialized constructor, may be called at any time without - // regard for order of dynamic initialization or destruction of objects - // in static storage. See the class comment for caveats. - T* get() { - absl::call_once(once_, SynchronizationStorage::Construct, this); - return reinterpret_cast<T*>(&space_); - } - - private: - static void Construct(SynchronizationStorage<T>* self) { - new (&self->space_) T(); - } - - // When true, T's destructor is run when this is destructed. - // - // The LinkerInitialized constructor assumes this value will be set - // false by static initialization. - bool is_dynamic_; - - absl::once_flag once_; - - // An aligned space for the T. - alignas(T) unsigned char space_[sizeof(T)]; -}; - -} // namespace synchronization_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc b/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc index 821ca9b4e9..a6031787e0 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc +++ b/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc @@ -68,12 +68,12 @@ ABSL_NAMESPACE_END extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalPerThreadSemPost( +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)( absl::base_internal::ThreadIdentity *identity) { absl::synchronization_internal::Waiter::GetWaiter(identity)->Post(); } -ABSL_ATTRIBUTE_WEAK bool AbslInternalPerThreadSemWait( +ABSL_ATTRIBUTE_WEAK bool ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)( absl::synchronization_internal::KernelTimeout t) { bool timeout = false; absl::base_internal::ThreadIdentity *identity; diff --git a/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.h b/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.h index 8ab439153a..7beae8ef1d 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.h +++ b/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem.h @@ -78,7 +78,7 @@ class PerThreadSem { // !t.has_timeout() => Wait(t) will return true. static inline bool Wait(KernelTimeout t); - // White-listed callers. + // Permitted callers. friend class PerThreadSemTest; friend class absl::Mutex; friend absl::base_internal::ThreadIdentity* CreateThreadIdentity(); @@ -96,20 +96,20 @@ ABSL_NAMESPACE_END // By changing our extension points to be extern "C", we dodge this // check. extern "C" { -void AbslInternalPerThreadSemPost( +void ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)( absl::base_internal::ThreadIdentity* identity); -bool AbslInternalPerThreadSemWait( +bool ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)( absl::synchronization_internal::KernelTimeout t); } // extern "C" void absl::synchronization_internal::PerThreadSem::Post( absl::base_internal::ThreadIdentity* identity) { - AbslInternalPerThreadSemPost(identity); + ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemPost)(identity); } bool absl::synchronization_internal::PerThreadSem::Wait( absl::synchronization_internal::KernelTimeout t) { - return AbslInternalPerThreadSemWait(t); + return ABSL_INTERNAL_C_SYMBOL(AbslInternalPerThreadSemWait)(t); } #endif // ABSL_SYNCHRONIZATION_INTERNAL_PER_THREAD_SEM_H_ diff --git a/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc b/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc index b5a2f6d4b5..db1184e679 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc +++ b/third_party/abseil-cpp/absl/synchronization/internal/per_thread_sem_test.cc @@ -23,6 +23,7 @@ #include <thread> // NOLINT(build/c++11) #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/base/internal/cycleclock.h" #include "absl/base/internal/thread_identity.h" #include "absl/strings/str_cat.h" @@ -158,7 +159,7 @@ TEST_F(PerThreadSemTest, Timeouts) { const absl::Duration elapsed = absl::Now() - start; // Allow for a slight early return, to account for quality of implementation // issues on various platforms. - const absl::Duration slop = absl::Microseconds(200); + const absl::Duration slop = absl::Milliseconds(1); EXPECT_LE(delay - slop, elapsed) << "Wait returned " << delay - elapsed << " early (with " << slop << " slop), start time was " << start; diff --git a/third_party/abseil-cpp/absl/synchronization/internal/waiter.cc b/third_party/abseil-cpp/absl/synchronization/internal/waiter.cc index 2949f5a84c..28ef311e4a 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/waiter.cc +++ b/third_party/abseil-cpp/absl/synchronization/internal/waiter.cc @@ -48,6 +48,7 @@ #include "absl/base/optimization.h" #include "absl/synchronization/internal/kernel_timeout.h" + namespace absl { ABSL_NAMESPACE_BEGIN namespace synchronization_internal { @@ -66,63 +67,6 @@ static void MaybeBecomeIdle() { #if ABSL_WAITER_MODE == ABSL_WAITER_MODE_FUTEX -// Some Android headers are missing these definitions even though they -// support these futex operations. -#ifdef __BIONIC__ -#ifndef SYS_futex -#define SYS_futex __NR_futex -#endif -#ifndef FUTEX_WAIT_BITSET -#define FUTEX_WAIT_BITSET 9 -#endif -#ifndef FUTEX_PRIVATE_FLAG -#define FUTEX_PRIVATE_FLAG 128 -#endif -#ifndef FUTEX_CLOCK_REALTIME -#define FUTEX_CLOCK_REALTIME 256 -#endif -#ifndef FUTEX_BITSET_MATCH_ANY -#define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF -#endif -#endif - -class Futex { - public: - static int WaitUntil(std::atomic<int32_t> *v, int32_t val, - KernelTimeout t) { - int err = 0; - if (t.has_timeout()) { - // https://locklessinc.com/articles/futex_cheat_sheet/ - // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. - struct timespec abs_timeout = t.MakeAbsTimespec(); - // Atomically check that the futex value is still 0, and if it - // is, sleep until abs_timeout or until woken by FUTEX_WAKE. - err = syscall( - SYS_futex, reinterpret_cast<int32_t *>(v), - FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val, - &abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY); - } else { - // Atomically check that the futex value is still 0, and if it - // is, sleep until woken by FUTEX_WAKE. - err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), - FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, nullptr); - } - if (err != 0) { - err = -errno; - } - return err; - } - - static int Wake(std::atomic<int32_t> *v, int32_t count) { - int err = syscall(SYS_futex, reinterpret_cast<int32_t *>(v), - FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count); - if (ABSL_PREDICT_FALSE(err < 0)) { - err = -errno; - } - return err; - } -}; - Waiter::Waiter() { futex_.store(0, std::memory_order_relaxed); } @@ -135,6 +79,7 @@ bool Waiter::Wait(KernelTimeout t) { // Note that, since the thread ticker is just reset, we don't need to check // whether the thread is idle on the very first pass of the loop. bool first_pass = true; + while (true) { int32_t x = futex_.load(std::memory_order_relaxed); while (x != 0) { @@ -146,7 +91,6 @@ bool Waiter::Wait(KernelTimeout t) { return true; // Consumed a wakeup, we are done. } - if (!first_pass) MaybeBecomeIdle(); const int err = Futex::WaitUntil(&futex_, 0, t); if (err != 0) { diff --git a/third_party/abseil-cpp/absl/synchronization/internal/waiter.h b/third_party/abseil-cpp/absl/synchronization/internal/waiter.h index a6e6d4c7ee..be3df180d4 100644 --- a/third_party/abseil-cpp/absl/synchronization/internal/waiter.h +++ b/third_party/abseil-cpp/absl/synchronization/internal/waiter.h @@ -36,6 +36,7 @@ #include <cstdint> #include "absl/base/internal/thread_identity.h" +#include "absl/synchronization/internal/futex.h" #include "absl/synchronization/internal/kernel_timeout.h" // May be chosen at compile time via -DABSL_FORCE_WAITER_MODE=<index> @@ -48,12 +49,7 @@ #define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE #elif defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA #define ABSL_WAITER_MODE ABSL_WAITER_MODE_WIN32 -#elif defined(__BIONIC__) -// Bionic supports all the futex operations we need even when some of the futex -// definitions are missing. -#define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX -#elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME) -// FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28. +#elif defined(ABSL_INTERNAL_HAVE_FUTEX) #define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX #elif defined(ABSL_HAVE_SEMAPHORE_H) #define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM @@ -100,8 +96,8 @@ class Waiter { } // How many periods to remain idle before releasing resources -#ifndef THREAD_SANITIZER - static const int kIdlePeriods = 60; +#ifndef ABSL_HAVE_THREAD_SANITIZER + static constexpr int kIdlePeriods = 60; #else // Memory consumption under ThreadSanitizer is a serious concern, // so we release resources sooner. The value of 1 leads to 1 to 2 second diff --git a/third_party/abseil-cpp/absl/synchronization/mutex.cc b/third_party/abseil-cpp/absl/synchronization/mutex.cc index e0879b059a..76ad41fe16 100644 --- a/third_party/abseil-cpp/absl/synchronization/mutex.cc +++ b/third_party/abseil-cpp/absl/synchronization/mutex.cc @@ -39,6 +39,7 @@ #include <thread> // NOLINT(build/c++11) #include "absl/base/attributes.h" +#include "absl/base/call_once.h" #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/internal/atomic_hook.h" @@ -49,6 +50,7 @@ #include "absl/base/internal/spinlock.h" #include "absl/base/internal/sysinfo.h" #include "absl/base/internal/thread_identity.h" +#include "absl/base/internal/tsan_mutex_interface.h" #include "absl/base/port.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" @@ -58,6 +60,7 @@ using absl::base_internal::CurrentThreadIdentityIfPresent; using absl::base_internal::PerThreadSynch; +using absl::base_internal::SchedulingGuard; using absl::base_internal::ThreadIdentity; using absl::synchronization_internal::GetOrCreateCurrentThreadIdentity; using absl::synchronization_internal::GraphCycles; @@ -67,7 +70,9 @@ using absl::synchronization_internal::KernelTimeout; using absl::synchronization_internal::PerThreadSem; extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); } +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)() { + std::this_thread::yield(); +} } // extern "C" namespace absl { @@ -75,7 +80,7 @@ ABSL_NAMESPACE_BEGIN namespace { -#if defined(THREAD_SANITIZER) +#if defined(ABSL_HAVE_THREAD_SANITIZER) constexpr OnDeadlockCycle kDeadlockDetectionDefault = OnDeadlockCycle::kIgnore; #else constexpr OnDeadlockCycle kDeadlockDetectionDefault = OnDeadlockCycle::kAbort; @@ -85,31 +90,9 @@ ABSL_CONST_INIT std::atomic<OnDeadlockCycle> synch_deadlock_detection( kDeadlockDetectionDefault); ABSL_CONST_INIT std::atomic<bool> synch_check_invariants(false); -// ------------------------------------------ spinlock support - -// Make sure read-only globals used in the Mutex code are contained on the -// same cacheline and cacheline aligned to eliminate any false sharing with -// other globals from this and other modules. -static struct MutexGlobals { - MutexGlobals() { - // Find machine-specific data needed for Delay() and - // TryAcquireWithSpinning(). This runs in the global constructor - // sequence, and before that zeros are safe values. - num_cpus = absl::base_internal::NumCPUs(); - spinloop_iterations = num_cpus > 1 ? 1500 : 0; - } - int num_cpus; - int spinloop_iterations; - // Pad this struct to a full cacheline to prevent false sharing. - char padding[ABSL_CACHELINE_SIZE - 2 * sizeof(int)]; -} ABSL_CACHELINE_ALIGNED mutex_globals; -static_assert( - sizeof(MutexGlobals) == ABSL_CACHELINE_SIZE, - "MutexGlobals must occupy an entire cacheline to prevent false sharing"); - ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES - absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)> - submit_profile_data; +absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)> + submit_profile_data; ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<void (*)( const char *msg, const void *obj, int64_t wait_cycles)> mutex_tracer; @@ -143,33 +126,64 @@ void RegisterSymbolizer(bool (*fn)(const void *pc, char *out, int out_size)) { symbolizer.Store(fn); } -// spinlock delay on iteration c. Returns new c. namespace { - enum DelayMode { AGGRESSIVE, GENTLE }; +// Represents the strategy for spin and yield. +// See the comment in GetMutexGlobals() for more information. +enum DelayMode { AGGRESSIVE, GENTLE }; + +struct ABSL_CACHELINE_ALIGNED MutexGlobals { + absl::once_flag once; + int spinloop_iterations = 0; + int32_t mutex_sleep_limit[2] = {}; }; -static int Delay(int32_t c, DelayMode mode) { - // If this a uniprocessor, only yield/sleep. Otherwise, if the mode is - // aggressive then spin many times before yielding. If the mode is - // gentle then spin only a few times before yielding. Aggressive spinning is - // used to ensure that an Unlock() call, which must get the spin lock for - // any thread to make progress gets it without undue delay. - int32_t limit = (mutex_globals.num_cpus > 1) ? - ((mode == AGGRESSIVE) ? 5000 : 250) : 0; + +const MutexGlobals &GetMutexGlobals() { + ABSL_CONST_INIT static MutexGlobals data; + absl::base_internal::LowLevelCallOnce(&data.once, [&]() { + const int num_cpus = absl::base_internal::NumCPUs(); + data.spinloop_iterations = num_cpus > 1 ? 1500 : 0; + // If this a uniprocessor, only yield/sleep. Otherwise, if the mode is + // aggressive then spin many times before yielding. If the mode is + // gentle then spin only a few times before yielding. Aggressive spinning + // is used to ensure that an Unlock() call, which must get the spin lock + // for any thread to make progress gets it without undue delay. + if (num_cpus > 1) { + data.mutex_sleep_limit[AGGRESSIVE] = 5000; + data.mutex_sleep_limit[GENTLE] = 250; + } else { + data.mutex_sleep_limit[AGGRESSIVE] = 0; + data.mutex_sleep_limit[GENTLE] = 0; + } + }); + return data; +} +} // namespace + +namespace synchronization_internal { +// Returns the Mutex delay on iteration `c` depending on the given `mode`. +// The returned value should be used as `c` for the next call to `MutexDelay`. +int MutexDelay(int32_t c, int mode) { + const int32_t limit = GetMutexGlobals().mutex_sleep_limit[mode]; if (c < limit) { - c++; // spin + // Spin. + c++; } else { + SchedulingGuard::ScopedEnable enable_rescheduling; ABSL_TSAN_MUTEX_PRE_DIVERT(nullptr, 0); - if (c == limit) { // yield once - AbslInternalMutexYield(); + if (c == limit) { + // Yield once. + ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)(); c++; - } else { // then wait + } else { + // Then wait. absl::SleepFor(absl::Microseconds(10)); c = 0; } ABSL_TSAN_MUTEX_POST_DIVERT(nullptr, 0); } - return (c); + return c; } +} // namespace synchronization_internal // --------------------------Generic atomic ops // Ensure that "(*pv & bits) == bits" by doing an atomic update of "*pv" to @@ -207,12 +221,12 @@ static void AtomicClearBits(std::atomic<intptr_t>* pv, intptr_t bits, //------------------------------------------------------------------ // Data for doing deadlock detection. -static absl::base_internal::SpinLock deadlock_graph_mu( - absl::base_internal::kLinkerInitialized); +ABSL_CONST_INIT static absl::base_internal::SpinLock deadlock_graph_mu( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); -// graph used to detect deadlocks. -static GraphCycles *deadlock_graph ABSL_GUARDED_BY(deadlock_graph_mu) - ABSL_PT_GUARDED_BY(deadlock_graph_mu); +// Graph used to detect deadlocks. +ABSL_CONST_INIT static GraphCycles *deadlock_graph + ABSL_GUARDED_BY(deadlock_graph_mu) ABSL_PT_GUARDED_BY(deadlock_graph_mu); //------------------------------------------------------------------ // An event mechanism for debugging mutex use. @@ -273,13 +287,12 @@ static const struct { {0, "SignalAll on "}, }; -static absl::base_internal::SpinLock synch_event_mu( - absl::base_internal::kLinkerInitialized); -// protects synch_event +ABSL_CONST_INIT static absl::base_internal::SpinLock synch_event_mu( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); // Hash table size; should be prime > 2. // Can't be too small, as it's used for deadlock detection information. -static const uint32_t kNSynchEvent = 1031; +static constexpr uint32_t kNSynchEvent = 1031; static struct SynchEvent { // this is a trivial hash table for the events // struct is freed when refcount reaches 0 @@ -299,7 +312,7 @@ static struct SynchEvent { // this is a trivial hash table for the events bool log; // logging turned on // Constant after initialization - char name[1]; // actually longer---NUL-terminated std::string + char name[1]; // actually longer---NUL-terminated string } * synch_event[kNSynchEvent] ABSL_GUARDED_BY(synch_event_mu); // Ensure that the object at "addr" has a SynchEvent struct associated with it, @@ -490,7 +503,7 @@ struct SynchWaitParams { std::atomic<intptr_t> *cv_word; int64_t contention_start_cycles; // Time (in cycles) when this thread started - // to contend for the mutex. + // to contend for the mutex. }; struct SynchLocksHeld { @@ -546,7 +559,7 @@ static SynchLocksHeld *Synch_GetAllLocks() { } // Post on "w"'s associated PerThreadSem. -inline void Mutex::IncrementSynchSem(Mutex *mu, PerThreadSynch *w) { +void Mutex::IncrementSynchSem(Mutex *mu, PerThreadSynch *w) { if (mu) { ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0); } @@ -704,7 +717,7 @@ static constexpr bool kDebugMode = false; static constexpr bool kDebugMode = true; #endif -#ifdef THREAD_SANITIZER +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE static unsigned TsanFlags(Mutex::MuHow how) { return how == kShared ? __tsan_mutex_read_lock : 0; } @@ -750,11 +763,13 @@ void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode) { synch_deadlock_detection.store(mode, std::memory_order_release); } -// Return true iff threads x and y are waiting on the same condition for the -// same type of lock. Requires that x and y be waiting on the same Mutex -// queue. -static bool MuSameCondition(PerThreadSynch *x, PerThreadSynch *y) { - return x->waitp->how == y->waitp->how && +// Return true iff threads x and y are part of the same equivalence +// class of waiters. An equivalence class is defined as the set of +// waiters with the same condition, type of lock, and thread priority. +// +// Requires that x and y be waiting on the same Mutex queue. +static bool MuEquivalentWaiter(PerThreadSynch *x, PerThreadSynch *y) { + return x->waitp->how == y->waitp->how && x->priority == y->priority && Condition::GuaranteedEqual(x->waitp->cond, y->waitp->cond); } @@ -773,18 +788,19 @@ static inline PerThreadSynch *GetPerThreadSynch(intptr_t v) { // - invalid (iff x is not in a Mutex wait queue), // - null, or // - a pointer to a distinct thread waiting later in the same Mutex queue -// such that all threads in [x, x->skip] have the same condition and -// lock type (MuSameCondition() is true for all pairs in [x, x->skip]). +// such that all threads in [x, x->skip] have the same condition, priority +// and lock type (MuEquivalentWaiter() is true for all pairs in [x, +// x->skip]). // In addition, if x->skip is valid, (x->may_skip || x->skip == null) // -// By the spec of MuSameCondition(), it is not necessary when removing the +// By the spec of MuEquivalentWaiter(), it is not necessary when removing the // first runnable thread y from the front a Mutex queue to adjust the skip // field of another thread x because if x->skip==y, x->skip must (have) become // invalid before y is removed. The function TryRemove can remove a specified // thread from an arbitrary position in the queue whether runnable or not, so // it fixes up skip fields that would otherwise be left dangling. // The statement -// if (x->may_skip && MuSameCondition(x, x->next)) { x->skip = x->next; } +// if (x->may_skip && MuEquivalentWaiter(x, x->next)) { x->skip = x->next; } // maintains the invariant provided x is not the last waiter in a Mutex queue // The statement // if (x->skip != null) { x->skip = x->skip->skip; } @@ -918,24 +934,17 @@ static PerThreadSynch *Enqueue(PerThreadSynch *head, if (s->priority > head->priority) { // s's priority is above head's // try to put s in priority-fifo order, or failing that at the front. if (!head->maybe_unlocking) { - // No unlocker can be scanning the queue, so we can insert between - // skip-chains, and within a skip-chain if it has the same condition as - // s. We insert in priority-fifo order, examining the end of every - // skip-chain, plus every element with the same condition as s. + // No unlocker can be scanning the queue, so we can insert into the + // middle of the queue. + // + // Within a skip chain, all waiters have the same priority, so we can + // skip forward through the chains until we find one with a lower + // priority than the waiter to be enqueued. PerThreadSynch *advance_to = head; // next value of enqueue_after - PerThreadSynch *cur; // successor of enqueue_after do { enqueue_after = advance_to; - cur = enqueue_after->next; // this advance ensures progress - advance_to = Skip(cur); // normally, advance to end of skip chain - // (side-effect: optimizes skip chain) - if (advance_to != cur && s->priority > advance_to->priority && - MuSameCondition(s, cur)) { - // but this skip chain is not a singleton, s has higher priority - // than its tail and has the same condition as the chain, - // so we can insert within the skip-chain - advance_to = cur; // advance by just one - } + // (side-effect: optimizes skip chain) + advance_to = Skip(enqueue_after->next); } while (s->priority <= advance_to->priority); // termination guaranteed because s->priority > head->priority // and head is the end of a skip chain @@ -954,21 +963,21 @@ static PerThreadSynch *Enqueue(PerThreadSynch *head, // enqueue_after can be: head, Skip(...), or cur. // The first two imply enqueue_after->skip == nullptr, and - // the last is used only if MuSameCondition(s, cur). + // the last is used only if MuEquivalentWaiter(s, cur). // We require this because clearing enqueue_after->skip // is impossible; enqueue_after's predecessors might also // incorrectly skip over s if we were to allow other // insertion points. - ABSL_RAW_CHECK( - enqueue_after->skip == nullptr || MuSameCondition(enqueue_after, s), - "Mutex Enqueue failure"); + ABSL_RAW_CHECK(enqueue_after->skip == nullptr || + MuEquivalentWaiter(enqueue_after, s), + "Mutex Enqueue failure"); if (enqueue_after != head && enqueue_after->may_skip && - MuSameCondition(enqueue_after, enqueue_after->next)) { + MuEquivalentWaiter(enqueue_after, enqueue_after->next)) { // enqueue_after can skip to its new successor, s enqueue_after->skip = enqueue_after->next; } - if (MuSameCondition(s, s->next)) { // s->may_skip is known to be true + if (MuEquivalentWaiter(s, s->next)) { // s->may_skip is known to be true s->skip = s->next; // s may skip to its successor } } else { // enqueue not done any other way, so @@ -978,7 +987,7 @@ static PerThreadSynch *Enqueue(PerThreadSynch *head, head->next = s; s->readers = head->readers; // reader count is from previous head s->maybe_unlocking = head->maybe_unlocking; // same for unlock hint - if (head->may_skip && MuSameCondition(head, s)) { + if (head->may_skip && MuEquivalentWaiter(head, s)) { // head now has successor; may skip head->skip = s; } @@ -998,7 +1007,7 @@ static PerThreadSynch *Dequeue(PerThreadSynch *head, PerThreadSynch *pw) { pw->next = w->next; // snip w out of list if (head == w) { // we removed the head head = (pw == w) ? nullptr : pw; // either emptied list, or pw is new head - } else if (pw != head && MuSameCondition(pw, pw->next)) { + } else if (pw != head && MuEquivalentWaiter(pw, pw->next)) { // pw can skip to its new successor if (pw->next->skip != nullptr) { // either skip to its successors skip target @@ -1055,6 +1064,7 @@ static PerThreadSynch *DequeueAllWakeable(PerThreadSynch *head, // Try to remove thread s from the list of waiters on this mutex. // Does nothing if s is not on the waiter list. void Mutex::TryRemove(PerThreadSynch *s) { + SchedulingGuard::ScopedDisable disable_rescheduling; intptr_t v = mu_.load(std::memory_order_relaxed); // acquire spinlock & lock if ((v & (kMuWait | kMuSpin | kMuWriter | kMuReader)) == kMuWait && @@ -1067,11 +1077,13 @@ void Mutex::TryRemove(PerThreadSynch *s) { PerThreadSynch *w; if ((w = pw->next) != s) { // search for thread, do { // processing at least one element - if (!MuSameCondition(s, w)) { // seeking different condition + // If the current element isn't equivalent to the waiter to be + // removed, we can skip the entire chain. + if (!MuEquivalentWaiter(s, w)) { pw = Skip(w); // so skip all that won't match // we don't have to worry about dangling skip fields // in the threads we skipped; none can point to s - // because their condition differs from s + // because they are in a different equivalence class. } else { // seeking same condition FixSkip(w, s); // fix up any skip pointer from w to s pw = w; @@ -1119,7 +1131,7 @@ ABSL_XRAY_LOG_ARGS(1) void Mutex::Block(PerThreadSynch *s) { this->TryRemove(s); int c = 0; while (s->next != nullptr) { - c = Delay(c, GENTLE); + c = synchronization_internal::MutexDelay(c, GENTLE); this->TryRemove(s); } if (kDebugMode) { @@ -1362,7 +1374,9 @@ static GraphId DeadlockCheck(Mutex *mu) { len += static_cast<int>(strlen(&b->buf[len])); } } - ABSL_RAW_LOG(ERROR, "Acquiring %p Mutexes held: %s", + ABSL_RAW_LOG(ERROR, + "Acquiring absl::Mutex %p while holding %s; a cycle in the " + "historical lock ordering graph has been observed", static_cast<void *>(mu), b->buf); ABSL_RAW_LOG(ERROR, "Cycle: "); int path_len = deadlock_graph->FindPath( @@ -1438,21 +1452,19 @@ void Mutex::AssertNotHeld() const { // Attempt to acquire *mu, and return whether successful. The implementation // may spin for a short while if the lock cannot be acquired immediately. static bool TryAcquireWithSpinning(std::atomic<intptr_t>* mu) { - int c = mutex_globals.spinloop_iterations; - int result = -1; // result of operation: 0=false, 1=true, -1=unknown - + int c = GetMutexGlobals().spinloop_iterations; do { // do/while somewhat faster on AMD intptr_t v = mu->load(std::memory_order_relaxed); - if ((v & (kMuReader|kMuEvent)) != 0) { // a reader or tracing -> give up - result = 0; + if ((v & (kMuReader|kMuEvent)) != 0) { + return false; // a reader or tracing -> give up } else if (((v & kMuWriter) == 0) && // no holder -> try to acquire mu->compare_exchange_strong(v, kMuWriter | v, std::memory_order_acquire, std::memory_order_relaxed)) { - result = 1; + return true; } - } while (result == -1 && --c > 0); - return result == 1; + } while (--c > 0); + return false; } ABSL_XRAY_LOG_ARGS(1) void Mutex::Lock() { @@ -1751,7 +1763,8 @@ static const intptr_t ignore_waiting_writers[] = { }; // Internal version of LockWhen(). See LockSlowWithDeadline() -void Mutex::LockSlow(MuHow how, const Condition *cond, int flags) { +ABSL_ATTRIBUTE_NOINLINE void Mutex::LockSlow(MuHow how, const Condition *cond, + int flags) { ABSL_RAW_CHECK( this->LockSlowWithDeadline(how, cond, KernelTimeout::Never(), flags), "condition untrue on return from LockSlow"); @@ -1766,7 +1779,7 @@ static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu, // All memory accesses are ignored inside of mutex operations + for unlock // operation tsan considers that we've already released the mutex. bool res = false; -#ifdef THREAD_SANITIZER +#ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE const int flags = read_lock ? __tsan_mutex_read_lock : 0; const int tryflags = flags | (trylock ? __tsan_mutex_try_lock : 0); #endif @@ -1816,9 +1829,9 @@ static inline bool EvalConditionIgnored(Mutex *mu, const Condition *cond) { // So we "divert" (which un-ignores both memory accesses and synchronization) // and then separately turn on ignores of memory accesses. ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0); - ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN(); + ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN(); bool res = cond->Eval(); - ANNOTATE_IGNORE_READS_AND_WRITES_END(); + ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END(); ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0); static_cast<void>(mu); // Prevent unused param warning in non-TSAN builds. return res; @@ -1899,6 +1912,7 @@ static void CheckForMutexCorruption(intptr_t v, const char* label) { } void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { + SchedulingGuard::ScopedDisable disable_rescheduling; int c = 0; intptr_t v = mu_.load(std::memory_order_relaxed); if ((v & kMuEvent) != 0) { @@ -2000,7 +2014,8 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { ABSL_RAW_CHECK( waitp->thread->waitp == nullptr || waitp->thread->suppress_fatal_errors, "detected illegal recursion into Mutex code"); - c = Delay(c, GENTLE); // delay, then try again + // delay, then try again + c = synchronization_internal::MutexDelay(c, GENTLE); } ABSL_RAW_CHECK( waitp->thread->waitp == nullptr || waitp->thread->suppress_fatal_errors, @@ -2017,7 +2032,8 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { // which holds the lock but is not runnable because its condition is false // or it is in the process of blocking on a condition variable; it must requeue // itself on the mutex/condvar to wait for its condition to become true. -void Mutex::UnlockSlow(SynchWaitParams *waitp) { +ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams *waitp) { + SchedulingGuard::ScopedDisable disable_rescheduling; intptr_t v = mu_.load(std::memory_order_relaxed); this->AssertReaderHeld(); CheckForMutexCorruption(v, "Unlock"); @@ -2134,7 +2150,7 @@ void Mutex::UnlockSlow(SynchWaitParams *waitp) { !old_h->may_skip) { // we used old_h as a terminator old_h->may_skip = true; // allow old_h to skip once more ABSL_RAW_CHECK(old_h->skip == nullptr, "illegal skip from head"); - if (h != old_h && MuSameCondition(old_h, old_h->next)) { + if (h != old_h && MuEquivalentWaiter(old_h, old_h->next)) { old_h->skip = old_h->next; // old_h not head & can skip to successor } } @@ -2294,7 +2310,8 @@ void Mutex::UnlockSlow(SynchWaitParams *waitp) { mu_.store(nv, std::memory_order_release); break; // out of for(;;)-loop } - c = Delay(c, AGGRESSIVE); // aggressive here; no one can proceed till we do + // aggressive here; no one can proceed till we do + c = synchronization_internal::MutexDelay(c, AGGRESSIVE); } // end of for(;;)-loop if (wake_list != kPerThreadSynchNull) { @@ -2306,7 +2323,8 @@ void Mutex::UnlockSlow(SynchWaitParams *waitp) { if (!cond_waiter) { // Sample lock contention events only if the (first) waiter was trying to // acquire the lock, not waiting on a condition variable or Condition. - int64_t wait_cycles = base_internal::CycleClock::Now() - enqueue_timestamp; + int64_t wait_cycles = + base_internal::CycleClock::Now() - enqueue_timestamp; mutex_tracer("slow release", this, wait_cycles); ABSL_TSAN_MUTEX_PRE_DIVERT(this, 0); submit_profile_data(enqueue_timestamp); @@ -2333,6 +2351,7 @@ void Mutex::Trans(MuHow how) { // It will later acquire the mutex with high probability. Otherwise, we // enqueue thread w on this mutex. void Mutex::Fer(PerThreadSynch *w) { + SchedulingGuard::ScopedDisable disable_rescheduling; int c = 0; ABSL_RAW_CHECK(w->waitp->cond == nullptr, "Mutex::Fer while waiting on Condition"); @@ -2382,7 +2401,7 @@ void Mutex::Fer(PerThreadSynch *w) { return; } } - c = Delay(c, GENTLE); + c = synchronization_internal::MutexDelay(c, GENTLE); } } @@ -2431,6 +2450,7 @@ CondVar::~CondVar() { // Remove thread s from the list of waiters on this condition variable. void CondVar::Remove(PerThreadSynch *s) { + SchedulingGuard::ScopedDisable disable_rescheduling; intptr_t v; int c = 0; for (v = cv_.load(std::memory_order_relaxed);; @@ -2459,7 +2479,8 @@ void CondVar::Remove(PerThreadSynch *s) { std::memory_order_release); return; } else { - c = Delay(c, GENTLE); // try again after a delay + // try again after a delay + c = synchronization_internal::MutexDelay(c, GENTLE); } } } @@ -2492,7 +2513,7 @@ static void CondVarEnqueue(SynchWaitParams *waitp) { !cv_word->compare_exchange_weak(v, v | kCvSpin, std::memory_order_acquire, std::memory_order_relaxed)) { - c = Delay(c, GENTLE); + c = synchronization_internal::MutexDelay(c, GENTLE); v = cv_word->load(std::memory_order_relaxed); } ABSL_RAW_CHECK(waitp->thread->waitp == nullptr, "waiting when shouldn't be"); @@ -2591,6 +2612,7 @@ void CondVar::Wakeup(PerThreadSynch *w) { } void CondVar::Signal() { + SchedulingGuard::ScopedDisable disable_rescheduling; ABSL_TSAN_MUTEX_PRE_SIGNAL(nullptr, 0); intptr_t v; int c = 0; @@ -2623,7 +2645,7 @@ void CondVar::Signal() { ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); return; } else { - c = Delay(c, GENTLE); + c = synchronization_internal::MutexDelay(c, GENTLE); } } ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); @@ -2660,7 +2682,8 @@ void CondVar::SignalAll () { ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); return; } else { - c = Delay(c, GENTLE); // try again after a delay + // try again after a delay + c = synchronization_internal::MutexDelay(c, GENTLE); } } ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); @@ -2673,7 +2696,7 @@ void ReleasableMutexLock::Release() { this->mu_ = nullptr; } -#ifdef THREAD_SANITIZER +#ifdef ABSL_HAVE_THREAD_SANITIZER extern "C" void __tsan_read1(void *addr); #else #define __tsan_read1(addr) // do nothing if TSan not enabled diff --git a/third_party/abseil-cpp/absl/synchronization/mutex.h b/third_party/abseil-cpp/absl/synchronization/mutex.h index 8c70c4ce61..38338f24df 100644 --- a/third_party/abseil-cpp/absl/synchronization/mutex.h +++ b/third_party/abseil-cpp/absl/synchronization/mutex.h @@ -31,22 +31,23 @@ // // MutexLock - An RAII wrapper to acquire and release a `Mutex` for exclusive/ // write access within the current scope. +// // ReaderMutexLock // - An RAII wrapper to acquire and release a `Mutex` for shared/read // access within the current scope. // // WriterMutexLock -// - Alias for `MutexLock` above, designed for use in distinguishing -// reader and writer locks within code. +// - Effectively an alias for `MutexLock` above, designed for use in +// distinguishing reader and writer locks within code. // // In addition to simple mutex locks, this file also defines ways to perform // locking under certain conditions. // -// Condition - (Preferred) Used to wait for a particular predicate that -// depends on state protected by the `Mutex` to become true. -// CondVar - A lower-level variant of `Condition` that relies on -// application code to explicitly signal the `CondVar` when -// a condition has been met. +// Condition - (Preferred) Used to wait for a particular predicate that +// depends on state protected by the `Mutex` to become true. +// CondVar - A lower-level variant of `Condition` that relies on +// application code to explicitly signal the `CondVar` when +// a condition has been met. // // See below for more information on using `Condition` or `CondVar`. // @@ -72,15 +73,6 @@ #include "absl/synchronization/internal/per_thread_sem.h" #include "absl/time/time.h" -// Decide if we should use the non-production implementation because -// the production implementation hasn't been fully ported yet. -#ifdef ABSL_INTERNAL_USE_NONPROD_MUTEX -#error ABSL_INTERNAL_USE_NONPROD_MUTEX cannot be directly set -#elif defined(ABSL_LOW_LEVEL_ALLOC_MISSING) -#define ABSL_INTERNAL_USE_NONPROD_MUTEX 1 -#include "absl/synchronization/internal/mutex_nonprod.inc" -#endif - namespace absl { ABSL_NAMESPACE_BEGIN @@ -155,7 +147,7 @@ class ABSL_LOCKABLE Mutex { // // Example usage: // namespace foo { - // ABSL_CONST_INIT Mutex mu(absl::kConstInit); + // ABSL_CONST_INIT absl::Mutex mu(absl::kConstInit); // } explicit constexpr Mutex(absl::ConstInitType); @@ -170,7 +162,7 @@ class ABSL_LOCKABLE Mutex { // Mutex::Unlock() // // Releases this `Mutex` and returns it from the exclusive/write state to the - // free state. Caller must hold the `Mutex` exclusively. + // free state. Calling thread must hold the `Mutex` exclusively. void Unlock() ABSL_UNLOCK_FUNCTION(); // Mutex::TryLock() @@ -331,17 +323,16 @@ class ABSL_LOCKABLE Mutex { // Mutex::AwaitWithTimeout() // Mutex::AwaitWithDeadline() // - // If `cond` is initially true, do nothing, or act as though `cond` is - // initially false. - // - // If `cond` is initially false, unlock this `Mutex` and block until - // simultaneously: + // Unlocks this `Mutex` and blocks until simultaneously: // - either `cond` is true or the {timeout has expired, deadline has passed} // and // - this `Mutex` can be reacquired, // then reacquire this `Mutex` in the same mode in which it was previously // held, returning `true` iff `cond` is `true` on return. // + // If the condition is initially `true`, the implementation *may* skip the + // release/re-acquire step and return immediately. + // // Deadlines in the past are equivalent to an immediate deadline. // Negative timeouts are equivalent to a zero timeout. // @@ -462,24 +453,13 @@ class ABSL_LOCKABLE Mutex { static void InternalAttemptToUseMutexInFatalSignalHandler(); private: -#ifdef ABSL_INTERNAL_USE_NONPROD_MUTEX - friend class CondVar; - - synchronization_internal::MutexImpl *impl() { return impl_.get(); } - - synchronization_internal::SynchronizationStorage< - synchronization_internal::MutexImpl> - impl_; -#else std::atomic<intptr_t> mu_; // The Mutex state. // Post()/Wait() versus associated PerThreadSem; in class for required // friendship with PerThreadSem. - static inline void IncrementSynchSem(Mutex *mu, - base_internal::PerThreadSynch *w); - static inline bool DecrementSynchSem( - Mutex *mu, base_internal::PerThreadSynch *w, - synchronization_internal::KernelTimeout t); + static void IncrementSynchSem(Mutex *mu, base_internal::PerThreadSynch *w); + static bool DecrementSynchSem(Mutex *mu, base_internal::PerThreadSynch *w, + synchronization_internal::KernelTimeout t); // slow path acquire void LockSlowLoop(SynchWaitParams *waitp, int flags); @@ -505,7 +485,6 @@ class ABSL_LOCKABLE Mutex { void Trans(MuHow how); // used for CondVar->Mutex transfer void Fer( base_internal::PerThreadSynch *w); // used for CondVar->Mutex transfer -#endif // Catch the error of writing Mutex when intending MutexLock. Mutex(const volatile Mutex * /*ignored*/) {} // NOLINT(runtime/explicit) @@ -526,22 +505,36 @@ class ABSL_LOCKABLE Mutex { // Example: // // Class Foo { -// +// public: // Foo::Bar* Baz() { -// MutexLock l(&lock_); +// MutexLock lock(&mu_); // ... // return bar; // } // // private: -// Mutex lock_; +// Mutex mu_; // }; class ABSL_SCOPED_LOCKABLE MutexLock { public: + // Constructors + + // Calls `mu->Lock()` and returns when that call returns. That is, `*mu` is + // guaranteed to be locked when this object is constructed. Requires that + // `mu` be dereferenceable. explicit MutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { this->mu_->Lock(); } + // Like above, but calls `mu->LockWhen(cond)` instead. That is, in addition to + // the above, the condition given by `cond` is also guaranteed to hold when + // this object is constructed. + explicit MutexLock(Mutex *mu, const Condition &cond) + ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + this->mu_->LockWhen(cond); + } + MutexLock(const MutexLock &) = delete; // NOLINT(runtime/mutex) MutexLock(MutexLock&&) = delete; // NOLINT(runtime/mutex) MutexLock& operator=(const MutexLock&) = delete; @@ -563,6 +556,12 @@ class ABSL_SCOPED_LOCKABLE ReaderMutexLock { mu->ReaderLock(); } + explicit ReaderMutexLock(Mutex *mu, const Condition &cond) + ABSL_SHARED_LOCK_FUNCTION(mu) + : mu_(mu) { + mu->ReaderLockWhen(cond); + } + ReaderMutexLock(const ReaderMutexLock&) = delete; ReaderMutexLock(ReaderMutexLock&&) = delete; ReaderMutexLock& operator=(const ReaderMutexLock&) = delete; @@ -585,6 +584,12 @@ class ABSL_SCOPED_LOCKABLE WriterMutexLock { mu->WriterLock(); } + explicit WriterMutexLock(Mutex *mu, const Condition &cond) + ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + mu->WriterLockWhen(cond); + } + WriterMutexLock(const WriterMutexLock&) = delete; WriterMutexLock(WriterMutexLock&&) = delete; WriterMutexLock& operator=(const WriterMutexLock&) = delete; @@ -623,16 +628,26 @@ class ABSL_SCOPED_LOCKABLE WriterMutexLock { // `noexcept`; until then this requirement cannot be enforced in the // type system.) // -// Note: to use a `Condition`, you need only construct it and pass it within the -// appropriate `Mutex' member function, such as `Mutex::Await()`. +// Note: to use a `Condition`, you need only construct it and pass it to a +// suitable `Mutex' member function, such as `Mutex::Await()`, or to the +// constructor of one of the scope guard classes. // -// Example: +// Example using LockWhen/Unlock: // // // assume count_ is not internal reference count // int count_ ABSL_GUARDED_BY(mu_); +// Condition count_is_zero(+[](int *count) { return *count == 0; }, &count_); +// +// mu_.LockWhen(count_is_zero); +// // ... +// mu_.Unlock(); // -// mu_.LockWhen(Condition(+[](int* count) { return *count == 0; }, -// &count_)); +// Example using a scope guard: +// +// { +// MutexLock lock(&mu_, count_is_zero); +// // ... +// } // // When multiple threads are waiting on exactly the same condition, make sure // that they are constructed with the same parameters (same pointer to function @@ -686,6 +701,11 @@ class Condition { // return processed_ >= current; // }; // mu_.Await(Condition(&reached)); + // + // NOTE: never use "mu_.AssertHeld()" instead of "mu_.AssertReaderHeld()" in + // the lambda as it may be called when the mutex is being unlocked from a + // scope holding only a reader lock, which will make the assertion not + // fulfilled and crash the binary. // See class comment for performance advice. In particular, if there // might be more than one waiter for the same condition, make sure @@ -758,9 +778,9 @@ class Condition { // // Usage to wake T is: // mu.Lock(); -// // process data, possibly establishing C -// if (C) { cv->Signal(); } -// mu.Unlock(); +// // process data, possibly establishing C +// if (C) { cv->Signal(); } +// mu.Unlock(); // // If C may be useful to more than one waiter, use `SignalAll()` instead of // `Signal()`. @@ -770,6 +790,8 @@ class Condition { // class CondVar { public: + // A `CondVar` allocated on the heap or on the stack can use the this + // constructor. CondVar(); ~CondVar(); @@ -832,17 +854,10 @@ class CondVar { void EnableDebugLog(const char *name); private: -#ifdef ABSL_INTERNAL_USE_NONPROD_MUTEX - synchronization_internal::CondVarImpl *impl() { return impl_.get(); } - synchronization_internal::SynchronizationStorage< - synchronization_internal::CondVarImpl> - impl_; -#else bool WaitCommon(Mutex *mutex, synchronization_internal::KernelTimeout t); void Remove(base_internal::PerThreadSynch *s); void Wakeup(base_internal::PerThreadSynch *w); std::atomic<intptr_t> cv_; // Condition variable state. -#endif CondVar(const CondVar&) = delete; CondVar& operator=(const CondVar&) = delete; }; @@ -864,6 +879,15 @@ class ABSL_SCOPED_LOCKABLE MutexLockMaybe { this->mu_->Lock(); } } + + explicit MutexLockMaybe(Mutex *mu, const Condition &cond) + ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + if (this->mu_ != nullptr) { + this->mu_->LockWhen(cond); + } + } + ~MutexLockMaybe() ABSL_UNLOCK_FUNCTION() { if (this->mu_ != nullptr) { this->mu_->Unlock(); } } @@ -886,6 +910,13 @@ class ABSL_SCOPED_LOCKABLE ReleasableMutexLock { : mu_(mu) { this->mu_->Lock(); } + + explicit ReleasableMutexLock(Mutex *mu, const Condition &cond) + ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) + : mu_(mu) { + this->mu_->LockWhen(cond); + } + ~ReleasableMutexLock() ABSL_UNLOCK_FUNCTION() { if (this->mu_ != nullptr) { this->mu_->Unlock(); } } @@ -900,10 +931,6 @@ class ABSL_SCOPED_LOCKABLE ReleasableMutexLock { ReleasableMutexLock& operator=(ReleasableMutexLock&&) = delete; }; -#ifdef ABSL_INTERNAL_USE_NONPROD_MUTEX -inline constexpr Mutex::Mutex(absl::ConstInitType) : impl_(absl::kConstInit) {} - -#else inline Mutex::Mutex() : mu_(0) { ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); } @@ -911,7 +938,6 @@ inline Mutex::Mutex() : mu_(0) { inline constexpr Mutex::Mutex(absl::ConstInitType) : mu_(0) {} inline CondVar::CondVar() : cv_(0) {} -#endif // static template <typename T> @@ -979,7 +1005,7 @@ void RegisterMutexProfiler(void (*fn)(int64_t wait_timestamp)); // // This has the same memory ordering concerns as RegisterMutexProfiler() above. void RegisterMutexTracer(void (*fn)(const char *msg, const void *obj, - int64_t wait_cycles)); + int64_t wait_cycles)); // TODO(gfalcon): Combine RegisterMutexProfiler() and RegisterMutexTracer() // into a single interface, since they are only ever called in pairs. @@ -1050,7 +1076,7 @@ ABSL_NAMESPACE_END // By changing our extension points to be extern "C", we dodge this // check. extern "C" { -void AbslInternalMutexYield(); +void ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)(); } // extern "C" #endif // ABSL_SYNCHRONIZATION_MUTEX_H_ diff --git a/third_party/abseil-cpp/absl/synchronization/mutex_benchmark.cc b/third_party/abseil-cpp/absl/synchronization/mutex_benchmark.cc index ab1880012a..b5d2fbc454 100644 --- a/third_party/abseil-cpp/absl/synchronization/mutex_benchmark.cc +++ b/third_party/abseil-cpp/absl/synchronization/mutex_benchmark.cc @@ -16,6 +16,7 @@ #include <mutex> // NOLINT(build/c++11) #include <vector> +#include "absl/base/config.h" #include "absl/base/internal/cycleclock.h" #include "absl/base/internal/spinlock.h" #include "absl/synchronization/blocking_counter.h" @@ -60,8 +61,124 @@ class RaiiLocker<std::mutex> { std::mutex* mu_; }; +// RAII object to change the Mutex priority of the running thread. +class ScopedThreadMutexPriority { + public: + explicit ScopedThreadMutexPriority(int priority) { + absl::base_internal::ThreadIdentity* identity = + absl::synchronization_internal::GetOrCreateCurrentThreadIdentity(); + identity->per_thread_synch.priority = priority; + // Bump next_priority_read_cycles to the infinite future so that the + // implementation doesn't re-read the thread's actual scheduler priority + // and replace our temporary scoped priority. + identity->per_thread_synch.next_priority_read_cycles = + std::numeric_limits<int64_t>::max(); + } + ~ScopedThreadMutexPriority() { + // Reset the "next priority read time" back to the infinite past so that + // the next time the Mutex implementation wants to know this thread's + // priority, it re-reads it from the OS instead of using our overridden + // priority. + absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() + ->per_thread_synch.next_priority_read_cycles = + std::numeric_limits<int64_t>::min(); + } +}; + +void BM_MutexEnqueue(benchmark::State& state) { + // In the "multiple priorities" variant of the benchmark, one of the + // threads runs with Mutex priority 0 while the rest run at elevated priority. + // This benchmarks the performance impact of the presence of a low priority + // waiter when a higher priority waiter adds itself of the queue + // (b/175224064). + // + // NOTE: The actual scheduler priority is not modified in this benchmark: + // all of the threads get CPU slices with the same priority. Only the + // Mutex queueing behavior is modified. + const bool multiple_priorities = state.range(0); + ScopedThreadMutexPriority priority_setter( + (multiple_priorities && state.thread_index() != 0) ? 1 : 0); + + struct Shared { + absl::Mutex mu; + std::atomic<int> looping_threads{0}; + std::atomic<int> blocked_threads{0}; + std::atomic<bool> thread_has_mutex{false}; + }; + static Shared* shared = new Shared; + + // Set up 'blocked_threads' to count how many threads are currently blocked + // in Abseil synchronization code. + // + // NOTE: Blocking done within the Google Benchmark library itself (e.g. + // the barrier which synchronizes threads entering and exiting the benchmark + // loop) does _not_ get registered in this counter. This is because Google + // Benchmark uses its own synchronization primitives based on std::mutex, not + // Abseil synchronization primitives. If at some point the benchmark library + // merges into Abseil, this code may break. + absl::synchronization_internal::PerThreadSem::SetThreadBlockedCounter( + &shared->blocked_threads); + + // The benchmark framework may run several iterations in the same process, + // reusing the same static-initialized 'shared' object. Given the semantics + // of the members, here, we expect everything to be reset to zero by the + // end of any iteration. Assert that's the case, just to be sure. + ABSL_RAW_CHECK( + shared->looping_threads.load(std::memory_order_relaxed) == 0 && + shared->blocked_threads.load(std::memory_order_relaxed) == 0 && + !shared->thread_has_mutex.load(std::memory_order_relaxed), + "Shared state isn't zeroed at start of benchmark iteration"); + + static constexpr int kBatchSize = 1000; + while (state.KeepRunningBatch(kBatchSize)) { + shared->looping_threads.fetch_add(1); + for (int i = 0; i < kBatchSize; i++) { + { + absl::MutexLock l(&shared->mu); + shared->thread_has_mutex.store(true, std::memory_order_relaxed); + // Spin until all other threads are either out of the benchmark loop + // or blocked on the mutex. This ensures that the mutex queue is kept + // at its maximal length to benchmark the performance of queueing on + // a highly contended mutex. + while (shared->looping_threads.load(std::memory_order_relaxed) - + shared->blocked_threads.load(std::memory_order_relaxed) != + 1) { + } + shared->thread_has_mutex.store(false); + } + // Spin until some other thread has acquired the mutex before we block + // again. This ensures that we always go through the slow (queueing) + // acquisition path rather than reacquiring the mutex we just released. + while (!shared->thread_has_mutex.load(std::memory_order_relaxed) && + shared->looping_threads.load(std::memory_order_relaxed) > 1) { + } + } + // The benchmark framework uses a barrier to ensure that all of the threads + // complete their benchmark loop together before any of the threads exit + // the loop. So, we need to remove ourselves from the "looping threads" + // counter here before potentially blocking on that barrier. Otherwise, + // another thread spinning above might wait forever for this thread to + // block on the mutex while we in fact are waiting to exit. + shared->looping_threads.fetch_add(-1); + } + absl::synchronization_internal::PerThreadSem::SetThreadBlockedCounter( + nullptr); +} + +BENCHMARK(BM_MutexEnqueue) + ->Threads(4) + ->Threads(64) + ->Threads(128) + ->Threads(512) + ->ArgName("multiple_priorities") + ->Arg(false) + ->Arg(true); + template <typename MutexType> void BM_Contended(benchmark::State& state) { + int priority = state.thread_index() % state.range(1); + ScopedThreadMutexPriority priority_setter(priority); + struct Shared { MutexType mu; int data = 0; @@ -79,86 +196,56 @@ void BM_Contended(benchmark::State& state) { // To achieve this amount of local work is multiplied by number of threads // to keep ratio between local work and critical section approximately // equal regardless of number of threads. - DelayNs(100 * state.threads, &local); + DelayNs(100 * state.threads(), &local); RaiiLocker<MutexType> locker(&shared->mu); DelayNs(state.range(0), &shared->data); } } +void SetupBenchmarkArgs(benchmark::internal::Benchmark* bm, + bool do_test_priorities) { + const int max_num_priorities = do_test_priorities ? 2 : 1; + bm->UseRealTime() + // ThreadPerCpu poorly handles non-power-of-two CPU counts. + ->Threads(1) + ->Threads(2) + ->Threads(4) + ->Threads(6) + ->Threads(8) + ->Threads(12) + ->Threads(16) + ->Threads(24) + ->Threads(32) + ->Threads(48) + ->Threads(64) + ->Threads(96) + ->Threads(128) + ->Threads(192) + ->Threads(256) + ->ArgNames({"cs_ns", "num_prios"}); + // Some empirically chosen amounts of work in critical section. + // 1 is low contention, 2000 is high contention and few values in between. + for (int critical_section_ns : {1, 20, 50, 200, 2000}) { + for (int num_priorities = 1; num_priorities <= max_num_priorities; + num_priorities++) { + bm->ArgPair(critical_section_ns, num_priorities); + } + } +} BENCHMARK_TEMPLATE(BM_Contended, absl::Mutex) - ->UseRealTime() - // ThreadPerCpu poorly handles non-power-of-two CPU counts. - ->Threads(1) - ->Threads(2) - ->Threads(4) - ->Threads(6) - ->Threads(8) - ->Threads(12) - ->Threads(16) - ->Threads(24) - ->Threads(32) - ->Threads(48) - ->Threads(64) - ->Threads(96) - ->Threads(128) - ->Threads(192) - ->Threads(256) - // Some empirically chosen amounts of work in critical section. - // 1 is low contention, 200 is high contention and few values in between. - ->Arg(1) - ->Arg(20) - ->Arg(50) - ->Arg(200); + ->Apply([](benchmark::internal::Benchmark* bm) { + SetupBenchmarkArgs(bm, /*do_test_priorities=*/true); + }); BENCHMARK_TEMPLATE(BM_Contended, absl::base_internal::SpinLock) - ->UseRealTime() - // ThreadPerCpu poorly handles non-power-of-two CPU counts. - ->Threads(1) - ->Threads(2) - ->Threads(4) - ->Threads(6) - ->Threads(8) - ->Threads(12) - ->Threads(16) - ->Threads(24) - ->Threads(32) - ->Threads(48) - ->Threads(64) - ->Threads(96) - ->Threads(128) - ->Threads(192) - ->Threads(256) - // Some empirically chosen amounts of work in critical section. - // 1 is low contention, 200 is high contention and few values in between. - ->Arg(1) - ->Arg(20) - ->Arg(50) - ->Arg(200); + ->Apply([](benchmark::internal::Benchmark* bm) { + SetupBenchmarkArgs(bm, /*do_test_priorities=*/false); + }); BENCHMARK_TEMPLATE(BM_Contended, std::mutex) - ->UseRealTime() - // ThreadPerCpu poorly handles non-power-of-two CPU counts. - ->Threads(1) - ->Threads(2) - ->Threads(4) - ->Threads(6) - ->Threads(8) - ->Threads(12) - ->Threads(16) - ->Threads(24) - ->Threads(32) - ->Threads(48) - ->Threads(64) - ->Threads(96) - ->Threads(128) - ->Threads(192) - ->Threads(256) - // Some empirically chosen amounts of work in critical section. - // 1 is low contention, 200 is high contention and few values in between. - ->Arg(1) - ->Arg(20) - ->Arg(50) - ->Arg(200); + ->Apply([](benchmark::internal::Benchmark* bm) { + SetupBenchmarkArgs(bm, /*do_test_priorities=*/false); + }); // Measure the overhead of conditions on mutex release (when they must be // evaluated). Mutex has (some) support for equivalence classes allowing @@ -213,7 +300,7 @@ void BM_ConditionWaiters(benchmark::State& state) { } // Some configurations have higher thread limits than others. -#if defined(__linux__) && !defined(THREAD_SANITIZER) +#if defined(__linux__) && !defined(ABSL_HAVE_THREAD_SANITIZER) constexpr int kMaxConditionWaiters = 8192; #else constexpr int kMaxConditionWaiters = 1024; diff --git a/third_party/abseil-cpp/absl/synchronization/mutex_test.cc b/third_party/abseil-cpp/absl/synchronization/mutex_test.cc index afb363af61..4f40317684 100644 --- a/third_party/abseil-cpp/absl/synchronization/mutex_test.cc +++ b/third_party/abseil-cpp/absl/synchronization/mutex_test.cc @@ -26,10 +26,12 @@ #include <random> #include <string> #include <thread> // NOLINT(build/c++11) +#include <type_traits> #include <vector> #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/sysinfo.h" #include "absl/memory/memory.h" @@ -706,6 +708,40 @@ TEST(Mutex, LockWhen) { t.join(); } +TEST(Mutex, LockWhenGuard) { + absl::Mutex mu; + int n = 30; + bool done = false; + + // We don't inline the lambda because the conversion is ambiguous in MSVC. + bool (*cond_eq_10)(int *) = [](int *p) { return *p == 10; }; + bool (*cond_lt_10)(int *) = [](int *p) { return *p < 10; }; + + std::thread t1([&mu, &n, &done, cond_eq_10]() { + absl::ReaderMutexLock lock(&mu, absl::Condition(cond_eq_10, &n)); + done = true; + }); + + std::thread t2[10]; + for (std::thread &t : t2) { + t = std::thread([&mu, &n, cond_lt_10]() { + absl::WriterMutexLock lock(&mu, absl::Condition(cond_lt_10, &n)); + ++n; + }); + } + + { + absl::MutexLock lock(&mu); + n = 0; + } + + for (std::thread &t : t2) t.join(); + t1.join(); + + EXPECT_TRUE(done); + EXPECT_EQ(n, 10); +} + // -------------------------------------------------------- // The following test requires Mutex::ReaderLock to be a real shared // lock, which is not the case in all builds. @@ -815,9 +851,9 @@ TEST(Mutex, MutexReaderDecrementBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { // Test that we correctly handle the situation when a lock is // held and then destroyed (w/o unlocking). -#ifdef THREAD_SANITIZER +#ifdef ABSL_HAVE_THREAD_SANITIZER // TSAN reports errors when locked Mutexes are destroyed. -TEST(Mutex, DISABLED_LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS { +TEST(Mutex, DISABLED_LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { #else TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { #endif @@ -835,33 +871,6 @@ TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { } } -// -------------------------------------------------------- -// Test for bug with pattern of readers using a condvar. The bug was that if a -// reader went to sleep on a condition variable while one or more other readers -// held the lock, but there were no waiters, the reader count (held in the -// mutex word) would be lost. (This is because Enqueue() had at one time -// always placed the thread on the Mutex queue. Later (CL 4075610), to -// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was -// changed so that it could also place a thread on a condition-variable. This -// introduced the case where Enqueue() returned with an empty queue, and this -// case was handled incorrectly in one place.) - -static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv, - int *running) { - std::random_device dev; - std::mt19937 gen(dev()); - std::uniform_int_distribution<int> random_millis(0, 15); - mu->ReaderLock(); - while (*running == 3) { - absl::SleepFor(absl::Milliseconds(random_millis(gen))); - cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen))); - } - mu->ReaderUnlock(); - mu->Lock(); - (*running)--; - mu->Unlock(); -} - struct True { template <class... Args> bool operator()(Args...) const { @@ -910,6 +919,33 @@ TEST(Mutex, FunctorCondition) { } } +// -------------------------------------------------------- +// Test for bug with pattern of readers using a condvar. The bug was that if a +// reader went to sleep on a condition variable while one or more other readers +// held the lock, but there were no waiters, the reader count (held in the +// mutex word) would be lost. (This is because Enqueue() had at one time +// always placed the thread on the Mutex queue. Later (CL 4075610), to +// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was +// changed so that it could also place a thread on a condition-variable. This +// introduced the case where Enqueue() returned with an empty queue, and this +// case was handled incorrectly in one place.) + +static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv, + int *running) { + std::random_device dev; + std::mt19937 gen(dev()); + std::uniform_int_distribution<int> random_millis(0, 15); + mu->ReaderLock(); + while (*running == 3) { + absl::SleepFor(absl::Milliseconds(random_millis(gen))); + cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen))); + } + mu->ReaderUnlock(); + mu->Lock(); + (*running)--; + mu->Unlock(); +} + static bool IntIsZero(int *x) { return *x == 0; } // Test for reader waiting condition variable when there are other readers @@ -1001,9 +1037,6 @@ TEST(Mutex, AcquireFromCondition) { x.mu0.Unlock(); } -// The deadlock detector is not part of non-prod builds, so do not test it. -#if !defined(ABSL_INTERNAL_USE_NONPROD_MUTEX) - TEST(Mutex, DeadlockDetector) { absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort); @@ -1067,7 +1100,7 @@ class ScopedDisableBazelTestWarnings { const char ScopedDisableBazelTestWarnings::kVarName[] = "TEST_WARNINGS_OUTPUT_FILE"; -#ifdef THREAD_SANITIZER +#ifdef ABSL_HAVE_THREAD_SANITIZER // This test intentionally creates deadlocks to test the deadlock detector. TEST(Mutex, DISABLED_DeadlockDetectorBazelWarning) { #else @@ -1101,7 +1134,7 @@ TEST(Mutex, DeadlockDetectorBazelWarning) { // annotation-based static thread-safety analysis is not currently // predicate-aware and cannot tell if the two for-loops that acquire and // release the locks have the same predicates. -TEST(Mutex, DeadlockDetectorStessTest) ABSL_NO_THREAD_SAFETY_ANALYSIS { +TEST(Mutex, DeadlockDetectorStressTest) ABSL_NO_THREAD_SAFETY_ANALYSIS { // Stress test: Here we create a large number of locks and use all of them. // If a deadlock detector keeps a full graph of lock acquisition order, // it will likely be too slow for this test to pass. @@ -1119,9 +1152,9 @@ TEST(Mutex, DeadlockDetectorStessTest) ABSL_NO_THREAD_SAFETY_ANALYSIS { } } -#ifdef THREAD_SANITIZER +#ifdef ABSL_HAVE_THREAD_SANITIZER // TSAN reports errors when locked Mutexes are destroyed. -TEST(Mutex, DISABLED_DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS { +TEST(Mutex, DISABLED_DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { #else TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { #endif @@ -1157,7 +1190,6 @@ TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { c.Lock(); c.Unlock(); } -#endif // !defined(ABSL_INTERNAL_USE_NONPROD_MUTEX) // -------------------------------------------------------- // Test for timeouts/deadlines on condition waits that are specified using diff --git a/third_party/abseil-cpp/absl/time/BUILD.bazel b/third_party/abseil-cpp/absl/time/BUILD.bazel index 9ab2adb886..e8c49660b2 100644 --- a/third_party/abseil-cpp/absl/time/BUILD.bazel +++ b/third_party/abseil-cpp/absl/time/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "time", @@ -70,6 +69,7 @@ cc_library( ], deps = [ ":time", + "//absl/base:config", "//absl/base:raw_logging_internal", "//absl/time/internal/cctz:time_zone", "@com_google_googletest//:gtest", @@ -118,6 +118,7 @@ cc_test( ":time", "//absl/base", "//absl/base:core_headers", + "//absl/flags:flag", "//absl/hash", "@com_github_google_benchmark//:benchmark_main", ], diff --git a/third_party/abseil-cpp/absl/time/CMakeLists.txt b/third_party/abseil-cpp/absl/time/CMakeLists.txt index 853563e875..f6ff8bd127 100644 --- a/third_party/abseil-cpp/absl/time/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/time/CMakeLists.txt @@ -99,9 +99,10 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::time + absl::config absl::raw_logging_internal absl::time_zone - gmock + GTest::gmock TESTONLY ) @@ -123,5 +124,5 @@ absl_cc_test( absl::config absl::core_headers absl::time_zone - gmock_main + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/time/civil_time.cc b/third_party/abseil-cpp/absl/time/civil_time.cc index ada82cbc8d..6a231edb2d 100644 --- a/third_party/abseil-cpp/absl/time/civil_time.cc +++ b/third_party/abseil-cpp/absl/time/civil_time.cc @@ -38,16 +38,14 @@ std::string FormatYearAnd(string_view fmt, CivilSecond cs) { const CivilSecond ncs(NormalizeYear(cs.year()), cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second()); const TimeZone utc = UTCTimeZone(); - // TODO(absl-team): Avoid conversion of fmt std::string. - return StrCat(cs.year(), - FormatTime(std::string(fmt), FromCivil(ncs, utc), utc)); + return StrCat(cs.year(), FormatTime(fmt, FromCivil(ncs, utc), utc)); } template <typename CivilT> bool ParseYearAnd(string_view fmt, string_view s, CivilT* c) { // Civil times support a larger year range than absl::Time, so we need to // parse the year separately, normalize it, then use absl::ParseTime on the - // normalized std::string. + // normalized string. const std::string ss = std::string(s); // TODO(absl-team): Avoid conversion. const char* const np = ss.c_str(); char* endp; @@ -82,7 +80,7 @@ bool ParseAs(string_view s, CivilT2* c) { template <typename CivilT> bool ParseLenient(string_view s, CivilT* c) { - // A fastpath for when the given std::string data parses exactly into the given + // A fastpath for when the given string data parses exactly into the given // type T (e.g., s="YYYY-MM-DD" and CivilT=CivilDay). if (ParseCivilTime(s, c)) return true; // Try parsing as each of the 6 types, trying the most common types first @@ -98,26 +96,26 @@ bool ParseLenient(string_view s, CivilT* c) { } // namespace std::string FormatCivilTime(CivilSecond c) { - return FormatYearAnd("-%m-%dT%H:%M:%S", c); + return FormatYearAnd("-%m-%d%ET%H:%M:%S", c); } std::string FormatCivilTime(CivilMinute c) { - return FormatYearAnd("-%m-%dT%H:%M", c); + return FormatYearAnd("-%m-%d%ET%H:%M", c); } std::string FormatCivilTime(CivilHour c) { - return FormatYearAnd("-%m-%dT%H", c); + return FormatYearAnd("-%m-%d%ET%H", c); } std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); } std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); } std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); } bool ParseCivilTime(string_view s, CivilSecond* c) { - return ParseYearAnd("-%m-%dT%H:%M:%S", s, c); + return ParseYearAnd("-%m-%d%ET%H:%M:%S", s, c); } bool ParseCivilTime(string_view s, CivilMinute* c) { - return ParseYearAnd("-%m-%dT%H:%M", s, c); + return ParseYearAnd("-%m-%d%ET%H:%M", s, c); } bool ParseCivilTime(string_view s, CivilHour* c) { - return ParseYearAnd("-%m-%dT%H", s, c); + return ParseYearAnd("-%m-%d%ET%H", s, c); } bool ParseCivilTime(string_view s, CivilDay* c) { return ParseYearAnd("-%m-%d", s, c); diff --git a/third_party/abseil-cpp/absl/time/clock.cc b/third_party/abseil-cpp/absl/time/clock.cc index 3b895c38e9..7b204c4ee0 100644 --- a/third_party/abseil-cpp/absl/time/clock.cc +++ b/third_party/abseil-cpp/absl/time/clock.cc @@ -15,6 +15,7 @@ #include "absl/time/clock.h" #include "absl/base/attributes.h" +#include "absl/base/optimization.h" #ifdef _WIN32 #include <windows.h> @@ -74,9 +75,7 @@ ABSL_NAMESPACE_END #if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS namespace absl { ABSL_NAMESPACE_BEGIN -int64_t GetCurrentTimeNanos() { - return GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); -} +int64_t GetCurrentTimeNanos() { return GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); } ABSL_NAMESPACE_END } // namespace absl #else // Use the cyclecounter-based implementation below. @@ -87,13 +86,6 @@ ABSL_NAMESPACE_END ::absl::time_internal::UnscaledCycleClockWrapperForGetCurrentTime::Now() #endif -// The following counters are used only by the test code. -static int64_t stats_initializations; -static int64_t stats_reinitializations; -static int64_t stats_calibrations; -static int64_t stats_slow_paths; -static int64_t stats_fast_slow_paths; - namespace absl { ABSL_NAMESPACE_BEGIN namespace time_internal { @@ -107,72 +99,6 @@ class UnscaledCycleClockWrapperForGetCurrentTime { // uint64_t is used in this module to provide an extra bit in multiplications -// Return the time in ns as told by the kernel interface. Place in *cycleclock -// the value of the cycleclock at about the time of the syscall. -// This call represents the time base that this module synchronizes to. -// Ensures that *cycleclock does not step back by up to (1 << 16) from -// last_cycleclock, to discard small backward counter steps. (Larger steps are -// assumed to be complete resyncs, which shouldn't happen. If they do, a full -// reinitialization of the outer algorithm should occur.) -static int64_t GetCurrentTimeNanosFromKernel(uint64_t last_cycleclock, - uint64_t *cycleclock) { - // We try to read clock values at about the same time as the kernel clock. - // This value gets adjusted up or down as estimate of how long that should - // take, so we can reject attempts that take unusually long. - static std::atomic<uint64_t> approx_syscall_time_in_cycles{10 * 1000}; - - uint64_t local_approx_syscall_time_in_cycles = // local copy - approx_syscall_time_in_cycles.load(std::memory_order_relaxed); - - int64_t current_time_nanos_from_system; - uint64_t before_cycles; - uint64_t after_cycles; - uint64_t elapsed_cycles; - int loops = 0; - do { - before_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW(); - current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); - after_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW(); - // elapsed_cycles is unsigned, so is large on overflow - elapsed_cycles = after_cycles - before_cycles; - if (elapsed_cycles >= local_approx_syscall_time_in_cycles && - ++loops == 20) { // clock changed frequencies? Back off. - loops = 0; - if (local_approx_syscall_time_in_cycles < 1000 * 1000) { - local_approx_syscall_time_in_cycles = - (local_approx_syscall_time_in_cycles + 1) << 1; - } - approx_syscall_time_in_cycles.store( - local_approx_syscall_time_in_cycles, - std::memory_order_relaxed); - } - } while (elapsed_cycles >= local_approx_syscall_time_in_cycles || - last_cycleclock - after_cycles < (static_cast<uint64_t>(1) << 16)); - - // Number of times in a row we've seen a kernel time call take substantially - // less than approx_syscall_time_in_cycles. - static std::atomic<uint32_t> seen_smaller{ 0 }; - - // Adjust approx_syscall_time_in_cycles to be within a factor of 2 - // of the typical time to execute one iteration of the loop above. - if ((local_approx_syscall_time_in_cycles >> 1) < elapsed_cycles) { - // measured time is no smaller than half current approximation - seen_smaller.store(0, std::memory_order_relaxed); - } else if (seen_smaller.fetch_add(1, std::memory_order_relaxed) >= 3) { - // smaller delays several times in a row; reduce approximation by 12.5% - const uint64_t new_approximation = - local_approx_syscall_time_in_cycles - - (local_approx_syscall_time_in_cycles >> 3); - approx_syscall_time_in_cycles.store(new_approximation, - std::memory_order_relaxed); - seen_smaller.store(0, std::memory_order_relaxed); - } - - *cycleclock = after_cycles; - return current_time_nanos_from_system; -} - - // --------------------------------------------------------------------- // An implementation of reader-write locks that use no atomic ops in the read // case. This is a generalization of Lamport's method for reading a multiword @@ -224,32 +150,110 @@ static_assert(((kMinNSBetweenSamples << (kScale + 1)) >> (kScale + 1)) == kMinNSBetweenSamples, "cannot represent kMaxBetweenSamplesNSScaled"); -// A reader-writer lock protecting the static locations below. -// See SeqAcquire() and SeqRelease() above. -static absl::base_internal::SpinLock lock( - absl::base_internal::kLinkerInitialized); -static std::atomic<uint64_t> seq(0); - // data from a sample of the kernel's time value struct TimeSampleAtomic { - std::atomic<uint64_t> raw_ns; // raw kernel time - std::atomic<uint64_t> base_ns; // our estimate of time - std::atomic<uint64_t> base_cycles; // cycle counter reading - std::atomic<uint64_t> nsscaled_per_cycle; // cycle period + std::atomic<uint64_t> raw_ns{0}; // raw kernel time + std::atomic<uint64_t> base_ns{0}; // our estimate of time + std::atomic<uint64_t> base_cycles{0}; // cycle counter reading + std::atomic<uint64_t> nsscaled_per_cycle{0}; // cycle period // cycles before we'll sample again (a scaled reciprocal of the period, // to avoid a division on the fast path). - std::atomic<uint64_t> min_cycles_per_sample; + std::atomic<uint64_t> min_cycles_per_sample{0}; }; // Same again, but with non-atomic types struct TimeSample { - uint64_t raw_ns; // raw kernel time - uint64_t base_ns; // our estimate of time - uint64_t base_cycles; // cycle counter reading - uint64_t nsscaled_per_cycle; // cycle period - uint64_t min_cycles_per_sample; // approx cycles before next sample + uint64_t raw_ns = 0; // raw kernel time + uint64_t base_ns = 0; // our estimate of time + uint64_t base_cycles = 0; // cycle counter reading + uint64_t nsscaled_per_cycle = 0; // cycle period + uint64_t min_cycles_per_sample = 0; // approx cycles before next sample }; -static struct TimeSampleAtomic last_sample; // the last sample; under seq +struct ABSL_CACHELINE_ALIGNED TimeState { + std::atomic<uint64_t> seq{0}; + TimeSampleAtomic last_sample; // the last sample; under seq + + // The following counters are used only by the test code. + int64_t stats_initializations{0}; + int64_t stats_reinitializations{0}; + int64_t stats_calibrations{0}; + int64_t stats_slow_paths{0}; + int64_t stats_fast_slow_paths{0}; + + uint64_t last_now_cycles ABSL_GUARDED_BY(lock){0}; + + // Used by GetCurrentTimeNanosFromKernel(). + // We try to read clock values at about the same time as the kernel clock. + // This value gets adjusted up or down as estimate of how long that should + // take, so we can reject attempts that take unusually long. + std::atomic<uint64_t> approx_syscall_time_in_cycles{10 * 1000}; + // Number of times in a row we've seen a kernel time call take substantially + // less than approx_syscall_time_in_cycles. + std::atomic<uint32_t> kernel_time_seen_smaller{0}; + + // A reader-writer lock protecting the static locations below. + // See SeqAcquire() and SeqRelease() above. + absl::base_internal::SpinLock lock{absl::kConstInit, + base_internal::SCHEDULE_KERNEL_ONLY}; +}; +ABSL_CONST_INIT static TimeState time_state{}; + +// Return the time in ns as told by the kernel interface. Place in *cycleclock +// the value of the cycleclock at about the time of the syscall. +// This call represents the time base that this module synchronizes to. +// Ensures that *cycleclock does not step back by up to (1 << 16) from +// last_cycleclock, to discard small backward counter steps. (Larger steps are +// assumed to be complete resyncs, which shouldn't happen. If they do, a full +// reinitialization of the outer algorithm should occur.) +static int64_t GetCurrentTimeNanosFromKernel(uint64_t last_cycleclock, + uint64_t *cycleclock) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(time_state.lock) { + uint64_t local_approx_syscall_time_in_cycles = // local copy + time_state.approx_syscall_time_in_cycles.load(std::memory_order_relaxed); + + int64_t current_time_nanos_from_system; + uint64_t before_cycles; + uint64_t after_cycles; + uint64_t elapsed_cycles; + int loops = 0; + do { + before_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW(); + current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); + after_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW(); + // elapsed_cycles is unsigned, so is large on overflow + elapsed_cycles = after_cycles - before_cycles; + if (elapsed_cycles >= local_approx_syscall_time_in_cycles && + ++loops == 20) { // clock changed frequencies? Back off. + loops = 0; + if (local_approx_syscall_time_in_cycles < 1000 * 1000) { + local_approx_syscall_time_in_cycles = + (local_approx_syscall_time_in_cycles + 1) << 1; + } + time_state.approx_syscall_time_in_cycles.store( + local_approx_syscall_time_in_cycles, std::memory_order_relaxed); + } + } while (elapsed_cycles >= local_approx_syscall_time_in_cycles || + last_cycleclock - after_cycles < (static_cast<uint64_t>(1) << 16)); + + // Adjust approx_syscall_time_in_cycles to be within a factor of 2 + // of the typical time to execute one iteration of the loop above. + if ((local_approx_syscall_time_in_cycles >> 1) < elapsed_cycles) { + // measured time is no smaller than half current approximation + time_state.kernel_time_seen_smaller.store(0, std::memory_order_relaxed); + } else if (time_state.kernel_time_seen_smaller.fetch_add( + 1, std::memory_order_relaxed) >= 3) { + // smaller delays several times in a row; reduce approximation by 12.5% + const uint64_t new_approximation = + local_approx_syscall_time_in_cycles - + (local_approx_syscall_time_in_cycles >> 3); + time_state.approx_syscall_time_in_cycles.store(new_approximation, + std::memory_order_relaxed); + time_state.kernel_time_seen_smaller.store(0, std::memory_order_relaxed); + } + + *cycleclock = after_cycles; + return current_time_nanos_from_system; +} static int64_t GetCurrentTimeNanosSlowPath() ABSL_ATTRIBUTE_COLD; @@ -317,14 +321,15 @@ int64_t GetCurrentTimeNanos() { // Acquire pairs with the barrier in SeqRelease - if this load sees that // store, the shared-data reads necessarily see that SeqRelease's updates // to the same shared data. - seq_read0 = seq.load(std::memory_order_acquire); + seq_read0 = time_state.seq.load(std::memory_order_acquire); - base_ns = last_sample.base_ns.load(std::memory_order_relaxed); - base_cycles = last_sample.base_cycles.load(std::memory_order_relaxed); + base_ns = time_state.last_sample.base_ns.load(std::memory_order_relaxed); + base_cycles = + time_state.last_sample.base_cycles.load(std::memory_order_relaxed); nsscaled_per_cycle = - last_sample.nsscaled_per_cycle.load(std::memory_order_relaxed); - min_cycles_per_sample = - last_sample.min_cycles_per_sample.load(std::memory_order_relaxed); + time_state.last_sample.nsscaled_per_cycle.load(std::memory_order_relaxed); + min_cycles_per_sample = time_state.last_sample.min_cycles_per_sample.load( + std::memory_order_relaxed); // This acquire fence pairs with the release fence in SeqAcquire. Since it // is sequenced between reads of shared data and seq_read1, the reads of @@ -335,7 +340,7 @@ int64_t GetCurrentTimeNanos() { // shared-data writes are effectively release ordered. Therefore if our // shared-data reads see any of a particular update's shared-data writes, // seq_read1 is guaranteed to see that update's SeqAcquire. - seq_read1 = seq.load(std::memory_order_relaxed); + seq_read1 = time_state.seq.load(std::memory_order_relaxed); // Fast path. Return if min_cycles_per_sample has not yet elapsed since the // last sample, and we read a consistent sample. The fast path activates @@ -348,9 +353,9 @@ int64_t GetCurrentTimeNanos() { // last_sample was updated). This is harmless, because delta_cycles will wrap // and report a time much much bigger than min_cycles_per_sample. In that case // we will take the slow path. - uint64_t delta_cycles = now_cycles - base_cycles; + uint64_t delta_cycles; if (seq_read0 == seq_read1 && (seq_read0 & 1) == 0 && - delta_cycles < min_cycles_per_sample) { + (delta_cycles = now_cycles - base_cycles) < min_cycles_per_sample) { return base_ns + ((delta_cycles * nsscaled_per_cycle) >> kScale); } return GetCurrentTimeNanosSlowPath(); @@ -390,24 +395,25 @@ static uint64_t UpdateLastSample( // TODO(absl-team): Remove this attribute when our compiler is smart enough // to do the right thing. ABSL_ATTRIBUTE_NOINLINE -static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) { +static int64_t GetCurrentTimeNanosSlowPath() + ABSL_LOCKS_EXCLUDED(time_state.lock) { // Serialize access to slow-path. Fast-path readers are not blocked yet, and // code below must not modify last_sample until the seqlock is acquired. - lock.Lock(); + time_state.lock.Lock(); // Sample the kernel time base. This is the definition of // "now" if we take the slow path. - static uint64_t last_now_cycles; // protected by lock uint64_t now_cycles; - uint64_t now_ns = GetCurrentTimeNanosFromKernel(last_now_cycles, &now_cycles); - last_now_cycles = now_cycles; + uint64_t now_ns = + GetCurrentTimeNanosFromKernel(time_state.last_now_cycles, &now_cycles); + time_state.last_now_cycles = now_cycles; uint64_t estimated_base_ns; // ---------- // Read the "last_sample" values again; this time holding the write lock. struct TimeSample sample; - ReadTimeSampleAtomic(&last_sample, &sample); + ReadTimeSampleAtomic(&time_state.last_sample, &sample); // ---------- // Try running the fast path again; another thread may have updated the @@ -418,13 +424,13 @@ static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) { // so that blocked readers can make progress without blocking new readers. estimated_base_ns = sample.base_ns + ((delta_cycles * sample.nsscaled_per_cycle) >> kScale); - stats_fast_slow_paths++; + time_state.stats_fast_slow_paths++; } else { estimated_base_ns = UpdateLastSample(now_cycles, now_ns, delta_cycles, &sample); } - lock.Unlock(); + time_state.lock.Unlock(); return estimated_base_ns; } @@ -435,9 +441,10 @@ static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) { static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, uint64_t delta_cycles, const struct TimeSample *sample) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock) { + ABSL_EXCLUSIVE_LOCKS_REQUIRED(time_state.lock) { uint64_t estimated_base_ns = now_ns; - uint64_t lock_value = SeqAcquire(&seq); // acquire seqlock to block readers + uint64_t lock_value = + SeqAcquire(&time_state.seq); // acquire seqlock to block readers // The 5s in the next if-statement limits the time for which we will trust // the cycle counter and our last sample to give a reasonable result. @@ -447,12 +454,16 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, sample->raw_ns + static_cast<uint64_t>(5) * 1000 * 1000 * 1000 < now_ns || now_ns < sample->raw_ns || now_cycles < sample->base_cycles) { // record this sample, and forget any previously known slope. - last_sample.raw_ns.store(now_ns, std::memory_order_relaxed); - last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed); - last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed); - last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed); - last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed); - stats_initializations++; + time_state.last_sample.raw_ns.store(now_ns, std::memory_order_relaxed); + time_state.last_sample.base_ns.store(estimated_base_ns, + std::memory_order_relaxed); + time_state.last_sample.base_cycles.store(now_cycles, + std::memory_order_relaxed); + time_state.last_sample.nsscaled_per_cycle.store(0, + std::memory_order_relaxed); + time_state.last_sample.min_cycles_per_sample.store( + 0, std::memory_order_relaxed); + time_state.stats_initializations++; } else if (sample->raw_ns + 500 * 1000 * 1000 < now_ns && sample->base_cycles + 50 < now_cycles) { // Enough time has passed to compute the cycle time. @@ -495,28 +506,32 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, if (new_nsscaled_per_cycle != 0 && diff_ns < 100 * 1000 * 1000 && -diff_ns < 100 * 1000 * 1000) { // record the cycle time measurement - last_sample.nsscaled_per_cycle.store( + time_state.last_sample.nsscaled_per_cycle.store( new_nsscaled_per_cycle, std::memory_order_relaxed); uint64_t new_min_cycles_per_sample = SafeDivideAndScale(kMinNSBetweenSamples, new_nsscaled_per_cycle); - last_sample.min_cycles_per_sample.store( + time_state.last_sample.min_cycles_per_sample.store( new_min_cycles_per_sample, std::memory_order_relaxed); - stats_calibrations++; + time_state.stats_calibrations++; } else { // something went wrong; forget the slope - last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed); - last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed); + time_state.last_sample.nsscaled_per_cycle.store( + 0, std::memory_order_relaxed); + time_state.last_sample.min_cycles_per_sample.store( + 0, std::memory_order_relaxed); estimated_base_ns = now_ns; - stats_reinitializations++; + time_state.stats_reinitializations++; } - last_sample.raw_ns.store(now_ns, std::memory_order_relaxed); - last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed); - last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed); + time_state.last_sample.raw_ns.store(now_ns, std::memory_order_relaxed); + time_state.last_sample.base_ns.store(estimated_base_ns, + std::memory_order_relaxed); + time_state.last_sample.base_cycles.store(now_cycles, + std::memory_order_relaxed); } else { // have a sample, but no slope; waiting for enough time for a calibration - stats_slow_paths++; + time_state.stats_slow_paths++; } - SeqRelease(&seq, lock_value); // release the readers + SeqRelease(&time_state.seq, lock_value); // release the readers return estimated_base_ns; } @@ -558,7 +573,8 @@ ABSL_NAMESPACE_END extern "C" { -ABSL_ATTRIBUTE_WEAK void AbslInternalSleepFor(absl::Duration duration) { +ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)( + absl::Duration duration) { while (duration > absl::ZeroDuration()) { absl::Duration to_sleep = std::min(duration, absl::MaxSleep()); absl::SleepOnce(to_sleep); diff --git a/third_party/abseil-cpp/absl/time/clock.h b/third_party/abseil-cpp/absl/time/clock.h index 27764a922d..5fe244d638 100644 --- a/third_party/abseil-cpp/absl/time/clock.h +++ b/third_party/abseil-cpp/absl/time/clock.h @@ -64,11 +64,11 @@ ABSL_NAMESPACE_END // By changing our extension points to be extern "C", we dodge this // check. extern "C" { -void AbslInternalSleepFor(absl::Duration duration); +void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(absl::Duration duration); } // extern "C" inline void absl::SleepFor(absl::Duration duration) { - AbslInternalSleepFor(duration); + ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(duration); } #endif // ABSL_TIME_CLOCK_H_ diff --git a/third_party/abseil-cpp/absl/time/clock_test.cc b/third_party/abseil-cpp/absl/time/clock_test.cc index 4bcfc6bc72..bc77dbc27b 100644 --- a/third_party/abseil-cpp/absl/time/clock_test.cc +++ b/third_party/abseil-cpp/absl/time/clock_test.cc @@ -18,6 +18,10 @@ #if defined(ABSL_HAVE_ALARM) #include <signal.h> #include <unistd.h> +#ifdef _AIX +// sig_t is not defined in AIX. +typedef void (*sig_t)(int); +#endif #elif defined(__linux__) || defined(__APPLE__) #error all known Linux and Apple targets have alarm #endif diff --git a/third_party/abseil-cpp/absl/time/duration.cc b/third_party/abseil-cpp/absl/time/duration.cc index b1af84061a..4443109a51 100644 --- a/third_party/abseil-cpp/absl/time/duration.cc +++ b/third_party/abseil-cpp/absl/time/duration.cc @@ -67,7 +67,10 @@ #include <string> #include "absl/base/casts.h" +#include "absl/base/macros.h" #include "absl/numeric/int128.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" #include "absl/time/time.h" namespace absl { @@ -353,7 +356,7 @@ namespace time_internal { // the remainder. If it does not saturate, the remainder remain accurate, // but the returned quotient will over/underflow int64_t and should not be used. int64_t IDivDuration(bool satq, const Duration num, const Duration den, - Duration* rem) { + Duration* rem) { int64_t q = 0; if (IDivFastPath(num, den, &q, rem)) { return q; @@ -708,16 +711,17 @@ char* Format64(char* ep, int width, int64_t v) { // fractional digits, because it is in the noise of what a Duration can // represent. struct DisplayUnit { - const char* abbr; + absl::string_view abbr; int prec; double pow10; }; -const DisplayUnit kDisplayNano = {"ns", 2, 1e2}; -const DisplayUnit kDisplayMicro = {"us", 5, 1e5}; -const DisplayUnit kDisplayMilli = {"ms", 8, 1e8}; -const DisplayUnit kDisplaySec = {"s", 11, 1e11}; -const DisplayUnit kDisplayMin = {"m", -1, 0.0}; // prec ignored -const DisplayUnit kDisplayHour = {"h", -1, 0.0}; // prec ignored +ABSL_CONST_INIT const DisplayUnit kDisplayNano = {"ns", 2, 1e2}; +ABSL_CONST_INIT const DisplayUnit kDisplayMicro = {"us", 5, 1e5}; +ABSL_CONST_INIT const DisplayUnit kDisplayMilli = {"ms", 8, 1e8}; +ABSL_CONST_INIT const DisplayUnit kDisplaySec = {"s", 11, 1e11}; +ABSL_CONST_INIT const DisplayUnit kDisplayMin = {"m", -1, 0.0}; // prec ignored +ABSL_CONST_INIT const DisplayUnit kDisplayHour = {"h", -1, + 0.0}; // prec ignored void AppendNumberUnit(std::string* out, int64_t n, DisplayUnit unit) { char buf[sizeof("2562047788015216")]; // hours in max duration @@ -725,16 +729,16 @@ void AppendNumberUnit(std::string* out, int64_t n, DisplayUnit unit) { char* bp = Format64(ep, 0, n); if (*bp != '0' || bp + 1 != ep) { out->append(bp, ep - bp); - out->append(unit.abbr); + out->append(unit.abbr.data(), unit.abbr.size()); } } // Note: unit.prec is limited to double's digits10 value (typically 15) so it // always fits in buf[]. void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) { - const int buf_size = std::numeric_limits<double>::digits10; - const int prec = std::min(buf_size, unit.prec); - char buf[buf_size]; // also large enough to hold integer part + constexpr int kBufferSize = std::numeric_limits<double>::digits10; + const int prec = std::min(kBufferSize, unit.prec); + char buf[kBufferSize]; // also large enough to hold integer part char* ep = buf + sizeof(buf); double d = 0; int64_t frac_part = Round(std::modf(n, &d) * unit.pow10); @@ -748,7 +752,7 @@ void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) { while (ep[-1] == '0') --ep; out->append(bp, ep - bp); } - out->append(unit.abbr); + out->append(unit.abbr.data(), unit.abbr.size()); } } @@ -759,7 +763,8 @@ void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) { // form "72h3m0.5s". Leading zero units are omitted. As a special // case, durations less than one second format use a smaller unit // (milli-, micro-, or nanoseconds) to ensure that the leading digit -// is non-zero. The zero duration formats as 0, with no unit. +// is non-zero. +// Unlike Go, we format the zero duration as 0, with no unit. std::string FormatDuration(Duration d) { const Duration min_duration = Seconds(kint64min); if (d == min_duration) { @@ -800,23 +805,27 @@ namespace { // A helper for ParseDuration() that parses a leading number from the given // string and stores the result in *int_part/*frac_part/*frac_scale. The // given string pointer is modified to point to the first unconsumed char. -bool ConsumeDurationNumber(const char** dpp, int64_t* int_part, +bool ConsumeDurationNumber(const char** dpp, const char* ep, int64_t* int_part, int64_t* frac_part, int64_t* frac_scale) { *int_part = 0; *frac_part = 0; *frac_scale = 1; // invariant: *frac_part < *frac_scale const char* start = *dpp; - for (; std::isdigit(**dpp); *dpp += 1) { + for (; *dpp != ep; *dpp += 1) { const int d = **dpp - '0'; // contiguous digits + if (d < 0 || 10 <= d) break; + if (*int_part > kint64max / 10) return false; *int_part *= 10; if (*int_part > kint64max - d) return false; *int_part += d; } const bool int_part_empty = (*dpp == start); - if (**dpp != '.') return !int_part_empty; - for (*dpp += 1; std::isdigit(**dpp); *dpp += 1) { + if (*dpp == ep || **dpp != '.') return !int_part_empty; + + for (*dpp += 1; *dpp != ep; *dpp += 1) { const int d = **dpp - '0'; // contiguous digits + if (d < 0 || 10 <= d) break; if (*frac_scale <= kint64max / 10) { *frac_part *= 10; *frac_part += d; @@ -830,32 +839,56 @@ bool ConsumeDurationNumber(const char** dpp, int64_t* int_part, // ns, us, ms, s, m, h) from the given string and stores the resulting unit // in "*unit". The given string pointer is modified to point to the first // unconsumed char. -bool ConsumeDurationUnit(const char** start, Duration* unit) { - const char *s = *start; - bool ok = true; - if (strncmp(s, "ns", 2) == 0) { - s += 2; - *unit = Nanoseconds(1); - } else if (strncmp(s, "us", 2) == 0) { - s += 2; - *unit = Microseconds(1); - } else if (strncmp(s, "ms", 2) == 0) { - s += 2; - *unit = Milliseconds(1); - } else if (strncmp(s, "s", 1) == 0) { - s += 1; - *unit = Seconds(1); - } else if (strncmp(s, "m", 1) == 0) { - s += 1; - *unit = Minutes(1); - } else if (strncmp(s, "h", 1) == 0) { - s += 1; - *unit = Hours(1); - } else { - ok = false; +bool ConsumeDurationUnit(const char** start, const char* end, Duration* unit) { + size_t size = end - *start; + switch (size) { + case 0: + return false; + default: + switch (**start) { + case 'n': + if (*(*start + 1) == 's') { + *start += 2; + *unit = Nanoseconds(1); + return true; + } + break; + case 'u': + if (*(*start + 1) == 's') { + *start += 2; + *unit = Microseconds(1); + return true; + } + break; + case 'm': + if (*(*start + 1) == 's') { + *start += 2; + *unit = Milliseconds(1); + return true; + } + break; + default: + break; + } + ABSL_FALLTHROUGH_INTENDED; + case 1: + switch (**start) { + case 's': + *unit = Seconds(1); + *start += 1; + return true; + case 'm': + *unit = Minutes(1); + *start += 1; + return true; + case 'h': + *unit = Hours(1); + *start += 1; + return true; + default: + return false; + } } - *start = s; - return ok; } } // namespace @@ -865,39 +898,38 @@ bool ConsumeDurationUnit(const char** start, Duration* unit) { // a possibly signed sequence of decimal numbers, each with optional // fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". // Valid time units are "ns", "us" "ms", "s", "m", "h". -bool ParseDuration(const std::string& dur_string, Duration* d) { - const char* start = dur_string.c_str(); +bool ParseDuration(absl::string_view dur_sv, Duration* d) { int sign = 1; - - if (*start == '-' || *start == '+') { - sign = *start == '-' ? -1 : 1; - ++start; - } - - // Can't parse a duration from an empty std::string. - if (*start == '\0') { - return false; + if (absl::ConsumePrefix(&dur_sv, "-")) { + sign = -1; + } else { + absl::ConsumePrefix(&dur_sv, "+"); } + if (dur_sv.empty()) return false; - // Special case for a std::string of "0". - if (*start == '0' && *(start + 1) == '\0') { + // Special case for a string of "0". + if (dur_sv == "0") { *d = ZeroDuration(); return true; } - if (strcmp(start, "inf") == 0) { + if (dur_sv == "inf") { *d = sign * InfiniteDuration(); return true; } + const char* start = dur_sv.data(); + const char* end = start + dur_sv.size(); + Duration dur; - while (*start != '\0') { + while (start != end) { int64_t int_part; int64_t frac_part; int64_t frac_scale; Duration unit; - if (!ConsumeDurationNumber(&start, &int_part, &frac_part, &frac_scale) || - !ConsumeDurationUnit(&start, &unit)) { + if (!ConsumeDurationNumber(&start, end, &int_part, &frac_part, + &frac_scale) || + !ConsumeDurationUnit(&start, end, &unit)) { return false; } if (int_part != 0) dur += sign * int_part * unit; @@ -908,7 +940,7 @@ bool ParseDuration(const std::string& dur_string, Duration* d) { } bool AbslParseFlag(absl::string_view text, Duration* dst, std::string*) { - return ParseDuration(std::string(text), dst); + return ParseDuration(text, dst); } std::string AbslUnparseFlag(Duration d) { return FormatDuration(d); } diff --git a/third_party/abseil-cpp/absl/time/duration_benchmark.cc b/third_party/abseil-cpp/absl/time/duration_benchmark.cc index 83a836c8c8..56820f377b 100644 --- a/third_party/abseil-cpp/absl/time/duration_benchmark.cc +++ b/third_party/abseil-cpp/absl/time/duration_benchmark.cc @@ -18,9 +18,14 @@ #include <string> #include "absl/base/attributes.h" +#include "absl/flags/flag.h" #include "absl/time/time.h" #include "benchmark/benchmark.h" +ABSL_FLAG(absl::Duration, absl_duration_flag_for_benchmark, + absl::Milliseconds(1), + "Flag to use for benchmarking duration flag access speed."); + namespace { // @@ -425,4 +430,15 @@ void BM_Duration_ParseDuration(benchmark::State& state) { } BENCHMARK(BM_Duration_ParseDuration)->DenseRange(0, kNumDurations - 1); +// +// Flag access +// +void BM_Duration_GetFlag(benchmark::State& state) { + while (state.KeepRunning()) { + benchmark::DoNotOptimize( + absl::GetFlag(FLAGS_absl_duration_flag_for_benchmark)); + } +} +BENCHMARK(BM_Duration_GetFlag); + } // namespace diff --git a/third_party/abseil-cpp/absl/time/duration_test.cc b/third_party/abseil-cpp/absl/time/duration_test.cc index 4d85a2c4f4..b7209e1c0a 100644 --- a/third_party/abseil-cpp/absl/time/duration_test.cc +++ b/third_party/abseil-cpp/absl/time/duration_test.cc @@ -17,6 +17,7 @@ #endif #include <chrono> // NOLINT(build/c++11) +#include <cfloat> #include <cmath> #include <cstdint> #include <ctime> @@ -1320,7 +1321,7 @@ TEST(Duration, SmallConversions) { EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0)); // TODO(bww): Is the next one OK? - EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(0.124999999e-9)); + EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(0.125e-9, 0))); EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.125e-9)); EXPECT_EQ(absl::Nanoseconds(1) / 4, absl::Seconds(0.250e-9)); EXPECT_EQ(absl::Nanoseconds(1) / 2, absl::Seconds(0.375e-9)); @@ -1330,7 +1331,7 @@ TEST(Duration, SmallConversions) { EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(0.875e-9)); EXPECT_EQ(absl::Nanoseconds(1), absl::Seconds(1.000e-9)); - EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(-0.124999999e-9)); + EXPECT_EQ(absl::ZeroDuration(), absl::Seconds(std::nextafter(-0.125e-9, 0))); EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.125e-9)); EXPECT_EQ(-absl::Nanoseconds(1) / 4, absl::Seconds(-0.250e-9)); EXPECT_EQ(-absl::Nanoseconds(1) / 2, absl::Seconds(-0.375e-9)); @@ -1369,10 +1370,13 @@ TEST(Duration, SmallConversions) { EXPECT_THAT(ToTimeval(absl::Nanoseconds(2000)), TimevalMatcher(tv)); } -void VerifySameAsMul(double time_as_seconds, int* const misses) { +void VerifyApproxSameAsMul(double time_as_seconds, int* const misses) { auto direct_seconds = absl::Seconds(time_as_seconds); auto mul_by_one_second = time_as_seconds * absl::Seconds(1); - if (direct_seconds != mul_by_one_second) { + // These are expected to differ by up to one tick due to fused multiply/add + // contraction. + if (absl::AbsDuration(direct_seconds - mul_by_one_second) > + absl::time_internal::MakeDuration(0, 1u)) { if (*misses > 10) return; ASSERT_LE(++(*misses), 10) << "Too many errors, not reporting more."; EXPECT_EQ(direct_seconds, mul_by_one_second) @@ -1384,8 +1388,17 @@ void VerifySameAsMul(double time_as_seconds, int* const misses) { // For a variety of interesting durations, we find the exact point // where one double converts to that duration, and the very next double // converts to the next duration. For both of those points, verify that -// Seconds(point) returns the same duration as point * Seconds(1.0) +// Seconds(point) returns a duration near point * Seconds(1.0). (They may +// not be exactly equal due to fused multiply/add contraction.) TEST(Duration, ToDoubleSecondsCheckEdgeCases) { +#if (defined(__i386__) || defined(_M_IX86)) && FLT_EVAL_METHOD != 0 + // We're using an x87-compatible FPU, and intermediate operations can be + // performed with 80-bit floats. This means the edge cases are different than + // what we expect here, so just skip this test. + GTEST_SKIP() + << "Skipping the test because we detected x87 floating-point semantics"; +#endif + constexpr uint32_t kTicksPerSecond = absl::time_internal::kTicksPerSecond; constexpr auto duration_tick = absl::time_internal::MakeDuration(0, 1u); int misses = 0; @@ -1423,8 +1436,8 @@ TEST(Duration, ToDoubleSecondsCheckEdgeCases) { } // Now low_edge is the highest double that converts to Duration d, // and high_edge is the lowest double that converts to Duration after_d. - VerifySameAsMul(low_edge, &misses); - VerifySameAsMul(high_edge, &misses); + VerifyApproxSameAsMul(low_edge, &misses); + VerifyApproxSameAsMul(high_edge, &misses); } } } @@ -1444,8 +1457,8 @@ TEST(Duration, ToDoubleSecondsCheckRandom) { int misses = 0; for (int i = 0; i < 1000000; ++i) { double d = std::exp(uniform(gen)); - VerifySameAsMul(d, &misses); - VerifySameAsMul(-d, &misses); + VerifyApproxSameAsMul(d, &misses); + VerifyApproxSameAsMul(-d, &misses); } } diff --git a/third_party/abseil-cpp/absl/time/format.cc b/third_party/abseil-cpp/absl/time/format.cc index ee088f33c3..4005fb704c 100644 --- a/third_party/abseil-cpp/absl/time/format.cc +++ b/third_party/abseil-cpp/absl/time/format.cc @@ -13,9 +13,12 @@ // limitations under the License. #include <string.h> + #include <cctype> #include <cstdint> +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" #include "absl/time/time.h" @@ -24,14 +27,11 @@ namespace cctz = absl::time_internal::cctz; namespace absl { ABSL_NAMESPACE_BEGIN -ABSL_DLL extern const char RFC3339_full[] = - "%Y-%m-%dT%H:%M:%E*S%Ez"; -ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; +ABSL_DLL extern const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez"; +ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; -ABSL_DLL extern const char RFC1123_full[] = - "%a, %d %b %E4Y %H:%M:%S %z"; -ABSL_DLL extern const char RFC1123_no_wday[] = - "%d %b %E4Y %H:%M:%S %z"; +ABSL_DLL extern const char RFC1123_full[] = "%a, %d %b %E4Y %H:%M:%S %z"; +ABSL_DLL extern const char RFC1123_no_wday[] = "%d %b %E4Y %H:%M:%S %z"; namespace { @@ -71,12 +71,12 @@ absl::Time Join(const cctz_parts& parts) { } // namespace -std::string FormatTime(const std::string& format, absl::Time t, +std::string FormatTime(absl::string_view format, absl::Time t, absl::TimeZone tz) { - if (t == absl::InfiniteFuture()) return kInfiniteFutureStr; - if (t == absl::InfinitePast()) return kInfinitePastStr; + if (t == absl::InfiniteFuture()) return std::string(kInfiniteFutureStr); + if (t == absl::InfinitePast()) return std::string(kInfinitePastStr); const auto parts = Split(t); - return cctz::detail::format(format, parts.sec, parts.fem, + return cctz::detail::format(std::string(format), parts.sec, parts.fem, cctz::time_zone(tz)); } @@ -88,42 +88,50 @@ std::string FormatTime(absl::Time t) { return absl::FormatTime(RFC3339_full, t, absl::LocalTimeZone()); } -bool ParseTime(const std::string& format, const std::string& input, +bool ParseTime(absl::string_view format, absl::string_view input, absl::Time* time, std::string* err) { return absl::ParseTime(format, input, absl::UTCTimeZone(), time, err); } // If the input string does not contain an explicit UTC offset, interpret // the fields with respect to the given TimeZone. -bool ParseTime(const std::string& format, const std::string& input, +bool ParseTime(absl::string_view format, absl::string_view input, absl::TimeZone tz, absl::Time* time, std::string* err) { - const char* data = input.c_str(); - while (std::isspace(*data)) ++data; - - size_t inf_size = strlen(kInfiniteFutureStr); - if (strncmp(data, kInfiniteFutureStr, inf_size) == 0) { - const char* new_data = data + inf_size; - while (std::isspace(*new_data)) ++new_data; - if (*new_data == '\0') { - *time = InfiniteFuture(); - return true; + auto strip_leading_space = [](absl::string_view* sv) { + while (!sv->empty()) { + if (!std::isspace(sv->front())) return; + sv->remove_prefix(1); } - } - - inf_size = strlen(kInfinitePastStr); - if (strncmp(data, kInfinitePastStr, inf_size) == 0) { - const char* new_data = data + inf_size; - while (std::isspace(*new_data)) ++new_data; - if (*new_data == '\0') { - *time = InfinitePast(); - return true; + }; + + // Portable toolchains means we don't get nice constexpr here. + struct Literal { + const char* name; + size_t size; + absl::Time value; + }; + static Literal literals[] = { + {kInfiniteFutureStr, strlen(kInfiniteFutureStr), InfiniteFuture()}, + {kInfinitePastStr, strlen(kInfinitePastStr), InfinitePast()}, + }; + strip_leading_space(&input); + for (const auto& lit : literals) { + if (absl::StartsWith(input, absl::string_view(lit.name, lit.size))) { + absl::string_view tail = input; + tail.remove_prefix(lit.size); + strip_leading_space(&tail); + if (tail.empty()) { + *time = lit.value; + return true; + } } } std::string error; cctz_parts parts; - const bool b = cctz::detail::parse(format, input, cctz::time_zone(tz), - &parts.sec, &parts.fem, &error); + const bool b = + cctz::detail::parse(std::string(format), std::string(input), + cctz::time_zone(tz), &parts.sec, &parts.fem, &error); if (b) { *time = Join(parts); } else if (err != nullptr) { @@ -134,8 +142,7 @@ bool ParseTime(const std::string& format, const std::string& input, // Functions required to support absl::Time flags. bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) { - return absl::ParseTime(RFC3339_full, std::string(text), absl::UTCTimeZone(), - t, error); + return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error); } std::string AbslUnparseFlag(absl::Time t) { diff --git a/third_party/abseil-cpp/absl/time/format_benchmark.cc b/third_party/abseil-cpp/absl/time/format_benchmark.cc index 249c51d875..19e481dbd1 100644 --- a/third_party/abseil-cpp/absl/time/format_benchmark.cc +++ b/third_party/abseil-cpp/absl/time/format_benchmark.cc @@ -26,7 +26,7 @@ const char* const kFormats[] = { absl::RFC1123_no_wday, // 1 absl::RFC3339_full, // 2 absl::RFC3339_sec, // 3 - "%Y-%m-%dT%H:%M:%S", // 4 + "%Y-%m-%d%ET%H:%M:%S", // 4 "%Y-%m-%d", // 5 }; const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); diff --git a/third_party/abseil-cpp/absl/time/format_test.cc b/third_party/abseil-cpp/absl/time/format_test.cc index ab1f30594e..a9a1eb8ee1 100644 --- a/third_party/abseil-cpp/absl/time/format_test.cc +++ b/third_party/abseil-cpp/absl/time/format_test.cc @@ -173,7 +173,7 @@ TEST(ParseTime, WithTimeZone) { absl::Time t; std::string e; - // We can parse a std::string without a UTC offset if we supply a timezone. + // We can parse a string without a UTC offset if we supply a timezone. EXPECT_TRUE( absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e)) << e; @@ -327,7 +327,7 @@ TEST(ParseTime, InfiniteTime) { EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-past ", &t, &err)); EXPECT_EQ(absl::InfinitePast(), t); - // "infinite-future" as literal std::string + // "infinite-future" as literal string absl::TimeZone tz = absl::UTCTimeZone(); EXPECT_TRUE(absl::ParseTime("infinite-future %H:%M", "infinite-future 03:04", &t, &err)); @@ -335,7 +335,7 @@ TEST(ParseTime, InfiniteTime) { EXPECT_EQ(3, tz.At(t).cs.hour()); EXPECT_EQ(4, tz.At(t).cs.minute()); - // "infinite-past" as literal std::string + // "infinite-past" as literal string EXPECT_TRUE( absl::ParseTime("infinite-past %H:%M", "infinite-past 03:04", &t, &err)); EXPECT_NE(absl::InfinitePast(), t); diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/BUILD.bazel b/third_party/abseil-cpp/absl/time/internal/cctz/BUILD.bazel index 7a53c815b9..bdc2e3091c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/BUILD.bazel +++ b/third_party/abseil-cpp/absl/time/internal/cctz/BUILD.bazel @@ -12,11 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") - package(features = ["-parse_headers"]) -licenses(["notice"]) # Apache License +licenses(["notice"]) filegroup( name = "zoneinfo", @@ -26,14 +24,14 @@ filegroup( config_setting( name = "osx", constraint_values = [ - "@bazel_tools//platforms:osx", + "@platforms//os:osx", ], ) config_setting( name = "ios", constraint_values = [ - "@bazel_tools//platforms:ios", + "@platforms//os:ios", ], ) @@ -92,6 +90,11 @@ cc_library( ### tests +test_suite( + name = "all_tests", + visibility = ["//visibility:public"], +) + cc_test( name = "civil_time_test", size = "small", diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h index 4cde96f1aa..8aadde57ca 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h +++ b/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h @@ -106,54 +106,64 @@ CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept { CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept { - y += (cd / 146097) * 400; + year_t ey = y % 400; + const year_t oey = ey; + ey += (cd / 146097) * 400; cd %= 146097; if (cd < 0) { - y -= 400; + ey -= 400; cd += 146097; } - y += (d / 146097) * 400; + ey += (d / 146097) * 400; d = d % 146097 + cd; if (d > 0) { if (d > 146097) { - y += 400; + ey += 400; d -= 146097; } } else { if (d > -365) { // We often hit the previous year when stepping a civil time backwards, // so special case it to avoid counting up by 100/4/1-year chunks. - y -= 1; - d += days_per_year(y, m); + ey -= 1; + d += days_per_year(ey, m); } else { - y -= 400; + ey -= 400; d += 146097; } } if (d > 365) { - for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) { + for (;;) { + int n = days_per_century(ey, m); + if (d <= n) break; d -= n; - y += 100; + ey += 100; } - for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) { + for (;;) { + int n = days_per_4years(ey, m); + if (d <= n) break; d -= n; - y += 4; + ey += 4; } - for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) { + for (;;) { + int n = days_per_year(ey, m); + if (d <= n) break; d -= n; - ++y; + ++ey; } } if (d > 28) { - for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) { + for (;;) { + int n = days_per_month(ey, m); + if (d <= n) break; d -= n; if (++m > 12) { - ++y; + ++ey; m = 1; } } } - return fields(y, m, static_cast<day_t>(d), hh, mm, ss); + return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss); } CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept { @@ -406,16 +416,10 @@ class civil_time { // Assigning arithmetic. CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept { - f_ = step(T{}, f_, n); - return *this; + return *this = *this + n; } CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept { - if (n != (std::numeric_limits<diff_t>::min)()) { - f_ = step(T{}, f_, -n); - } else { - f_ = step(T{}, step(T{}, f_, -(n + 1)), 1); - } - return *this; + return *this = *this - n; } CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; } CONSTEXPR_M civil_time operator++(int) noexcept { @@ -432,13 +436,15 @@ class civil_time { // Binary arithmetic operators. friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept { - return a += n; + return civil_time(step(T{}, a.f_, n)); } friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept { - return a += n; + return a + n; } friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept { - return a -= n; + return n != (std::numeric_limits<diff_t>::min)() + ? civil_time(step(T{}, a.f_, -n)) + : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1)); } friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept { return difference(T{}, lhs.f_, rhs.f_); diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h b/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h index d05147a17c..6e382dc6c9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h +++ b/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h @@ -22,6 +22,7 @@ #include <chrono> #include <cstdint> +#include <limits> #include <string> #include <utility> @@ -41,20 +42,9 @@ using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead. namespace detail { template <typename D> -inline std::pair<time_point<seconds>, D> split_seconds( - const time_point<D>& tp) { - auto sec = std::chrono::time_point_cast<seconds>(tp); - auto sub = tp - sec; - if (sub.count() < 0) { - sec -= seconds(1); - sub += seconds(1); - } - return {sec, std::chrono::duration_cast<D>(sub)}; -} -inline std::pair<time_point<seconds>, seconds> split_seconds( - const time_point<seconds>& tp) { - return {tp, seconds::zero()}; -} +std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp); +std::pair<time_point<seconds>, seconds> split_seconds( + const time_point<seconds>& tp); } // namespace detail // cctz::time_zone is an opaque, small, value-type class representing a @@ -209,7 +199,7 @@ class time_zone { // version() and description() provide additional information about the // time zone. The content of each of the returned strings is unspecified, // however, when the IANA Time Zone Database is the underlying data source - // the version() std::string will be in the familar form (e.g, "2018e") or + // the version() string will be in the familar form (e.g, "2018e") or // empty when unavailable. // // Note: These functions are for informational or testing purposes only. @@ -279,6 +269,20 @@ std::string format(const std::string&, const time_point<seconds>&, const femtoseconds&, const time_zone&); bool parse(const std::string&, const std::string&, const time_zone&, time_point<seconds>*, femtoseconds*, std::string* err = nullptr); +template <typename Rep, std::intmax_t Denom> +bool join_seconds( + const time_point<seconds>& sec, const femtoseconds& fs, + time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp); +template <typename Rep, std::intmax_t Num> +bool join_seconds( + const time_point<seconds>& sec, const femtoseconds& fs, + time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp); +template <typename Rep> +bool join_seconds( + const time_point<seconds>& sec, const femtoseconds& fs, + time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp); +bool join_seconds(const time_point<seconds>& sec, const femtoseconds&, + time_point<seconds>* tpp); } // namespace detail // Formats the given time_point in the given cctz::time_zone according to @@ -292,6 +296,7 @@ bool parse(const std::string&, const std::string&, const time_zone&, // - %E#f - Fractional seconds with # digits of precision // - %E*f - Fractional seconds with full precision (a literal '*') // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) +// - %ET - The RFC3339 "date-time" separator "T" // // Note that %E0S behaves like %S, and %E0f produces no characters. In // contrast %E*f always produces at least one digit, which may be '0'. @@ -321,7 +326,8 @@ inline std::string format(const std::string& fmt, const time_point<D>& tp, // returns the corresponding time_point. Uses strftime()-like formatting // options, with the same extensions as cctz::format(), but with the // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez -// and %E*z also accept the same inputs. +// and %E*z also accept the same inputs, which (along with %z) includes +// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'. // // %Y consumes as many numeric characters as it can, so the matching data // should always be terminated with a non-numeric. %E4Y always consumes @@ -367,15 +373,84 @@ inline bool parse(const std::string& fmt, const std::string& input, const time_zone& tz, time_point<D>* tpp) { time_point<seconds> sec; detail::femtoseconds fs; - const bool b = detail::parse(fmt, input, tz, &sec, &fs); - if (b) { - // TODO: Return false if unrepresentable as a time_point<D>. - *tpp = std::chrono::time_point_cast<D>(sec); - *tpp += std::chrono::duration_cast<D>(fs); + return detail::parse(fmt, input, tz, &sec, &fs) && + detail::join_seconds(sec, fs, tpp); +} + +namespace detail { + +// Split a time_point<D> into a time_point<seconds> and a D subseconds. +// Undefined behavior if time_point<seconds> is not of sufficient range. +// Note that this means it is UB to call cctz::time_zone::lookup(tp) or +// cctz::format(fmt, tp, tz) with a time_point that is outside the range +// of a 64-bit std::time_t. +template <typename D> +std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp) { + auto sec = std::chrono::time_point_cast<seconds>(tp); + auto sub = tp - sec; + if (sub.count() < 0) { + sec -= seconds(1); + sub += seconds(1); } - return b; + return {sec, std::chrono::duration_cast<D>(sub)}; +} + +inline std::pair<time_point<seconds>, seconds> split_seconds( + const time_point<seconds>& tp) { + return {tp, seconds::zero()}; } +// Join a time_point<seconds> and femto subseconds into a time_point<D>. +// Floors to the resolution of time_point<D>. Returns false if time_point<D> +// is not of sufficient range. +template <typename Rep, std::intmax_t Denom> +bool join_seconds( + const time_point<seconds>& sec, const femtoseconds& fs, + time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp) { + using D = std::chrono::duration<Rep, std::ratio<1, Denom>>; + // TODO(#199): Return false if result unrepresentable as a time_point<D>. + *tpp = std::chrono::time_point_cast<D>(sec); + *tpp += std::chrono::duration_cast<D>(fs); + return true; +} + +template <typename Rep, std::intmax_t Num> +bool join_seconds( + const time_point<seconds>& sec, const femtoseconds&, + time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp) { + using D = std::chrono::duration<Rep, std::ratio<Num, 1>>; + auto count = sec.time_since_epoch().count(); + if (count >= 0 || count % Num == 0) { + count /= Num; + } else { + count /= Num; + count -= 1; + } + if (count > (std::numeric_limits<Rep>::max)()) return false; + if (count < (std::numeric_limits<Rep>::min)()) return false; + *tpp = time_point<D>() + D{static_cast<Rep>(count)}; + return true; +} + +template <typename Rep> +bool join_seconds( + const time_point<seconds>& sec, const femtoseconds&, + time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp) { + using D = std::chrono::duration<Rep, std::ratio<1, 1>>; + auto count = sec.time_since_epoch().count(); + if (count > (std::numeric_limits<Rep>::max)()) return false; + if (count < (std::numeric_limits<Rep>::min)()) return false; + *tpp = time_point<D>() + D{static_cast<Rep>(count)}; + return true; +} + +inline bool join_seconds(const time_point<seconds>& sec, const femtoseconds&, + time_point<seconds>* tpp) { + *tpp = sec; + return true; +} + +} // namespace detail } // namespace cctz } // namespace time_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h b/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h index 912b44ba8f..012eb4ec30 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h +++ b/third_party/abseil-cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h @@ -37,7 +37,7 @@ class ZoneInfoSource { // Until the zoneinfo data supports versioning information, we provide // a way for a ZoneInfoSource to indicate it out-of-band. The default - // implementation returns an empty std::string. + // implementation returns an empty string. virtual std::string Version() const; }; diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc index d30a644e09..6770ad6b97 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc @@ -97,8 +97,8 @@ void BM_PrevWeekday(benchmark::State& state) { } BENCHMARK(BM_PrevWeekday); -const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; -const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; +const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez"; +const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z"; const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; @@ -280,6 +280,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan", "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", + "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Pangnirtung", @@ -647,6 +648,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Johnston", + "Pacific/Kanton", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", @@ -990,12 +992,12 @@ void BM_Time_FromCivilDay0_Libc(benchmark::State& state) { BENCHMARK(BM_Time_FromCivilDay0_Libc); const char* const kFormats[] = { - RFC1123_full, // 0 - RFC1123_no_wday, // 1 - RFC3339_full, // 2 - RFC3339_sec, // 3 - "%Y-%m-%dT%H:%M:%S", // 4 - "%Y-%m-%d", // 5 + RFC1123_full, // 0 + RFC1123_no_wday, // 1 + RFC3339_full, // 2 + RFC3339_sec, // 3 + "%Y-%m-%d%ET%H:%M:%S", // 4 + "%Y-%m-%d", // 5 }; const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc index be894d7072..a5a7123041 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/civil_time_test.cc @@ -235,6 +235,16 @@ TEST(CivilTime, Difference) { } // NOTE: Run this with --copt=-ftrapv to detect overflow problems. +TEST(CivilTime, ConstructionWithHugeYear) { + constexpr civil_hour h(-9223372036854775807, 1, 1, -1); + static_assert(h.year() == -9223372036854775807 - 1, + "ConstructionWithHugeYear"); + static_assert(h.month() == 12, "ConstructionWithHugeYear"); + static_assert(h.day() == 31, "ConstructionWithHugeYear"); + static_assert(h.hour() == 23, "ConstructionWithHugeYear"); +} + +// NOTE: Run this with --copt=-ftrapv to detect overflow problems. TEST(CivilTime, DifferenceWithHugeYear) { { constexpr civil_day d1(9223372036854775807, 1, 1); diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc index 303c0244a8..f2b3294ef7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -53,7 +53,7 @@ int Parse02d(const char* p) { } // namespace bool FixedOffsetFromName(const std::string& name, seconds* offset) { - if (name.compare(0, std::string::npos, "UTC", 3) == 0) { + if (name == "UTC" || name == "UTC0") { *offset = seconds::zero(); return true; } diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc index 950b23a1d2..d8cb047425 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc @@ -67,6 +67,48 @@ char* strptime(const char* s, const char* fmt, std::tm* tm) { } #endif +// Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0). +int ToTmWday(weekday wd) { + switch (wd) { + case weekday::sunday: + return 0; + case weekday::monday: + return 1; + case weekday::tuesday: + return 2; + case weekday::wednesday: + return 3; + case weekday::thursday: + return 4; + case weekday::friday: + return 5; + case weekday::saturday: + return 6; + } + return 0; /*NOTREACHED*/ +} + +// Convert a tm_wday value (0-6, Sunday = 0) to a cctz::weekday. +weekday FromTmWday(int tm_wday) { + switch (tm_wday) { + case 0: + return weekday::sunday; + case 1: + return weekday::monday; + case 2: + return weekday::tuesday; + case 3: + return weekday::wednesday; + case 4: + return weekday::thursday; + case 5: + return weekday::friday; + case 6: + return weekday::saturday; + } + return weekday::sunday; /*NOTREACHED*/ +} + std::tm ToTM(const time_zone::absolute_lookup& al) { std::tm tm{}; tm.tm_sec = al.cs.second(); @@ -84,34 +126,19 @@ std::tm ToTM(const time_zone::absolute_lookup& al) { tm.tm_year = static_cast<int>(al.cs.year() - 1900); } - switch (get_weekday(al.cs)) { - case weekday::sunday: - tm.tm_wday = 0; - break; - case weekday::monday: - tm.tm_wday = 1; - break; - case weekday::tuesday: - tm.tm_wday = 2; - break; - case weekday::wednesday: - tm.tm_wday = 3; - break; - case weekday::thursday: - tm.tm_wday = 4; - break; - case weekday::friday: - tm.tm_wday = 5; - break; - case weekday::saturday: - tm.tm_wday = 6; - break; - } + tm.tm_wday = ToTmWday(get_weekday(al.cs)); tm.tm_yday = get_yearday(al.cs) - 1; tm.tm_isdst = al.is_dst ? 1 : 0; return tm; } +// Returns the week of the year [0:53] given a civil day and the day on +// which weeks are defined to start. +int ToWeek(const civil_day& cd, weekday week_start) { + const civil_day d(cd.year() % 400, cd.month(), cd.day()); + return static_cast<int>((d - prev_weekday(civil_year(d), week_start)) / 7); +} + const char kDigits[] = "0123456789"; // Formats a 64-bit integer in the given field width. Note that it is up @@ -189,7 +216,7 @@ void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) { // strftime(3) returns the number of characters placed in the output // array (which may be 0 characters). It also returns 0 to indicate // an error, like the array wasn't large enough. To accommodate this, - // the following code grows the buffer size from 2x the format std::string + // the following code grows the buffer size from 2x the format string // length up to 32x. for (std::size_t i = 2; i != 32; i *= 2) { std::size_t buf_size = fmt.size() * i; @@ -290,6 +317,7 @@ const std::int_fast64_t kExp10[kDigits10_64 + 1] = { // - %E#S - Seconds with # digits of fractional precision // - %E*S - Seconds with full fractional precision (a literal '*') // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) +// - %ET - The RFC3339 "date-time" separator "T" // // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are // handled internally for performance reasons. strftime(3) is slow due to @@ -354,7 +382,7 @@ std::string format(const std::string& format, const time_point<seconds>& tp, if (cur == end || (cur - percent) % 2 == 0) continue; // Simple specifiers that we handle ourselves. - if (strchr("YmdeHMSzZs%", *cur)) { + if (strchr("YmdeUuWwHMSzZs%", *cur)) { if (cur - 1 != pending) { FormatTM(&result, std::string(pending, cur - 1), tm); } @@ -375,6 +403,22 @@ std::string format(const std::string& format, const time_point<seconds>& tp, if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows result.append(bp, static_cast<std::size_t>(ep - bp)); break; + case 'U': + bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday)); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'u': + bp = Format64(ep, 0, tm.tm_wday ? tm.tm_wday : 7); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'W': + bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::monday)); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'w': + bp = Format64(ep, 0, tm.tm_wday); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; case 'H': bp = Format02d(ep, al.cs.hour()); result.append(bp, static_cast<std::size_t>(ep - bp)); @@ -448,7 +492,14 @@ std::string format(const std::string& format, const time_point<seconds>& tp, if (*cur != 'E' || ++cur == end) continue; // Format our extensions. - if (*cur == 'z') { + if (*cur == 'T') { + // Formats %ET. + if (cur - 2 != pending) { + FormatTM(&result, std::string(pending, cur - 2), tm); + } + result.append("T"); + pending = ++cur; + } else if (*cur == 'z') { // Formats %Ez. if (cur - 2 != pending) { FormatTM(&result, std::string(pending, cur - 2), tm); @@ -551,7 +602,7 @@ const char* ParseOffset(const char* dp, const char* mode, int* offset) { } else { dp = nullptr; } - } else if (first == 'Z') { // Zulu + } else if (first == 'Z' || first == 'z') { // Zulu *offset = 0; } else { dp = nullptr; @@ -602,12 +653,32 @@ const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) { return dp; } +// Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday, +// and the day on which weeks are defined to start. Returns false if year +// would need to move outside its bounds. +bool FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) { + const civil_year y(*year % 400); + civil_day cd = prev_weekday(y, week_start); // week 0 + cd = next_weekday(cd - 1, FromTmWday(tm->tm_wday)) + (week_num * 7); + if (const year_t shift = cd.year() - y.year()) { + if (shift > 0) { + if (*year > std::numeric_limits<year_t>::max() - shift) return false; + } else { + if (*year < std::numeric_limits<year_t>::min() - shift) return false; + } + *year += shift; + } + tm->tm_mon = cd.month() - 1; + tm->tm_mday = cd.day(); + return true; +} + } // namespace // Uses strptime(3) to parse the given input. Supports the same extended // format specifiers as format(), although %E#S and %E*S are treated // identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept -// the same inputs. +// the same inputs. %ET accepts either 'T' or 't'. // // The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are // handled internally so that we can normally avoid strptime() altogether @@ -651,6 +722,8 @@ bool parse(const std::string& format, const std::string& input, const char* fmt = format.c_str(); // NUL terminated bool twelve_hour = false; bool afternoon = false; + int week_num = -1; + weekday week_start = weekday::sunday; bool saw_percent_s = false; std::int_fast64_t percent_s = 0; @@ -689,10 +762,27 @@ bool parse(const std::string& format, const std::string& input, case 'm': data = ParseInt(data, 2, 1, 12, &tm.tm_mon); if (data != nullptr) tm.tm_mon -= 1; + week_num = -1; continue; case 'd': case 'e': data = ParseInt(data, 2, 1, 31, &tm.tm_mday); + week_num = -1; + continue; + case 'U': + data = ParseInt(data, 0, 0, 53, &week_num); + week_start = weekday::sunday; + continue; + case 'W': + data = ParseInt(data, 0, 0, 53, &week_num); + week_start = weekday::monday; + continue; + case 'u': + data = ParseInt(data, 0, 1, 7, &tm.tm_wday); + if (data != nullptr) tm.tm_wday %= 7; + continue; + case 'w': + data = ParseInt(data, 0, 0, 6, &tm.tm_wday); continue; case 'H': data = ParseInt(data, 2, 0, 23, &tm.tm_hour); @@ -742,6 +832,15 @@ bool parse(const std::string& format, const std::string& input, data = (*data == '%' ? data + 1 : nullptr); continue; case 'E': + if (fmt[0] == 'T') { + if (*data == 'T' || *data == 't') { + ++data; + ++fmt; + } else { + data = nullptr; + } + continue; + } if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) { data = ParseOffset(data, ":", &offset); if (data != nullptr) saw_offset = true; @@ -839,7 +938,7 @@ bool parse(const std::string& format, const std::string& input, // Skip any remaining whitespace. while (std::isspace(*data)) ++data; - // parse() must consume the entire input std::string. + // parse() must consume the entire input string. if (*data != '\0') { if (err != nullptr) *err = "Illegal trailing data in input string"; return false; @@ -874,6 +973,14 @@ bool parse(const std::string& format, const std::string& input, year += 1900; } + // Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number. + if (week_num != -1) { + if (!FromWeek(week_num, week_start, &year, &tm)) { + if (err != nullptr) *err = "Out-of-range field"; + return false; + } + } + const int month = tm.tm_mon + 1; civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc index caebcc4d95..6487fa9373 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -13,6 +13,7 @@ // limitations under the License. #include <chrono> +#include <cstdint> #include <iomanip> #include <sstream> #include <string> @@ -48,8 +49,8 @@ namespace { EXPECT_STREQ(zone, al.abbr); \ } while (0) -const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; -const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; +const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez"; +const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z"; const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z"; @@ -679,6 +680,34 @@ TEST(Format, RFC1123Format) { // locale specific EXPECT_EQ("28 Jun 1977 09:08:07 -0700", format(RFC1123_no_wday, tp, tz)); } +TEST(Format, Week) { + const time_zone utc = utc_time_zone(); + + auto tp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("2017-01-7", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2017-00-0", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc); + EXPECT_EQ("2017-53-7", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2017-52-0", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("2018-00-1", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2018-01-1", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc); + EXPECT_EQ("2018-52-1", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2018-53-1", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc); + EXPECT_EQ("2019-00-2", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2019-00-2", format("%Y-%W-%w", tp, utc)); + + tp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); + EXPECT_EQ("2019-52-2", format("%Y-%U-%u", tp, utc)); + EXPECT_EQ("2019-52-2", format("%Y-%W-%w", tp, utc)); +} + // // Testing parse() // @@ -767,7 +796,7 @@ TEST(Parse, WithTimeZone) { EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); time_point<chrono::nanoseconds> tp; - // We can parse a std::string without a UTC offset if we supply a timezone. + // We can parse a string without a UTC offset if we supply a timezone. EXPECT_TRUE(parse("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &tp)); ExpectTime(tp, tz, 2013, 6, 28, 19, 8, 9, -7 * 60 * 60, true, "PDT"); @@ -1107,7 +1136,7 @@ TEST(Parse, ExtendedSeconds) { // All %E<prec>S cases are treated the same as %E*S on input. auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}; - for (const std::string& prec : precisions) { + for (const std::string prec : precisions) { const std::string fmt = "%E" + prec + "S"; SCOPED_TRACE(fmt); time_point<chrono::nanoseconds> tp = unix_epoch; @@ -1189,7 +1218,7 @@ TEST(Parse, ExtendedSubeconds) { // All %E<prec>f cases are treated the same as %E*f on input. auto precisions = {"*", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"}; - for (const std::string& prec : precisions) { + for (const std::string prec : precisions) { const std::string fmt = "%E" + prec + "f"; SCOPED_TRACE(fmt); time_point<chrono::nanoseconds> tp = unix_epoch - chrono::seconds(1); @@ -1379,10 +1408,85 @@ TEST(Parse, RFC3339Format) { EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00+00:00", tz, &tp)); ExpectTime(tp, tz, 2014, 2, 12, 20, 21, 0, 0, false, "UTC"); - // Check that %Ez also accepts "Z" as a synonym for "+00:00". + // Check that %ET also accepts "t". time_point<chrono::nanoseconds> tp2; - EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp2)); + EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12t20:21:00+00:00", tz, &tp2)); EXPECT_EQ(tp, tp2); + + // Check that %Ez also accepts "Z" as a synonym for "+00:00". + time_point<chrono::nanoseconds> tp3; + EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00Z", tz, &tp3)); + EXPECT_EQ(tp, tp3); + + // Check that %Ez also accepts "z" as a synonym for "+00:00". + time_point<chrono::nanoseconds> tp4; + EXPECT_TRUE(parse(RFC3339_sec, "2014-02-12T20:21:00z", tz, &tp4)); + EXPECT_EQ(tp, tp4); +} + +TEST(Parse, Week) { + const time_zone utc = utc_time_zone(); + time_point<absl::time_internal::cctz::seconds> tp; + + auto exp = convert(civil_second(2017, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2017-01-7", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2017-00-0", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2017, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2017-53-7", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2017-52-0", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2018, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2018-00-1", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2018-01-1", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2018, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2018-52-1", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2018-53-1", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2019, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2019-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2019-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2019-52-2", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2019-52-2", utc, &tp)); + EXPECT_EQ(exp, tp); +} + +TEST(Parse, WeekYearShift) { + // %U/%W conversions with week values in {0, 52, 53} can slip + // into the previous/following calendar years. + const time_zone utc = utc_time_zone(); + time_point<absl::time_internal::cctz::seconds> tp; + + auto exp = convert(civil_second(2019, 12, 31, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2020-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2020-00-2", utc, &tp)); + EXPECT_EQ(exp, tp); + + exp = convert(civil_second(2021, 1, 1, 0, 0, 0), utc); + EXPECT_TRUE(parse("%Y-%U-%u", "2020-52-5", utc, &tp)); + EXPECT_EQ(exp, tp); + EXPECT_TRUE(parse("%Y-%W-%w", "2020-52-5", utc, &tp)); + EXPECT_EQ(exp, tp); + + // Slipping into the previous/following calendar years should fail when + // we're already at the extremes. + EXPECT_FALSE(parse("%Y-%U-%u", "-9223372036854775808-0-7", utc, &tp)); + EXPECT_FALSE(parse("%Y-%U-%u", "9223372036854775807-53-7", utc, &tp)); } TEST(Parse, MaxRange) { @@ -1401,7 +1505,7 @@ TEST(Parse, MaxRange) { parse(RFC3339_sec, "292277026596-12-04T14:30:07-01:00", utc, &tp)); EXPECT_EQ(tp, time_point<absl::time_internal::cctz::seconds>::max()); EXPECT_FALSE( - parse(RFC3339_sec, "292277026596-12-04T15:30:07-01:00", utc, &tp)); + parse(RFC3339_sec, "292277026596-12-04T14:30:08-01:00", utc, &tp)); // tests the lower limit using +00:00 offset EXPECT_TRUE( @@ -1422,10 +1526,82 @@ TEST(Parse, MaxRange) { parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp)); EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01", utc, &tp)); +} + +TEST(Parse, TimePointOverflow) { + const time_zone utc = utc_time_zone(); - // TODO: Add tests that parsing times with fractional seconds overflow - // appropriately. This can't be done until cctz::parse() properly detects - // overflow when combining the chrono seconds and femto. + using D = chrono::duration<std::int64_t, std::nano>; + time_point<D> tp; + + EXPECT_TRUE( + parse(RFC3339_full, "2262-04-11T23:47:16.8547758079+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point<D>::max()); + EXPECT_EQ("2262-04-11T23:47:16.854775807+00:00", + format(RFC3339_full, tp, utc)); +#if 0 + // TODO(#199): Will fail until cctz::parse() properly detects overflow. + EXPECT_FALSE( + parse(RFC3339_full, "2262-04-11T23:47:16.8547758080+00:00", utc, &tp)); + EXPECT_TRUE( + parse(RFC3339_full, "1677-09-21T00:12:43.1452241920+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point<D>::min()); + EXPECT_EQ("1677-09-21T00:12:43.145224192+00:00", + format(RFC3339_full, tp, utc)); + EXPECT_FALSE( + parse(RFC3339_full, "1677-09-21T00:12:43.1452241919+00:00", utc, &tp)); +#endif + + using DS = chrono::duration<std::int8_t, chrono::seconds::period>; + time_point<DS> stp; + + EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T00:02:07.9+00:00", utc, &stp)); + EXPECT_EQ(stp, time_point<DS>::max()); + EXPECT_EQ("1970-01-01T00:02:07+00:00", format(RFC3339_full, stp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T00:02:08+00:00", utc, &stp)); + + EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T23:57:52+00:00", utc, &stp)); + EXPECT_EQ(stp, time_point<DS>::min()); + EXPECT_EQ("1969-12-31T23:57:52+00:00", format(RFC3339_full, stp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T23:57:51.9+00:00", utc, &stp)); + + using DM = chrono::duration<std::int8_t, chrono::minutes::period>; + time_point<DM> mtp; + + EXPECT_TRUE(parse(RFC3339_full, "1970-01-01T02:07:59+00:00", utc, &mtp)); + EXPECT_EQ(mtp, time_point<DM>::max()); + EXPECT_EQ("1970-01-01T02:07:00+00:00", format(RFC3339_full, mtp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1970-01-01T02:08:00+00:00", utc, &mtp)); + + EXPECT_TRUE(parse(RFC3339_full, "1969-12-31T21:52:00+00:00", utc, &mtp)); + EXPECT_EQ(mtp, time_point<DM>::min()); + EXPECT_EQ("1969-12-31T21:52:00+00:00", format(RFC3339_full, mtp, utc)); + EXPECT_FALSE(parse(RFC3339_full, "1969-12-31T21:51:59+00:00", utc, &mtp)); +} + +TEST(Parse, TimePointOverflowFloor) { + const time_zone utc = utc_time_zone(); + + using D = chrono::duration<std::int64_t, std::micro>; + time_point<D> tp; + + EXPECT_TRUE( + parse(RFC3339_full, "294247-01-10T04:00:54.7758079+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point<D>::max()); + EXPECT_EQ("294247-01-10T04:00:54.775807+00:00", + format(RFC3339_full, tp, utc)); +#if 0 + // TODO(#199): Will fail until cctz::parse() properly detects overflow. + EXPECT_FALSE( + parse(RFC3339_full, "294247-01-10T04:00:54.7758080+00:00", utc, &tp)); + EXPECT_TRUE( + parse(RFC3339_full, "-290308-12-21T19:59:05.2241920+00:00", utc, &tp)); + EXPECT_EQ(tp, time_point<D>::min()); + EXPECT_EQ("-290308-12-21T19:59:05.224192+00:00", + format(RFC3339_full, tp, utc)); + EXPECT_FALSE( + parse(RFC3339_full, "-290308-12-21T19:59:05.2241919+00:00", utc, &tp)); +#endif } // diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h index 32c0891c1e..7d3e42d3cd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_if.h @@ -56,7 +56,8 @@ class TimeZoneIf { // Convert between time_point<seconds> and a count of seconds since the // Unix epoch. We assume that the std::chrono::system_clock and the -// Unix clock are second aligned, but not that they share an epoch. +// Unix clock are second aligned, and that the results are representable. +// (That is, that they share an epoch, which is required since C++20.) inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) { return (tp - std::chrono::time_point_cast<seconds>( std::chrono::system_clock::from_time_t(0))) diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc index 030ae0e19e..f34e3aec84 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.cc @@ -15,6 +15,7 @@ #include "time_zone_impl.h" #include <deque> +#include <memory> #include <mutex> #include <string> #include <unordered_map> @@ -48,17 +49,16 @@ std::mutex& TimeZoneMutex() { time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); } bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { - const time_zone::Impl* const utc_impl = UTCImpl(); + const Impl* const utc_impl = UTCImpl(); - // First check for UTC (which is never a key in time_zone_map). + // Check for UTC (which is never a key in time_zone_map). auto offset = seconds::zero(); if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) { *tz = time_zone(utc_impl); return true; } - // Then check, under a shared lock, whether the time zone has already - // been loaded. This is the common path. TODO: Move to shared_mutex. + // Check whether the time zone has already been loaded. { std::lock_guard<std::mutex> lock(TimeZoneMutex()); if (time_zone_map != nullptr) { @@ -70,20 +70,15 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { } } - // Now check again, under an exclusive lock. + // Load the new time zone (outside the lock). + std::unique_ptr<const Impl> new_impl(new Impl(name)); + + // Add the new time zone to the map. std::lock_guard<std::mutex> lock(TimeZoneMutex()); if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; const Impl*& impl = (*time_zone_map)[name]; - if (impl == nullptr) { - // The first thread in loads the new time zone. - Impl* new_impl = new Impl(name); - new_impl->zone_ = TimeZoneIf::Load(new_impl->name_); - if (new_impl->zone_ == nullptr) { - delete new_impl; // free the nascent Impl - impl = utc_impl; // and fallback to UTC - } else { - impl = new_impl; // install new time zone - } + if (impl == nullptr) { // this thread won any load race + impl = new_impl->zone_ ? new_impl.release() : utc_impl; } *tz = time_zone(impl); return impl != utc_impl; @@ -104,14 +99,11 @@ void time_zone::Impl::ClearTimeZoneMapTestOnly() { } } -time_zone::Impl::Impl(const std::string& name) : name_(name) {} +time_zone::Impl::Impl(const std::string& name) + : name_(name), zone_(TimeZoneIf::Load(name_)) {} const time_zone::Impl* time_zone::Impl::UTCImpl() { - static Impl* utc_impl = [] { - Impl* impl = new Impl("UTC"); - impl->zone_ = TimeZoneIf::Load(impl->name_); // never fails - return impl; - }(); + static const Impl* utc_impl = new Impl("UTC"); // never fails return utc_impl; } diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h index 69806c10d9..7d747ba966 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_impl.h @@ -71,7 +71,7 @@ class time_zone::Impl { return zone_->PrevTransition(tp, trans); } - // Returns an implementation-defined version std::string for this time zone. + // Returns an implementation-defined version string for this time zone. std::string Version() const { return zone_->Version(); } // Returns an implementation-defined description of this time zone. diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc index f1697cdf04..4f175d95fc 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.cc @@ -39,11 +39,12 @@ #include <cstdio> #include <cstdlib> #include <cstring> +#include <fstream> #include <functional> -#include <iostream> #include <memory> #include <sstream> #include <string> +#include <utility> #include "absl/base/config.h" #include "absl/time/internal/cctz/include/cctz/civil_time.h" @@ -83,6 +84,27 @@ const std::int_least32_t kSecsPerYear[2] = { 366 * kSecsPerDay, }; +// Convert a cctz::weekday to a POSIX TZ weekday number (0==Sun, ..., 6=Sat). +inline int ToPosixWeekday(weekday wd) { + switch (wd) { + case weekday::sunday: + return 0; + case weekday::monday: + return 1; + case weekday::tuesday: + return 2; + case weekday::wednesday: + return 3; + case weekday::thursday: + return 4; + case weekday::friday: + return 5; + case weekday::saturday: + return 6; + } + return 0; /*NOTREACHED*/ +} + // Single-byte, unsigned numeric values are encoded directly. inline std::uint_fast8_t Decode8(const char* cp) { return static_cast<std::uint_fast8_t>(*cp) & 0xff; @@ -188,15 +210,13 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { tt.is_dst = false; tt.abbr_index = 0; - // We temporarily add some redundant, contemporary (2013 through 2023) + // We temporarily add some redundant, contemporary (2015 through 2025) // transitions for performance reasons. See TimeZoneInfo::LocalTime(). // TODO: Fix the performance issue and remove the extra transitions. transitions_.clear(); transitions_.reserve(12); for (const std::int_fast64_t unix_time : { - -(1LL << 59), // BIG_BANG - 1356998400LL, // 2013-01-01T00:00:00+00:00 - 1388534400LL, // 2014-01-01T00:00:00+00:00 + -(1LL << 59), // a "first half" transition 1420070400LL, // 2015-01-01T00:00:00+00:00 1451606400LL, // 2016-01-01T00:00:00+00:00 1483228800LL, // 2017-01-01T00:00:00+00:00 @@ -206,7 +226,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { 1609459200LL, // 2021-01-01T00:00:00+00:00 1640995200LL, // 2022-01-01T00:00:00+00:00 1672531200LL, // 2023-01-01T00:00:00+00:00 - 2147483647LL, // 2^31 - 1 + 1704067200LL, // 2024-01-01T00:00:00+00:00 + 1735689600LL, // 2025-01-01T00:00:00+00:00 }) { Transition& tr(*transitions_.emplace(transitions_.end())); tr.unix_time = unix_time; @@ -217,8 +238,8 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { default_transition_type_ = 0; abbreviations_ = FixedOffsetToAbbr(offset); - abbreviations_.append(1, '\0'); // add NUL - future_spec_.clear(); // never needed for a fixed-offset zone + abbreviations_.append(1, '\0'); + future_spec_.clear(); // never needed for a fixed-offset zone extended_ = false; tt.civil_max = LocalTime(seconds::max().count(), tt).cs; @@ -259,21 +280,6 @@ std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const { return len; } -// Check that the TransitionType has the expected offset/is_dst/abbreviation. -void TimeZoneInfo::CheckTransition(const std::string& name, - const TransitionType& tt, - std::int_fast32_t offset, bool is_dst, - const std::string& abbr) const { - if (tt.utc_offset != offset || tt.is_dst != is_dst || - &abbreviations_[tt.abbr_index] != abbr) { - std::clog << name << ": Transition" - << " offset=" << tt.utc_offset << "/" - << (tt.is_dst ? "DST" : "STD") - << "/abbr=" << &abbreviations_[tt.abbr_index] - << " does not match POSIX spec '" << future_spec_ << "'\n"; - } -} - // zic(8) can generate no-op transitions when a zone changes rules at an // instant when there is actually no discontinuity. So we check whether // two transitions have equivalent types (same offset/is_dst/abbr). @@ -282,117 +288,108 @@ bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index, if (tt1_index == tt2_index) return true; const TransitionType& tt1(transition_types_[tt1_index]); const TransitionType& tt2(transition_types_[tt2_index]); - if (tt1.is_dst != tt2.is_dst) return false; if (tt1.utc_offset != tt2.utc_offset) return false; + if (tt1.is_dst != tt2.is_dst) return false; if (tt1.abbr_index != tt2.abbr_index) return false; return true; } +// Find/make a transition type with these attributes. +bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, + const std::string& abbr, + std::uint_least8_t* index) { + std::size_t type_index = 0; + std::size_t abbr_index = abbreviations_.size(); + for (; type_index != transition_types_.size(); ++type_index) { + const TransitionType& tt(transition_types_[type_index]); + const char* tt_abbr = &abbreviations_[tt.abbr_index]; + if (tt_abbr == abbr) abbr_index = tt.abbr_index; + if (tt.utc_offset == utc_offset && tt.is_dst == is_dst) { + if (abbr_index == tt.abbr_index) break; // reuse + } + } + if (type_index > 255 || abbr_index > 255) { + // No index space (8 bits) available for a new type or abbreviation. + return false; + } + if (type_index == transition_types_.size()) { + TransitionType& tt(*transition_types_.emplace(transition_types_.end())); + tt.utc_offset = static_cast<std::int_least32_t>(utc_offset); + tt.is_dst = is_dst; + if (abbr_index == abbreviations_.size()) { + abbreviations_.append(abbr); + abbreviations_.append(1, '\0'); + } + tt.abbr_index = static_cast<std::uint_least8_t>(abbr_index); + } + *index = static_cast<std::uint_least8_t>(type_index); + return true; +} + // Use the POSIX-TZ-environment-variable-style string to handle times // in years after the last transition stored in the zoneinfo data. -void TimeZoneInfo::ExtendTransitions(const std::string& name, - const Header& hdr) { +bool TimeZoneInfo::ExtendTransitions() { extended_ = false; - bool extending = !future_spec_.empty(); + if (future_spec_.empty()) return true; // last transition prevails PosixTimeZone posix; - if (extending && !ParsePosixSpec(future_spec_, &posix)) { - std::clog << name << ": Failed to parse '" << future_spec_ << "'\n"; - extending = false; - } - - if (extending && posix.dst_abbr.empty()) { // std only - // The future specification should match the last/default transition, - // and that means that handling the future will fall out naturally. - std::uint_fast8_t index = default_transition_type_; - if (hdr.timecnt != 0) index = transitions_[hdr.timecnt - 1].type_index; - const TransitionType& tt(transition_types_[index]); - CheckTransition(name, tt, posix.std_offset, false, posix.std_abbr); - extending = false; - } - - if (extending && hdr.timecnt < 2) { - std::clog << name << ": Too few transitions for POSIX spec\n"; - extending = false; - } - - if (!extending) { - // Ensure that there is always a transition in the second half of the - // time line (the BIG_BANG transition is in the first half) so that the - // signed difference between a civil_second and the civil_second of its - // previous transition is always representable, without overflow. - const Transition& last(transitions_.back()); - if (last.unix_time < 0) { - const std::uint_fast8_t type_index = last.type_index; - Transition& tr(*transitions_.emplace(transitions_.end())); - tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00 - tr.type_index = type_index; - } - return; // last transition wins + if (!ParsePosixSpec(future_spec_, &posix)) return false; + + // Find transition type for the future std specification. + std::uint_least8_t std_ti; + if (!GetTransitionType(posix.std_offset, false, posix.std_abbr, &std_ti)) + return false; + + if (posix.dst_abbr.empty()) { // std only + // The future specification should match the last transition, and + // that means that handling the future will fall out naturally. + return EquivTransitions(transitions_.back().type_index, std_ti); } + // Find transition type for the future dst specification. + std::uint_least8_t dst_ti; + if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti)) + return false; + // Extend the transitions for an additional 400 years using the // future specification. Years beyond those can be handled by // mapping back to a cycle-equivalent year within that range. - // zic(8) should probably do this so that we don't have to. - // TODO: Reduce the extension by the number of compatible - // transitions already in place. - transitions_.reserve(hdr.timecnt + 400 * 2 + 1); - transitions_.resize(hdr.timecnt + 400 * 2); + // We may need two additional transitions for the current year. + transitions_.reserve(transitions_.size() + 400 * 2 + 2); extended_ = true; - // The future specification should match the last two transitions, - // and those transitions should have different is_dst flags. Note - // that nothing says the UTC offset used by the is_dst transition - // must be greater than that used by the !is_dst transition. (See - // Europe/Dublin, for example.) - const Transition* tr0 = &transitions_[hdr.timecnt - 1]; - const Transition* tr1 = &transitions_[hdr.timecnt - 2]; - const TransitionType* tt0 = &transition_types_[tr0->type_index]; - const TransitionType* tt1 = &transition_types_[tr1->type_index]; - const TransitionType& dst(tt0->is_dst ? *tt0 : *tt1); - const TransitionType& std(tt0->is_dst ? *tt1 : *tt0); - CheckTransition(name, dst, posix.dst_offset, true, posix.dst_abbr); - CheckTransition(name, std, posix.std_offset, false, posix.std_abbr); - - // Add the transitions to tr1 and back to tr0 for each extra year. - last_year_ = LocalTime(tr0->unix_time, *tt0).cs.year(); + const Transition& last(transitions_.back()); + const std::int_fast64_t last_time = last.unix_time; + const TransitionType& last_tt(transition_types_[last.type_index]); + last_year_ = LocalTime(last_time, last_tt).cs.year(); bool leap_year = IsLeap(last_year_); - const civil_day jan1(last_year_, 1, 1); - std::int_fast64_t jan1_time = civil_second(jan1) - civil_second(); - int jan1_weekday = (static_cast<int>(get_weekday(jan1)) + 1) % 7; - Transition* tr = &transitions_[hdr.timecnt]; // next trans to fill - if (LocalTime(tr1->unix_time, *tt1).cs.year() != last_year_) { - // Add a single extra transition to align to a calendar year. - transitions_.resize(transitions_.size() + 1); - assert(tr == &transitions_[hdr.timecnt]); // no reallocation - const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start); - std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1); - tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset; - tr++->type_index = tr1->type_index; - tr0 = &transitions_[hdr.timecnt]; - tr1 = &transitions_[hdr.timecnt - 1]; - tt0 = &transition_types_[tr0->type_index]; - tt1 = &transition_types_[tr1->type_index]; - } - const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start); - const PosixTransition& pt0(tt0->is_dst ? posix.dst_start : posix.dst_end); - for (const year_t limit = last_year_ + 400; last_year_ < limit;) { - last_year_ += 1; // an additional year of generated transitions + const civil_second jan1(last_year_); + std::int_fast64_t jan1_time = jan1 - civil_second(); + int jan1_weekday = ToPosixWeekday(get_weekday(jan1)); + + Transition dst = {0, dst_ti, civil_second(), civil_second()}; + Transition std = {0, std_ti, civil_second(), civil_second()}; + for (const year_t limit = last_year_ + 400;; ++last_year_) { + auto dst_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_start); + auto std_trans_off = TransOffset(leap_year, jan1_weekday, posix.dst_end); + dst.unix_time = jan1_time + dst_trans_off - posix.std_offset; + std.unix_time = jan1_time + std_trans_off - posix.dst_offset; + const auto* ta = dst.unix_time < std.unix_time ? &dst : &std; + const auto* tb = dst.unix_time < std.unix_time ? &std : &dst; + if (last_time < tb->unix_time) { + if (last_time < ta->unix_time) transitions_.push_back(*ta); + transitions_.push_back(*tb); + } + if (last_year_ == limit) break; jan1_time += kSecsPerYear[leap_year]; jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7; - leap_year = !leap_year && IsLeap(last_year_); - std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1); - tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset; - tr++->type_index = tr1->type_index; - std::int_fast64_t tr0_offset = TransOffset(leap_year, jan1_weekday, pt0); - tr->unix_time = jan1_time + tr0_offset - tt1->utc_offset; - tr++->type_index = tr0->type_index; - } - assert(tr == &transitions_[0] + transitions_.size()); + leap_year = !leap_year && IsLeap(last_year_ + 1); + } + + return true; } -bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { +bool TimeZoneInfo::Load(ZoneInfoSource* zip) { // Read and validate the header. tzhead tzh; if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false; @@ -430,7 +427,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { const char* bp = tbuf.data(); // Decode and validate the transitions. - transitions_.reserve(hdr.timecnt + 2); // We might add a couple. + transitions_.reserve(hdr.timecnt + 2); transitions_.resize(hdr.timecnt); for (std::size_t i = 0; i != hdr.timecnt; ++i) { transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp); @@ -449,6 +446,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { } // Decode and validate the transition types. + transition_types_.reserve(hdr.typecnt + 2); transition_types_.resize(hdr.typecnt); for (std::size_t i = 0; i != hdr.typecnt; ++i) { transition_types_[i].utc_offset = @@ -475,6 +473,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { } // Copy all the abbreviations. + abbreviations_.reserve(hdr.charcnt + 10); abbreviations_.assign(bp, hdr.charcnt); bp += hdr.charcnt; @@ -506,7 +505,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { // If we did not find version information during the standard loading // process (as of tzh_version '3' that is unsupported), then ask the - // ZoneInfoSource for any out-of-bound version std::string it may be privy to. + // ZoneInfoSource for any out-of-bound version string it may be privy to. if (version_.empty()) { version_ = zip->Version(); } @@ -525,19 +524,29 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { transitions_.resize(hdr.timecnt); // Ensure that there is always a transition in the first half of the - // time line (the second half is handled in ExtendTransitions()) so that - // the signed difference between a civil_second and the civil_second of - // its previous transition is always representable, without overflow. - // A contemporary zic will usually have already done this for us. + // time line (the second half is handled below) so that the signed + // difference between a civil_second and the civil_second of its + // previous transition is always representable, without overflow. if (transitions_.empty() || transitions_.front().unix_time >= 0) { Transition& tr(*transitions_.emplace(transitions_.begin())); - tr.unix_time = -(1LL << 59); // see tz/zic.c "BIG_BANG" + tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00 tr.type_index = default_transition_type_; - hdr.timecnt += 1; } // Extend the transitions using the future specification. - ExtendTransitions(name, hdr); + if (!ExtendTransitions()) return false; + + // Ensure that there is always a transition in the second half of the + // time line (the first half is handled above) so that the signed + // difference between a civil_second and the civil_second of its + // previous transition is always representable, without overflow. + const Transition& last(transitions_.back()); + if (last.unix_time < 0) { + const std::uint_fast8_t type_index = last.type_index; + Transition& tr(*transitions_.emplace(transitions_.end())); + tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00 + tr.type_index = type_index; + } // Compute the local civil time for each transition and the preceding // second. These will be used for reverse conversions in MakeTime(). @@ -569,14 +578,17 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { namespace { +using FilePtr = std::unique_ptr<FILE, int (*)(FILE*)>; + // fopen(3) adaptor. -inline FILE* FOpen(const char* path, const char* mode) { +inline FilePtr FOpen(const char* path, const char* mode) { #if defined(_MSC_VER) FILE* fp; if (fopen_s(&fp, path, mode) != 0) fp = nullptr; - return fp; + return FilePtr(fp, fclose); #else - return fopen(path, mode); // TODO: Enable the close-on-exec flag. + // TODO: Enable the close-on-exec flag. + return FilePtr(fopen(path, mode), fclose); #endif } @@ -604,11 +616,11 @@ class FileZoneInfoSource : public ZoneInfoSource { protected: explicit FileZoneInfoSource( - FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max()) - : fp_(fp, fclose), len_(len) {} + FilePtr fp, std::size_t len = std::numeric_limits<std::size_t>::max()) + : fp_(std::move(fp)), len_(len) {} private: - std::unique_ptr<FILE, int (*)(FILE*)> fp_; + FilePtr fp_; std::size_t len_; }; @@ -637,17 +649,9 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( path.append(name, pos, std::string::npos); // Open the zoneinfo file. - FILE* fp = FOpen(path.c_str(), "rb"); + auto fp = FOpen(path.c_str(), "rb"); if (fp == nullptr) return nullptr; - std::size_t length = 0; - if (fseek(fp, 0, SEEK_END) == 0) { - long offset = ftell(fp); - if (offset >= 0) { - length = static_cast<std::size_t>(offset); - } - rewind(fp); - } - return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length)); + return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(std::move(fp))); } class AndroidZoneInfoSource : public FileZoneInfoSource { @@ -656,8 +660,9 @@ class AndroidZoneInfoSource : public FileZoneInfoSource { std::string Version() const override { return version_; } private: - explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers) - : FileZoneInfoSource(fp, len), version_(vers) {} + explicit AndroidZoneInfoSource(FilePtr fp, std::size_t len, + std::string version) + : FileZoneInfoSource(std::move(fp), len), version_(std::move(version)) {} std::string version_; }; @@ -669,8 +674,8 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( // See Android's libc/tzcode/bionic.cpp for additional information. for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", "/system/usr/share/zoneinfo/tzdata"}) { - std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose); - if (fp.get() == nullptr) continue; + auto fp = FOpen(tzdata, "rb"); + if (fp == nullptr) continue; char hbuf[24]; // covers header.zonetab_offset too if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; @@ -696,7 +701,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( if (strcmp(name.c_str() + pos, ebuf) == 0) { if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break; return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource( - fp.release(), static_cast<std::size_t>(length), vers)); + std::move(fp), static_cast<std::size_t>(length), vers)); } } } @@ -704,6 +709,69 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( return nullptr; } +// A zoneinfo source for use inside Fuchsia components. This attempts to +// read zoneinfo files from one of several known paths in a component's +// incoming namespace. [Config data][1] is preferred, but package-specific +// resources are also supported. +// +// Fuchsia's implementation supports `FileZoneInfoSource::Version()`. +// +// [1]: +// https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component +class FuchsiaZoneInfoSource : public FileZoneInfoSource { + public: + static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); + std::string Version() const override { return version_; } + + private: + explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version) + : FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {} + std::string version_; +}; + +std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open( + const std::string& name) { + // Use of the "file:" prefix is intended for testing purposes only. + const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; + + // Prefixes where a Fuchsia component might find zoneinfo files, + // in descending order of preference. + const auto kTzdataPrefixes = { + "/config/data/tzdata/", + "/pkg/data/tzdata/", + "/data/tzdata/", + }; + const auto kEmptyPrefix = {""}; + const bool name_absolute = (pos != name.size() && name[pos] == '/'); + const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes; + + // Fuchsia builds place zoneinfo files at "<prefix><format><name>". + for (const std::string prefix : prefixes) { + std::string path = prefix; + if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format + path.append(name, pos, std::string::npos); + + auto fp = FOpen(path.c_str(), "rb"); + if (fp == nullptr) continue; + + std::string version; + if (!prefix.empty()) { + // Fuchsia builds place the version in "<prefix>revision.txt". + std::ifstream version_stream(prefix + "revision.txt"); + if (version_stream.is_open()) { + // revision.txt should contain no newlines, but to be + // defensive we read just the first line. + std::getline(version_stream, version); + } + } + + return std::unique_ptr<ZoneInfoSource>( + new FuchsiaZoneInfoSource(std::move(fp), std::move(version))); + } + + return nullptr; +} + } // namespace bool TimeZoneInfo::Load(const std::string& name) { @@ -718,12 +786,13 @@ bool TimeZoneInfo::Load(const std::string& name) { // Find and use a ZoneInfoSource to load the named zone. auto zip = cctz_extension::zone_info_source_factory( - name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> { - if (auto zip = FileZoneInfoSource::Open(name)) return zip; - if (auto zip = AndroidZoneInfoSource::Open(name)) return zip; + name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> { + if (auto z = FileZoneInfoSource::Open(n)) return z; + if (auto z = AndroidZoneInfoSource::Open(n)) return z; + if (auto z = FuchsiaZoneInfoSource::Open(n)) return z; return nullptr; }); - return zip != nullptr && Load(name, zip.get()); + return zip != nullptr && Load(zip.get()); } // BreakTime() translation for a particular transition type. @@ -897,8 +966,8 @@ bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp, const Transition* begin = &transitions_[0]; const Transition* end = begin + transitions_.size(); if (begin->unix_time <= -(1LL << 59)) { - // Do not report the BIG_BANG found in recent zoneinfo data as it is - // really a sentinel, not a transition. See tz/zic.c. + // Do not report the BIG_BANG found in some zoneinfo data as it is + // really a sentinel, not a transition. See pre-2018f tz/zic.c. ++begin; } std::int_fast64_t unix_time = ToUnixSeconds(tp); @@ -923,8 +992,8 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp, const Transition* begin = &transitions_[0]; const Transition* end = begin + transitions_.size(); if (begin->unix_time <= -(1LL << 59)) { - // Do not report the BIG_BANG found in recent zoneinfo data as it is - // really a sentinel, not a transition. See tz/zic.c. + // Do not report the BIG_BANG found in some zoneinfo data as it is + // really a sentinel, not a transition. See pre-2018f tz/zic.c. ++begin; } std::int_fast64_t unix_time = ToUnixSeconds(tp); diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h index 2a10c06c77..2467ff559d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_info.h @@ -95,15 +95,14 @@ class TimeZoneInfo : public TimeZoneIf { std::size_t DataLength(std::size_t time_len) const; }; - void CheckTransition(const std::string& name, const TransitionType& tt, - std::int_fast32_t offset, bool is_dst, - const std::string& abbr) const; + bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, + const std::string& abbr, std::uint_least8_t* index); bool EquivTransitions(std::uint_fast8_t tt1_index, std::uint_fast8_t tt2_index) const; - void ExtendTransitions(const std::string& name, const Header& hdr); + bool ExtendTransitions(); bool ResetToBuiltinUTC(const seconds& offset); - bool Load(const std::string& name, ZoneInfoSource* zip); + bool Load(ZoneInfoSource* zip); // Helpers for BreakTime() and MakeTime(). time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc index 47cf84c663..887dd097c6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_libc.cc @@ -27,6 +27,12 @@ #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" +#if defined(_AIX) +extern "C" { +extern long altzone; +} +#endif + namespace absl { ABSL_NAMESPACE_BEGIN namespace time_internal { @@ -44,7 +50,7 @@ auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return _tzname[is_dst]; } -#elif defined(__sun) +#elif defined(__sun) || defined(_AIX) // Uses the globals: 'timezone', 'altzone' and 'tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) { const bool is_dst = tm.tm_isdst > 0; @@ -153,7 +159,8 @@ 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)) { + std::tm* tmp = local_time(&mid, &tm); + if (tmp != nullptr) { if (tm_gmtoff(*tmp) == offset) { hi = mid; } else { @@ -163,7 +170,8 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { // 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)) { + tmp = local_time(&lo, &tm); + if (tmp != nullptr) { if (tm_gmtoff(*tmp) == offset) break; } } @@ -223,11 +231,10 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { 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()); + 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}; } diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc index efdea64b4e..898d04c125 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -28,6 +28,13 @@ #include <vector> #endif +#if defined(__Fuchsia__) +#include <fuchsia/intl/cpp/fidl.h> +#include <lib/async-loop/cpp/loop.h> +#include <lib/sys/cpp/component_context.h> +#include <zircon/types.h> +#endif + #include <cstdlib> #include <cstring> #include <string> @@ -140,6 +147,48 @@ time_zone local_time_zone() { } CFRelease(tz_default); #endif +#if defined(__Fuchsia__) + std::string primary_tz; + [&]() { + // Note: We can't use the synchronous FIDL API here because it doesn't + // allow timeouts; if the FIDL call failed, local_time_zone() would never + // return. + + const zx::duration kTimeout = zx::msec(500); + + // Don't attach to the thread because otherwise the thread's dispatcher + // would be set to null when the loop is destroyed, causing any other FIDL + // code running on the same thread to crash. + async::Loop loop(&kAsyncLoopConfigNeverAttachToThread); + std::unique_ptr<sys::ComponentContext> context = + sys::ComponentContext::Create(); + + fuchsia::intl::PropertyProviderHandle handle; + zx_status_t status = context->svc()->Connect(handle.NewRequest()); + if (status != ZX_OK) { + return; + } + + fuchsia::intl::PropertyProviderPtr intl_provider; + status = intl_provider.Bind(std::move(handle), loop.dispatcher()); + if (status != ZX_OK) { + return; + } + + intl_provider->GetProfile( + [&loop, &primary_tz](fuchsia::intl::Profile profile) { + if (!profile.time_zones().empty()) { + primary_tz = profile.time_zones()[0].id; + } + loop.Quit(); + }); + loop.Run(zx::deadline_after(kTimeout)); + }(); + + if (!primary_tz.empty()) { + zone = primary_tz.c_str(); + } +#endif // Allow ${TZ} to override to default zone. char* tz_env = nullptr; diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 99137a082a..0226ab71ab 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -211,6 +211,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan", "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", + "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Pangnirtung", @@ -578,6 +579,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Johnston", + "Pacific/Kanton", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", @@ -716,6 +718,18 @@ TEST(TimeZones, LoadZonesConcurrently) { } #endif +TEST(TimeZone, UTC) { + const time_zone utc = utc_time_zone(); + + time_zone loaded_utc; + EXPECT_TRUE(load_time_zone("UTC", &loaded_utc)); + EXPECT_EQ(loaded_utc, utc); + + time_zone loaded_utc0; + EXPECT_TRUE(load_time_zone("UTC0", &loaded_utc0)); + EXPECT_EQ(loaded_utc0, utc); +} + TEST(TimeZone, NamedTimeZones) { const time_zone utc = utc_time_zone(); EXPECT_EQ("UTC", utc.name()); @@ -749,7 +763,7 @@ TEST(TimeZone, Failures) { EXPECT_EQ(chrono::system_clock::from_time_t(0), convert(civil_second(1970, 1, 1, 0, 0, 0), tz)); // UTC - // Loading an empty std::string timezone should fail. + // Loading an empty string timezone should fail. tz = LoadZone("America/Los_Angeles"); EXPECT_FALSE(load_time_zone("", &tz)); EXPECT_EQ(chrono::system_clock::from_time_t(0), @@ -932,7 +946,7 @@ TEST(MakeTime, Normalization) { // NOTE: Run this with -ftrapv to detect overflow problems. TEST(MakeTime, SysSecondsLimits) { - const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez"; + const char RFC3339[] = "%Y-%m-%d%ET%H:%M:%S%Ez"; const time_zone utc = utc_time_zone(); const time_zone east = fixed_time_zone(chrono::hours(14)); const time_zone west = fixed_time_zone(-chrono::hours(14)); @@ -1003,13 +1017,21 @@ TEST(MakeTime, SysSecondsLimits) { #if defined(_WIN32) || defined(_WIN64) // localtime_s() and gmtime_s() don't believe in years outside [1970:3000]. #else - const time_zone utc = LoadZone("libc:UTC"); + const time_zone cut = 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)); + tp = convert(civil_second(max_tm_year, 12, 31, 23, 59, 59), cut); +#if defined(__FreeBSD__) || defined(__OpenBSD__) + // The BSD gmtime_r() fails on extreme positive tm_year values. +#else + EXPECT_EQ("2147485547-12-31T23:59:59+00:00", format(RFC3339, tp, cut)); +#endif 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)); + tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut); +#if defined(__Fuchsia__) + // Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527). +#else + EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut)); +#endif #endif } } diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h b/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h index 1ed55e0f95..31e8598257 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/tzfile.h @@ -43,7 +43,7 @@ struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ + char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */ char tzh_reserved[15]; /* reserved; must be zero */ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ @@ -83,13 +83,13 @@ struct tzhead { ** If tzh_version is '2' or greater, the above is followed by a second instance ** of tzhead and a second instance of the data in which each coded transition ** time uses 8 rather than 4 chars, -** then a POSIX-TZ-environment-variable-style std::string for use in handling +** then a POSIX-TZ-environment-variable-style string for use in handling ** instants after the last transition time stored in the file ** (with nothing between the newlines if there is no POSIX representation for ** such instants). ** ** If tz_version is '3' or greater, the above is extended as follows. -** First, the POSIX TZ std::string's hour offset may range from -167 +** First, the POSIX TZ string's hour offset may range from -167 ** through 167 as compared to the POSIX-required 0 through 24. ** Second, its DST start time may be January 1 at 00:00 and its stop ** time December 31 at 24:00 plus the difference between DST and diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc b/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc index 98ea161267..5ab5a59ecf 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc +++ b/third_party/abseil-cpp/absl/time/internal/cctz/src/zone_info_source.cc @@ -65,7 +65,7 @@ ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) = extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; -#if defined(_M_IX86) +#if defined(_M_IX86) || defined(_M_ARM) #pragma comment( \ linker, \ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \ diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/version b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/version index db18f8311d..8ee898ba7d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/version +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2019c +2021e diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra Binary files differindex 697b9933eb..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers Binary files differindex ae04342313..56a4dd2a19 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau Binary files differindex 82ea5aaf0c..0da1d1e211 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo Binary files differindex d3f819623f..ea38c97008 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca Binary files differindex 245f4ebe18..0263c90bd0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta Binary files differindex 850c8f06fa..a461dceaa2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun Binary files differindex a91f65f242..772e23c4cd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg Binary files differindex b1c425dace..bada0638f8 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba Binary files differindex 625b1acccf..0aba9ffd89 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum Binary files differindex 8ee8cb92e7..3f8e44b8a6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo Binary files differindex 52753c0f87..651e5cf67a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru Binary files differindex b1c425dace..bada0638f8 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane Binary files differindex b1c425dace..bada0638f8 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia Binary files differindex 6d688502a1..837780922f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena Binary files differindex a968845e29..ecbc0966dc 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo Binary files differindex 0c80137c74..3d7a71ba0e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome Binary files differindex 59f3759c40..425ad3fda7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli Binary files differindex 07b393bb7d..e0c89971aa 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis Binary files differindex 427fa56303..ca324cb4cd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek Binary files differindex abecd137b1..0edc52b9b7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak Binary files differindex 43236498f6..b1497bda63 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage Binary files differindex 9bbb2fd3b3..cdf0572be3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina Binary files differindex 49381b4108..f66c9f79d6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires Binary files differindex 260f86a918..d6f999b860 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca Binary files differindex 0ae222a2f8..1dcc8d8543 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia Binary files differindex 0ae222a2f8..1dcc8d8543 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba Binary files differindex da4c23a545..35a52e53d1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy Binary files differindex 604b856636..b275f27c02 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja Binary files differindex 2218e36bfd..23fca12205 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza Binary files differindex f9e677f171..691c56978a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos Binary files differindex c36587e1c2..991d1fae69 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta Binary files differindex 0e797f2215..58863e0436 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan Binary files differindex 2698495bb3..7eba33c1c5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis Binary files differindex fe50f6211c..0a81cbddfa 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman Binary files differindex c954000ba9..10556d5d85 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia Binary files differindex 3643628a24..e031750276 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba Binary files differindex f7ab6efcc0..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion Binary files differindex 2f3bbda6d3..6225036742 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan Binary files differindex 629ed42319..9154643f4c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atikokan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atka b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atka Binary files differindex 43236498f6..b1497bda63 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atka +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Atka diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia Binary files differindex 15808d30fb..7969e30766 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas Binary files differindex 896af3f56a..cbe22a7622 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados Binary files differindex 9b90e306a6..720c9863f2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belem b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belem Binary files differindex 60b5924dc1..e0d7653c64 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belem +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belem diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belize b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belize Binary files differindex 851051ae94..bfc19f4e58 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belize +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Belize diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon Binary files differindex f9f13a1679..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Blanc-Sablon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista Binary files differindex 978c33100f..fca97207b2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota Binary files differindex b2647d7a83..6cb53d4e61 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boise b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boise Binary files differindex f8d54e2747..72fec9e8c5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boise +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Boise diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires Binary files differindex 260f86a918..d6f999b860 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay Binary files differindex f8db4b6ebf..0a2225244a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cambridge_Bay diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande Binary files differindex 81206247d9..6855e4e9fe 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun Binary files differindex f907f0a5ba..640b259fd0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas Binary files differindex eedf725e8d..8dbe6ff741 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca Binary files differindex 0ae222a2f8..1dcc8d8543 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne Binary files differindex e5bc06fdbe..cd49f05344 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman Binary files differindex 9964b9a334..9154643f4c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago Binary files differindex a5b1617c7f..b016880653 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chicago diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua Binary files differindex 8ed5f93b02..e1780a5750 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour Binary files differindex 629ed42319..9154643f4c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coral_Harbour diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba Binary files differindex da4c23a545..35a52e53d1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica Binary files differindex 37cb85e4db..08f0128ee6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Creston b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Creston Binary files differindex ca648573e4..c2bd2f949b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Creston +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Creston diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba Binary files differindex 9bea3d4079..c09a87558d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao Binary files differindex f7ab6efcc0..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn Binary files differindex 9549adcb65..8718efcce2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Danmarkshavn diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson Binary files differindex db9ceadd96..07e4c5f4ac 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek Binary files differindex db9e339655..761d1d9af5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson_Creek diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denver b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denver Binary files differindex 5fbe26b1d9..09e54e5c7c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denver +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Denver diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit Binary files differindex e104faa465..6eb3ac46ec 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton Binary files differindex cd78a6f8be..645ee94530 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe Binary files differindex 39d6daeb9b..7da4b98fe3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador Binary files differindex e2f22304aa..43484117e2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada Binary files differindex ada6bf78b2..19ccd3576d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson Binary files differindex 5a0b7f1ca0..2a49c6c50f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Nelson diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne Binary files differindex 09511ccdcf..6b08d15bda 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fort_Wayne diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza Binary files differindex be57dc20b4..092e40d701 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay Binary files differindex 48412a4cbf..f85eb34157 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Glace_Bay diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab Binary files differindex 0160308bf6..4ddc99d8b7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay Binary files differindex a3f299079a..820e0dd2cd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk Binary files differindex b9bb063b62..9d90e745b0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala Binary files differindex 407138caf9..8aa8e588e3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil Binary files differindex 0559a7a4a4..381ae6c463 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana Binary files differindex d5dab14969..bcc66881c1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax Binary files differindex 756099abe6..9fa850a7d4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Halifax diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havana b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havana Binary files differindex b69ac45107..e06629d368 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havana +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Havana diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo Binary files differindex 791a9fa2b3..8283239eca 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis Binary files differindex 09511ccdcf..6b08d15bda 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Indianapolis diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox Binary files differindex fcd408d74d..b187d5f8c7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Knox diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo Binary files differindex 1abf75e7e8..a730fe666b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Marengo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg Binary files differindex 0133548eca..341a0235ef 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Petersburg diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City Binary files differindex 7bbb653cd7..76e1f6285b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay Binary files differindex d236b7c077..f2acf6cbbd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vevay diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes Binary files differindex c818929d19..c255f89b6d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Vincennes diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac Binary files differindex 630935c1e1..8700ed9f06 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis Binary files differindex 09511ccdcf..6b08d15bda 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Indianapolis diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik Binary files differindex 87bb355295..af3107db51 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit Binary files differindex c8138bdbb3..eb2c99cca5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Iqaluit diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica Binary files differindex 2a9b7fd52d..be6b1b6f1e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy Binary files differindex 604b856636..b275f27c02 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau Binary files differindex 451f349009..e347b369f7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Juneau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville Binary files differindex 177836e4fd..f2136d6ed4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello Binary files differindex 438e3eab4a..d9f54a18bb 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Monticello diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN Binary files differindex fcd408d74d..b187d5f8c7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Knox_IN diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk Binary files differindex f7ab6efcc0..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz Binary files differindex a101372435..68ddaae768 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lima b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lima Binary files differindex 3c6529b756..b643c5517f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lima +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lima diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles Binary files differindex 9dad4f4c75..aaf07787ad 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Los_Angeles diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville Binary files differindex 177836e4fd..f2136d6ed4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes Binary files differindex f7ab6efcc0..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio Binary files differindex bc8b951d2e..dbb8d57d91 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managua b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managua Binary files differindex e0242bff6e..86ef76bf22 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managua +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Managua diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus Binary files differindex 63d58f80f5..59c952ebc6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique Binary files differindex 8df43dcf1c..25c0232d95 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros Binary files differindex 047968dfff..722751b20e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan Binary files differindex e4a785743d..4c819fab02 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza Binary files differindex f9e677f171..691c56978a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee Binary files differindex 314613866d..28d2c56e1a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Menominee diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Merida b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Merida Binary files differindex ea852da33a..d3b0ca12c9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Merida +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Merida diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla Binary files differindex 1e94be3d55..9fefee388e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City Binary files differindex e7fb6f2953..ffcf8bee10 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon Binary files differindex b924b71004..3b62585d0a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton Binary files differindex 9df8d0f2ec..ecb69ef2c9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey Binary files differindex a8928c8dc9..dea9e3f586 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo Binary files differindex 2f357bcf50..4b2fb3e560 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal Binary files differindex 6752c5b052..fe6be8ea8c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montreal diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau Binary files differindex 33cc6c6265..fe6be8ea8c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_York b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_York Binary files differindex 2f75480e06..2b6c2eea14 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_York +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/New_York diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon Binary files differindex f6a856e693..b9f67a9f94 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nipigon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nome b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nome Binary files differindex 10998df3bb..23ead1c004 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nome +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nome diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha Binary files differindex f140726f2a..9e74745ca7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah Binary files differindex 246345dde7..becf438330 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Beulah diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center Binary files differindex 1fa0703778..d03bda045d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/Center diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem Binary files differindex 123f2aeecf..ecefc15d8c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/North_Dakota/New_Salem diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk Binary files differnew file mode 100644 index 0000000000..4ddc99d8b7 --- /dev/null +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga Binary files differindex fc4a03e369..da0909cb21 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panama b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panama Binary files differindex 9964b9a334..9154643f4c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panama +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Panama diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung Binary files differindex 3e4e0db6ae..5be6f9b016 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Pangnirtung diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo Binary files differindex bc8a6edf13..24f925a2dd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix Binary files differindex ac6bb0c782..c2bd2f949b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince Binary files differindex 287f143926..3e75731baa 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre Binary files differindex a374cb43d9..fb5185ca60 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho Binary files differindex 2e873a5aa8..7f8047d939 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico Binary files differindex a662a57137..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Puerto_Rico diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas Binary files differindex a5a8af52c2..5c9a20b947 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Punta_Arenas diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River Binary files differindex ea66099155..d6ddda4822 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rainy_River diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet Binary files differindex 3a70587472..92e2ed2dbe 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recife b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recife Binary files differindex d7abb168a7..305abcb8a2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recife +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Recife diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Regina b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Regina Binary files differindex 20c9c84df4..a3f8217a54 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Regina +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Regina diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute Binary files differindex 0a73b753ba..a84d1dfdb3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco Binary files differindex a374cb43d9..fb5185ca60 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario Binary files differindex da4c23a545..35a52e53d1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel Binary files differindex ada6bf78b2..19ccd3576d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem Binary files differindex c28f36063b..f81d144206 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago Binary files differindex 816a042818..8d6032264b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santiago diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo Binary files differindex 4fe36fd4c1..3e07850866 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo Binary files differindex 13ff083869..a16da2c4d5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund Binary files differindex e20e9e1c42..6db49124e2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock Binary files differindex 5fbe26b1d9..09e54e5c7c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Shiprock diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka Binary files differindex 31f7061371..36681ed78e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Sitka diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns Binary files differindex 65a5b0c720..e5f2aec2bb 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current Binary files differindex 8e9ef255ee..bdbb494487 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Swift_Current diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa Binary files differindex 2adacb2e50..38036a3283 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thule b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thule Binary files differindex 6f802f1c2a..f38dc56bf2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thule +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thule diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay Binary files differindex e504c9acf1..fcb0328043 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Thunder_Bay diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana Binary files differindex ada6bf78b2..19ccd3576d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto Binary files differindex 6752c5b052..fe6be8ea8c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Toronto diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver Binary files differindex bb60cbced3..c998491112 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin Binary files differindex 697cf5bcf7..47b4dc3416 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse Binary files differindex fb3cd71a69..878b6a92f7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg Binary files differindex ac40299f6b..7e646d18e1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat Binary files differindex da209f9f0a..773feba89d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yakutat diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife Binary files differindex e6afa390e8..c779cef92c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Yellowknife diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey Binary files differindex f100f47461..30315cc078 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis Binary files differindex 916f2c2592..3ec32224f2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Davis diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville Binary files differindex a71b39c004..5d8fc3a1b2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie Binary files differindex 616afd9c83..3fc1f231cb 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson Binary files differindex b32e7fd6c6..05e4c6c586 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo Binary files differindex 6575fdce31..afb3929318 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer Binary files differindex 3dd85f84ff..32c1941634 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Palmer diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera Binary files differindex 8b2430a20e..ea49c00b22 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole Binary files differindex 6575fdce31..afb3929318 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa Binary files differindex 254af7d12f..01c47ccb86 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll Binary files differindex 5e565da2f6..4e31affb50 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok Binary files differindex 728305305d..6e3290717e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen Binary files differindex 15a34c3ced..dfc509570a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden Binary files differindex 2aea25f8c2..01c47ccb86 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty Binary files differindex a4b0077900..3ec4fc89fe 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman Binary files differindex c9e8707912..d97d308d7a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr Binary files differindex 6ed8b7cb07..551884d322 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau Binary files differindex e2d0f91954..3a40d1175a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe Binary files differindex 06f0a13a66..62c5840a83 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat Binary files differindex 73891af1ee..8482167269 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad Binary files differindex 73891af1ee..8482167269 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau Binary files differindex 8b5153e054..cb2c82f657 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad Binary files differindex f7162edf93..a3ce975991 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain Binary files differindex 63188b269d..7409d74983 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku Binary files differindex a0de74b958..96203d7a42 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok Binary files differindex c292ac5b5f..ed687d2985 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul Binary files differindex 759592a255..ff976dd3b2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut Binary files differindex fb266ede22..55dce5722c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek Binary files differindex f6e20dd3a8..fe7832cdf9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei Binary files differindex 3dab0abf4e..e67b411b9f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta Binary files differindex 0014046d29..00bc80a65e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita Binary files differindex c4149c05ce..9d49cd35cd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan Binary files differindex e48daa8243..0a948c2eac 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing Binary files differindex 3c0bef2061..d6b66984a2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking Binary files differindex 3c0bef2061..d6b66984a2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo Binary files differindex 62c64d85df..3eeb1b72b6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca Binary files differindex b11c928410..28136808b6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus Binary files differindex d9104a7ab8..168ef9baa4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka Binary files differindex b11c928410..28136808b6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili Binary files differindex 30943bbd0a..bb7be9f3a4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai Binary files differindex fc0a589e2b..58d75bc26e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe Binary files differindex 82d85b8c1b..d83fb076a2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta Binary files differindex 653b146a60..cc44179564 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Famagusta diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza Binary files differindex 592b632606..ccc57c9ce1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin Binary files differindex 3c0bef2061..d6b66984a2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron Binary files differindex ae82f9b546..906d8d5cba 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh Binary files differindex e2934e371b..7ca9972502 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong Binary files differindex 23d0375fba..c80e364801 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd Binary files differindex 4cb800a918..6e08a26127 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk Binary files differindex 4dcbbb7ea2..550e2a0877 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul Binary files differindex 508446bb6a..c891866873 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta Binary files differindex 5baa3a8f2e..c9752d2f23 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura Binary files differindex 3002c82022..7c22f539d9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem Binary files differindex 440ef06b5d..4c49bbf524 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul Binary files differindex d19b9bd51d..660ce4cf69 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka Binary files differindex 3e80b4e09f..c65155402d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi Binary files differindex ba65c0e8d3..e56d5afdaf 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar Binary files differindex faa14d92d5..69ff7f6fb4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu Binary files differindex a5d510753f..3a0d330ffd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu Binary files differindex a5d510753f..3a0d330ffd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga Binary files differindex 72bea64ba8..aeb733202a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata Binary files differindex 0014046d29..00bc80a65e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk Binary files differindex 30c6f16505..e0d4fcb5c3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur Binary files differindex 612b01e71c..e93dd5141b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching Binary files differindex c86750cb7d..59bc6e40b7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait Binary files differindex 2aea25f8c2..01c47ccb86 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao Binary files differindex cac65063d0..c22f75e42d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau Binary files differindex cac65063d0..c22f75e42d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan Binary files differindex b4fcac18e3..16bac84446 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar Binary files differindex 556ba86693..5990010b64 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila Binary files differindex f4f4b04efa..3c3584e09a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat Binary files differindex fc0a589e2b..58d75bc26e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia Binary files differindex f7f10ab766..c210d0a598 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk Binary files differindex d983276119..9378d50539 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk Binary files differindex e0ee5fcea9..65a9fa2cd2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk Binary files differindex b29b769311..dc0ed422f6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral Binary files differindex ad1f9ca1ca..25a63ec8b9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh Binary files differindex c292ac5b5f..ed687d2985 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak Binary files differindex 12ce24cbea..285bed2c63 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang Binary files differindex 7ad7e0b2cf..57240cf89f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar Binary files differindex 63188b269d..7409d74983 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay Binary files differindex 73b9d963ef..ff6fe61d50 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda Binary files differindex c2fe4c144c..fe4d6c6d6d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon Binary files differindex dd77395b05..14b2ad09ea 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh Binary files differindex 2aea25f8c2..01c47ccb86 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon Binary files differindex e2934e371b..7ca9972502 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin Binary files differindex 485459ce03..69f0faad1e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand Binary files differindex 030d47ce07..c43e27c5d4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul Binary files differindex 96199e73e7..1755147fab 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai Binary files differindex 3c0bef2061..d6b66984a2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore Binary files differindex 2364b2178b..350d77e28e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk Binary files differindex 261a9832b3..7fdee5cbee 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei Binary files differindex 24c43444b6..35d89d036d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent Binary files differindex 32a9d7d0c9..65ee428ce1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi Binary files differindex b608d79748..166e4341d6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran Binary files differindex 8cec5ad7de..f1555f0032 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv Binary files differindex 440ef06b5d..4c49bbf524 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu Binary files differindex fe409c7a2a..0edc72cfe4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu Binary files differindex fe409c7a2a..0edc72cfe4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo Binary files differindex 26f4d34d67..1aa066ce38 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tokyo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk Binary files differindex 670e2ad2ce..c3c307d7b9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang Binary files differindex 556ba86693..5990010b64 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar Binary files differindex 2e20cc3a43..6f5d3a15ab 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator Binary files differindex 2e20cc3a43..6f5d3a15ab 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi Binary files differindex faa14d92d5..69ff7f6fb4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera Binary files differindex 9e4a78f6a5..c39331e3aa 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane Binary files differindex c292ac5b5f..ed687d2985 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok Binary files differindex 8ab253ce73..72a3d4e87a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk Binary files differindex c815e99b1a..336f932e8d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon Binary files differindex dd77395b05..14b2ad09ea 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg Binary files differindex 6958d7eddd..a3bf7f29b6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan Binary files differindex 250bfe020a..6dd927cb94 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores Binary files differindex 56593dbfff..e6e2616e98 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Azores diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda Binary files differindex 419c660ba7..abc75ea7ef 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary Binary files differindex f3192156ff..5ab3243a5f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Canary diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde Binary files differindex e2a49d248d..8f7de1c0a1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Cape_Verde diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe Binary files differindex 4dab7ef085..9558bf7180 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faeroe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe Binary files differindex 4dab7ef085..9558bf7180 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Faroe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen Binary files differindex 15a34c3ced..dfc509570a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira Binary files differindex 5213761f89..cf965c3f92 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Madeira diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik Binary files differindex 10e0fc8190..2451aca76d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia Binary files differindex 4466608612..7fa5f46835 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena Binary files differindex 28b32ab2e0..8906e88c81 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley Binary files differindex 88077f1107..1a4c8ea863 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT Binary files differindex 7636592aa7..1975a3a4bd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide Binary files differindex 0b1252abb7..3bfbbc563c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane Binary files differindex 3021bdb614..dc9a980a65 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill Binary files differindex 1ac3fc8f52..947b50995f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra Binary files differindex 7636592aa7..1975a3a4bd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie Binary files differindex f65a990efe..dc2ef554dc 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin Binary files differindex 1cf502984a..a6a67300dd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla Binary files differindex 98ae557064..9080f5cdb1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart Binary files differindex 02b07ca232..dc2ef554dc 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI Binary files differindex 9e04a80ece..4d4ec8ceea 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman Binary files differindex eab0fb998a..131d77b54a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe Binary files differindex 9e04a80ece..4d4ec8ceea 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne Binary files differindex ba457338ba..d3f195ac2f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW Binary files differindex 7636592aa7..1975a3a4bd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/North b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/North Binary files differindex 1cf502984a..a6a67300dd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/North +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/North diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth Binary files differindex a876b9e785..4f771828c9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland Binary files differindex 3021bdb614..dc9a980a65 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/South b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/South Binary files differindex 0b1252abb7..3bfbbc563c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/South +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/South diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney Binary files differindex 7636592aa7..1975a3a4bd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania Binary files differindex 02b07ca232..dc2ef554dc 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria Binary files differindex ba457338ba..d3f195ac2f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/West b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/West Binary files differindex a876b9e785..4f771828c9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/West +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/West diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna Binary files differindex 1ac3fc8f52..947b50995f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre Binary files differindex a374cb43d9..fb5185ca60 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha Binary files differindex f140726f2a..9e74745ca7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East Binary files differindex 13ff083869..a16da2c4d5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West Binary files differindex 63d58f80f5..59c952ebc6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CET b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CET Binary files differindex 122e934210..546748d6ea 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CET +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CET diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT Binary files differindex ca67929fbe..d931558058 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic Binary files differindex 756099abe6..9fa850a7d4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Atlantic diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central Binary files differindex ac40299f6b..7e646d18e1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern Binary files differindex 6752c5b052..fe6be8ea8c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Eastern diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain Binary files differindex cd78a6f8be..645ee94530 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland Binary files differindex 65a5b0c720..e5f2aec2bb 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific Binary files differindex bb60cbced3..c998491112 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan Binary files differindex 20c9c84df4..a3f8217a54 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Saskatchewan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon Binary files differindex fb3cd71a69..878b6a92f7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental Binary files differindex 816a042818..8d6032264b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/Continental diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland Binary files differindex cae3744096..d29bcd68b0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Chile/EasterIsland diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Cuba b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Cuba Binary files differindex b69ac45107..e06629d368 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Cuba +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Cuba diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EET b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EET Binary files differindex cbdb71ddd3..378919ea11 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EET +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EET diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST Binary files differindex 21ebc00b3f..3ae9691145 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT Binary files differindex 9bce5007d4..50c95e0cb0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Egypt b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Egypt Binary files differindex d3f819623f..ea38c97008 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Egypt +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Egypt diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Eire b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Eire Binary files differindex 1d994902db..4a45ea8f73 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Eire +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Eire diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 Binary files differindex 4dab6f9005..98d5dcf917 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 Binary files differindex c749290af2..ecb287e667 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 Binary files differindex d969982309..e941412971 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 Binary files differindex cdeec90973..9c95bd0736 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 Binary files differindex fbd2a941fd..6d5ce3db73 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 Binary files differindex ee246ef56f..5ef7be71fd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 Binary files differindex 5a25ff2a6a..75f16216f0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 Binary files differindex c0b745f1cc..589990ae89 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 Binary files differindex 06e777d57e..fcb60ca246 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 Binary files differindex 4e0b53a082..c0427a40ee 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 Binary files differindex 714b0c5628..9bdc2283c0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 Binary files differindex 78b9daa373..ca7a81f656 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 Binary files differindex a838bebf5e..cb45601c95 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 Binary files differindex 68ff77db0d..11d988e10a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 Binary files differindex 66af5a42be..f4c5d5cc29 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 Binary files differindex 17ba505772..cd397b02cd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 Binary files differindex 5f3706ce64..8fad7c6b0b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 Binary files differindex 7e9f9c465c..a595e60eea 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 Binary files differindex fcef6d9acb..97b44a9bae 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 Binary files differindex 27973bc857..4eb17ff005 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 Binary files differindex 1efd841261..13aef80cbb 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 Binary files differindex 1f761844fc..83a2816955 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 Binary files differindex 952681ed46..79a983e545 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 Binary files differindex cefc9126c6..e136690e16 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 Binary files differindex afb093da00..bc70fe416f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 Binary files differindex 9265fb7c20..d18cedd524 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam Binary files differindex c3ff07b436..4a6fa1d494 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra Binary files differindex 5962550392..38685d4219 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Andorra diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan Binary files differindex 73a4d013fc..aff8d82d2a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens Binary files differindex 9f3a0678d7..231bf9c3b7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Athens diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast Binary files differindex ac02a81440..323cd3818a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade Binary files differindex 27de456f16..a1bf9281ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin Binary files differindex 7f6d958f86..465546bd39 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava Binary files differindex ce8f433ece..fb7c145ac4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels Binary files differindex 40d7124e53..31973271d2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest Binary files differindex 4303b903e5..efa689ba9e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest Binary files differindex 6b94a4f312..940be4670a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen Binary files differindex ad6cf59281..388df2969f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Busingen diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau Binary files differindex 5ee23fe0e5..6970b14c50 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen Binary files differindex 776be6e4a6..45984a7599 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin Binary files differindex 1d994902db..4a45ea8f73 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar Binary files differindex 117aadb836..017bb2e347 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Gibraltar diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey Binary files differindex ac02a81440..323cd3818a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki Binary files differindex b4f8f9cbb5..ff5e565305 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Helsinki diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man Binary files differindex ac02a81440..323cd3818a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul Binary files differindex 508446bb6a..c891866873 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey Binary files differindex ac02a81440..323cd3818a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad Binary files differindex cc99beabe4..0ec4756470 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev Binary files differindex 9337c9ea27..8f83cefbcc 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov Binary files differindex a3b5320a0b..d1c93c540c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kirov diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon Binary files differindex 355817b52b..f0c70b6906 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Lisbon diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana Binary files differindex 27de456f16..a1bf9281ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/London b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/London Binary files differindex ac02a81440..323cd3818a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/London +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/London diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg Binary files differindex c4ca733f53..682bcbf61a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid Binary files differindex 16f6420ab7..60bdf4d07e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta Binary files differindex bf2452da40..27539c2243 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Malta diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn Binary files differindex b4f8f9cbb5..ff5e565305 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Mariehamn diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk Binary files differindex 453306c075..30d3a672bf 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco Binary files differindex 686ae88315..f30dfc7014 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Monaco diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow Binary files differindex ddb3f4e99a..5e6b6de645 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Moscow diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia Binary files differindex f7f10ab766..c210d0a598 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo Binary files differindex 15a34c3ced..dfc509570a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris Binary files differindex ca85435168..00a27264c2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Paris diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica Binary files differindex 27de456f16..a1bf9281ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague Binary files differindex ce8f433ece..fb7c145ac4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga Binary files differindex 8db477d017..26af4c90b3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome Binary files differindex ac4c16342b..639ca3be40 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara Binary files differindex 97d5dd9e6e..8d0c26e5c8 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Samara diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino Binary files differindex ac4c16342b..639ca3be40 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo Binary files differindex 27de456f16..a1bf9281ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov Binary files differindex 8fd5f6d4b8..2684d8f8b8 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Saratov diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol Binary files differindex 432e8315bc..88a6f3bdb4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje Binary files differindex 27de456f16..a1bf9281ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia Binary files differindex 0e4d879332..eabc972a22 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm Binary files differindex f3e0c7f0f2..dd3eb32278 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Stockholm diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn Binary files differindex b5acca3cf5..5321bbd46e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane Binary files differindex 0b86017d24..743a7337ff 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tirane diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol Binary files differindex 5ee23fe0e5..6970b14c50 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk Binary files differindex 7b61bdc522..bb842cb1f5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ulyanovsk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod Binary files differindex 66ae8d69e3..a5755685e3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz Binary files differindex ad6cf59281..388df2969f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vaduz diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican Binary files differindex ac4c16342b..639ca3be40 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna Binary files differindex 3582bb15cd..75339e98d0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius Binary files differindex 7abd63fa60..75b2eebb57 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd Binary files differindex d1cfac0e38..c517002604 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw Binary files differindex e33cf67171..efe1a40f2a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb Binary files differindex 27de456f16..a1bf9281ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye Binary files differindex e42edfc850..4ea8dae45a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich Binary files differindex ad6cf59281..388df2969f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zurich diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Factory b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Factory Binary files differindex 60aa2a0d69..b4dd7735ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Factory +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Factory diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB Binary files differindex ac02a81440..323cd3818a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire Binary files differindex ac02a81440..323cd3818a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0 b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0 Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0 +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/GMT0 diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwich b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwich Binary files differindex c63474664a..157573b1d3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwich +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Greenwich diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/HST b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/HST Binary files differindex cccd45eb8c..160a53e045 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/HST +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/HST diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkong b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkong Binary files differindex 23d0375fba..c80e364801 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkong +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Hongkong diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iceland b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iceland Binary files differindex 10e0fc8190..2451aca76d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iceland +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iceland diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos Binary files differindex 93d6dda50f..8b8ce226b6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas Binary files differindex d18c3810d9..766024b36c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos Binary files differindex f8116e7025..117503410c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen Binary files differindex cde4cf7ea7..8ce93e0124 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe Binary files differindex cba7dfe735..e7fccf8296 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives Binary files differindex 7c839cfa9b..58a82e4eb7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius Binary files differindex 17f2616990..7c11134882 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte Binary files differindex 9a2918f404..5f4ebcb7f9 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion Binary files differindex dfe08313df..248a7c93a5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran Binary files differindex 8cec5ad7de..f1555f0032 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Israel b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Israel Binary files differindex 440ef06b5d..4c49bbf524 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Israel +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Israel diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaica b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaica Binary files differindex 2a9b7fd52d..be6b1b6f1e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaica +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Jamaica diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Japan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Japan Binary files differindex 26f4d34d67..1aa066ce38 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Japan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Japan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein Binary files differindex 1a7975fad7..9416d522d0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Libya b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Libya Binary files differindex 07b393bb7d..e0c89971aa 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Libya +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Libya diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MET b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MET Binary files differindex 4a826bb185..6f0558c3b6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MET +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MET diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST Binary files differindex c93a58eee8..a0953d1e79 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT Binary files differindex 4506a6e150..137867c8bf 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte Binary files differindex ada6bf78b2..19ccd3576d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur Binary files differindex e4a785743d..4c819fab02 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General Binary files differindex e7fb6f2953..ffcf8bee10 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ Binary files differindex 6575fdce31..afb3929318 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT Binary files differindex c004109882..f06065ebd1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajo Binary files differindex 5fbe26b1d9..09e54e5c7c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Navajo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PRC b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PRC Binary files differindex 3c0bef2061..d6b66984a2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PRC +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PRC diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT Binary files differindex 99d246baa3..fde4833f6b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia Binary files differindex dab1f3f602..a6b835aab4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland Binary files differindex 6575fdce31..afb3929318 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville Binary files differindex 2892d26809..7c667093c5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham Binary files differindex c004109882..f06065ebd1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk Binary files differindex 07c84b7110..ea3fb5cd3c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter Binary files differindex cae3744096..d29bcd68b0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Easter diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate Binary files differindex 60150175c0..bf7471dd3f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury Binary files differindex f0b8252362..2b6a06088e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo Binary files differindex e40307f6aa..b7b30213e1 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji Binary files differindex d39bf53645..8b2dd52b17 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti Binary files differindex ea728637ac..78ab35b6b0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos Binary files differindex 31f0921ea0..a9403eca46 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier Binary files differindex e1fc3daa55..ddfc34ffc0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal Binary files differindex 7e9d10a100..720c679017 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam Binary files differindex 66490d25df..bf9a2d955f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu Binary files differindex c7cd060159..40e3d492e6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Honolulu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston Binary files differindex c7cd060159..40e3d492e6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Johnston diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton Binary files differnew file mode 100644 index 0000000000..2b6a06088e --- /dev/null +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kanton diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati Binary files differindex 7cae0cb756..2f676d3bf5 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae Binary files differindex a584aae5eb..f5d58242c8 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein Binary files differindex 1a7975fad7..9416d522d0 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro Binary files differindex 9ef8374de4..9228ee02ed 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas Binary files differindex 74d6792bf6..6ea24b72cd 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway Binary files differindex cb56709a77..001289ceec 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru Binary files differindex acec0429f1..ae13aac779 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue Binary files differindex 684b010e8b..be874e2472 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk Binary files differindex 53c1aad4e0..79e2a9419a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea Binary files differindex 931a1a306f..824f814160 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago Binary files differindex cb56709a77..001289ceec 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau Binary files differindex 146b35152a..bc8eb7a55b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn Binary files differindex ef91b061bb..8a4ba4d30a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei Binary files differindex c298ddd4de..b92b254a9a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape Binary files differindex c298ddd4de..b92b254a9a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby Binary files differindex 920ad27e62..5d8fc3a1b2 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga Binary files differindex da6b0fadea..7220bda0ad 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan Binary files differindex 66490d25df..bf9a2d955f 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa Binary files differindex cb56709a77..001289ceec 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti Binary files differindex 442b8eb5a4..50a064fa01 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa Binary files differindex 3db6c75033..6bc216823e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu Binary files differindex 5553c6009a..f28c840184 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk Binary files differindex 07c84b7110..ea3fb5cd3c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake Binary files differindex c9e310670f..71cca8877d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis Binary files differindex b35344b312..4bce893003 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap Binary files differindex 07c84b7110..ea3fb5cd3c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Poland b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Poland Binary files differindex e33cf67171..efe1a40f2a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Poland +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Poland diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugal b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugal Binary files differindex 355817b52b..f0c70b6906 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugal +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Portugal diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROC b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROC Binary files differindex 24c43444b6..35d89d036d 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROC +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROC diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROK b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROK Binary files differindex 96199e73e7..1755147fab 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROK +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/ROK diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Singapore b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Singapore Binary files differindex 2364b2178b..350d77e28e 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Singapore +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Singapore diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkey b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkey Binary files differindex 508446bb6a..c891866873 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkey +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Turkey diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UCT b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UCT Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UCT +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UCT diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska Binary files differindex 9bbb2fd3b3..cdf0572be3 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Alaska diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian Binary files differindex 43236498f6..b1497bda63 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Aleutian diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona Binary files differindex ac6bb0c782..c2bd2f949b 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Central b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Central Binary files differindex a5b1617c7f..b016880653 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Central +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Central diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana Binary files differindex 09511ccdcf..6b08d15bda 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/East-Indiana diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern Binary files differindex 2f75480e06..2b6c2eea14 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Eastern diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii Binary files differindex c7cd060159..40e3d492e6 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Hawaii diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke Binary files differindex fcd408d74d..b187d5f8c7 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Indiana-Starke diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan Binary files differindex e104faa465..6eb3ac46ec 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain Binary files differindex 5fbe26b1d9..09e54e5c7c 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Mountain diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific Binary files differindex 9dad4f4c75..aaf07787ad 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Pacific diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa Binary files differindex cb56709a77..001289ceec 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UTC b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UTC Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UTC +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/UTC diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Universal b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Universal Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Universal +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Universal diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SU b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SU Binary files differindex ddb3f4e99a..5e6b6de645 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SU +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/W-SU diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/WET b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/WET Binary files differindex c27390b5b6..423c6c203a 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/WET +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/WET diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulu b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulu Binary files differindex 91558be0c2..00841a6221 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulu +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Zulu diff --git a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab index 822ffa1f1a..c614be81f4 100644 --- a/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab +++ b/third_party/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab @@ -40,11 +40,9 @@ AL +4120+01950 Europe/Tirane AM +4011+04430 Asia/Yerevan AQ -6617+11031 Antarctica/Casey Casey AQ -6835+07758 Antarctica/Davis Davis -AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville AQ -6736+06253 Antarctica/Mawson Mawson AQ -6448-06406 Antarctica/Palmer Palmer AQ -6734-06808 Antarctica/Rothera Rothera -AQ -690022+0393524 Antarctica/Syowa Syowa AQ -720041+0023206 Antarctica/Troll Troll AQ -7824+10654 Antarctica/Vostok Vostok AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) @@ -63,8 +61,7 @@ AS,UM -1416-17042 Pacific/Pago_Pago Samoa, Midway AT +4813+01620 Europe/Vienna AU -3133+15905 Australia/Lord_Howe Lord Howe Island AU -5430+15857 Antarctica/Macquarie Macquarie Island -AU -4253+14719 Australia/Hobart Tasmania (most areas) -AU -3956+14352 Australia/Currie Tasmania (King Island) +AU -4253+14719 Australia/Hobart Tasmania AU -3749+14458 Australia/Melbourne Victoria AU -3352+15113 Australia/Sydney New South Wales (most areas) AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) @@ -98,7 +95,6 @@ BR +0249-06040 America/Boa_Vista Roraima BR -0308-06001 America/Manaus Amazonas (east) BR -0640-06952 America/Eirunepe Amazonas (west) BR -0958-06748 America/Rio_Branco Acre -BS +2505-07721 America/Nassau BT +2728+08939 Asia/Thimphu BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize @@ -107,13 +103,11 @@ CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) -CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) -CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas), Bahamas CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) -CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) CA +744144-0944945 America/Resolute Central - NU (Resolute) @@ -124,15 +118,14 @@ CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +6227-11421 America/Yellowknife Mountain - NT (central) CA +682059-1334300 America/Inuvik Mountain - NT (west) -CA +4906-11631 America/Creston MST - BC (Creston) CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) +CA +6043-13503 America/Whitehorse MST - Yukon (east) +CA +6404-13925 America/Dawson MST - Yukon (west) CA +4916-12307 America/Vancouver Pacific - BC (most areas) -CA +6043-13503 America/Whitehorse Pacific - Yukon (south) -CA +6404-13925 America/Dawson Pacific - Yukon (north) CC -1210+09655 Indian/Cocos CH,DE,LI +4723+00832 Europe/Zurich Swiss time -CI,BF,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan +CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga CL -3327-07040 America/Santiago Chile (most areas) CL -5309-07055 America/Punta_Arenas Region of Magallanes @@ -143,7 +136,6 @@ CO +0436-07405 America/Bogota CR +0956-08405 America/Costa_Rica CU +2308-08222 America/Havana CV +1455-02331 Atlantic/Cape_Verde -CW,AW,BQ,SX +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas CY +3510+03322 Asia/Nicosia Cyprus (most areas) CY +3507+03357 Asia/Famagusta Northern Cyprus @@ -171,9 +163,8 @@ FR +4852+00220 Europe/Paris GB,GG,IM,JE +513030-0000731 Europe/London GE +4143+04449 Asia/Tbilisi GF +0456-05220 America/Cayenne -GH +0533-00013 Africa/Accra GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Godthab Greenland (most areas) +GL +6411-05144 America/Nuuk Greenland (most areas) GL +7646-01840 America/Danmarkshavn National Park (east coast) GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit GL +7634-06847 America/Thule Thule/Pituffik @@ -205,7 +196,7 @@ JP +353916+1394441 Asia/Tokyo KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi KG +4254+07436 Asia/Bishkek KI +0125+17300 Pacific/Tarawa Gilbert Islands -KI -0308-17105 Pacific/Enderbury Phoenix Islands +KI -0247-17143 Pacific/Kanton Phoenix Islands KI +0152-15720 Pacific/Kiritimati Line Islands KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul @@ -263,19 +254,19 @@ NR -0031+16655 Pacific/Nauru NU -1901-16955 Pacific/Niue NZ,AQ -3652+17446 Pacific/Auckland New Zealand time NZ -4357-17633 Pacific/Chatham Chatham Islands -PA,KY +0858-07932 America/Panama +PA,CA,KY +0858-07932 America/Panama EST - Panama, Cayman, ON (Atikokan), NU (Coral H) PE -1203-07703 America/Lima PF -1732-14934 Pacific/Tahiti Society Islands PF -0900-13930 Pacific/Marquesas Marquesas Islands PF -2308-13457 Pacific/Gambier Gambier Islands -PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) +PG,AQ -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Dumont d'Urville PG -0613+15534 Pacific/Bougainville Bougainville PH +1435+12100 Asia/Manila PK +2452+06703 Asia/Karachi PL +5215+02100 Europe/Warsaw PM +4703-05620 America/Miquelon PN -2504-13005 Pacific/Pitcairn -PR +182806-0660622 America/Puerto_Rico +PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST PS +3130+03428 Asia/Gaza Gaza Strip PS +313200+0350542 Asia/Hebron West Bank PT +3843-00908 Europe/Lisbon Portugal (mainland) @@ -290,10 +281,10 @@ RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area # Mention RU and UA alphabetically. See "territorial claims" above. -RU,UA +4457+03406 Europe/Simferopol MSK+00 - Crimea +RU,UA +4457+03406 Europe/Simferopol Crimea RU +5836+04939 Europe/Kirov MSK+00 - Kirov +RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan -RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd RU +5134+04602 Europe/Saratov MSK+01 - Saratov RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia @@ -315,12 +306,12 @@ RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea -SA,KW,YE +2438+04643 Asia/Riyadh +SA,AQ,KW,YE +2438+04643 Asia/Riyadh Arabia, Syowa SB -0932+16012 Pacific/Guadalcanal SC -0440+05528 Indian/Mahe SD +1536+03232 Africa/Khartoum SE +5920+01803 Europe/Stockholm -SG +0117+10351 Asia/Singapore +SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia SR +0550-05510 America/Paramaribo SS +0451+03137 Africa/Juba ST +0020+00644 Africa/Sao_Tome @@ -335,14 +326,13 @@ TK -0922-17114 Pacific/Fakaofo TL -0833+12535 Asia/Dili TM +3757+05823 Asia/Ashgabat TN +3648+01011 Africa/Tunis -TO -2110-17510 Pacific/Tongatapu +TO -210800-1751200 Pacific/Tongatapu TR +4101+02858 Europe/Istanbul -TT,AG,AI,BL,DM,GD,GP,KN,LC,MF,MS,VC,VG,VI +1039-06131 America/Port_of_Spain TV -0831+17913 Pacific/Funafuti TW +2503+12130 Asia/Taipei UA +5026+03031 Europe/Kiev Ukraine (most areas) -UA +4837+02218 Europe/Uzhgorod Ruthenia -UA +4750+03510 Europe/Zaporozhye Zaporozh'ye/Zaporizhia; Lugansk/Luhansk (east) +UA +4837+02218 Europe/Uzhgorod Transcarpathia +UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk UM +1917+16637 Pacific/Wake Wake Island US +404251-0740023 America/New_York Eastern (most areas) US +421953-0830245 America/Detroit Eastern - MI (most areas) @@ -363,7 +353,7 @@ US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) US +433649-1161209 America/Boise Mountain - ID (south); OR (east) -US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) +US,CA +332654-1120424 America/Phoenix MST - Arizona (except Navajo), Creston BC US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) US +581807-1342511 America/Juneau Alaska - Juneau area diff --git a/third_party/abseil-cpp/absl/time/internal/test_util.cc b/third_party/abseil-cpp/absl/time/internal/test_util.cc index 9bffe121da..9a485a0750 100644 --- a/third_party/abseil-cpp/absl/time/internal/test_util.cc +++ b/third_party/abseil-cpp/absl/time/internal/test_util.cc @@ -18,6 +18,7 @@ #include <cstddef> #include <cstring> +#include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/time/internal/cctz/include/cctz/zone_info_source.h" diff --git a/third_party/abseil-cpp/absl/time/time.cc b/third_party/abseil-cpp/absl/time/time.cc index 6bb36cb3e7..1ec2026e25 100644 --- a/third_party/abseil-cpp/absl/time/time.cc +++ b/third_party/abseil-cpp/absl/time/time.cc @@ -60,9 +60,10 @@ inline cctz::time_point<cctz::seconds> unix_epoch() { inline int64_t FloorToUnit(absl::Duration d, absl::Duration unit) { absl::Duration rem; int64_t q = absl::IDivDuration(d, unit, &rem); - return (q > 0 || - rem >= ZeroDuration() || - q == std::numeric_limits<int64_t>::min()) ? q : q - 1; + return (q > 0 || rem >= ZeroDuration() || + q == std::numeric_limits<int64_t>::min()) + ? q + : q - 1; } inline absl::Time::Breakdown InfiniteFutureBreakdown() { diff --git a/third_party/abseil-cpp/absl/time/time.h b/third_party/abseil-cpp/absl/time/time.h index 33a4a630c2..5abd815a79 100644 --- a/third_party/abseil-cpp/absl/time/time.h +++ b/third_party/abseil-cpp/absl/time/time.h @@ -182,18 +182,29 @@ class Duration { // Overloads that forward to either the int64_t or double overloads above. // Integer operands must be representable as int64_t. - template <typename T> + template <typename T, time_internal::EnableIfIntegral<T> = 0> Duration& operator*=(T r) { int64_t x = r; return *this *= x; } - template <typename T> + + template <typename T, time_internal::EnableIfIntegral<T> = 0> Duration& operator/=(T r) { int64_t x = r; return *this /= x; } - Duration& operator*=(float r) { return *this *= static_cast<double>(r); } - Duration& operator/=(float r) { return *this /= static_cast<double>(r); } + + template <typename T, time_internal::EnableIfFloat<T> = 0> + Duration& operator*=(T r) { + double x = r; + return *this *= x; + } + + template <typename T, time_internal::EnableIfFloat<T> = 0> + Duration& operator/=(T r) { + double x = r; + return *this /= x; + } template <typename H> friend H AbslHashValue(H h, Duration d) { @@ -392,12 +403,30 @@ constexpr Duration InfiniteDuration(); // // absl::Duration a = absl::Seconds(60); // absl::Duration b = absl::Minutes(1); // b == a -constexpr Duration Nanoseconds(int64_t n); -constexpr Duration Microseconds(int64_t n); -constexpr Duration Milliseconds(int64_t n); -constexpr Duration Seconds(int64_t n); -constexpr Duration Minutes(int64_t n); -constexpr Duration Hours(int64_t n); +template <typename T, time_internal::EnableIfIntegral<T> = 0> +constexpr Duration Nanoseconds(T n) { + return time_internal::FromInt64(n, std::nano{}); +} +template <typename T, time_internal::EnableIfIntegral<T> = 0> +constexpr Duration Microseconds(T n) { + return time_internal::FromInt64(n, std::micro{}); +} +template <typename T, time_internal::EnableIfIntegral<T> = 0> +constexpr Duration Milliseconds(T n) { + return time_internal::FromInt64(n, std::milli{}); +} +template <typename T, time_internal::EnableIfIntegral<T> = 0> +constexpr Duration Seconds(T n) { + return time_internal::FromInt64(n, std::ratio<1>{}); +} +template <typename T, time_internal::EnableIfIntegral<T> = 0> +constexpr Duration Minutes(T n) { + return time_internal::FromInt64(n, std::ratio<60>{}); +} +template <typename T, time_internal::EnableIfIntegral<T> = 0> +constexpr Duration Hours(T n) { + return time_internal::FromInt64(n, std::ratio<3600>{}); +} // Factory overloads for constructing `Duration` values from a floating-point // number of the unit indicated by the factory function's name. These functions @@ -451,19 +480,20 @@ Duration Hours(T n) { // ToInt64Hours() // // Helper functions that convert a Duration to an integral count of the -// indicated unit. These functions are shorthand for the `IDivDuration()` -// function above; see its documentation for details about overflow, etc. +// indicated unit. These return the same results as the `IDivDuration()` +// function, though they usually do so more efficiently; see the +// documentation of `IDivDuration()` for details about overflow, etc. // // Example: // // absl::Duration d = absl::Milliseconds(1500); // int64_t isec = absl::ToInt64Seconds(d); // isec == 1 -int64_t ToInt64Nanoseconds(Duration d); -int64_t ToInt64Microseconds(Duration d); -int64_t ToInt64Milliseconds(Duration d); -int64_t ToInt64Seconds(Duration d); -int64_t ToInt64Minutes(Duration d); -int64_t ToInt64Hours(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Nanoseconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Microseconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Seconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Minutes(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Hours(Duration d); // ToDoubleNanoSeconds() // ToDoubleMicroseconds() @@ -480,12 +510,12 @@ int64_t ToInt64Hours(Duration d); // // absl::Duration d = absl::Milliseconds(1500); // double dsec = absl::ToDoubleSeconds(d); // dsec == 1.5 -double ToDoubleNanoseconds(Duration d); -double ToDoubleMicroseconds(Duration d); -double ToDoubleMilliseconds(Duration d); -double ToDoubleSeconds(Duration d); -double ToDoubleMinutes(Duration d); -double ToDoubleHours(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleNanoseconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMicroseconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMilliseconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleSeconds(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleMinutes(Duration d); +ABSL_ATTRIBUTE_PURE_FUNCTION double ToDoubleHours(Duration d); // FromChrono() // @@ -545,12 +575,22 @@ inline std::ostream& operator<<(std::ostream& os, Duration d) { // suffix. The valid suffixes are "ns", "us" "ms", "s", "m", and "h". // Simple examples include "300ms", "-1.5h", and "2h45m". Parses "0" as // `ZeroDuration()`. Parses "inf" and "-inf" as +/- `InfiniteDuration()`. -bool ParseDuration(const std::string& dur_string, Duration* d); +bool ParseDuration(absl::string_view dur_string, Duration* d); -// Support for flag values of type Duration. Duration flags must be specified -// in a format that is valid input for absl::ParseDuration(). +// AbslParseFlag() +// +// Parses a command-line flag string representation `text` into a a Duration +// value. Duration flags must be specified in a format that is valid input for +// `absl::ParseDuration()`. bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error); + + +// AbslUnparseFlag() +// +// Unparses a Duration value into a command-line string representation using +// the format specified by `absl::ParseDuration()`. std::string AbslUnparseFlag(Duration d); + ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Duration* dst, std::string* error); ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") @@ -639,7 +679,7 @@ class Time { // Deprecated. Use `absl::TimeZone::CivilInfo`. struct Breakdown { - int64_t year; // year (e.g., 2013) + int64_t year; // year (e.g., 2013) int month; // month of year [1:12] int day; // day of month [1:31] int hour; // hour of day [0:23] @@ -813,8 +853,12 @@ Time FromChrono(const std::chrono::system_clock::time_point& tp); // // tp == std::chrono::system_clock::from_time_t(123); std::chrono::system_clock::time_point ToChronoTime(Time); -// Support for flag values of type Time. Time flags must be specified in a -// format that matches absl::RFC3339_full. For example: +// AbslParseFlag() +// +// Parses the command-line flag string representation `text` into a Time value. +// Time flags must be specified in a format that matches absl::RFC3339_full. +// +// For example: // // --start_time=2016-01-02T03:04:05.678+08:00 // @@ -824,7 +868,13 @@ std::chrono::system_clock::time_point ToChronoTime(Time); // seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag // and add that duration to absl::UnixEpoch() to get an absl::Time. bool AbslParseFlag(absl::string_view text, Time* t, std::string* error); + +// AbslUnparseFlag() +// +// Unparses a Time value into a command-line string representation using +// the format specified by `absl::ParseTime()`. std::string AbslUnparseFlag(Time t); + ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Time* t, std::string* error); ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") @@ -1021,13 +1071,13 @@ class TimeZone { // Loads the named zone. May perform I/O on the initial load of the named // zone. If the name is invalid, or some other kind of error occurs, returns // `false` and `*tz` is set to the UTC time zone. -inline bool LoadTimeZone(const std::string& name, TimeZone* tz) { +inline bool LoadTimeZone(absl::string_view name, TimeZone* tz) { if (name == "localtime") { *tz = TimeZone(time_internal::cctz::local_time_zone()); return true; } time_internal::cctz::time_zone cz; - const bool b = time_internal::cctz::load_time_zone(name, &cz); + const bool b = time_internal::cctz::load_time_zone(std::string(name), &cz); *tz = TimeZone(cz); return b; } @@ -1180,11 +1230,15 @@ inline Time FromDateTime(int64_t year, int mon, int day, int hour, // // Converts the `tm_year`, `tm_mon`, `tm_mday`, `tm_hour`, `tm_min`, and // `tm_sec` fields to an `absl::Time` using the given time zone. See ctime(3) -// for a description of the expected values of the tm fields. If the indicated -// time instant is not unique (see `absl::TimeZone::At(absl::CivilSecond)` -// above), the `tm_isdst` field is consulted to select the desired instant -// (`tm_isdst` > 0 means DST, `tm_isdst` == 0 means no DST, `tm_isdst` < 0 -// means use the post-transition offset). +// for a description of the expected values of the tm fields. If the civil time +// is unique (see `absl::TimeZone::At(absl::CivilSecond)` above), the matching +// time instant is returned. Otherwise, the `tm_isdst` field is consulted to +// choose between the possible results. For a repeated civil time, `tm_isdst != +// 0` returns the matching DST instant, while `tm_isdst == 0` returns the +// matching non-DST instant. For a skipped civil time there is no matching +// instant, so `tm_isdst != 0` returns the DST instant, and `tm_isdst == 0` +// returns the non-DST instant, that would have matched if the transition never +// happened. Time FromTM(const struct tm& tm, TimeZone tz); // ToTM() @@ -1203,18 +1257,15 @@ struct tm ToTM(Time t, TimeZone tz); // time with UTC offset. Also note the use of "%Y": RFC3339 mandates that // years have exactly four digits, but we allow them to take their natural // width. -ABSL_DLL extern const char - RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez -ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez +ABSL_DLL extern const char RFC3339_full[]; // %Y-%m-%d%ET%H:%M:%E*S%Ez +ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%d%ET%H:%M:%S%Ez // RFC1123_full // RFC1123_no_wday // // FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings. -ABSL_DLL extern const char - RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z -ABSL_DLL extern const char - RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z +ABSL_DLL extern const char RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z +ABSL_DLL extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z // FormatTime() // @@ -1229,6 +1280,7 @@ ABSL_DLL extern const char // - %E#f - Fractional seconds with # digits of precision // - %E*f - Fractional seconds with full precision (a literal '*') // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) +// - %ET - The RFC3339 "date-time" separator "T" // // Note that %E0S behaves like %S, and %E0f produces no characters. In // contrast %E*f always produces at least one digit, which may be '0'. @@ -1252,7 +1304,7 @@ ABSL_DLL extern const char // `absl::InfinitePast()`, the returned string will be exactly "infinite-past". // In both cases the given format string and `absl::TimeZone` are ignored. // -std::string FormatTime(const std::string& format, Time t, TimeZone tz); +std::string FormatTime(absl::string_view format, Time t, TimeZone tz); // Convenience functions that format the given time using the RFC3339_full // format. The first overload uses the provided TimeZone, while the second @@ -1271,7 +1323,8 @@ inline std::ostream& operator<<(std::ostream& os, Time t) { // returns the corresponding `absl::Time`. Uses strftime()-like formatting // options, with the same extensions as FormatTime(), but with the // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez -// and %E*z also accept the same inputs. +// and %E*z also accept the same inputs, which (along with %z) includes +// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'. // // %Y consumes as many numeric characters as it can, so the matching data // should always be terminated with a non-numeric. %E4Y always consumes @@ -1313,7 +1366,7 @@ inline std::ostream& operator<<(std::ostream& os, Time t) { // If the input string is "infinite-past", the returned `absl::Time` will be // `absl::InfinitePast()` and `true` will be returned. // -bool ParseTime(const std::string& format, const std::string& input, Time* time, +bool ParseTime(absl::string_view format, absl::string_view input, Time* time, std::string* err); // Like ParseTime() above, but if the format string does not contain a UTC @@ -1323,7 +1376,7 @@ bool ParseTime(const std::string& format, const std::string& input, Time* time, // of ambiguity or non-existence, in which case the "pre" time (as defined // by TimeZone::TimeInfo) is returned. For these reasons we recommend that // all date/time strings include a UTC offset so they're context independent. -bool ParseTime(const std::string& format, const std::string& input, TimeZone tz, +bool ParseTime(absl::string_view format, absl::string_view input, TimeZone tz, Time* time, std::string* err); // ============================================================================ @@ -1348,8 +1401,8 @@ constexpr Duration MakeDuration(int64_t hi, int64_t lo) { // it's positive and can be converted to int64_t without risk of UB. inline Duration MakePosDoubleDuration(double n) { const int64_t int_secs = static_cast<int64_t>(n); - const uint32_t ticks = - static_cast<uint32_t>((n - int_secs) * kTicksPerSecond + 0.5); + const uint32_t ticks = static_cast<uint32_t>( + std::round((n - static_cast<double>(int_secs)) * kTicksPerSecond)); return ticks < kTicksPerSecond ? MakeDuration(int_secs, ticks) : MakeDuration(int_secs + 1, ticks - kTicksPerSecond); @@ -1473,34 +1526,13 @@ T ToChronoDuration(Duration d) { } // namespace time_internal -constexpr Duration Nanoseconds(int64_t n) { - return time_internal::FromInt64(n, std::nano{}); -} -constexpr Duration Microseconds(int64_t n) { - return time_internal::FromInt64(n, std::micro{}); -} -constexpr Duration Milliseconds(int64_t n) { - return time_internal::FromInt64(n, std::milli{}); -} -constexpr Duration Seconds(int64_t n) { - return time_internal::FromInt64(n, std::ratio<1>{}); -} -constexpr Duration Minutes(int64_t n) { - return time_internal::FromInt64(n, std::ratio<60>{}); -} -constexpr Duration Hours(int64_t n) { - return time_internal::FromInt64(n, std::ratio<3600>{}); -} - constexpr bool operator<(Duration lhs, Duration rhs) { return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs) ? time_internal::GetRepHi(lhs) < time_internal::GetRepHi(rhs) - : time_internal::GetRepHi(lhs) == - (std::numeric_limits<int64_t>::min)() - ? time_internal::GetRepLo(lhs) + 1 < - time_internal::GetRepLo(rhs) + 1 - : time_internal::GetRepLo(lhs) < - time_internal::GetRepLo(rhs); + : time_internal::GetRepHi(lhs) == (std::numeric_limits<int64_t>::min)() + ? time_internal::GetRepLo(lhs) + 1 < + time_internal::GetRepLo(rhs) + 1 + : time_internal::GetRepLo(lhs) < time_internal::GetRepLo(rhs); } constexpr bool operator==(Duration lhs, Duration rhs) { diff --git a/third_party/abseil-cpp/absl/time/time_test.cc b/third_party/abseil-cpp/absl/time/time_test.cc index 6f89672c66..cde9423feb 100644 --- a/third_party/abseil-cpp/absl/time/time_test.cc +++ b/third_party/abseil-cpp/absl/time/time_test.cc @@ -58,8 +58,7 @@ const char kZoneAbbrRE[] = "[A-Za-z]{3,4}|[-+][0-9]{2}([0-9]{2})?"; // timespec ts1, ts2; // EXPECT_THAT(ts1, TimespecMatcher(ts2)); MATCHER_P(TimespecMatcher, ts, "") { - if (ts.tv_sec == arg.tv_sec && ts.tv_nsec == arg.tv_nsec) - return true; + if (ts.tv_sec == arg.tv_sec && ts.tv_nsec == arg.tv_nsec) return true; *result_listener << "expected: {" << ts.tv_sec << ", " << ts.tv_nsec << "} "; *result_listener << "actual: {" << arg.tv_sec << ", " << arg.tv_nsec << "}"; return false; @@ -69,8 +68,7 @@ MATCHER_P(TimespecMatcher, ts, "") { // timeval tv1, tv2; // EXPECT_THAT(tv1, TimevalMatcher(tv2)); MATCHER_P(TimevalMatcher, tv, "") { - if (tv.tv_sec == arg.tv_sec && tv.tv_usec == arg.tv_usec) - return true; + if (tv.tv_sec == arg.tv_sec && tv.tv_usec == arg.tv_usec) return true; *result_listener << "expected: {" << tv.tv_sec << ", " << tv.tv_usec << "} "; *result_listener << "actual: {" << arg.tv_sec << ", " << arg.tv_usec << "}"; return false; @@ -103,7 +101,7 @@ TEST(Time, ValueSemantics) { EXPECT_EQ(a, b); EXPECT_EQ(a, c); EXPECT_EQ(b, c); - b = c; // Assignment + b = c; // Assignment EXPECT_EQ(a, b); EXPECT_EQ(a, c); EXPECT_EQ(b, c); @@ -228,6 +226,9 @@ TEST(Time, Infinity) { constexpr absl::Time t = absl::UnixEpoch(); // Any finite time. static_assert(t < ifuture, ""); static_assert(t > ipast, ""); + + EXPECT_EQ(ifuture, t + absl::InfiniteDuration()); + EXPECT_EQ(ipast, t - absl::InfiniteDuration()); } TEST(Time, FloorConversion) { @@ -358,19 +359,21 @@ TEST(Time, FloorConversion) { const int64_t min_plus_1 = std::numeric_limits<int64_t>::min() + 1; EXPECT_EQ(min_plus_1, absl::ToUnixSeconds(absl::FromUnixSeconds(min_plus_1))); EXPECT_EQ(std::numeric_limits<int64_t>::min(), - absl::ToUnixSeconds( - absl::FromUnixSeconds(min_plus_1) - absl::Nanoseconds(1) / 2)); + absl::ToUnixSeconds(absl::FromUnixSeconds(min_plus_1) - + absl::Nanoseconds(1) / 2)); // Tests flooring near positive infinity. EXPECT_EQ(std::numeric_limits<int64_t>::max(), - absl::ToUnixSeconds(absl::FromUnixSeconds( - std::numeric_limits<int64_t>::max()) + absl::Nanoseconds(1) / 2)); + absl::ToUnixSeconds( + absl::FromUnixSeconds(std::numeric_limits<int64_t>::max()) + + absl::Nanoseconds(1) / 2)); EXPECT_EQ(std::numeric_limits<int64_t>::max(), absl::ToUnixSeconds( absl::FromUnixSeconds(std::numeric_limits<int64_t>::max()))); EXPECT_EQ(std::numeric_limits<int64_t>::max() - 1, - absl::ToUnixSeconds(absl::FromUnixSeconds( - std::numeric_limits<int64_t>::max()) - absl::Nanoseconds(1) / 2)); + absl::ToUnixSeconds( + absl::FromUnixSeconds(std::numeric_limits<int64_t>::max()) - + absl::Nanoseconds(1) / 2)); } TEST(Time, RoundtripConversion) { @@ -1045,15 +1048,15 @@ TEST(Time, ConversionSaturation) { // Checks how TimeZone::At() saturates on infinities. auto ci = utc.At(absl::InfiniteFuture()); - EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::max(), 12, 31, 23, - 59, 59, 0, false); + EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::max(), 12, 31, 23, 59, 59, + 0, false); EXPECT_EQ(absl::InfiniteDuration(), ci.subsecond); EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(ci.cs)); EXPECT_EQ(365, absl::GetYearDay(ci.cs)); EXPECT_STREQ("-00", ci.zone_abbr); // artifact of TimeZone::At() ci = utc.At(absl::InfinitePast()); - EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0, - 0, 0, false); + EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0, 0, 0, + false); EXPECT_EQ(-absl::InfiniteDuration(), ci.subsecond); EXPECT_EQ(absl::Weekday::sunday, absl::GetWeekday(ci.cs)); EXPECT_EQ(1, absl::GetYearDay(ci.cs)); @@ -1067,7 +1070,8 @@ TEST(Time, ConversionSaturation) { EXPECT_EQ("292277026596-12-04T15:30:07+00:00", absl::FormatTime(absl::RFC3339_full, t, utc)); EXPECT_EQ( - absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::max()), t); + absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::max()), + t); // Checks that we can also get the maximal Time value for a far-east zone. const absl::TimeZone plus14 = absl::FixedTimeZone(14 * 60 * 60); @@ -1075,7 +1079,8 @@ TEST(Time, ConversionSaturation) { EXPECT_EQ("292277026596-12-05T05:30:07+14:00", absl::FormatTime(absl::RFC3339_full, t, plus14)); EXPECT_EQ( - absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::max()), t); + absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::max()), + t); // One second later should push us to infinity. t = absl::FromCivil(absl::CivilSecond(292277026596, 12, 4, 15, 30, 8), utc); @@ -1089,7 +1094,8 @@ TEST(Time, ConversionSaturation) { EXPECT_EQ("-292277022657-01-27T08:29:52+00:00", absl::FormatTime(absl::RFC3339_full, t, utc)); EXPECT_EQ( - absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::min()), t); + absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::min()), + t); // Checks that we can also get the minimal Time value for a far-west zone. const absl::TimeZone minus12 = absl::FixedTimeZone(-12 * 60 * 60); @@ -1098,7 +1104,8 @@ TEST(Time, ConversionSaturation) { EXPECT_EQ("-292277022657-01-26T20:29:52-12:00", absl::FormatTime(absl::RFC3339_full, t, minus12)); EXPECT_EQ( - absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::min()), t); + absl::UnixEpoch() + absl::Seconds(std::numeric_limits<int64_t>::min()), + t); // One second before should push us to -infinity. t = absl::FromCivil(absl::CivilSecond(-292277022657, 1, 27, 8, 29, 51), utc); @@ -1171,14 +1178,13 @@ TEST(Time, LegacyDateTime) { const int kMin = std::numeric_limits<int>::min(); absl::Time t; - t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::max(), - kMax, kMax, kMax, kMax, kMax, utc); + t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::max(), kMax, + kMax, kMax, kMax, kMax, utc); EXPECT_EQ("infinite-future", absl::FormatTime(ymdhms, t, utc)); // no overflow - t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::min(), - kMin, kMin, kMin, kMin, kMin, utc); - EXPECT_EQ("infinite-past", - absl::FormatTime(ymdhms, t, utc)); // no overflow + t = absl::FromDateTime(std::numeric_limits<absl::civil_year_t>::min(), kMin, + kMin, kMin, kMin, kMin, utc); + EXPECT_EQ("infinite-past", absl::FormatTime(ymdhms, t, utc)); // no overflow // Check normalization. EXPECT_TRUE(absl::ConvertDateTime(2013, 10, 32, 8, 30, 0, utc).normalized); diff --git a/third_party/abseil-cpp/absl/time/time_zone_test.cc b/third_party/abseil-cpp/absl/time/time_zone_test.cc index 8f1e74aca5..229fcfccb0 100644 --- a/third_party/abseil-cpp/absl/time/time_zone_test.cc +++ b/third_party/abseil-cpp/absl/time/time_zone_test.cc @@ -88,7 +88,7 @@ TEST(TimeZone, Failures) { EXPECT_FALSE(LoadTimeZone("Invalid/TimeZone", &tz)); EXPECT_EQ(absl::UTCTimeZone(), tz); // guaranteed fallback to UTC - // Loading an empty std::string timezone should fail. + // Loading an empty string timezone should fail. tz = absl::time_internal::LoadTimeZone("America/Los_Angeles"); EXPECT_FALSE(LoadTimeZone("", &tz)); EXPECT_EQ(absl::UTCTimeZone(), tz); // guaranteed fallback to UTC diff --git a/third_party/abseil-cpp/absl/types/BUILD.bazel b/third_party/abseil-cpp/absl/types/BUILD.bazel index f2ea9f395a..38ed228679 100644 --- a/third_party/abseil-cpp/absl/types/BUILD.bazel +++ b/third_party/abseil-cpp/absl/types/BUILD.bazel @@ -12,9 +12,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. -# -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +22,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "any", @@ -35,6 +33,7 @@ cc_library( ":bad_any_cast", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:fast_type_id", "//absl/meta:type_traits", "//absl/utility", ], @@ -215,11 +214,15 @@ cc_library( "internal/conformance_aliases.h", "internal/conformance_archetype.h", "internal/conformance_profile.h", + "internal/conformance_testing.h", + "internal/conformance_testing_helpers.h", + "internal/parentheses.h", + "internal/transform_args.h", ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - "//absl/debugging:demangle_internal", + "//absl/algorithm:container", "//absl/meta:type_traits", "//absl/strings", "//absl/utility", diff --git a/third_party/abseil-cpp/absl/types/CMakeLists.txt b/third_party/abseil-cpp/absl/types/CMakeLists.txt index c7c882507f..d7e8614e0d 100644 --- a/third_party/abseil-cpp/absl/types/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/types/CMakeLists.txt @@ -24,6 +24,7 @@ absl_cc_library( absl::bad_any_cast absl::config absl::core_headers + absl::fast_type_id absl::type_traits absl::utility PUBLIC @@ -68,7 +69,7 @@ absl_cc_test( absl::exception_testing absl::raw_logging_internal absl::test_instance_tracker - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -84,7 +85,7 @@ absl_cc_test( absl::exception_testing absl::raw_logging_internal absl::test_instance_tracker - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -98,7 +99,7 @@ absl_cc_test( absl::any absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -135,7 +136,7 @@ absl_cc_test( absl::inlined_vector absl::hash_testing absl::strings - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -155,7 +156,7 @@ absl_cc_test( absl::inlined_vector absl::hash_testing absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -221,7 +222,7 @@ absl_cc_test( absl::raw_logging_internal absl::strings absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -235,7 +236,7 @@ absl_cc_test( absl::optional absl::config absl::exception_safety_testing - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -245,15 +246,20 @@ absl_cc_library( "internal/conformance_aliases.h" "internal/conformance_archetype.h" "internal/conformance_profile.h" + "internal/conformance_testing.h" + "internal/conformance_testing_helpers.h" + "internal/parentheses.h" + "internal/transform_args.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::algorithm absl::debugging absl::type_traits absl::strings absl::utility - gmock_main - PUBLIC + GTest::gmock_main + TESTONLY ) absl_cc_test( @@ -269,7 +275,7 @@ absl_cc_test( DEPS absl::conformance_testing absl::type_traits - gmock_main + GTest::gmock_main ) absl_cc_test( @@ -281,7 +287,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::conformance_testing - gmock_main + absl::type_traits + GTest::gmock_main ) absl_cc_library( @@ -317,7 +324,7 @@ absl_cc_test( absl::memory absl::type_traits absl::strings - gmock_main + GTest::gmock_main ) absl_cc_library( @@ -343,12 +350,9 @@ absl_cc_test( DEPS absl::base absl::compare - gmock_main + GTest::gmock_main ) -# TODO(cohenjon,zhangxy) Figure out why this test is failing on gcc 4.8 -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) -else() absl_cc_test( NAME variant_exception_safety_test @@ -361,6 +365,5 @@ absl_cc_test( absl::config absl::exception_safety_testing absl::memory - gmock_main + GTest::gmock_main ) -endif() diff --git a/third_party/abseil-cpp/absl/types/any.h b/third_party/abseil-cpp/absl/types/any.h index 16bda79cc7..fc5a07469f 100644 --- a/third_party/abseil-cpp/absl/types/any.h +++ b/third_party/abseil-cpp/absl/types/any.h @@ -47,9 +47,9 @@ // this abstraction, make sure that you should not instead be rewriting your // code to be more specific. // -// Abseil expects to release an `absl::variant` type shortly (a C++11 compatible -// version of the C++17 `std::variant), which is generally preferred for use -// over `absl::any`. +// Abseil has also released an `absl::variant` type (a C++11 compatible version +// of the C++17 `std::variant`), which is generally preferred for use over +// `absl::any`. #ifndef ABSL_TYPES_ANY_H_ #define ABSL_TYPES_ANY_H_ @@ -80,6 +80,7 @@ ABSL_NAMESPACE_END #include <typeinfo> #include <utility> +#include "absl/base/internal/fast_type_id.h" #include "absl/base/macros.h" #include "absl/meta/type_traits.h" #include "absl/types/bad_any_cast.h" @@ -95,26 +96,6 @@ ABSL_NAMESPACE_END namespace absl { ABSL_NAMESPACE_BEGIN -namespace any_internal { - -template <typename Type> -struct TypeTag { - constexpr static char dummy_var = 0; -}; - -template <typename Type> -constexpr char TypeTag<Type>::dummy_var; - -// FastTypeId<Type>() evaluates at compile/link-time to a unique pointer for the -// passed in type. These are meant to be good match for keys into maps or -// straight up comparisons. -template<typename Type> -constexpr inline const void* FastTypeId() { - return &TypeTag<Type>::dummy_var; -} - -} // namespace any_internal - class any; // swap() @@ -423,11 +404,11 @@ class any { using NormalizedType = typename std::remove_cv<typename std::remove_reference<T>::type>::type; - return any_internal::FastTypeId<NormalizedType>(); + return base_internal::FastTypeId<NormalizedType>(); } const void* GetObjTypeId() const { - return obj_ ? obj_->ObjTypeId() : any_internal::FastTypeId<void>(); + return obj_ ? obj_->ObjTypeId() : base_internal::FastTypeId<void>(); } // `absl::any` nonmember functions // diff --git a/third_party/abseil-cpp/absl/types/bad_optional_access.h b/third_party/abseil-cpp/absl/types/bad_optional_access.h index a500286adc..049e72ad9a 100644 --- a/third_party/abseil-cpp/absl/types/bad_optional_access.h +++ b/third_party/abseil-cpp/absl/types/bad_optional_access.h @@ -67,7 +67,7 @@ class bad_optional_access : public std::exception { namespace optional_internal { // throw delegator -[[noreturn]] void throw_bad_optional_access(); +[[noreturn]] ABSL_DLL void throw_bad_optional_access(); } // namespace optional_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/types/bad_variant_access.h b/third_party/abseil-cpp/absl/types/bad_variant_access.h index 095969f91e..8ab215e97d 100644 --- a/third_party/abseil-cpp/absl/types/bad_variant_access.h +++ b/third_party/abseil-cpp/absl/types/bad_variant_access.h @@ -70,8 +70,8 @@ class bad_variant_access : public std::exception { namespace variant_internal { -[[noreturn]] void ThrowBadVariantAccess(); -[[noreturn]] void Rethrow(); +[[noreturn]] ABSL_DLL void ThrowBadVariantAccess(); +[[noreturn]] ABSL_DLL void Rethrow(); } // namespace variant_internal ABSL_NAMESPACE_END diff --git a/third_party/abseil-cpp/absl/types/compare.h b/third_party/abseil-cpp/absl/types/compare.h index 62ca70f9a7..19b076e7f1 100644 --- a/third_party/abseil-cpp/absl/types/compare.h +++ b/third_party/abseil-cpp/absl/types/compare.h @@ -86,7 +86,8 @@ enum class ncmp : value_type { unordered = -127 }; // incomplete types so they need to be defined after the types are complete. #ifdef __cpp_inline_variables -#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) +// A no-op expansion that can be followed by a semicolon at class level. +#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) static_assert(true, "") #define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) \ static const type name @@ -99,7 +100,8 @@ enum class ncmp : value_type { unordered = -127 }; #define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) \ ABSL_CONST_INIT static const T name -#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) +// A no-op expansion that can be followed by a semicolon at class level. +#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) static_assert(true, "") #define ABSL_COMPARE_INLINE_INIT(type, name, init) \ template <typename T> \ diff --git a/third_party/abseil-cpp/absl/types/internal/conformance_profile.h b/third_party/abseil-cpp/absl/types/internal/conformance_profile.h index e62004fdf2..cf64ff4fcd 100644 --- a/third_party/abseil-cpp/absl/types/internal/conformance_profile.h +++ b/third_party/abseil-cpp/absl/types/internal/conformance_profile.h @@ -36,10 +36,19 @@ #ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ #define ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ +#include <set> #include <type_traits> #include <utility> +#include <vector> +#include "gtest/gtest.h" +#include "absl/algorithm/container.h" #include "absl/meta/type_traits.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/internal/conformance_testing_helpers.h" +#include "absl/utility/utility.h" // TODO(calabrese) Add support for extending profiles. @@ -47,6 +56,187 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace types_internal { +// Converts an enum to its underlying integral value. +template <typename Enum> +constexpr absl::underlying_type_t<Enum> UnderlyingValue(Enum value) { + return static_cast<absl::underlying_type_t<Enum>>(value); +} + +// A tag type used in place of a matcher when checking that an assertion result +// does not actually contain any errors. +struct NoError {}; + +// ----------------------------------------------------------------------------- +// ConformanceErrors +// ----------------------------------------------------------------------------- +class ConformanceErrors { + public: + // Setup the error reporting mechanism by seeding it with the name of the type + // that is being tested. + explicit ConformanceErrors(std::string type_name) + : assertion_result_(false), type_name_(std::move(type_name)) { + assertion_result_ << "\n\n" + "Assuming the following type alias:\n" + "\n" + " using _T = " + << type_name_ << ";\n\n"; + outputDivider(); + } + + // Adds the test name to the list of successfully run tests iff it was not + // previously reported as failing. This behavior is useful for tests that + // have multiple parts, where failures and successes are reported individually + // with the same test name. + void addTestSuccess(absl::string_view test_name) { + auto normalized_test_name = absl::AsciiStrToLower(test_name); + + // If the test is already reported as failing, do not add it to the list of + // successes. + if (test_failures_.find(normalized_test_name) == test_failures_.end()) { + test_successes_.insert(std::move(normalized_test_name)); + } + } + + // Streams a single error description into the internal buffer (a visual + // divider is automatically inserted after the error so that multiple errors + // are visibly distinct). + // + // This function increases the error count by 1. + // + // TODO(calabrese) Determine desired behavior when if this function throws. + template <class... P> + void addTestFailure(absl::string_view test_name, const P&... args) { + // Output a message related to the test failure. + assertion_result_ << "\n\n" + "Failed test: " + << test_name << "\n\n"; + addTestFailureImpl(args...); + assertion_result_ << "\n\n"; + outputDivider(); + + auto normalized_test_name = absl::AsciiStrToLower(test_name); + + // If previous parts of this test succeeded, remove it from that set. + test_successes_.erase(normalized_test_name); + + // Add the test name to the list of failed tests. + test_failures_.insert(std::move(normalized_test_name)); + + has_error_ = true; + } + + // Convert this object into a testing::AssertionResult instance such that it + // can be used with gtest. + ::testing::AssertionResult assertionResult() const { + return has_error_ ? assertion_result_ : ::testing::AssertionSuccess(); + } + + // Convert this object into a testing::AssertionResult instance such that it + // can be used with gtest. This overload expects errors, using the specified + // matcher. + ::testing::AssertionResult expectFailedTests( + const std::set<std::string>& test_names) const { + // Since we are expecting nonconformance, output an error message when the + // type actually conformed to the specified profile. + if (!has_error_) { + return ::testing::AssertionFailure() + << "Unexpected conformance of type:\n" + " " + << type_name_ << "\n\n"; + } + + // Get a list of all expected failures that did not actually fail + // (or that were not run). + std::vector<std::string> nonfailing_tests; + absl::c_set_difference(test_names, test_failures_, + std::back_inserter(nonfailing_tests)); + + // Get a list of all "expected failures" that were never actually run. + std::vector<std::string> unrun_tests; + absl::c_set_difference(nonfailing_tests, test_successes_, + std::back_inserter(unrun_tests)); + + // Report when the user specified tests that were not run. + if (!unrun_tests.empty()) { + const bool tests_were_run = + !(test_failures_.empty() && test_successes_.empty()); + + // Prepare an assertion result used in the case that tests pass that were + // expected to fail. + ::testing::AssertionResult result = ::testing::AssertionFailure(); + result << "When testing type:\n " << type_name_ + << "\n\nThe following tests were expected to fail but were not " + "run"; + + if (tests_were_run) result << " (was the test name spelled correctly?)"; + + result << ":\n\n"; + + // List all of the tests that unexpectedly passed. + for (const auto& test_name : unrun_tests) { + result << " " << test_name << "\n"; + } + + if (!tests_were_run) result << "\nNo tests were run."; + + if (!test_failures_.empty()) { + // List test failures + result << "\nThe tests that were run and failed are:\n\n"; + for (const auto& test_name : test_failures_) { + result << " " << test_name << "\n"; + } + } + + if (!test_successes_.empty()) { + // List test successes + result << "\nThe tests that were run and succeeded are:\n\n"; + for (const auto& test_name : test_successes_) { + result << " " << test_name << "\n"; + } + } + + return result; + } + + // If some tests passed when they were expected to fail, alert the caller. + if (nonfailing_tests.empty()) return ::testing::AssertionSuccess(); + + // Prepare an assertion result used in the case that tests pass that were + // expected to fail. + ::testing::AssertionResult unexpected_successes = + ::testing::AssertionFailure(); + unexpected_successes << "When testing type:\n " << type_name_ + << "\n\nThe following tests passed when they were " + "expected to fail:\n\n"; + + // List all of the tests that unexpectedly passed. + for (const auto& test_name : nonfailing_tests) { + unexpected_successes << " " << test_name << "\n"; + } + + return unexpected_successes; + } + + private: + void outputDivider() { + assertion_result_ << "========================================"; + } + + void addTestFailureImpl() {} + + template <class H, class... T> + void addTestFailureImpl(const H& head, const T&... tail) { + assertion_result_ << head; + addTestFailureImpl(tail...); + } + + ::testing::AssertionResult assertion_result_; + std::set<std::string> test_failures_; + std::set<std::string> test_successes_; + std::string type_name_; + bool has_error_ = false; +}; + template <class T, class /*Enabler*/ = void> struct PropertiesOfImpl {}; @@ -70,31 +260,100 @@ using PropertiesOfT = typename PropertiesOf<T>::type; // standard trait names, which is useful since it allows us to match up each // enum name with a corresponding trait name in macro definitions. -enum class function_kind { maybe, yes, nothrow, trivial }; +// An enum that describes the various expectations on an operations existence. +enum class function_support { maybe, yes, nothrow, trivial }; + +constexpr const char* PessimisticPropertyDescription(function_support v) { + return v == function_support::maybe + ? "no" + : v == function_support::yes + ? "yes, potentially throwing" + : v == function_support::nothrow ? "yes, nothrow" + : "yes, trivial"; +} + +// Return a string that describes the kind of property support that was +// expected. +inline std::string ExpectedFunctionKindList(function_support min, + function_support max) { + if (min == max) { + std::string result = + absl::StrCat("Expected:\n ", + PessimisticPropertyDescription( + static_cast<function_support>(UnderlyingValue(min))), + "\n"); + return result; + } + + std::string result = "Expected one of:\n"; + for (auto curr_support = UnderlyingValue(min); + curr_support <= UnderlyingValue(max); ++curr_support) { + absl::StrAppend(&result, " ", + PessimisticPropertyDescription( + static_cast<function_support>(curr_support)), + "\n"); + } + + return result; +} -#define ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(name) \ - enum class name { maybe, yes, nothrow, trivial } +template <class Enum> +void ExpectModelOfImpl(ConformanceErrors* errors, Enum min_support, + Enum max_support, Enum kind) { + const auto kind_value = UnderlyingValue(kind); + const auto min_support_value = UnderlyingValue(min_support); + const auto max_support_value = UnderlyingValue(max_support); + + if (!(kind_value >= min_support_value && kind_value <= max_support_value)) { + errors->addTestFailure( + PropertyName(kind), "**Failed property expectation**\n\n", + ExpectedFunctionKindList( + static_cast<function_support>(min_support_value), + static_cast<function_support>(max_support_value)), + '\n', "Actual:\n ", + PessimisticPropertyDescription( + static_cast<function_support>(kind_value))); + } else { + errors->addTestSuccess(PropertyName(kind)); + } +} -ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(default_constructible); -ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(move_constructible); -ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(copy_constructible); -ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(move_assignable); -ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(copy_assignable); -ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(destructible); +#define ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(description, name) \ + enum class name { maybe, yes, nothrow, trivial }; \ + \ + constexpr const char* PropertyName(name v) { return description; } \ + static_assert(true, "") // Force a semicolon when using this macro. + +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for default construction", + default_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move construction", + move_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy construction", + copy_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move assignment", + move_assignable); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy assignment", + copy_assignable); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for destruction", + destructible); #undef ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM -#define ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(name) \ - enum class name { maybe, yes, nothrow } +#define ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(description, name) \ + enum class name { maybe, yes, nothrow }; \ + \ + constexpr const char* PropertyName(name v) { return description; } \ + static_assert(true, "") // Force a semicolon when using this macro. -ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(equality_comparable); -ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(inequality_comparable); -ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(less_than_comparable); -ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(less_equal_comparable); -ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(greater_equal_comparable); -ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(greater_than_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for ==", equality_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for !=", inequality_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <", less_than_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <=", less_equal_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >=", + greater_equal_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >", greater_than_comparable); -ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(swappable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for swap", swappable); #undef ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM @@ -104,6 +363,184 @@ constexpr const char* PropertyName(hashable v) { return "support for std::hash"; } +template <class T> +using AlwaysFalse = std::false_type; + +#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(name, property) \ + template <class T> \ + constexpr property property##_support_of() { \ + return std::is_##property<T>::value \ + ? std::is_nothrow_##property<T>::value \ + ? absl::is_trivially_##property<T>::value \ + ? property::trivial \ + : property::nothrow \ + : property::yes \ + : property::maybe; \ + } \ + \ + template <class T, class MinProf, class MaxProf> \ + void ExpectModelOf##name(ConformanceErrors* errors) { \ + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support, \ + PropertiesOfT<MaxProf>::property##_support, \ + property##_support_of<T>()); \ + } + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(DefaultConstructible, + default_constructible); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveConstructible, + move_constructible); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyConstructible, + copy_constructible); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveAssignable, + move_assignable); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyAssignable, + copy_assignable); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(Destructible, destructible); + +#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER + +void BoolFunction(bool) noexcept; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction for checking if an operation exists through SFINAE. +// +// `T` is the type to test and Op is an alias containing the expression to test. +template <class T, template <class...> class Op, class = void> +struct IsOpableImpl : std::false_type {}; + +template <class T, template <class...> class Op> +struct IsOpableImpl<T, Op, absl::void_t<Op<T>>> : std::true_type {}; + +template <template <class...> class Op> +struct IsOpable { + template <class T> + using apply = typename IsOpableImpl<T, Op>::type; +}; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction for checking if an operation exists and is also noexcept +// through SFINAE and the noexcept operator. +/// +// `T` is the type to test and Op is an alias containing the expression to test. +template <class T, template <class...> class Op, class = void> +struct IsNothrowOpableImpl : std::false_type {}; + +template <class T, template <class...> class Op> +struct IsNothrowOpableImpl<T, Op, absl::enable_if_t<Op<T>::value>> + : std::true_type {}; + +template <template <class...> class Op> +struct IsNothrowOpable { + template <class T> + using apply = typename IsNothrowOpableImpl<T, Op>::type; +}; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A macro that produces the necessary function for reporting what kind of +// support a specific comparison operation has and a function for reporting an +// error if a given type's support for that operation does not meet the expected +// requirements. +#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(name, property, op) \ + template <class T, \ + class Result = std::integral_constant< \ + bool, noexcept((BoolFunction)(std::declval<const T&>() op \ + std::declval<const T&>()))>> \ + using name = Result; \ + \ + template <class T> \ + constexpr property property##_support_of() { \ + return IsOpable<name>::apply<T>::value \ + ? IsNothrowOpable<name>::apply<T>::value ? property::nothrow \ + : property::yes \ + : property::maybe; \ + } \ + \ + template <class T, class MinProf, class MaxProf> \ + void ExpectModelOf##name(ConformanceErrors* errors) { \ + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support, \ + PropertiesOfT<MaxProf>::property##_support, \ + property##_support_of<T>()); \ + } +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// Generate the necessary support-checking and error reporting functions for +// each of the comparison operators. +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(EqualityComparable, + equality_comparable, ==); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(InequalityComparable, + inequality_comparable, !=); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessThanComparable, + less_than_comparable, <); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessEqualComparable, + less_equal_comparable, <=); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterEqualComparable, + greater_equal_comparable, >=); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterThanComparable, + greater_than_comparable, >); + +#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// The necessary support-checking and error-reporting functions for swap. +template <class T> +constexpr swappable swappable_support_of() { + return type_traits_internal::IsSwappable<T>::value + ? type_traits_internal::IsNothrowSwappable<T>::value + ? swappable::nothrow + : swappable::yes + : swappable::maybe; +} + +template <class T, class MinProf, class MaxProf> +void ExpectModelOfSwappable(ConformanceErrors* errors) { + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::swappable_support, + PropertiesOfT<MaxProf>::swappable_support, + swappable_support_of<T>()); +} +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// The necessary support-checking and error-reporting functions for std::hash. +template <class T> +constexpr hashable hashable_support_of() { + return type_traits_internal::IsHashable<T>::value ? hashable::yes + : hashable::maybe; +} + +template <class T, class MinProf, class MaxProf> +void ExpectModelOfHashable(ConformanceErrors* errors) { + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::hashable_support, + PropertiesOfT<MaxProf>::hashable_support, + hashable_support_of<T>()); +} +// +//////////////////////////////////////////////////////////////////////////////// + template < default_constructible DefaultConstructibleValue = default_constructible::maybe, @@ -216,6 +653,45 @@ struct ConformanceProfile { HashableValue != hashable::maybe; }; +//////////////////////////////////////////////////////////////////////////////// +// +// Compliant SFINAE-friendliness is not always present on the standard library +// implementations that we support. This helper-struct (and associated enum) is +// used as a means to conditionally check the hashability support of a type. +enum class CheckHashability { no, yes }; + +template <class T, CheckHashability ShouldCheckHashability> +struct conservative_hashable_support_of; + +template <class T> +struct conservative_hashable_support_of<T, CheckHashability::no> { + static constexpr hashable Invoke() { return hashable::maybe; } +}; + +template <class T> +struct conservative_hashable_support_of<T, CheckHashability::yes> { + static constexpr hashable Invoke() { return hashable_support_of<T>(); } +}; +// +//////////////////////////////////////////////////////////////////////////////// + +// The ConformanceProfile that is expected based on introspection into the type +// by way of trait checks. +template <class T, CheckHashability ShouldCheckHashability> +struct SyntacticConformanceProfileOf { + using properties = ConformanceProfile< + default_constructible_support_of<T>(), move_constructible_support_of<T>(), + copy_constructible_support_of<T>(), move_assignable_support_of<T>(), + copy_assignable_support_of<T>(), destructible_support_of<T>(), + equality_comparable_support_of<T>(), + inequality_comparable_support_of<T>(), + less_than_comparable_support_of<T>(), + less_equal_comparable_support_of<T>(), + greater_equal_comparable_support_of<T>(), + greater_than_comparable_support_of<T>(), swappable_support_of<T>(), + conservative_hashable_support_of<T, ShouldCheckHashability>::Invoke()>; +}; + #define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, name) \ template <default_constructible DefaultConstructibleValue, \ move_constructible MoveConstructibleValue, \ @@ -261,12 +737,80 @@ ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable); #undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF #undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL -// Converts an enum to its underlying integral value. -template <class Enum> -constexpr absl::underlying_type_t<Enum> UnderlyingValue(Enum value) { - return static_cast<absl::underlying_type_t<Enum>>(value); +// Retrieve the enum with the minimum underlying value. +// Note: std::min is not constexpr in C++11, which is why this is necessary. +template <class H> +constexpr H MinEnum(H head) { + return head; } +template <class H, class N, class... T> +constexpr H MinEnum(H head, N next, T... tail) { + return (UnderlyingValue)(head) < (UnderlyingValue)(next) + ? (MinEnum)(head, tail...) + : (MinEnum)(next, tail...); +} + +template <class... Profs> +struct MinimalProfiles { + static constexpr default_constructible + default_constructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::default_constructible_support...); + + static constexpr move_constructible move_constructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::move_constructible_support...); + + static constexpr copy_constructible copy_constructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::copy_constructible_support...); + + static constexpr move_assignable move_assignable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::move_assignable_support...); + + static constexpr copy_assignable copy_assignable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::copy_assignable_support...); + + static constexpr destructible destructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::destructible_support...); + + static constexpr equality_comparable equality_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::equality_comparable_support...); + + static constexpr inequality_comparable + inequality_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::inequality_comparable_support...); + + static constexpr less_than_comparable + less_than_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::less_than_comparable_support...); + + static constexpr less_equal_comparable + less_equal_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...); + + static constexpr greater_equal_comparable + greater_equal_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...); + + static constexpr greater_than_comparable + greater_than_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...); + + static constexpr swappable swappable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::swappable_support...); + + static constexpr hashable hashable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::hashable_support...); + + using properties = ConformanceProfile< + default_constructible_support, move_constructible_support, + copy_constructible_support, move_assignable_support, + copy_assignable_support, destructible_support, + equality_comparable_support, inequality_comparable_support, + less_than_comparable_support, less_equal_comparable_support, + greater_equal_comparable_support, greater_than_comparable_support, + swappable_support, hashable_support>; +}; + // Retrieve the enum with the greatest underlying value. // Note: std::max is not constexpr in C++11, which is why this is necessary. template <class H> @@ -369,6 +913,17 @@ struct IsProfileImpl<T, absl::void_t<PropertiesOfT<T>>> : std::true_type {}; template <class T> struct IsProfile : IsProfileImpl<T>::type {}; +// A tag that describes which set of properties we will check when the user +// requires a strict match in conformance (as opposed to a loose match which +// allows more-refined support of any given operation). +// +// Currently only the RegularityDomain exists and it includes all operations +// that the conformance testing suite knows about. The intent is that if the +// suite is expanded to support extension, such as for checking conformance of +// concepts like Iterators or Containers, additional corresponding domains can +// be created. +struct RegularityDomain {}; + } // namespace types_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/third_party/abseil-cpp/absl/types/internal/conformance_testing.h b/third_party/abseil-cpp/absl/types/internal/conformance_testing.h new file mode 100644 index 0000000000..487b0f786b --- /dev/null +++ b/third_party/abseil-cpp/absl/types/internal/conformance_testing.h @@ -0,0 +1,1386 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// conformance_testing.h +// ----------------------------------------------------------------------------- +// + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_ + +//////////////////////////////////////////////////////////////////////////////// +// // +// Many templates in this file take a `T` and a `Prof` type as explicit // +// template arguments. These are a type to be checked and a // +// "Regularity Profile" that describes what operations that type `T` is // +// expected to support. See "regularity_profiles.h" for more details // +// regarding Regularity Profiles. // +// // +//////////////////////////////////////////////////////////////////////////////// + +#include <cstddef> +#include <set> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/internal/conformance_aliases.h" +#include "absl/types/internal/conformance_archetype.h" +#include "absl/types/internal/conformance_profile.h" +#include "absl/types/internal/conformance_testing_helpers.h" +#include "absl/types/internal/parentheses.h" +#include "absl/types/internal/transform_args.h" +#include "absl/utility/utility.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +// Returns true if the compiler incorrectly greedily instantiates constexpr +// templates in any unevaluated context. +constexpr bool constexpr_instantiation_when_unevaluated() { +#if defined(__apple_build_version__) // TODO(calabrese) Make more specific + return true; +#elif defined(__clang__) + return __clang_major__ < 4; +#elif defined(__GNUC__) + // TODO(calabrese) Figure out why gcc 7 fails (seems like a different bug) + return __GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 2) || __GNUC__ >= 7; +#else + return false; +#endif +} + +// Returns true if the standard library being used incorrectly produces an error +// when instantiating the definition of a poisoned std::hash specialization. +constexpr bool poisoned_hash_fails_instantiation() { +#if defined(_MSC_VER) && !defined(_LIBCPP_VERSION) + return _MSC_VER < 1914; +#else + return false; +#endif +} + +template <class Fun> +struct GeneratorType { + decltype(std::declval<const Fun&>()()) operator()() const + noexcept(noexcept(std::declval<const Fun&>()())) { + return fun(); + } + + Fun fun; + const char* description; +}; + +// A "make" function for the GeneratorType template that deduces the function +// object type. +template <class Fun, + absl::enable_if_t<IsNullaryCallable<Fun>::value>** = nullptr> +GeneratorType<Fun> Generator(Fun fun, const char* description) { + return GeneratorType<Fun>{absl::move(fun), description}; +} + +// A type that contains a set of nullary function objects that each return an +// instance of the same type and value (though possibly different +// representations, such as +0 and -0 or two vectors with the same elements but +// with different capacities). +template <class... Funs> +struct EquivalenceClassType { + std::tuple<GeneratorType<Funs>...> generators; +}; + +// A "make" function for the EquivalenceClassType template that deduces the +// function object types and is constrained such that a user can only pass in +// function objects that all have the same return type. +template <class... Funs, absl::enable_if_t<AreGeneratorsWithTheSameReturnType< + Funs...>::value>** = nullptr> +EquivalenceClassType<Funs...> EquivalenceClass(GeneratorType<Funs>... funs) { + return {std::make_tuple(absl::move(funs)...)}; +} + +// A type that contains an ordered series of EquivalenceClassTypes, from +// smallest value to largest value. +template <class... EqClasses> +struct OrderedEquivalenceClasses { + std::tuple<EqClasses...> eq_classes; +}; + +// An object containing the parts of a given (name, initialization expression), +// and is capable of generating a string that describes the given. +struct GivenDeclaration { + std::string outputDeclaration(std::size_t width) const { + const std::size_t indent_size = 2; + std::string result = absl::StrCat(" ", name); + + if (!expression.empty()) { + // Indent + result.resize(indent_size + width, ' '); + absl::StrAppend(&result, " = ", expression, ";\n"); + } else { + absl::StrAppend(&result, ";\n"); + } + + return result; + } + + std::string name; + std::string expression; +}; + +// Produce a string that contains all of the givens of an error report. +template <class... Decls> +std::string PrepareGivenContext(const Decls&... decls) { + const std::size_t width = (std::max)({decls.name.size()...}); + return absl::StrCat("Given:\n", decls.outputDeclaration(width)..., "\n"); +} + +//////////////////////////////////////////////////////////////////////////////// +// Function objects that perform a check for each comparison operator // +//////////////////////////////////////////////////////////////////////////////// + +#define ABSL_INTERNAL_EXPECT_OP(name, op) \ + struct Expect##name { \ + template <class T> \ + void operator()(absl::string_view test_name, absl::string_view context, \ + const T& lhs, const T& rhs, absl::string_view lhs_name, \ + absl::string_view rhs_name) const { \ + if (!static_cast<bool>(lhs op rhs)) { \ + errors->addTestFailure( \ + test_name, absl::StrCat(context, \ + "**Unexpected comparison result**\n" \ + "\n" \ + "Expression:\n" \ + " ", \ + lhs_name, " " #op " ", rhs_name, \ + "\n" \ + "\n" \ + "Expected: true\n" \ + " Actual: false")); \ + } else { \ + errors->addTestSuccess(test_name); \ + } \ + } \ + \ + ConformanceErrors* errors; \ + }; \ + \ + struct ExpectNot##name { \ + template <class T> \ + void operator()(absl::string_view test_name, absl::string_view context, \ + const T& lhs, const T& rhs, absl::string_view lhs_name, \ + absl::string_view rhs_name) const { \ + if (lhs op rhs) { \ + errors->addTestFailure( \ + test_name, absl::StrCat(context, \ + "**Unexpected comparison result**\n" \ + "\n" \ + "Expression:\n" \ + " ", \ + lhs_name, " " #op " ", rhs_name, \ + "\n" \ + "\n" \ + "Expected: false\n" \ + " Actual: true")); \ + } else { \ + errors->addTestSuccess(test_name); \ + } \ + } \ + \ + ConformanceErrors* errors; \ + } + +ABSL_INTERNAL_EXPECT_OP(Eq, ==); +ABSL_INTERNAL_EXPECT_OP(Ne, !=); +ABSL_INTERNAL_EXPECT_OP(Lt, <); +ABSL_INTERNAL_EXPECT_OP(Le, <=); +ABSL_INTERNAL_EXPECT_OP(Ge, >=); +ABSL_INTERNAL_EXPECT_OP(Gt, >); + +#undef ABSL_INTERNAL_EXPECT_OP + +// A function object that verifies that two objects hash to the same value by +// way of the std::hash specialization. +struct ExpectSameHash { + template <class T> + void operator()(absl::string_view test_name, absl::string_view context, + const T& lhs, const T& rhs, absl::string_view lhs_name, + absl::string_view rhs_name) const { + if (std::hash<T>()(lhs) != std::hash<T>()(rhs)) { + errors->addTestFailure( + test_name, absl::StrCat(context, + "**Unexpected hash result**\n" + "\n" + "Expression:\n" + " std::hash<T>()(", + lhs_name, ") == std::hash<T>()(", rhs_name, + ")\n" + "\n" + "Expected: true\n" + " Actual: false")); + } else { + errors->addTestSuccess(test_name); + } + } + + ConformanceErrors* errors; +}; + +// A function template that takes two objects and verifies that each comparison +// operator behaves in a way that is consistent with equality. It has "OneWay" +// in the name because the first argument will always be the left-hand operand +// of the corresponding comparison operator and the second argument will +// always be the right-hand operand. It will never switch that order. +// At a higher level in the test suite, the one-way form is called once for each +// of the two possible orders whenever lhs and rhs are not the same initializer. +template <class T, class Prof> +void ExpectOneWayEquality(ConformanceErrors* errors, + absl::string_view test_name, + absl::string_view context, const T& lhs, const T& rhs, + absl::string_view lhs_name, + absl::string_view rhs_name) { + If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke( + ExpectEq{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke( + ExpectNotNe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke( + ExpectNotLt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke( + ExpectLe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke( + ExpectGe{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke( + ExpectNotGt{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); + + If<PropertiesOfT<Prof>::is_hashable>::Invoke( + ExpectSameHash{errors}, test_name, context, lhs, rhs, lhs_name, rhs_name); +} + +// A function template that takes two objects and verifies that each comparison +// operator behaves in a way that is consistent with equality. This function +// differs from ExpectOneWayEquality in that this will do checks with argument +// order reversed in addition to in-order. +template <class T, class Prof> +void ExpectEquality(ConformanceErrors* errors, absl::string_view test_name, + absl::string_view context, const T& lhs, const T& rhs, + absl::string_view lhs_name, absl::string_view rhs_name) { + (ExpectOneWayEquality<T, Prof>)(errors, test_name, context, lhs, rhs, + lhs_name, rhs_name); + (ExpectOneWayEquality<T, Prof>)(errors, test_name, context, rhs, lhs, + rhs_name, lhs_name); +} + +// Given a generator, makes sure that a generated value and a moved-from +// generated value are equal. +template <class T, class Prof> +struct ExpectMoveConstructOneGenerator { + template <class Fun> + void operator()(const Fun& generator) const { + const T object = generator(); + const T moved_object = absl::move(generator()); // Force no elision. + + (ExpectEquality<T, Prof>)(errors, "Move construction", + PrepareGivenContext( + GivenDeclaration{"const _T object", + generator.description}, + GivenDeclaration{"const _T moved_object", + std::string("std::move(") + + generator.description + + ")"}), + object, moved_object, "object", "moved_object"); + } + + ConformanceErrors* errors; +}; + +// Given a generator, makes sure that a generated value and a copied-from +// generated value are equal. +template <class T, class Prof> +struct ExpectCopyConstructOneGenerator { + template <class Fun> + void operator()(const Fun& generator) const { + const T object = generator(); + const T copied_object = static_cast<const T&>(generator()); + + (ExpectEquality<T, Prof>)(errors, "Copy construction", + PrepareGivenContext( + GivenDeclaration{"const _T object", + generator.description}, + GivenDeclaration{ + "const _T copied_object", + std::string("static_cast<const _T&>(") + + generator.description + ")"}), + object, copied_object, "object", "copied_object"); + } + + ConformanceErrors* errors; +}; + +// Default-construct and do nothing before destruction. +// +// This is useful in exercising the codepath of default construction followed by +// destruction, but does not explicitly test anything. An example of where this +// might fail is a default destructor that default-initializes a scalar and a +// destructor reads the value of that member. Sanitizers can catch this as long +// as our test attempts to execute such a case. +template <class T> +struct ExpectDefaultConstructWithDestruct { + void operator()() const { + // Scoped so that destructor gets called before reporting success. + { + T object; + static_cast<void>(object); + } + + errors->addTestSuccess("Default construction"); + } + + ConformanceErrors* errors; +}; + +// Check move-assign into a default-constructed object. +template <class T, class Prof> +struct ExpectDefaultConstructWithMoveAssign { + template <class Fun> + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object; + object = generator(); + + (ExpectEquality<T, Prof>)(errors, "Move assignment", + PrepareGivenContext( + GivenDeclaration{"const _T object", + generator.description}, + GivenDeclaration{"_T object", ""}, + GivenDeclaration{"object", + generator.description}), + object, source_of_truth, "std::as_const(object)", + "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Check copy-assign into a default-constructed object. +template <class T, class Prof> +struct ExpectDefaultConstructWithCopyAssign { + template <class Fun> + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object; + object = static_cast<const T&>(generator()); + + (ExpectEquality<T, Prof>)(errors, "Copy assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth", + generator.description}, + GivenDeclaration{"_T object", ""}, + GivenDeclaration{ + "object", + std::string("static_cast<const _T&>(") + + generator.description + ")"}), + object, source_of_truth, "std::as_const(object)", + "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Perform a self move-assign. +template <class T, class Prof> +struct ExpectSelfMoveAssign { + template <class Fun> + void operator()(const Fun& generator) const { + T object = generator(); + object = absl::move(object); + + // NOTE: Self move-assign results in a valid-but-unspecified state. + + (ExpectEquality<T, Prof>)(errors, "Move assignment", + PrepareGivenContext( + GivenDeclaration{"_T object", + generator.description}, + GivenDeclaration{"object", + "std::move(object)"}), + object, object, "object", "object"); + } + + ConformanceErrors* errors; +}; + +// Perform a self copy-assign. +template <class T, class Prof> +struct ExpectSelfCopyAssign { + template <class Fun> + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object = generator(); + const T& const_object = object; + object = const_object; + + (ExpectEquality<T, Prof>)(errors, "Copy assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth", + generator.description}, + GivenDeclaration{"_T object", + generator.description}, + GivenDeclaration{"object", + "std::as_const(object)"}), + const_object, source_of_truth, + "std::as_const(object)", "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Perform a self-swap. +template <class T, class Prof> +struct ExpectSelfSwap { + template <class Fun> + void operator()(const Fun& generator) const { + const T source_of_truth = generator(); + T object = generator(); + + type_traits_internal::Swap(object, object); + + std::string preliminary_info = absl::StrCat( + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth", generator.description}, + GivenDeclaration{"_T object", generator.description}), + "After performing a self-swap:\n" + " using std::swap;\n" + " swap(object, object);\n" + "\n"); + + (ExpectEquality<T, Prof>)(errors, "Swap", std::move(preliminary_info), + object, source_of_truth, "std::as_const(object)", + "source_of_truth"); + } + + ConformanceErrors* errors; +}; + +// Perform each of the single-generator checks when necessary operations are +// supported. +template <class T, class Prof> +struct ExpectSelfComparison { + template <class Fun> + void operator()(const Fun& generator) const { + const T object = generator(); + (ExpectOneWayEquality<T, Prof>)(errors, "Comparison", + PrepareGivenContext(GivenDeclaration{ + "const _T object", + generator.description}), + object, object, "object", "object"); + } + + ConformanceErrors* errors; +}; + +// Perform each of the single-generator checks when necessary operations are +// supported. +template <class T, class Prof> +struct ExpectConsistency { + template <class Fun> + void operator()(const Fun& generator) const { + If<PropertiesOfT<Prof>::is_move_constructible>::Invoke( + ExpectMoveConstructOneGenerator<T, Prof>{errors}, generator); + + If<PropertiesOfT<Prof>::is_copy_constructible>::Invoke( + ExpectCopyConstructOneGenerator<T, Prof>{errors}, generator); + + If<PropertiesOfT<Prof>::is_default_constructible && + PropertiesOfT<Prof>::is_move_assignable>:: + Invoke(ExpectDefaultConstructWithMoveAssign<T, Prof>{errors}, + generator); + + If<PropertiesOfT<Prof>::is_default_constructible && + PropertiesOfT<Prof>::is_copy_assignable>:: + Invoke(ExpectDefaultConstructWithCopyAssign<T, Prof>{errors}, + generator); + + If<PropertiesOfT<Prof>::is_move_assignable>::Invoke( + ExpectSelfMoveAssign<T, Prof>{errors}, generator); + + If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke( + ExpectSelfCopyAssign<T, Prof>{errors}, generator); + + If<PropertiesOfT<Prof>::is_swappable>::Invoke( + ExpectSelfSwap<T, Prof>{errors}, generator); + } + + ConformanceErrors* errors; +}; + +// Check move-assign with two different values. +template <class T, class Prof> +struct ExpectMoveAssign { + template <class Fun0, class Fun1> + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T source_of_truth1 = generator1(); + T object = generator0(); + object = generator1(); + + (ExpectEquality<T, Prof>)(errors, "Move assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth1", + generator1.description}, + GivenDeclaration{"_T object", + generator0.description}, + GivenDeclaration{"object", + generator1.description}), + object, source_of_truth1, "std::as_const(object)", + "source_of_truth1"); + } + + ConformanceErrors* errors; +}; + +// Check copy-assign with two different values. +template <class T, class Prof> +struct ExpectCopyAssign { + template <class Fun0, class Fun1> + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T source_of_truth1 = generator1(); + T object = generator0(); + object = static_cast<const T&>(generator1()); + + (ExpectEquality<T, Prof>)(errors, "Copy assignment", + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth1", + generator1.description}, + GivenDeclaration{"_T object", + generator0.description}, + GivenDeclaration{ + "object", + std::string("static_cast<const _T&>(") + + generator1.description + ")"}), + object, source_of_truth1, "std::as_const(object)", + "source_of_truth1"); + } + + ConformanceErrors* errors; +}; + +// Check swap with two different values. +template <class T, class Prof> +struct ExpectSwap { + template <class Fun0, class Fun1> + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T source_of_truth0 = generator0(); + const T source_of_truth1 = generator1(); + T object0 = generator0(); + T object1 = generator1(); + + type_traits_internal::Swap(object0, object1); + + const std::string context = + PrepareGivenContext( + GivenDeclaration{"const _T source_of_truth0", + generator0.description}, + GivenDeclaration{"const _T source_of_truth1", + generator1.description}, + GivenDeclaration{"_T object0", generator0.description}, + GivenDeclaration{"_T object1", generator1.description}) + + "After performing a swap:\n" + " using std::swap;\n" + " swap(object0, object1);\n" + "\n"; + + (ExpectEquality<T, Prof>)(errors, "Swap", context, object0, + source_of_truth1, "std::as_const(object0)", + "source_of_truth1"); + (ExpectEquality<T, Prof>)(errors, "Swap", context, object1, + source_of_truth0, "std::as_const(object1)", + "source_of_truth0"); + } + + ConformanceErrors* errors; +}; + +// Validate that `generator0` and `generator1` produce values that are equal. +template <class T, class Prof> +struct ExpectEquivalenceClassComparison { + template <class Fun0, class Fun1> + void operator()(const Fun0& generator0, const Fun1& generator1) const { + const T object0 = generator0(); + const T object1 = generator1(); + + (ExpectEquality<T, Prof>)(errors, "Comparison", + PrepareGivenContext( + GivenDeclaration{"const _T object0", + generator0.description}, + GivenDeclaration{"const _T object1", + generator1.description}), + object0, object1, "object0", "object1"); + } + + ConformanceErrors* errors; +}; + +// Validate that all objects in the same equivalence-class have the same value. +template <class T, class Prof> +struct ExpectEquivalenceClassConsistency { + template <class Fun0, class Fun1> + void operator()(const Fun0& generator0, const Fun1& generator1) const { + If<PropertiesOfT<Prof>::is_move_assignable>::Invoke( + ExpectMoveAssign<T, Prof>{errors}, generator0, generator1); + + If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke( + ExpectCopyAssign<T, Prof>{errors}, generator0, generator1); + + If<PropertiesOfT<Prof>::is_swappable>::Invoke(ExpectSwap<T, Prof>{errors}, + generator0, generator1); + } + + ConformanceErrors* errors; +}; + +// Given a "lesser" object and a "greater" object, perform every combination of +// comparison operators supported for the type, expecting consistent results. +template <class T, class Prof> +void ExpectOrdered(ConformanceErrors* errors, absl::string_view context, + const T& small, const T& big, absl::string_view small_name, + absl::string_view big_name) { + const absl::string_view test_name = "Comparison"; + + If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke( + ExpectNotEq{errors}, test_name, context, small, big, small_name, + big_name); + If<PropertiesOfT<Prof>::is_equality_comparable>::Invoke( + ExpectNotEq{errors}, test_name, context, big, small, big_name, + small_name); + + If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke( + ExpectNe{errors}, test_name, context, small, big, small_name, big_name); + If<PropertiesOfT<Prof>::is_inequality_comparable>::Invoke( + ExpectNe{errors}, test_name, context, big, small, big_name, small_name); + + If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke( + ExpectLt{errors}, test_name, context, small, big, small_name, big_name); + If<PropertiesOfT<Prof>::is_less_than_comparable>::Invoke( + ExpectNotLt{errors}, test_name, context, big, small, big_name, + small_name); + + If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke( + ExpectLe{errors}, test_name, context, small, big, small_name, big_name); + If<PropertiesOfT<Prof>::is_less_equal_comparable>::Invoke( + ExpectNotLe{errors}, test_name, context, big, small, big_name, + small_name); + + If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke( + ExpectNotGe{errors}, test_name, context, small, big, small_name, + big_name); + If<PropertiesOfT<Prof>::is_greater_equal_comparable>::Invoke( + ExpectGe{errors}, test_name, context, big, small, big_name, small_name); + + If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke( + ExpectNotGt{errors}, test_name, context, small, big, small_name, + big_name); + If<PropertiesOfT<Prof>::is_greater_than_comparable>::Invoke( + ExpectGt{errors}, test_name, context, big, small, big_name, small_name); +} + +// For every two elements of an equivalence class, makes sure that those two +// elements compare equal, including checks with the same argument passed as +// both operands. +template <class T, class Prof> +struct ExpectEquivalenceClassComparisons { + template <class... Funs> + void operator()(EquivalenceClassType<Funs...> eq_class) const { + (ForEachTupleElement)(ExpectSelfComparison<T, Prof>{errors}, + eq_class.generators); + + (ForEveryTwo)(ExpectEquivalenceClassComparison<T, Prof>{errors}, + eq_class.generators); + } + + ConformanceErrors* errors; +}; + +// For every element of an equivalence class, makes sure that the element is +// self-consistent (in other words, if any of move/copy/swap are defined, +// perform those operations and make such that results and operands still +// compare equal to known values whenever it is required for that operation. +template <class T, class Prof> +struct ExpectEquivalenceClass { + template <class... Funs> + void operator()(EquivalenceClassType<Funs...> eq_class) const { + (ForEachTupleElement)(ExpectConsistency<T, Prof>{errors}, + eq_class.generators); + + (ForEveryTwo)(ExpectEquivalenceClassConsistency<T, Prof>{errors}, + eq_class.generators); + } + + ConformanceErrors* errors; +}; + +// Validate that the passed-in argument is a generator of a greater value than +// the one produced by the "small_gen" datamember with respect to all of the +// comparison operators that Prof requires, with both argument orders to test. +template <class T, class Prof, class SmallGenerator> +struct ExpectBiggerGeneratorThanComparisons { + template <class BigGenerator> + void operator()(BigGenerator big_gen) const { + const T small = small_gen(); + const T big = big_gen(); + + (ExpectOrdered<T, Prof>)(errors, + PrepareGivenContext( + GivenDeclaration{"const _T small", + small_gen.description}, + GivenDeclaration{"const _T big", + big_gen.description}), + small, big, "small", "big"); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Perform all of the move, copy, and swap checks on the value generated by +// `small_gen` and the value generated by `big_gen`. +template <class T, class Prof, class SmallGenerator> +struct ExpectBiggerGeneratorThan { + template <class BigGenerator> + void operator()(BigGenerator big_gen) const { + If<PropertiesOfT<Prof>::is_move_assignable>::Invoke( + ExpectMoveAssign<T, Prof>{errors}, small_gen, big_gen); + If<PropertiesOfT<Prof>::is_move_assignable>::Invoke( + ExpectMoveAssign<T, Prof>{errors}, big_gen, small_gen); + + If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke( + ExpectCopyAssign<T, Prof>{errors}, small_gen, big_gen); + If<PropertiesOfT<Prof>::is_copy_assignable>::Invoke( + ExpectCopyAssign<T, Prof>{errors}, big_gen, small_gen); + + If<PropertiesOfT<Prof>::is_swappable>::Invoke(ExpectSwap<T, Prof>{errors}, + small_gen, big_gen); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Validate that the result of a generator is greater than the results of all +// generators in an equivalence class with respect to comparisons. +template <class T, class Prof, class SmallGenerator> +struct ExpectBiggerGeneratorThanEqClassesComparisons { + template <class BigEqClass> + void operator()(BigEqClass big_eq_class) const { + (ForEachTupleElement)( + ExpectBiggerGeneratorThanComparisons<T, Prof, SmallGenerator>{small_gen, + errors}, + big_eq_class.generators); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Validate that the non-comparison binary operations required by Prof are +// correct for the result of each generator of big_eq_class and a generator of +// the logically smaller value returned by small_gen. +template <class T, class Prof, class SmallGenerator> +struct ExpectBiggerGeneratorThanEqClasses { + template <class BigEqClass> + void operator()(BigEqClass big_eq_class) const { + (ForEachTupleElement)( + ExpectBiggerGeneratorThan<T, Prof, SmallGenerator>{small_gen, errors}, + big_eq_class.generators); + } + + SmallGenerator small_gen; + ConformanceErrors* errors; +}; + +// Validate that each equivalence class that is passed is logically less than +// the equivalence classes that comes later on in the argument list. +template <class T, class Prof> +struct ExpectOrderedEquivalenceClassesComparisons { + template <class... BigEqClasses> + struct Impl { + // Validate that the value produced by `small_gen` is less than all of the + // values generated by those of the logically larger equivalence classes. + template <class SmallGenerator> + void operator()(SmallGenerator small_gen) const { + (ForEachTupleElement)(ExpectBiggerGeneratorThanEqClassesComparisons< + T, Prof, SmallGenerator>{small_gen, errors}, + big_eq_classes); + } + + std::tuple<BigEqClasses...> big_eq_classes; + ConformanceErrors* errors; + }; + + // When given no equivalence classes, no validation is necessary. + void operator()() const {} + + template <class SmallEqClass, class... BigEqClasses> + void operator()(SmallEqClass small_eq_class, + BigEqClasses... big_eq_classes) const { + // For each generator in the first equivalence class, make sure that it is + // less than each of those in the logically greater equivalence classes. + (ForEachTupleElement)( + Impl<BigEqClasses...>{std::make_tuple(absl::move(big_eq_classes)...), + errors}, + small_eq_class.generators); + + // Recurse so that all equivalence class combinations are checked. + (*this)(absl::move(big_eq_classes)...); + } + + ConformanceErrors* errors; +}; + +// Validate that the non-comparison binary operations required by Prof are +// correct for the result of each generator of big_eq_classes and a generator of +// the logically smaller value returned by small_gen. +template <class T, class Prof> +struct ExpectOrderedEquivalenceClasses { + template <class... BigEqClasses> + struct Impl { + template <class SmallGenerator> + void operator()(SmallGenerator small_gen) const { + (ForEachTupleElement)( + ExpectBiggerGeneratorThanEqClasses<T, Prof, SmallGenerator>{small_gen, + errors}, + big_eq_classes); + } + + std::tuple<BigEqClasses...> big_eq_classes; + ConformanceErrors* errors; + }; + + // Check that small_eq_class is logically consistent and also is logically + // less than all values in big_eq_classes. + template <class SmallEqClass, class... BigEqClasses> + void operator()(SmallEqClass small_eq_class, + BigEqClasses... big_eq_classes) const { + (ForEachTupleElement)( + Impl<BigEqClasses...>{std::make_tuple(absl::move(big_eq_classes)...), + errors}, + small_eq_class.generators); + + (*this)(absl::move(big_eq_classes)...); + } + + // Terminating case of operator(). + void operator()() const {} + + ConformanceErrors* errors; +}; + +// Validate that a type meets the syntactic requirements of std::hash if the +// range of profiles requires it. +template <class T, class MinProf, class MaxProf> +struct ExpectHashable { + void operator()() const { + ExpectModelOfHashable<T, MinProf, MaxProf>(errors); + } + + ConformanceErrors* errors; +}; + +// Validate that the type `T` meets all of the requirements associated with +// `MinProf` and without going beyond the syntactic properties of `MaxProf`. +template <class T, class MinProf, class MaxProf> +struct ExpectModels { + void operator()(ConformanceErrors* errors) const { + ExpectModelOfDefaultConstructible<T, MinProf, MaxProf>(errors); + ExpectModelOfMoveConstructible<T, MinProf, MaxProf>(errors); + ExpectModelOfCopyConstructible<T, MinProf, MaxProf>(errors); + ExpectModelOfMoveAssignable<T, MinProf, MaxProf>(errors); + ExpectModelOfCopyAssignable<T, MinProf, MaxProf>(errors); + ExpectModelOfDestructible<T, MinProf, MaxProf>(errors); + ExpectModelOfEqualityComparable<T, MinProf, MaxProf>(errors); + ExpectModelOfInequalityComparable<T, MinProf, MaxProf>(errors); + ExpectModelOfLessThanComparable<T, MinProf, MaxProf>(errors); + ExpectModelOfLessEqualComparable<T, MinProf, MaxProf>(errors); + ExpectModelOfGreaterEqualComparable<T, MinProf, MaxProf>(errors); + ExpectModelOfGreaterThanComparable<T, MinProf, MaxProf>(errors); + ExpectModelOfSwappable<T, MinProf, MaxProf>(errors); + + // Only check hashability on compilers that have a compliant default-hash. + If<!poisoned_hash_fails_instantiation()>::Invoke( + ExpectHashable<T, MinProf, MaxProf>{errors}); + } +}; + +// A metafunction that yields a Profile matching the set of properties that are +// safe to be checked (lack-of-hashability is only checked on standard library +// implementations that are standards compliant in that they provide a std::hash +// primary template that is SFINAE-friendly) +template <class LogicalProf, class T> +struct MinimalCheckableProfile { + using type = + MinimalProfiles<PropertiesOfT<LogicalProf>, + PropertiesOfT<SyntacticConformanceProfileOf< + T, !PropertiesOfT<LogicalProf>::is_hashable && + poisoned_hash_fails_instantiation() + ? CheckHashability::no + : CheckHashability::yes>>>; +}; + +// An identity metafunction +template <class T> +struct Always { + using type = T; +}; + +// Validate the T meets all of the necessary requirements of LogicalProf, with +// syntactic requirements defined by the profile range [MinProf, MaxProf]. +template <class T, class LogicalProf, class MinProf, class MaxProf, + class... EqClasses> +ConformanceErrors ExpectRegularityImpl( + OrderedEquivalenceClasses<EqClasses...> vals) { + ConformanceErrors errors((NameOf<T>())); + + If<!constexpr_instantiation_when_unevaluated()>::Invoke( + ExpectModels<T, MinProf, MaxProf>(), &errors); + + using minimal_profile = typename absl::conditional_t< + constexpr_instantiation_when_unevaluated(), Always<LogicalProf>, + MinimalCheckableProfile<LogicalProf, T>>::type; + + If<PropertiesOfT<minimal_profile>::is_default_constructible>::Invoke( + ExpectDefaultConstructWithDestruct<T>{&errors}); + + ////////////////////////////////////////////////////////////////////////////// + // Perform all comparison checks first, since later checks depend on their + // correctness. + // + // Check all of the comparisons for all values in the same equivalence + // class (equal with respect to comparison operators and hash the same). + (ForEachTupleElement)( + ExpectEquivalenceClassComparisons<T, minimal_profile>{&errors}, + vals.eq_classes); + + // Check all of the comparisons for each combination of values that are in + // different equivalence classes (not equal with respect to comparison + // operators). + absl::apply( + ExpectOrderedEquivalenceClassesComparisons<T, minimal_profile>{&errors}, + vals.eq_classes); + // + ////////////////////////////////////////////////////////////////////////////// + + // Perform remaining checks, relying on comparisons. + // TODO(calabrese) short circuit if any comparisons above failed. + (ForEachTupleElement)(ExpectEquivalenceClass<T, minimal_profile>{&errors}, + vals.eq_classes); + + absl::apply(ExpectOrderedEquivalenceClasses<T, minimal_profile>{&errors}, + vals.eq_classes); + + return errors; +} + +// A type that represents a range of profiles that are acceptable to be matched. +// +// `MinProf` is the minimum set of syntactic requirements that must be met. +// +// `MaxProf` is the maximum set of syntactic requirements that must be met. +// This maximum is particularly useful for certain "strictness" checking. Some +// examples for when this is useful: +// +// * Making sure that a type is move-only (rather than simply movable) +// +// * Making sure that a member function is *not* noexcept in cases where it +// cannot be noexcept, such as if a dependent datamember has certain +// operations that are not noexcept. +// +// * Making sure that a type tightly matches a spec, such as the standard. +// +// `LogicalProf` is the Profile for which run-time testing is to take place. +// +// Note: The reason for `LogicalProf` is because it is often the case, when +// dealing with templates, that a declaration of a given operation is specified, +// but whose body would fail to instantiate. Examples include the +// copy-constructor of a standard container when the element-type is move-only, +// or the comparison operators of a standard container when the element-type +// does not have the necessary comparison operations defined. The `LogicalProf` +// parameter allows us to capture the intent of what should be tested at +// run-time, even in the cases where syntactically it might otherwise appear as +// though the type undergoing testing supports more than it actually does. +template <class LogicalProf, class MinProf = LogicalProf, + class MaxProf = MinProf> +struct ProfileRange { + using logical_profile = LogicalProf; + using min_profile = MinProf; + using max_profile = MaxProf; +}; + +// Similar to ProfileRange except that it creates a profile range that is +// coupled with a Domain and is used when testing that a type matches exactly +// the "minimum" requirements of LogicalProf. +template <class StrictnessDomain, class LogicalProf, + class MinProf = LogicalProf, class MaxProf = MinProf> +struct StrictProfileRange { + // We do not yet support extension. + static_assert( + std::is_same<StrictnessDomain, RegularityDomain>::value, + "Currently, the only valid StrictnessDomain is RegularityDomain."); + using strictness_domain = StrictnessDomain; + using logical_profile = LogicalProf; + using min_profile = MinProf; + using max_profile = MaxProf; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that creates a StrictProfileRange from a Domain and either a +// Profile or ProfileRange. +template <class StrictnessDomain, class ProfOrRange> +struct MakeStrictProfileRange; + +template <class StrictnessDomain, class LogicalProf> +struct MakeStrictProfileRange { + using type = StrictProfileRange<StrictnessDomain, LogicalProf>; +}; + +template <class StrictnessDomain, class LogicalProf, class MinProf, + class MaxProf> +struct MakeStrictProfileRange<StrictnessDomain, + ProfileRange<LogicalProf, MinProf, MaxProf>> { + using type = + StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>; +}; + +template <class StrictnessDomain, class ProfOrRange> +using MakeStrictProfileRangeT = + typename MakeStrictProfileRange<StrictnessDomain, ProfOrRange>::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// A profile in the RegularityDomain with the strongest possible requirements. +using MostStrictProfile = + CombineProfiles<TriviallyCompleteProfile, NothrowComparableProfile>; + +// Forms a ProfileRange that treats the Profile as the bare minimum requirements +// of a type. +template <class LogicalProf, class MinProf = LogicalProf> +using LooseProfileRange = StrictProfileRange<RegularityDomain, LogicalProf, + MinProf, MostStrictProfile>; + +template <class Prof> +using MakeLooseProfileRangeT = Prof; + +//////////////////////////////////////////////////////////////////////////////// +// +// The following classes implement the metafunction ProfileRangeOfT<T> that +// takes either a Profile or ProfileRange and yields the ProfileRange to be +// used during testing. +// +template <class T, class /*Enabler*/ = void> +struct ProfileRangeOfImpl; + +template <class T> +struct ProfileRangeOfImpl<T, absl::void_t<PropertiesOfT<T>>> { + using type = LooseProfileRange<T>; +}; + +template <class T> +struct ProfileRangeOf : ProfileRangeOfImpl<T> {}; + +template <class StrictnessDomain, class LogicalProf, class MinProf, + class MaxProf> +struct ProfileRangeOf< + StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>> { + using type = + StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>; +}; + +template <class T> +using ProfileRangeOfT = typename ProfileRangeOf<T>::type; +// +//////////////////////////////////////////////////////////////////////////////// + +// Extract the logical profile of a range (what will be runtime tested). +template <class T> +using LogicalProfileOfT = typename ProfileRangeOfT<T>::logical_profile; + +// Extract the minimal syntactic profile of a range (error if not at least). +template <class T> +using MinProfileOfT = typename ProfileRangeOfT<T>::min_profile; + +// Extract the maximum syntactic profile of a range (error if more than). +template <class T> +using MaxProfileOfT = typename ProfileRangeOfT<T>::max_profile; + +//////////////////////////////////////////////////////////////////////////////// +// +template <class T> +struct IsProfileOrProfileRange : IsProfile<T>::type {}; + +template <class StrictnessDomain, class LogicalProf, class MinProf, + class MaxProf> +struct IsProfileOrProfileRange< + StrictProfileRange<StrictnessDomain, LogicalProf, MinProf, MaxProf>> + : std::true_type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// TODO(calabrese): Consider naming the functions in this class the same as +// the macros (defined later on) so that auto-complete leads to the correct name +// and so that a user cannot accidentally call a function rather than the macro +// form. +template <bool ExpectSuccess, class T, class... EqClasses> +struct ExpectConformanceOf { + // Add a value to be tested. Subsequent calls to this function on the same + // object must specify logically "larger" values with respect to the + // comparison operators of the type, if any. + // + // NOTE: This function should not be called directly. A stateless lambda is + // implicitly formed and passed when using the INITIALIZER macro at the bottom + // of this file. + template <class Fun, + absl::enable_if_t<std::is_same< + ResultOfGeneratorT<GeneratorType<Fun>>, T>::value>** = nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses..., + EquivalenceClassType<Fun>> + initializer(GeneratorType<Fun> fun) && { + return { + {std::tuple_cat(absl::move(ordered_vals.eq_classes), + std::make_tuple((EquivalenceClass)(absl::move(fun))))}, + std::move(expected_failed_tests)}; + } + + template <class... TestNames, + absl::enable_if_t<!ExpectSuccess && sizeof...(EqClasses) == 0 && + absl::conjunction<std::is_convertible< + TestNames, absl::string_view>...>::value>** = + nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...> + due_to(TestNames&&... test_names) && { + (InsertEach)(&expected_failed_tests, + absl::AsciiStrToLower(absl::string_view(test_names))...); + + return {absl::move(ordered_vals), std::move(expected_failed_tests)}; + } + + template <class... TestNames, int = 0, // MSVC disambiguator + absl::enable_if_t<ExpectSuccess && sizeof...(EqClasses) == 0 && + absl::conjunction<std::is_convertible< + TestNames, absl::string_view>...>::value>** = + nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses...> + due_to(TestNames&&... test_names) && { + // TODO(calabrese) Instead have DUE_TO only exist via a CRTP base. + // This would produce better errors messages than the static_assert. + static_assert(!ExpectSuccess, + "DUE_TO cannot be called when conformance is expected -- did " + "you mean to use ASSERT_NONCONFORMANCE_OF?"); + } + + // Add a value to be tested. Subsequent calls to this function on the same + // object must specify logically "larger" values with respect to the + // comparison operators of the type, if any. + // + // NOTE: This function should not be called directly. A stateful lambda is + // implicitly formed and passed when using the INITIALIZER macro at the bottom + // of this file. + template <class Fun, + absl::enable_if_t<std::is_same< + ResultOfGeneratorT<GeneratorType<Fun>>, T>::value>** = nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses..., + EquivalenceClassType<Fun>> + dont_class_directly_stateful_initializer(GeneratorType<Fun> fun) && { + return { + {std::tuple_cat(absl::move(ordered_vals.eq_classes), + std::make_tuple((EquivalenceClass)(absl::move(fun))))}, + std::move(expected_failed_tests)}; + } + + // Add a set of value to be tested, where each value is equal with respect to + // the comparison operators and std::hash specialization, if defined. + template < + class... Funs, + absl::void_t<absl::enable_if_t<std::is_same< + ResultOfGeneratorT<GeneratorType<Funs>>, T>::value>...>** = nullptr> + ABSL_MUST_USE_RESULT ExpectConformanceOf<ExpectSuccess, T, EqClasses..., + EquivalenceClassType<Funs...>> + equivalence_class(GeneratorType<Funs>... funs) && { + return {{std::tuple_cat( + absl::move(ordered_vals.eq_classes), + std::make_tuple((EquivalenceClass)(absl::move(funs)...)))}, + std::move(expected_failed_tests)}; + } + + // Execute the tests for the captured set of values, strictly matching a range + // of expected profiles in a given domain. + template < + class ProfRange, + absl::enable_if_t<IsProfileOrProfileRange<ProfRange>::value>** = nullptr> + ABSL_MUST_USE_RESULT ::testing::AssertionResult with_strict_profile( + ProfRange /*profile*/) { + ConformanceErrors test_result = + (ExpectRegularityImpl< + T, LogicalProfileOfT<ProfRange>, MinProfileOfT<ProfRange>, + MaxProfileOfT<ProfRange>>)(absl::move(ordered_vals)); + + return ExpectSuccess ? test_result.assertionResult() + : test_result.expectFailedTests(expected_failed_tests); + } + + // Execute the tests for the captured set of values, loosely matching a range + // of expected profiles (loose in that an interface is allowed to be more + // refined that a profile suggests, such as a type having a noexcept copy + // constructor when all that is required is that the copy constructor exists). + template <class Prof, absl::enable_if_t<IsProfile<Prof>::value>** = nullptr> + ABSL_MUST_USE_RESULT ::testing::AssertionResult with_loose_profile( + Prof /*profile*/) { + ConformanceErrors test_result = + (ExpectRegularityImpl< + T, Prof, Prof, + CombineProfiles<TriviallyCompleteProfile, + NothrowComparableProfile>>)(absl:: + move(ordered_vals)); + + return ExpectSuccess ? test_result.assertionResult() + : test_result.expectFailedTests(expected_failed_tests); + } + + OrderedEquivalenceClasses<EqClasses...> ordered_vals; + std::set<std::string> expected_failed_tests; +}; + +template <class T> +using ExpectConformanceOfType = ExpectConformanceOf</*ExpectSuccess=*/true, T>; + +template <class T> +using ExpectNonconformanceOfType = + ExpectConformanceOf</*ExpectSuccess=*/false, T>; + +struct EquivalenceClassMaker { + // TODO(calabrese) Constrain to callable + template <class Fun> + static GeneratorType<Fun> initializer(GeneratorType<Fun> fun) { + return fun; + } +}; + +// A top-level macro that begins the builder pattern. +// +// The argument here takes the datatype to be tested. +#define ABSL_INTERNAL_ASSERT_CONFORMANCE_OF(...) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if ABSL_INTERNAL_LPAREN \ + const ::testing::AssertionResult gtest_ar = \ + ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectConformanceOfType< \ + __VA_ARGS__>() + +// Akin to ASSERT_CONFORMANCE_OF except that it expects failure and tries to +// match text. +#define ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(...) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if ABSL_INTERNAL_LPAREN \ + const ::testing::AssertionResult gtest_ar = \ + ABSL_INTERNAL_LPAREN ::absl::types_internal::ExpectNonconformanceOfType< \ + __VA_ARGS__>() + +//////////////////////////////////////////////////////////////////////////////// +// NOTE: The following macros look like they are recursive, but are not (macros +// cannot recurse). These actually refer to member functions of the same name. +// This is done intentionally so that a user cannot accidentally invoke a +// member function of the conformance-testing suite without going through the +// macro. +//////////////////////////////////////////////////////////////////////////////// + +// Specify expected test failures as comma-separated strings. +#define DUE_TO(...) due_to(__VA_ARGS__) + +// Specify a value to be tested. +// +// Note: Internally, this takes an expression and turns it into the return value +// of lambda that captures no data. The expression is stringized during +// preprocessing so that it can be used in error reports. +#define INITIALIZER(...) \ + initializer(::absl::types_internal::Generator( \ + [] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__))) + +// Specify a value to be tested. +// +// Note: Internally, this takes an expression and turns it into the return value +// of lambda that captures data by reference. The expression is stringized +// during preprocessing so that it can be used in error reports. +#define STATEFUL_INITIALIZER(...) \ + stateful_initializer(::absl::types_internal::Generator( \ + [&] { return __VA_ARGS__; }, ABSL_INTERNAL_STRINGIZE(__VA_ARGS__))) + +// Used in the builder-pattern. +// +// Takes a series of INITIALIZER and/or STATEFUL_INITIALIZER invocations and +// forwards them along to be tested, grouping them such that the testing suite +// knows that they are supposed to represent the same logical value (the values +// compare the same, hash the same, etc.). +#define EQUIVALENCE_CLASS(...) \ + equivalence_class(ABSL_INTERNAL_TRANSFORM_ARGS( \ + ABSL_INTERNAL_PREPEND_EQ_MAKER, __VA_ARGS__)) + +// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern. +// It takes a Profile as its argument. +// +// This executes the tests and allows types that are "more referined" than the +// profile specifies, but not less. For instance, if the Profile specifies +// noexcept copy-constructiblity, the test will fail if the copy-constructor is +// not noexcept, however, it will succeed if the copy constructor is trivial. +// +// This is useful for testing that a type meets some minimum set of +// requirements. +#define WITH_LOOSE_PROFILE(...) \ + with_loose_profile( \ + ::absl::types_internal::MakeLooseProfileRangeT<__VA_ARGS__>()) \ + ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN; \ + else GTEST_FATAL_FAILURE_(gtest_ar.failure_message()) // NOLINT + +// An invocation of this or WITH_STRICT_PROFILE must end the builder-pattern. +// It takes a Domain and a Profile as its arguments. +// +// This executes the tests and disallows types that differ at all from the +// properties of the Profile. For instance, if the Profile specifies noexcept +// copy-constructiblity, the test will fail if the copy constructor is trivial. +// +// This is useful for testing that a type does not do anything more than a +// specification requires, such as to minimize things like Hyrum's Law, or more +// commonly, to prevent a type from being "accidentally" copy-constructible in +// a way that may produce incorrect results, simply because the user forget to +// delete that operation. +#define WITH_STRICT_PROFILE(...) \ + with_strict_profile( \ + ::absl::types_internal::MakeStrictProfileRangeT<__VA_ARGS__>()) \ + ABSL_INTERNAL_RPAREN ABSL_INTERNAL_RPAREN; \ + else GTEST_FATAL_FAILURE_(gtest_ar.failure_message()) // NOLINT + +// Internal macro that is used in the internals of the EDSL when forming +// equivalence classes. +#define ABSL_INTERNAL_PREPEND_EQ_MAKER(arg) \ + ::absl::types_internal::EquivalenceClassMaker().arg + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_H_ diff --git a/third_party/abseil-cpp/absl/types/internal/conformance_testing_helpers.h b/third_party/abseil-cpp/absl/types/internal/conformance_testing_helpers.h new file mode 100644 index 0000000000..00775f960c --- /dev/null +++ b/third_party/abseil-cpp/absl/types/internal/conformance_testing_helpers.h @@ -0,0 +1,391 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ + +// Checks to determine whether or not we can use abi::__cxa_demangle +#if (defined(__ANDROID__) || defined(ANDROID)) && !defined(OS_ANDROID) +#define ABSL_INTERNAL_OS_ANDROID +#endif + +// We support certain compilers only. See demangle.h for details. +#if defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__)) +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0 +#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \ + !defined(__mips__) +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1 +#elif defined(__clang__) && !defined(_MSC_VER) +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1 +#else +#define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0 +#endif + +#include <tuple> +#include <type_traits> +#include <utility> + +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" +#include "absl/utility/utility.h" + +#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE +#include <cxxabi.h> + +#include <cstdlib> +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +// Return a readable name for type T. +template <class T> +absl::string_view NameOfImpl() { +// TODO(calabrese) Investigate using debugging:internal_demangle as a fallback. +#if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE + int status = 0; + char* demangled_name = nullptr; + + demangled_name = + abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); + + if (status == 0 && demangled_name != nullptr) { + return demangled_name; + } else { + return typeid(T).name(); + } +#else + return typeid(T).name(); +#endif + // NOTE: We intentionally leak demangled_name so that it remains valid + // throughout the remainder of the program. +} + +// Given a type, returns as nice of a type name as we can produce (demangled). +// +// Note: This currently strips cv-qualifiers and references, but that is okay +// because we only use this internally with unqualified object types. +template <class T> +std::string NameOf() { + static const absl::string_view result = NameOfImpl<T>(); + return std::string(result); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Metafunction to check if a type is callable with no explicit arguments +template <class Fun, class /*Enabler*/ = void> +struct IsNullaryCallableImpl : std::false_type {}; + +template <class Fun> +struct IsNullaryCallableImpl< + Fun, absl::void_t<decltype(std::declval<const Fun&>()())>> + : std::true_type { + using result_type = decltype(std::declval<const Fun&>()()); + + template <class ValueType> + using for_type = std::is_same<ValueType, result_type>; + + using void_if_true = void; +}; + +template <class Fun> +struct IsNullaryCallable : IsNullaryCallableImpl<Fun> {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A type that contains a function object that returns an instance of a type +// that is undergoing conformance testing. This function is required to always +// return the same value upon invocation. +template <class Fun> +struct GeneratorType; + +// A type that contains a tuple of GeneratorType<Fun> where each Fun has the +// same return type. The result of each of the different generators should all +// be equal values, though the underlying object representation may differ (such +// as if one returns 0.0 and another return -0.0, or if one returns an empty +// vector and another returns an empty vector with a different capacity. +template <class... Funs> +struct EquivalenceClassType; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction to check if a type is a specialization of EquivalenceClassType +template <class T> +struct IsEquivalenceClass : std::false_type {}; + +template <> +struct IsEquivalenceClass<EquivalenceClassType<>> : std::true_type { + using self = IsEquivalenceClass; + + // A metafunction to check if this EquivalenceClassType is a valid + // EquivalenceClassType for a type `ValueType` that is undergoing testing + template <class ValueType> + using for_type = std::true_type; +}; + +template <class Head, class... Tail> +struct IsEquivalenceClass<EquivalenceClassType<Head, Tail...>> + : std::true_type { + using self = IsEquivalenceClass; + + // The type undergoing conformance testing that this EquivalenceClass + // corresponds to + using result_type = typename IsNullaryCallable<Head>::result_type; + + // A metafunction to check if this EquivalenceClassType is a valid + // EquivalenceClassType for a type `ValueType` that is undergoing testing + template <class ValueType> + using for_type = std::is_same<ValueType, result_type>; +}; +// +//////////////////////////////////////////////////////////////////////////////// + +// A type that contains an ordered series of EquivalenceClassTypes, where the +// the function object of each underlying GeneratorType has the same return type +// +// These equivalence classes are required to be in a logical ascending order +// that is consistent with comparison operators that are defined for the return +// type of each GeneratorType, if any. +template <class... EqClasses> +struct OrderedEquivalenceClasses; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction to determine the return type of the function object contained +// in a GeneratorType specialization. +template <class T> +struct ResultOfGenerator {}; + +template <class Fun> +struct ResultOfGenerator<GeneratorType<Fun>> { + using type = decltype(std::declval<const Fun&>()()); +}; + +template <class Fun> +using ResultOfGeneratorT = typename ResultOfGenerator<GeneratorType<Fun>>::type; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that yields true iff each of Funs is a GeneratorType +// specialization and they all contain functions with the same return type +template <class /*Enabler*/, class... Funs> +struct AreGeneratorsWithTheSameReturnTypeImpl : std::false_type {}; + +template <> +struct AreGeneratorsWithTheSameReturnTypeImpl<void> : std::true_type {}; + +template <class Head, class... Tail> +struct AreGeneratorsWithTheSameReturnTypeImpl< + typename std::enable_if<absl::conjunction<std::is_same< + ResultOfGeneratorT<Head>, ResultOfGeneratorT<Tail>>...>::value>::type, + Head, Tail...> : std::true_type {}; + +template <class... Funs> +struct AreGeneratorsWithTheSameReturnType + : AreGeneratorsWithTheSameReturnTypeImpl<void, Funs...>::type {}; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction that yields true iff each of Funs is an EquivalenceClassType +// specialization and they all contain GeneratorType specializations that have +// the same return type +template <class... EqClasses> +struct AreEquivalenceClassesOfTheSameType { + static_assert(sizeof...(EqClasses) != sizeof...(EqClasses), ""); +}; + +template <> +struct AreEquivalenceClassesOfTheSameType<> : std::true_type { + using self = AreEquivalenceClassesOfTheSameType; + + // Metafunction to check that a type is the same as all of the equivalence + // classes, if any. + // Note: In this specialization there are no equivalence classes, so the + // value type is always compatible. + template <class /*ValueType*/> + using for_type = std::true_type; +}; + +template <class... Funs> +struct AreEquivalenceClassesOfTheSameType<EquivalenceClassType<Funs...>> + : std::true_type { + using self = AreEquivalenceClassesOfTheSameType; + + // Metafunction to check that a type is the same as all of the equivalence + // classes, if any. + template <class ValueType> + using for_type = typename IsEquivalenceClass< + EquivalenceClassType<Funs...>>::template for_type<ValueType>; +}; + +template <class... TailEqClasses> +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<>, EquivalenceClassType<>, TailEqClasses...> + : AreEquivalenceClassesOfTheSameType<TailEqClasses...>::self {}; + +template <class HeadNextFun, class... TailNextFuns, class... TailEqClasses> +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<>, EquivalenceClassType<HeadNextFun, TailNextFuns...>, + TailEqClasses...> + : AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<HeadNextFun, TailNextFuns...>, + TailEqClasses...>::self {}; + +template <class HeadHeadFun, class... TailHeadFuns, class... TailEqClasses> +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<HeadHeadFun, TailHeadFuns...>, EquivalenceClassType<>, + TailEqClasses...> + : AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<HeadHeadFun, TailHeadFuns...>, + TailEqClasses...>::self {}; + +template <class HeadHeadFun, class... TailHeadFuns, class HeadNextFun, + class... TailNextFuns, class... TailEqClasses> +struct AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<HeadHeadFun, TailHeadFuns...>, + EquivalenceClassType<HeadNextFun, TailNextFuns...>, TailEqClasses...> + : absl::conditional_t< + IsNullaryCallable<HeadNextFun>::template for_type< + typename IsNullaryCallable<HeadHeadFun>::result_type>::value, + AreEquivalenceClassesOfTheSameType< + EquivalenceClassType<HeadHeadFun, TailHeadFuns...>, + TailEqClasses...>, + std::false_type> {}; +// +//////////////////////////////////////////////////////////////////////////////// + +// Execute a function for each passed-in parameter. +template <class Fun, class... Cases> +void ForEachParameter(const Fun& fun, const Cases&... cases) { + const std::initializer_list<bool> results = { + (static_cast<void>(fun(cases)), true)...}; + + (void)results; +} + +// Execute a function on each passed-in parameter (using a bound function). +template <class Fun> +struct ForEachParameterFun { + template <class... T> + void operator()(const T&... cases) const { + (ForEachParameter)(fun, cases...); + } + + Fun fun; +}; + +// Execute a function on each element of a tuple. +template <class Fun, class Tup> +void ForEachTupleElement(const Fun& fun, const Tup& tup) { + absl::apply(ForEachParameterFun<Fun>{fun}, tup); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Execute a function for each combination of two elements of a tuple, including +// combinations of an element with itself. +template <class Fun, class... T> +struct ForEveryTwoImpl { + template <class Lhs> + struct WithBoundLhs { + template <class Rhs> + void operator()(const Rhs& rhs) const { + fun(lhs, rhs); + } + + Fun fun; + Lhs lhs; + }; + + template <class Lhs> + void operator()(const Lhs& lhs) const { + (ForEachTupleElement)(WithBoundLhs<Lhs>{fun, lhs}, args); + } + + Fun fun; + std::tuple<T...> args; +}; + +template <class Fun, class... T> +void ForEveryTwo(const Fun& fun, std::tuple<T...> args) { + (ForEachTupleElement)(ForEveryTwoImpl<Fun, T...>{fun, args}, args); +} +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// Insert all values into an associative container +template<class Container> +void InsertEach(Container* cont) { +} + +template<class Container, class H, class... T> +void InsertEach(Container* cont, H&& head, T&&... tail) { + cont->insert(head); + (InsertEach)(cont, tail...); +} +// +//////////////////////////////////////////////////////////////////////////////// +// A template with a nested "Invoke" static-member-function that executes a +// passed-in Callable when `Condition` is true, otherwise it ignores the +// Callable. This is useful for executing a function object with a condition +// that corresponds to whether or not the Callable can be safely instantiated. +// It has some overlapping uses with C++17 `if constexpr`. +template <bool Condition> +struct If; + +template <> +struct If</*Condition =*/false> { + template <class Fun, class... P> + static void Invoke(const Fun& /*fun*/, P&&... /*args*/) {} +}; + +template <> +struct If</*Condition =*/true> { + template <class Fun, class... P> + static void Invoke(const Fun& fun, P&&... args) { + // TODO(calabrese) Use std::invoke equivalent instead of function-call. + fun(absl::forward<P>(args)...); + } +}; + +// +// ABSL_INTERNAL_STRINGIZE(...) +// +// This variadic macro transforms its arguments into a c-string literal after +// expansion. +// +// Example: +// +// ABSL_INTERNAL_STRINGIZE(std::array<int, 10>) +// +// Results in: +// +// "std::array<int, 10>" +#define ABSL_INTERNAL_STRINGIZE(...) ABSL_INTERNAL_STRINGIZE_IMPL((__VA_ARGS__)) +#define ABSL_INTERNAL_STRINGIZE_IMPL(arg) ABSL_INTERNAL_STRINGIZE_IMPL2 arg +#define ABSL_INTERNAL_STRINGIZE_IMPL2(...) #__VA_ARGS__ + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ diff --git a/third_party/abseil-cpp/absl/types/internal/conformance_testing_test.cc b/third_party/abseil-cpp/absl/types/internal/conformance_testing_test.cc index 3dcf530567..cf262fa6c2 100644 --- a/third_party/abseil-cpp/absl/types/internal/conformance_testing_test.cc +++ b/third_party/abseil-cpp/absl/types/internal/conformance_testing_test.cc @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "absl/types/internal/conformance_testing.h" + #include <new> #include <type_traits> #include <utility> @@ -19,6 +21,7 @@ #include "gtest/gtest.h" #include "absl/meta/type_traits.h" #include "absl/types/internal/conformance_aliases.h" +#include "absl/types/internal/conformance_profile.h" namespace { @@ -1181,6 +1184,373 @@ INSTANTIATE_TYPED_TEST_SUITE_P(CommonComparable, ProfileTest, CommonComparableProfilesToTest); INSTANTIATE_TYPED_TEST_SUITE_P(Trivial, ProfileTest, TrivialProfilesToTest); -// TODO(calabrese) Test runtime results +TEST(ConformanceTestingTest, Basic) { + using profile = ti::CombineProfiles<ti::TriviallyCompleteProfile, + ti::NothrowComparableProfile>; + + using lim = std::numeric_limits<float>; + + ABSL_INTERNAL_ASSERT_CONFORMANCE_OF(float) + .INITIALIZER(-lim::infinity()) + .INITIALIZER(lim::lowest()) + .INITIALIZER(-1.f) + .INITIALIZER(-lim::min()) + .EQUIVALENCE_CLASS(INITIALIZER(-0.f), INITIALIZER(0.f)) + .INITIALIZER(lim::min()) + .INITIALIZER(1.f) + .INITIALIZER(lim::max()) + .INITIALIZER(lim::infinity()) + .WITH_STRICT_PROFILE(absl::types_internal::RegularityDomain, profile); +} + +struct BadMoveConstruct { + BadMoveConstruct() = default; + BadMoveConstruct(BadMoveConstruct&& other) noexcept + : value(other.value + 1) {} + BadMoveConstruct& operator=(BadMoveConstruct&& other) noexcept = default; + int value = 0; + + friend bool operator==(BadMoveConstruct const& lhs, + BadMoveConstruct const& rhs) { + return lhs.value == rhs.value; + } + friend bool operator!=(BadMoveConstruct const& lhs, + BadMoveConstruct const& rhs) { + return lhs.value != rhs.value; + } +}; + +struct BadMoveAssign { + BadMoveAssign() = default; + BadMoveAssign(BadMoveAssign&& other) noexcept = default; + BadMoveAssign& operator=(BadMoveAssign&& other) noexcept { + int new_value = other.value + 1; + value = new_value; + return *this; + } + int value = 0; + + friend bool operator==(BadMoveAssign const& lhs, BadMoveAssign const& rhs) { + return lhs.value == rhs.value; + } + friend bool operator!=(BadMoveAssign const& lhs, BadMoveAssign const& rhs) { + return lhs.value != rhs.value; + } +}; + +enum class WhichCompIsBad { eq, ne, lt, le, ge, gt }; + +template <WhichCompIsBad Which> +struct BadCompare { + int value; + + friend bool operator==(BadCompare const& lhs, BadCompare const& rhs) { + return Which == WhichCompIsBad::eq ? lhs.value != rhs.value + : lhs.value == rhs.value; + } + + friend bool operator!=(BadCompare const& lhs, BadCompare const& rhs) { + return Which == WhichCompIsBad::ne ? lhs.value == rhs.value + : lhs.value != rhs.value; + } + + friend bool operator<(BadCompare const& lhs, BadCompare const& rhs) { + return Which == WhichCompIsBad::lt ? lhs.value >= rhs.value + : lhs.value < rhs.value; + } + + friend bool operator<=(BadCompare const& lhs, BadCompare const& rhs) { + return Which == WhichCompIsBad::le ? lhs.value > rhs.value + : lhs.value <= rhs.value; + } + + friend bool operator>=(BadCompare const& lhs, BadCompare const& rhs) { + return Which == WhichCompIsBad::ge ? lhs.value < rhs.value + : lhs.value >= rhs.value; + } + + friend bool operator>(BadCompare const& lhs, BadCompare const& rhs) { + return Which == WhichCompIsBad::gt ? lhs.value <= rhs.value + : lhs.value > rhs.value; + } +}; + +TEST(ConformanceTestingDeathTest, Failures) { + { + using profile = ti::CombineProfiles<ti::TriviallyCompleteProfile, + ti::NothrowComparableProfile>; + + // Note: The initializers are intentionally in the wrong order. + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(float) + .INITIALIZER(1.f) + .INITIALIZER(0.f) + .WITH_LOOSE_PROFILE(profile); + } + + { + using profile = + ti::CombineProfiles<ti::NothrowMovableProfile, ti::EquatableProfile>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadMoveConstruct) + .DUE_TO("Move construction") + .INITIALIZER(BadMoveConstruct()) + .WITH_LOOSE_PROFILE(profile); + } + + { + using profile = + ti::CombineProfiles<ti::NothrowMovableProfile, ti::EquatableProfile>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadMoveAssign) + .DUE_TO("Move assignment") + .INITIALIZER(BadMoveAssign()) + .WITH_LOOSE_PROFILE(profile); + } +} + +TEST(ConformanceTestingDeathTest, CompFailures) { + using profile = ti::ComparableProfile; + + { + using BadComp = BadCompare<WhichCompIsBad::eq>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp) + .DUE_TO("Comparison") + .INITIALIZER(BadComp{0}) + .INITIALIZER(BadComp{1}) + .WITH_LOOSE_PROFILE(profile); + } + + { + using BadComp = BadCompare<WhichCompIsBad::ne>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp) + .DUE_TO("Comparison") + .INITIALIZER(BadComp{0}) + .INITIALIZER(BadComp{1}) + .WITH_LOOSE_PROFILE(profile); + } + + { + using BadComp = BadCompare<WhichCompIsBad::lt>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp) + .DUE_TO("Comparison") + .INITIALIZER(BadComp{0}) + .INITIALIZER(BadComp{1}) + .WITH_LOOSE_PROFILE(profile); + } + + { + using BadComp = BadCompare<WhichCompIsBad::le>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp) + .DUE_TO("Comparison") + .INITIALIZER(BadComp{0}) + .INITIALIZER(BadComp{1}) + .WITH_LOOSE_PROFILE(profile); + } + + { + using BadComp = BadCompare<WhichCompIsBad::ge>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp) + .DUE_TO("Comparison") + .INITIALIZER(BadComp{0}) + .INITIALIZER(BadComp{1}) + .WITH_LOOSE_PROFILE(profile); + } + + { + using BadComp = BadCompare<WhichCompIsBad::gt>; + + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadComp) + .DUE_TO("Comparison") + .INITIALIZER(BadComp{0}) + .INITIALIZER(BadComp{1}) + .WITH_LOOSE_PROFILE(profile); + } +} + +struct BadSelfMove { + BadSelfMove() = default; + BadSelfMove(BadSelfMove&&) = default; + BadSelfMove& operator=(BadSelfMove&& other) noexcept { + if (this == &other) { + broken_state = true; + } + return *this; + } + + friend bool operator==(const BadSelfMove& lhs, const BadSelfMove& rhs) { + return !(lhs.broken_state || rhs.broken_state); + } + + friend bool operator!=(const BadSelfMove& lhs, const BadSelfMove& rhs) { + return lhs.broken_state || rhs.broken_state; + } + + bool broken_state = false; +}; + +TEST(ConformanceTestingDeathTest, SelfMoveFailure) { + using profile = ti::EquatableNothrowMovableProfile; + + { + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfMove) + .DUE_TO("Move assignment") + .INITIALIZER(BadSelfMove()) + .WITH_LOOSE_PROFILE(profile); + } +} + +struct BadSelfCopy { + BadSelfCopy() = default; + BadSelfCopy(BadSelfCopy&&) = default; + BadSelfCopy(const BadSelfCopy&) = default; + BadSelfCopy& operator=(BadSelfCopy&&) = default; + BadSelfCopy& operator=(BadSelfCopy const& other) { + if (this == &other) { + broken_state = true; + } + return *this; + } + + friend bool operator==(const BadSelfCopy& lhs, const BadSelfCopy& rhs) { + return !(lhs.broken_state || rhs.broken_state); + } + + friend bool operator!=(const BadSelfCopy& lhs, const BadSelfCopy& rhs) { + return lhs.broken_state || rhs.broken_state; + } + + bool broken_state = false; +}; + +TEST(ConformanceTestingDeathTest, SelfCopyFailure) { + using profile = ti::EquatableValueProfile; + + { + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfCopy) + .DUE_TO("Copy assignment") + .INITIALIZER(BadSelfCopy()) + .WITH_LOOSE_PROFILE(profile); + } +} + +struct BadSelfSwap { + friend void swap(BadSelfSwap& lhs, BadSelfSwap& rhs) noexcept { + if (&lhs == &rhs) lhs.broken_state = true; + } + + friend bool operator==(const BadSelfSwap& lhs, const BadSelfSwap& rhs) { + return !(lhs.broken_state || rhs.broken_state); + } + + friend bool operator!=(const BadSelfSwap& lhs, const BadSelfSwap& rhs) { + return lhs.broken_state || rhs.broken_state; + } + + bool broken_state = false; +}; + +TEST(ConformanceTestingDeathTest, SelfSwapFailure) { + using profile = ti::EquatableNothrowMovableProfile; + + { + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadSelfSwap) + .DUE_TO("Swap") + .INITIALIZER(BadSelfSwap()) + .WITH_LOOSE_PROFILE(profile); + } +} + +struct BadDefaultInitializedMoveAssign { + BadDefaultInitializedMoveAssign() : default_initialized(true) {} + explicit BadDefaultInitializedMoveAssign(int v) : value(v) {} + BadDefaultInitializedMoveAssign( + BadDefaultInitializedMoveAssign&& other) noexcept + : value(other.value) {} + BadDefaultInitializedMoveAssign& operator=( + BadDefaultInitializedMoveAssign&& other) noexcept { + value = other.value; + if (default_initialized) ++value; // Bad move if lhs is default initialized + return *this; + } + + friend bool operator==(const BadDefaultInitializedMoveAssign& lhs, + const BadDefaultInitializedMoveAssign& rhs) { + return lhs.value == rhs.value; + } + + friend bool operator!=(const BadDefaultInitializedMoveAssign& lhs, + const BadDefaultInitializedMoveAssign& rhs) { + return lhs.value != rhs.value; + } + + bool default_initialized = false; + int value = 0; +}; + +TEST(ConformanceTestingDeathTest, DefaultInitializedMoveAssignFailure) { + using profile = + ti::CombineProfiles<ti::DefaultConstructibleNothrowMovableProfile, + ti::EquatableProfile>; + + { + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadDefaultInitializedMoveAssign) + .DUE_TO("move assignment") + .INITIALIZER(BadDefaultInitializedMoveAssign(0)) + .WITH_LOOSE_PROFILE(profile); + } +} + +struct BadDefaultInitializedCopyAssign { + BadDefaultInitializedCopyAssign() : default_initialized(true) {} + explicit BadDefaultInitializedCopyAssign(int v) : value(v) {} + BadDefaultInitializedCopyAssign( + BadDefaultInitializedCopyAssign&& other) noexcept + : value(other.value) {} + BadDefaultInitializedCopyAssign(const BadDefaultInitializedCopyAssign& other) + : value(other.value) {} + + BadDefaultInitializedCopyAssign& operator=( + BadDefaultInitializedCopyAssign&& other) noexcept { + value = other.value; + return *this; + } + + BadDefaultInitializedCopyAssign& operator=( + const BadDefaultInitializedCopyAssign& other) { + value = other.value; + if (default_initialized) ++value; // Bad move if lhs is default initialized + return *this; + } + + friend bool operator==(const BadDefaultInitializedCopyAssign& lhs, + const BadDefaultInitializedCopyAssign& rhs) { + return lhs.value == rhs.value; + } + + friend bool operator!=(const BadDefaultInitializedCopyAssign& lhs, + const BadDefaultInitializedCopyAssign& rhs) { + return lhs.value != rhs.value; + } + + bool default_initialized = false; + int value = 0; +}; + +TEST(ConformanceTestingDeathTest, DefaultInitializedAssignFailure) { + using profile = ti::CombineProfiles<ti::DefaultConstructibleValueProfile, + ti::EquatableProfile>; + + { + ABSL_INTERNAL_ASSERT_NONCONFORMANCE_OF(BadDefaultInitializedCopyAssign) + .DUE_TO("copy assignment") + .INITIALIZER(BadDefaultInitializedCopyAssign(0)) + .WITH_LOOSE_PROFILE(profile); + } +} } // namespace diff --git a/third_party/abseil-cpp/absl/types/internal/parentheses.h b/third_party/abseil-cpp/absl/types/internal/parentheses.h new file mode 100644 index 0000000000..5aebee8fde --- /dev/null +++ b/third_party/abseil-cpp/absl/types/internal/parentheses.h @@ -0,0 +1,34 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// parentheses.h +// ----------------------------------------------------------------------------- +// +// This file contains macros that expand to a left parenthesis and a right +// parenthesis. These are in their own file and are generated from macros +// because otherwise clang-format gets confused and clang-format off directives +// do not help. +// +// The parentheses macros are used when wanting to require a rescan before +// expansion of parenthesized text appearing after a function-style macro name. + +#ifndef ABSL_TYPES_INTERNAL_PARENTHESES_H_ +#define ABSL_TYPES_INTERNAL_PARENTHESES_H_ + +#define ABSL_INTERNAL_LPAREN ( + +#define ABSL_INTERNAL_RPAREN ) + +#endif // ABSL_TYPES_INTERNAL_PARENTHESES_H_ diff --git a/third_party/abseil-cpp/absl/types/internal/transform_args.h b/third_party/abseil-cpp/absl/types/internal/transform_args.h new file mode 100644 index 0000000000..4a0ab42ac4 --- /dev/null +++ b/third_party/abseil-cpp/absl/types/internal/transform_args.h @@ -0,0 +1,246 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. +// +// ----------------------------------------------------------------------------- +// transform_args.h +// ----------------------------------------------------------------------------- +// +// This file contains a higher-order macro that "transforms" each element of a +// a variadic argument by a provided secondary macro. + +#ifndef ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_ +#define ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_ + +// +// ABSL_INTERNAL_CAT(a, b) +// +// This macro takes two arguments and concatenates them together via ## after +// expansion. +// +// Example: +// +// ABSL_INTERNAL_CAT(foo_, bar) +// +// Results in: +// +// foo_bar +#define ABSL_INTERNAL_CAT(a, b) ABSL_INTERNAL_CAT_IMPL(a, b) +#define ABSL_INTERNAL_CAT_IMPL(a, b) a##b + +// +// ABSL_INTERNAL_TRANSFORM_ARGS(m, ...) +// +// This macro takes another macro as an argument followed by a trailing series +// of additional parameters (up to 32 additional arguments). It invokes the +// passed-in macro once for each of the additional arguments, with the +// expansions separated by commas. +// +// Example: +// +// ABSL_INTERNAL_TRANSFORM_ARGS(MY_MACRO, a, b, c) +// +// Results in: +// +// MY_MACRO(a), MY_MACRO(b), MY_MACRO(c) +// +// TODO(calabrese) Handle no arguments as a special case. +#define ABSL_INTERNAL_TRANSFORM_ARGS(m, ...) \ + ABSL_INTERNAL_CAT(ABSL_INTERNAL_TRANSFORM_ARGS, \ + ABSL_INTERNAL_NUM_ARGS(__VA_ARGS__)) \ + (m, __VA_ARGS__) + +#define ABSL_INTERNAL_TRANSFORM_ARGS1(m, a0) m(a0) + +#define ABSL_INTERNAL_TRANSFORM_ARGS2(m, a0, a1) m(a0), m(a1) + +#define ABSL_INTERNAL_TRANSFORM_ARGS3(m, a0, a1, a2) m(a0), m(a1), m(a2) + +#define ABSL_INTERNAL_TRANSFORM_ARGS4(m, a0, a1, a2, a3) \ + m(a0), m(a1), m(a2), m(a3) + +#define ABSL_INTERNAL_TRANSFORM_ARGS5(m, a0, a1, a2, a3, a4) \ + m(a0), m(a1), m(a2), m(a3), m(a4) + +#define ABSL_INTERNAL_TRANSFORM_ARGS6(m, a0, a1, a2, a3, a4, a5) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5) + +#define ABSL_INTERNAL_TRANSFORM_ARGS7(m, a0, a1, a2, a3, a4, a5, a6) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6) + +#define ABSL_INTERNAL_TRANSFORM_ARGS8(m, a0, a1, a2, a3, a4, a5, a6, a7) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7) + +#define ABSL_INTERNAL_TRANSFORM_ARGS9(m, a0, a1, a2, a3, a4, a5, a6, a7, a8) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8) + +#define ABSL_INTERNAL_TRANSFORM_ARGS10(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9) + +#define ABSL_INTERNAL_TRANSFORM_ARGS11(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), m(a10) + +#define ABSL_INTERNAL_TRANSFORM_ARGS12(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11) + +#define ABSL_INTERNAL_TRANSFORM_ARGS13(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12) + +#define ABSL_INTERNAL_TRANSFORM_ARGS14(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13) + +#define ABSL_INTERNAL_TRANSFORM_ARGS15(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14) + +#define ABSL_INTERNAL_TRANSFORM_ARGS16(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15) + +#define ABSL_INTERNAL_TRANSFORM_ARGS17(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16) + +#define ABSL_INTERNAL_TRANSFORM_ARGS18(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17) + +#define ABSL_INTERNAL_TRANSFORM_ARGS19(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18) + +#define ABSL_INTERNAL_TRANSFORM_ARGS20(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18, a19) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19) + +#define ABSL_INTERNAL_TRANSFORM_ARGS21(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18, a19, a20) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20) + +#define ABSL_INTERNAL_TRANSFORM_ARGS22(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18, a19, a20, a21) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21) + +#define ABSL_INTERNAL_TRANSFORM_ARGS23(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18, a19, a20, a21, a22) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22) + +#define ABSL_INTERNAL_TRANSFORM_ARGS24(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18, a19, a20, a21, a22, a23) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23) + +#define ABSL_INTERNAL_TRANSFORM_ARGS25(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18, a19, a20, a21, a22, a23, a24) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24) + +#define ABSL_INTERNAL_TRANSFORM_ARGS26( \ + m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, \ + a16, a17, a18, a19, a20, a21, a22, a23, a24, a25) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25) + +#define ABSL_INTERNAL_TRANSFORM_ARGS27( \ + m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, \ + a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26) + +#define ABSL_INTERNAL_TRANSFORM_ARGS28( \ + m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, \ + a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27) + +#define ABSL_INTERNAL_TRANSFORM_ARGS29( \ + m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, \ + a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \ + m(a28) + +#define ABSL_INTERNAL_TRANSFORM_ARGS30( \ + m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, \ + a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \ + m(a28), m(a29) + +#define ABSL_INTERNAL_TRANSFORM_ARGS31( \ + m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, \ + a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \ + m(a28), m(a29), m(a30) + +#define ABSL_INTERNAL_TRANSFORM_ARGS32(m, a0, a1, a2, a3, a4, a5, a6, a7, a8, \ + a9, a10, a11, a12, a13, a14, a15, a16, \ + a17, a18, a19, a20, a21, a22, a23, a24, \ + a25, a26, a27, a28, a29, a30, a31) \ + m(a0), m(a1), m(a2), m(a3), m(a4), m(a5), m(a6), m(a7), m(a8), m(a9), \ + m(a10), m(a11), m(a12), m(a13), m(a14), m(a15), m(a16), m(a17), m(a18), \ + m(a19), m(a20), m(a21), m(a22), m(a23), m(a24), m(a25), m(a26), m(a27), \ + m(a28), m(a29), m(a30), m(a31) + +#define ABSL_INTERNAL_NUM_ARGS_IMPL(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, \ + a10, a11, a12, a13, a14, a15, a16, a17, \ + a18, a19, a20, a21, a22, a23, a24, a25, \ + a26, a27, a28, a29, a30, a31, result, ...) \ + result + +#define ABSL_INTERNAL_FORCE_EXPANSION(...) __VA_ARGS__ + +#define ABSL_INTERNAL_NUM_ARGS(...) \ + ABSL_INTERNAL_FORCE_EXPANSION(ABSL_INTERNAL_NUM_ARGS_IMPL( \ + __VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, \ + 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, )) + +#endif // ABSL_TYPES_INTERNAL_TRANSFORM_ARGS_H_ diff --git a/third_party/abseil-cpp/absl/types/internal/variant.h b/third_party/abseil-cpp/absl/types/internal/variant.h index 71bd3adfc6..772008c74e 100644 --- a/third_party/abseil-cpp/absl/types/internal/variant.h +++ b/third_party/abseil-cpp/absl/types/internal/variant.h @@ -45,7 +45,7 @@ ABSL_NAMESPACE_BEGIN template <class... Types> class variant; -ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1); +ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, static_cast<size_t>(-1)); template <class T> struct variant_size; @@ -292,7 +292,7 @@ struct UnreachableSwitchCase { template <class Op, std::size_t I> struct ReachableSwitchCase { static VisitIndicesResultT<Op, std::size_t> Run(Op&& op) { - return absl::base_internal::Invoke(absl::forward<Op>(op), SizeT<I>()); + return absl::base_internal::invoke(absl::forward<Op>(op), SizeT<I>()); } }; @@ -424,7 +424,7 @@ struct VisitIndicesSwitch { return PickCase<Op, 32, EndIndex>::Run(absl::forward<Op>(op)); default: ABSL_ASSERT(i == variant_npos); - return absl::base_internal::Invoke(absl::forward<Op>(op), NPos()); + return absl::base_internal::invoke(absl::forward<Op>(op), NPos()); } } }; @@ -488,7 +488,7 @@ struct VisitIndicesVariadicImpl<absl::index_sequence<N...>, EndIndices...> { template <std::size_t I> VisitIndicesResultT<Op, decltype(EndIndices)...> operator()( SizeT<I> /*index*/) && { - return base_internal::Invoke( + return base_internal::invoke( absl::forward<Op>(op), SizeT<UnflattenIndex<I, N, (EndIndices + 1)...>::value - std::size_t{1}>()...); @@ -930,7 +930,7 @@ struct PerformVisitation { absl::result_of_t<Op(VariantAccessResult< Is, QualifiedVariants>...)>>::value, "All visitation overloads must have the same return type."); - return absl::base_internal::Invoke( + return absl::base_internal::invoke( absl::forward<Op>(op), VariantCoreAccess::Access<Is>( absl::forward<QualifiedVariants>(std::get<TupIs>(variant_tup)))...); diff --git a/third_party/abseil-cpp/absl/types/optional.h b/third_party/abseil-cpp/absl/types/optional.h index 2025e29f86..61540cfdb2 100644 --- a/third_party/abseil-cpp/absl/types/optional.h +++ b/third_party/abseil-cpp/absl/types/optional.h @@ -136,10 +136,10 @@ class optional : private optional_internal::optional_data<T>, constexpr optional(nullopt_t) noexcept {} // NOLINT(runtime/explicit) // Copy constructor, standard semantics - optional(const optional& src) = default; + optional(const optional&) = default; // Move constructor, standard semantics - optional(optional&& src) = default; + optional(optional&&) = default; // Constructs a non-empty `optional` direct-initialized value of type `T` from // the arguments `std::forward<Args>(args)...` within the `optional`. @@ -412,11 +412,11 @@ class optional : private optional_internal::optional_data<T>, // // If you need myOpt->foo in constexpr, use (*myOpt).foo instead. const T* operator->() const { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return std::addressof(this->data_); } T* operator->() { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return std::addressof(this->data_); } @@ -425,17 +425,17 @@ class optional : private optional_internal::optional_data<T>, // Accesses the underlying `T` value of an `optional`. If the `optional` is // empty, behavior is undefined. constexpr const T& operator*() const& { - return ABSL_ASSERT(this->engaged_), reference(); + return ABSL_HARDENING_ASSERT(this->engaged_), reference(); } T& operator*() & { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return reference(); } constexpr const T&& operator*() const && { - return absl::move(reference()); + return ABSL_HARDENING_ASSERT(this->engaged_), absl::move(reference()); } T&& operator*() && { - assert(this->engaged_); + ABSL_HARDENING_ASSERT(this->engaged_); return std::move(reference()); } @@ -444,7 +444,7 @@ class optional : private optional_internal::optional_data<T>, // Returns false if and only if the `optional` is empty. // // if (opt) { - // // do something with opt.value(); + // // do something with *opt or opt->; // } else { // // opt is empty. // } diff --git a/third_party/abseil-cpp/absl/types/optional_test.cc b/third_party/abseil-cpp/absl/types/optional_test.cc index 47d5c8a29e..7ef142cb99 100644 --- a/third_party/abseil-cpp/absl/types/optional_test.cc +++ b/third_party/abseil-cpp/absl/types/optional_test.cc @@ -1051,14 +1051,13 @@ TEST(optionalTest, Value) { #ifdef ABSL_HAVE_EXCEPTIONS EXPECT_THROW((void)empty.value(), absl::bad_optional_access); #else - EXPECT_DEATH((void)empty.value(), "Bad optional access"); + EXPECT_DEATH_IF_SUPPORTED((void)empty.value(), "Bad optional access"); #endif // test constexpr value() constexpr absl::optional<int> o1(1); static_assert(1 == o1.value(), ""); // const & -#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ - !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) +#if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) using COI = const absl::optional<int>; static_assert(2 == COI(2).value(), ""); // const && #endif @@ -1098,8 +1097,7 @@ TEST(optionalTest, DerefOperator) { constexpr absl::optional<int> opt1(1); static_assert(*opt1 == 1, ""); -#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ - !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) +#if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG) using COI = const absl::optional<int>; static_assert(*COI(2) == 2, ""); #endif diff --git a/third_party/abseil-cpp/absl/types/span.h b/third_party/abseil-cpp/absl/types/span.h index 3283145a56..6272bb7ad1 100644 --- a/third_party/abseil-cpp/absl/types/span.h +++ b/third_party/abseil-cpp/absl/types/span.h @@ -17,32 +17,29 @@ // span.h // ----------------------------------------------------------------------------- // -// This header file defines a `Span<T>` type for holding a view of an existing -// array of data. The `Span` object, much like the `absl::string_view` object, -// does not own such data itself. A span provides a lightweight way to pass -// around view of such data. +// This header file defines a `Span<T>` type for holding a reference to existing +// array data. The `Span` object, much like the `absl::string_view` object, +// does not own such data itself, and the data being referenced by the span must +// outlive the span itself. Unlike `view` type references, a span can hold a +// reference to mutable data (and can mutate it for underlying types of +// non-const T.) A span provides a lightweight way to pass a reference to such +// data. // // Additionally, this header file defines `MakeSpan()` and `MakeConstSpan()` // factory functions, for clearly creating spans of type `Span<T>` or read-only // `Span<const T>` when such types may be difficult to identify due to issues // with implicit conversion. // -// The C++ standards committee currently has a proposal for a `std::span` type, -// (http://wg21.link/p0122), which is not yet part of the standard (though may -// become part of C++20). As of August 2017, the differences between -// `absl::Span` and this proposal are: -// * `absl::Span` uses `size_t` for `size_type` -// * `absl::Span` has no `operator()` -// * `absl::Span` has no constructors for `std::unique_ptr` or -// `std::shared_ptr` +// The C++20 draft standard includes a `std::span` type. As of June 2020, the +// differences between `absl::Span` and `std::span` are: +// * `absl::Span` has `operator==` (which is likely a design bug, +// per https://abseil.io/blog/20180531-regular-types) // * `absl::Span` has the factory functions `MakeSpan()` and // `MakeConstSpan()` -// * `absl::Span` has `front()` and `back()` methods // * bounds-checked access to `absl::Span` is accomplished with `at()` // * `absl::Span` has compiler-provided move and copy constructors and // assignment. This is due to them being specified as `constexpr`, but that // implies const in C++11. -// * `absl::Span` has no `element_type` or `index_type` typedefs // * A read-only `absl::Span<const T>` can be implicitly constructed from an // initializer list. // * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or @@ -77,9 +74,9 @@ ABSL_NAMESPACE_BEGIN // Span //------------------------------------------------------------------------------ // -// A `Span` is an "array view" type for holding a view of a contiguous data -// array; the `Span` object does not and cannot own such data itself. A span -// provides an easy way to provide overloads for anything operating on +// A `Span` is an "array reference" type for holding a reference of contiguous +// array data; the `Span` object does not and cannot own such data itself. A +// span provides an easy way to provide overloads for anything operating on // contiguous sequences without needing to manage pointers and array lengths // manually. @@ -97,7 +94,8 @@ ABSL_NAMESPACE_BEGIN // constructors. // // A `Span<T>` is somewhat analogous to an `absl::string_view`, but for an array -// of elements of type `T`. A user of `Span` must ensure that the data being +// of elements of type `T`, and unlike an `absl::string_view`, a span can hold a +// reference to mutable data. A user of `Span` must ensure that the data being // pointed to outlives the `Span` itself. // // You can construct a `Span<T>` in several ways: @@ -127,7 +125,7 @@ ABSL_NAMESPACE_BEGIN // Note that `Span` objects, in addition to requiring that the memory they // point to remains alive, must also ensure that such memory does not get // reallocated. Therefore, to avoid undefined behavior, containers with -// associated span views should not invoke operations that may reallocate memory +// associated spans should not invoke operations that may reallocate memory // (such as resizing) or invalidate iterators into the container. // // One common use for a `Span` is when passing arguments to a routine that can @@ -171,6 +169,7 @@ class Span { typename std::enable_if<!std::is_const<T>::value, U>::type; public: + using element_type = T; using value_type = absl::remove_cv_t<T>; using pointer = T*; using const_pointer = const T*; @@ -244,8 +243,8 @@ class Span { // template <typename LazyT = T, typename = EnableIfConstView<LazyT>> - Span( - std::initializer_list<value_type> v) noexcept // NOLINT(runtime/explicit) + Span(std::initializer_list<value_type> v + ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/explicit) : Span(v.begin(), v.size()) {} // Accessors @@ -276,7 +275,7 @@ class Span { // Returns a reference to the i'th element of this span. constexpr reference operator[](size_type i) const noexcept { // MSVC 2015 accepts this as constexpr, but not ptr_[i] - return *(data() + i); + return ABSL_HARDENING_ASSERT(i < size()), *(data() + i); } // Span::at() @@ -292,60 +291,74 @@ class Span { // Span::front() // - // Returns a reference to the first element of this span. + // Returns a reference to the first element of this span. The span must not + // be empty. constexpr reference front() const noexcept { - return ABSL_ASSERT(size() > 0), *data(); + return ABSL_HARDENING_ASSERT(size() > 0), *data(); } // Span::back() // - // Returns a reference to the last element of this span. + // Returns a reference to the last element of this span. The span must not + // be empty. constexpr reference back() const noexcept { - return ABSL_ASSERT(size() > 0), *(data() + size() - 1); + return ABSL_HARDENING_ASSERT(size() > 0), *(data() + size() - 1); } // Span::begin() // - // Returns an iterator to the first element of this span. + // Returns an iterator pointing to the first element of this span, or `end()` + // if the span is empty. constexpr iterator begin() const noexcept { return data(); } // Span::cbegin() // - // Returns a const iterator to the first element of this span. + // Returns a const iterator pointing to the first element of this span, or + // `end()` if the span is empty. constexpr const_iterator cbegin() const noexcept { return begin(); } // Span::end() // - // Returns an iterator to the last element of this span. + // Returns an iterator pointing just beyond the last element at the + // end of this span. This iterator acts as a placeholder; attempting to + // access it results in undefined behavior. constexpr iterator end() const noexcept { return data() + size(); } // Span::cend() // - // Returns a const iterator to the last element of this span. + // Returns a const iterator pointing just beyond the last element at the + // end of this span. This iterator acts as a placeholder; attempting to + // access it results in undefined behavior. constexpr const_iterator cend() const noexcept { return end(); } // Span::rbegin() // - // Returns a reverse iterator starting at the last element of this span. + // Returns a reverse iterator pointing to the last element at the end of this + // span, or `rend()` if the span is empty. constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } // Span::crbegin() // - // Returns a reverse const iterator starting at the last element of this span. + // Returns a const reverse iterator pointing to the last element at the end of + // this span, or `crend()` if the span is empty. constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } // Span::rend() // - // Returns a reverse iterator starting at the first element of this span. + // Returns a reverse iterator pointing just before the first element + // at the beginning of this span. This pointer acts as a placeholder; + // attempting to access its element results in undefined behavior. constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } // Span::crend() // - // Returns a reverse iterator starting at the first element of this span. + // Returns a reverse const iterator pointing just before the first element + // at the beginning of this span. This pointer acts as a placeholder; + // attempting to access its element results in undefined behavior. constexpr const_reverse_iterator crend() const noexcept { return rend(); } // Span mutations @@ -354,7 +367,7 @@ class Span { // // Removes the first `n` elements from the span. void remove_prefix(size_type n) noexcept { - assert(size() >= n); + ABSL_HARDENING_ASSERT(size() >= n); ptr_ += n; len_ -= n; } @@ -363,7 +376,7 @@ class Span { // // Removes the last `n` elements from the span. void remove_suffix(size_type n) noexcept { - assert(size() >= n); + ABSL_HARDENING_ASSERT(size() >= n); len_ -= n; } @@ -651,7 +664,7 @@ constexpr Span<T> MakeSpan(T* ptr, size_t size) noexcept { template <int&... ExplicitArgumentBarrier, typename T> Span<T> MakeSpan(T* begin, T* end) noexcept { - return ABSL_ASSERT(begin <= end), Span<T>(begin, end - begin); + return ABSL_HARDENING_ASSERT(begin <= end), Span<T>(begin, end - begin); } template <int&... ExplicitArgumentBarrier, typename C> @@ -696,7 +709,7 @@ constexpr Span<const T> MakeConstSpan(T* ptr, size_t size) noexcept { template <int&... ExplicitArgumentBarrier, typename T> Span<const T> MakeConstSpan(T* begin, T* end) noexcept { - return ABSL_ASSERT(begin <= end), Span<const T>(begin, end - begin); + return ABSL_HARDENING_ASSERT(begin <= end), Span<const T>(begin, end - begin); } template <int&... ExplicitArgumentBarrier, typename C> diff --git a/third_party/abseil-cpp/absl/types/span_test.cc b/third_party/abseil-cpp/absl/types/span_test.cc index 22467a0a76..13264aae14 100644 --- a/third_party/abseil-cpp/absl/types/span_test.cc +++ b/third_party/abseil-cpp/absl/types/span_test.cc @@ -27,6 +27,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/exception_testing.h" +#include "absl/base/options.h" #include "absl/container/fixed_array.h" #include "absl/container/inlined_vector.h" #include "absl/hash/hash_testing.h" @@ -232,6 +233,11 @@ TEST(IntSpan, ElementAccess) { EXPECT_EQ(s.front(), s[0]); EXPECT_EQ(s.back(), s[9]); + +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + EXPECT_DEATH_IF_SUPPORTED(s[-1], ""); + EXPECT_DEATH_IF_SUPPORTED(s[10], ""); +#endif } TEST(IntSpan, AtThrows) { @@ -268,6 +274,13 @@ TEST(IntSpan, RemovePrefixAndSuffix) { EXPECT_EQ(s.size(), 0); EXPECT_EQ(v, MakeRamp(20, 1)); + +#if !defined(NDEBUG) || ABSL_OPTION_HARDENED + absl::Span<int> prefix_death(v); + EXPECT_DEATH_IF_SUPPORTED(prefix_death.remove_prefix(21), ""); + absl::Span<int> suffix_death(v); + EXPECT_DEATH_IF_SUPPORTED(suffix_death.remove_suffix(21), ""); +#endif } TEST(IntSpan, Subspan) { @@ -648,6 +661,8 @@ TEST(IntSpan, ExposesContainerTypesAndConsts) { CheckType<absl::Span<int>::const_reverse_iterator>(slice.crend()); testing::StaticAssertTypeEq<int, absl::Span<int>::value_type>(); testing::StaticAssertTypeEq<int, absl::Span<const int>::value_type>(); + testing::StaticAssertTypeEq<int, absl::Span<int>::element_type>(); + testing::StaticAssertTypeEq<const int, absl::Span<const int>::element_type>(); testing::StaticAssertTypeEq<int*, absl::Span<int>::pointer>(); testing::StaticAssertTypeEq<const int*, absl::Span<const int>::pointer>(); testing::StaticAssertTypeEq<int&, absl::Span<int>::reference>(); diff --git a/third_party/abseil-cpp/absl/types/variant.h b/third_party/abseil-cpp/absl/types/variant.h index 776d19a1c5..ac93464bf8 100644 --- a/third_party/abseil-cpp/absl/types/variant.h +++ b/third_party/abseil-cpp/absl/types/variant.h @@ -604,7 +604,10 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> { // emplace() Functions - // Constructs a value of the given alternative type T within the variant. + // Constructs a value of the given alternative type T within the variant. The + // existing value of the variant is destroyed first (provided that + // `absl::valueless_by_exception()` is false). Requires that T is unambiguous + // in the variant. // // Example: // @@ -624,7 +627,9 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> { } // Constructs a value of the given alternative type T within the variant using - // an initializer list. + // an initializer list. The existing value of the variant is destroyed first + // (provided that `absl::valueless_by_exception()` is false). Requires that T + // is unambiguous in the variant. // // Example: // @@ -643,7 +648,7 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> { } // Destroys the current value of the variant (provided that - // `absl::valueless_by_exception()` is false, and constructs a new value at + // `absl::valueless_by_exception()` is false) and constructs a new value at // the given index. // // Example: @@ -662,7 +667,7 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> { } // Destroys the current value of the variant (provided that - // `absl::valueless_by_exception()` is false, and constructs a new value at + // `absl::valueless_by_exception()` is false) and constructs a new value at // the given index using an initializer list and the provided arguments. // // Example: diff --git a/third_party/abseil-cpp/absl/types/variant_test.cc b/third_party/abseil-cpp/absl/types/variant_test.cc index 96393333b8..cf237334da 100644 --- a/third_party/abseil-cpp/absl/types/variant_test.cc +++ b/third_party/abseil-cpp/absl/types/variant_test.cc @@ -50,7 +50,7 @@ #else #define ABSL_VARIANT_TEST_EXPECT_FAIL(expr, exception_t, text) \ - EXPECT_DEATH(expr, text) + EXPECT_DEATH_IF_SUPPORTED(expr, text) #endif // ABSL_HAVE_EXCEPTIONS @@ -679,7 +679,7 @@ TEST(VariantTest, TestSelfAssignment) { object.operator=(object); EXPECT_EQ(0, counter); - // A std::string long enough that it's likely to defeat any inline representation + // A string long enough that it's likely to defeat any inline representation // optimization. const std::string long_str(128, 'a'); @@ -2311,7 +2311,8 @@ TEST(VariantTest, TestRvalueConversion) { ASSERT_TRUE(absl::holds_alternative<int32_t>(variant2)); EXPECT_EQ(42, absl::get<int32_t>(variant2)); - variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42)); + variant2 = + ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42)); ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2)); EXPECT_EQ(42, absl::get<uint32_t>(variant2)); #endif // !ABSL_USES_STD_VARIANT @@ -2453,7 +2454,8 @@ TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { ConvertVariantTo<variant<int32_t, uint32_t>>(variant<int32_t>(42))); EXPECT_THAT(absl::get_if<int32_t>(&variant2), Pointee(42)); - variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42)); + variant2 = + ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42)); EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42)); #endif diff --git a/third_party/abseil-cpp/absl/utility/BUILD.bazel b/third_party/abseil-cpp/absl/utility/BUILD.bazel index 6881f939c5..ca4bc0a6b8 100644 --- a/third_party/abseil-cpp/absl/utility/BUILD.bazel +++ b/third_party/abseil-cpp/absl/utility/BUILD.bazel @@ -14,7 +14,6 @@ # limitations under the License. # -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", @@ -24,7 +23,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "utility", diff --git a/third_party/abseil-cpp/absl/utility/CMakeLists.txt b/third_party/abseil-cpp/absl/utility/CMakeLists.txt index e1edd19aa0..865b758f23 100644 --- a/third_party/abseil-cpp/absl/utility/CMakeLists.txt +++ b/third_party/abseil-cpp/absl/utility/CMakeLists.txt @@ -40,5 +40,5 @@ absl_cc_test( absl::core_headers absl::memory absl::strings - gmock_main + GTest::gmock_main ) diff --git a/third_party/abseil-cpp/absl/utility/utility.h b/third_party/abseil-cpp/absl/utility/utility.h index e6647c7b2e..bf9232209a 100644 --- a/third_party/abseil-cpp/absl/utility/utility.h +++ b/third_party/abseil-cpp/absl/utility/utility.h @@ -236,10 +236,10 @@ namespace utility_internal { // Helper method for expanding tuple into a called method. template <typename Functor, typename Tuple, std::size_t... Indexes> auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>) - -> decltype(absl::base_internal::Invoke( + -> decltype(absl::base_internal::invoke( absl::forward<Functor>(functor), std::get<Indexes>(absl::forward<Tuple>(t))...)) { - return absl::base_internal::Invoke( + return absl::base_internal::invoke( absl::forward<Functor>(functor), std::get<Indexes>(absl::forward<Tuple>(t))...); } diff --git a/third_party/abseil-cpp/ci/absl_alternate_options.h b/third_party/abseil-cpp/ci/absl_alternate_options.h index f0c21fea9d..29b020d9fa 100644 --- a/third_party/abseil-cpp/ci/absl_alternate_options.h +++ b/third_party/abseil-cpp/ci/absl_alternate_options.h @@ -1,6 +1,3 @@ -#ifndef ABSL_BASE_OPTIONS_H_ -#define ABSL_BASE_OPTIONS_H_ - // Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,11 +15,15 @@ // Alternate options.h file, used in continuous integration testing to exercise // option settings not used by default. +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + #define ABSL_OPTION_USE_STD_ANY 0 #define ABSL_OPTION_USE_STD_OPTIONAL 0 #define ABSL_OPTION_USE_STD_STRING_VIEW 0 #define ABSL_OPTION_USE_STD_VARIANT 0 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns +#define ABSL_OPTION_HARDENED 1 #endif // ABSL_BASE_OPTIONS_H_ diff --git a/third_party/abseil-cpp/ci/cmake_common.sh b/third_party/abseil-cpp/ci/cmake_common.sh new file mode 100644 index 0000000000..51f310693e --- /dev/null +++ b/third_party/abseil-cpp/ci/cmake_common.sh @@ -0,0 +1,25 @@ +# Copyright 2020 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +# The commit of GoogleTest to be used in the CMake tests in this directory. +# Keep this in sync with the commit in the WORKSPACE file. +readonly ABSL_GOOGLETEST_COMMIT="8d51ffdfab10b3fba636ae69bc03da4b54f8c235" + +# Avoid depending on GitHub by looking for a cached copy of the commit first. +if [[ -r "${KOKORO_GFILE_DIR:-}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}" + ABSL_GOOGLETEST_DOWNLOAD_URL="file:///distdir/${ABSL_GOOGLETEST_COMMIT}.zip" +else + ABSL_GOOGLETEST_DOWNLOAD_URL="https://github.com/google/googletest/archive/${ABSL_GOOGLETEST_COMMIT}.zip" +fi diff --git a/third_party/abseil-cpp/ci/cmake_install_test.sh b/third_party/abseil-cpp/ci/cmake_install_test.sh index 55fb4f120b..97ed8478e0 100644..100755 --- a/third_party/abseil-cpp/ci/cmake_install_test.sh +++ b/third_party/abseil-cpp/ci/cmake_install_test.sh @@ -16,17 +16,31 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ - --workdir=/abseil-cpp \ +if [[ -z ${LINK_TYPE:-} ]]; then + LINK_TYPE="STATIC DYNAMIC" +fi + +source "${ABSEIL_ROOT}/ci/cmake_common.sh" + +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER} + +for link_type in ${LINK_TYPE}; do + time docker run \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp-ro,readonly \ --tmpfs=/buildfs:exec \ + --tmpfs=/abseil-cpp:exec \ + --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ + -e "ABSL_GOOGLETEST_COMMIT=${ABSL_GOOGLETEST_COMMIT}" \ + -e "ABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL}" \ + -e "LINK_TYPE=${link_type}" \ --rm \ - -e CFLAGS="-Werror" \ - -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20200106 \ - /bin/bash CMake/install_test_project/test.sh $@ + ${DOCKER_EXTRA_ARGS:-} \ + ${DOCKER_CONTAINER} \ + /bin/bash -c "cp -r /abseil-cpp-ro/* . && CMake/install_test_project/test.sh" +done diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh index 24efe3b7a0..5245933a5f 100644..100755 --- a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh +++ b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh @@ -20,28 +20,29 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${STD:-} ]; then - STD="c++11 c++14 c++17" +if [[ -z ${STD:-} ]]; then + STD="c++11 c++14 c++17 c++20" fi -if [ -z ${COMPILATION_MODE:-} ]; then +if [[ -z ${COMPILATION_MODE:-} ]]; then COMPILATION_MODE="fastbuild opt" fi -if [ -z ${EXCEPTIONS_MODE:-} ]; then +if [[ -z ${EXCEPTIONS_MODE:-} ]]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. -if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then - DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}" +if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}" # Bazel doesn't track changes to tools outside of the workspace # (e.g. /usr/bin/gcc), so by appending the docker container to the # remote_http_cache url, we make changes to the container part of @@ -50,17 +51,24 @@ if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do echo "--------------------------------------------------------------------" time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \ --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ --rm \ -e CC="/opt/llvm/clang/bin/clang" \ - -e BAZEL_COMPILER="llvm" \ -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \ -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \ -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \ @@ -69,15 +77,13 @@ for std in ${STD}; do /usr/local/bin/bazel test ... \ --compilation_mode="${compilation_mode}" \ --copt="${exceptions_mode}" \ - --copt="-DDYNAMIC_ANNOTATIONS_ENABLED=1" \ - --copt="-DADDRESS_SANITIZER" \ - --copt="-DUNDEFINED_BEHAVIOR_SANITIZER" \ --copt="-fsanitize=address" \ --copt="-fsanitize=float-divide-by-zero" \ --copt="-fsanitize=nullability" \ --copt="-fsanitize=undefined" \ --copt="-fno-sanitize-blacklist" \ --copt=-Werror \ + --distdir="/bazel-distdir" \ --keep_going \ --linkopt="-fsanitize=address" \ --linkopt="-fsanitize-link-c++-runtime" \ diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh index 127e7bc7b2..e0fe653de7 100644..100755 --- a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh +++ b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh @@ -20,28 +20,29 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${STD:-} ]; then - STD="c++11 c++14 c++17" +if [[ -z ${STD:-} ]]; then + STD="c++11 c++14 c++17 c++20" fi -if [ -z ${COMPILATION_MODE:-} ]; then +if [[ -z ${COMPILATION_MODE:-} ]]; then COMPILATION_MODE="fastbuild opt" fi -if [ -z ${EXCEPTIONS_MODE:-} ]; then +if [[ -z ${EXCEPTIONS_MODE:-} ]]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. -if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then - DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}" +if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}" # Bazel doesn't track changes to tools outside of the workspace # (e.g. /usr/bin/gcc), so by appending the docker container to the # remote_http_cache url, we make changes to the container part of @@ -50,18 +51,25 @@ if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do echo "--------------------------------------------------------------------" time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp-ro:ro" \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp-ro,readonly \ --tmpfs=/abseil-cpp \ --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ --rm \ -e CC="/opt/llvm/clang/bin/clang" \ - -e BAZEL_COMPILER="llvm" \ -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \ -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \ -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/c++/v1" \ @@ -77,6 +85,7 @@ for std in ${STD}; do --copt=\"${exceptions_mode}\" \ --copt=-Werror \ --define=\"absl=1\" \ + --distdir=\"/bazel-distdir\" \ --keep_going \ --show_timestamps \ --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \ diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh index 00257b36ad..555f6b1c2a 100644..100755 --- a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh +++ b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh @@ -20,28 +20,29 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${STD:-} ]; then - STD="c++11 c++14 c++17" +if [[ -z ${STD:-} ]]; then + STD="c++11 c++14 c++17 c++20" fi -if [ -z ${COMPILATION_MODE:-} ]; then +if [[ -z ${COMPILATION_MODE:-} ]]; then COMPILATION_MODE="fastbuild opt" fi -if [ -z ${EXCEPTIONS_MODE:-} ]; then +if [[ -z ${EXCEPTIONS_MODE:-} ]]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. -if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then - DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}" +if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}" # Bazel doesn't track changes to tools outside of the workspace # (e.g. /usr/bin/gcc), so by appending the docker container to the # remote_http_cache url, we make changes to the container part of @@ -50,17 +51,24 @@ if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do echo "--------------------------------------------------------------------" time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \ --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ --rm \ -e CC="/opt/llvm/clang/bin/clang" \ - -e BAZEL_COMPILER="llvm" \ -e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \ -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx-tsan/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib" \ -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx-tsan/include/c++/v1" \ @@ -70,11 +78,10 @@ for std in ${STD}; do --build_tag_filters="-notsan" \ --compilation_mode="${compilation_mode}" \ --copt="${exceptions_mode}" \ - --copt="-DDYNAMIC_ANNOTATIONS_ENABLED=1" \ - --copt="-DTHREAD_SANITIZER" \ --copt="-fsanitize=thread" \ --copt="-fno-sanitize-blacklist" \ --copt=-Werror \ + --distdir="/bazel-distdir" \ --keep_going \ --linkopt="-fsanitize=thread" \ --show_timestamps \ diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh index 9fe71d3760..36fdf82c1d 100644..100755 --- a/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh +++ b/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh @@ -20,28 +20,29 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${STD:-} ]; then +if [[ -z ${STD:-} ]]; then STD="c++11 c++14 c++17" fi -if [ -z ${COMPILATION_MODE:-} ]; then +if [[ -z ${COMPILATION_MODE:-} ]]; then COMPILATION_MODE="fastbuild opt" fi -if [ -z ${EXCEPTIONS_MODE:-} ]; then +if [[ -z ${EXCEPTIONS_MODE:-} ]]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_CLANG_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. -if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then - DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}" +if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}" # Bazel doesn't track changes to tools outside of the workspace # (e.g. /usr/bin/gcc), so by appending the docker container to the # remote_http_cache url, we make changes to the container part of @@ -50,27 +51,36 @@ if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do echo "--------------------------------------------------------------------" time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \ --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ --rm \ -e CC="/opt/llvm/clang/bin/clang" \ - -e BAZEL_COMPILER="llvm" \ -e BAZEL_CXXOPTS="-std=${std}" \ - -e CPLUS_INCLUDE_PATH="/usr/include/c++/6" \ ${DOCKER_EXTRA_ARGS:-} \ ${DOCKER_CONTAINER} \ /usr/local/bin/bazel test ... \ --compilation_mode="${compilation_mode}" \ + --copt="--gcc-toolchain=/usr/local" \ --copt="${exceptions_mode}" \ --copt=-Werror \ --define="absl=1" \ + --distdir="/bazel-distdir" \ --keep_going \ + --linkopt="--gcc-toolchain=/usr/local" \ --show_timestamps \ --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \ --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \ diff --git a/third_party/abseil-cpp/ci/linux_docker_containers.sh b/third_party/abseil-cpp/ci/linux_docker_containers.sh new file mode 100644 index 0000000000..32865b8348 --- /dev/null +++ b/third_party/abseil-cpp/ci/linux_docker_containers.sh @@ -0,0 +1,21 @@ +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. + +# The file contains Docker container identifiers currently used by test scripts. +# Test scripts should source this file to get the identifiers. + +readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20201026" +readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617" +readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20210617" +readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20210617" diff --git a/third_party/abseil-cpp/ci/linux_gcc-4.9_libstdcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh index f8102cc0a1..54ab68a37e 100644..100755 --- a/third_party/abseil-cpp/ci/linux_gcc-4.9_libstdcxx_bazel.sh +++ b/third_party/abseil-cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2019 The Abseil Authors. +# Copyright 2020 The Abseil Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,27 +20,28 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${STD:-} ]; then +if [[ -z ${STD:-} ]]; then STD="c++11 c++14" fi -if [ -z ${COMPILATION_MODE:-} ]; then +if [[ -z ${COMPILATION_MODE:-} ]]; then COMPILATION_MODE="fastbuild opt" fi -if [ -z ${EXCEPTIONS_MODE:-} ]; then +if [[ -z ${EXCEPTIONS_MODE:-} ]]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_FLOOR_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. -if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then +if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}" # Bazel doesn't track changes to tools outside of the workspace # (e.g. /usr/bin/gcc), so by appending the docker container to the @@ -50,6 +51,14 @@ if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then + DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do @@ -59,7 +68,7 @@ for std in ${STD}; do --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ --rm \ - -e CC="/usr/bin/gcc-4.9" \ + -e CC="/usr/local/bin/gcc" \ -e BAZEL_CXXOPTS="-std=${std}" \ ${DOCKER_EXTRA_ARGS:-} \ ${DOCKER_CONTAINER} \ @@ -68,6 +77,7 @@ for std in ${STD}; do --copt="${exceptions_mode}" \ --copt=-Werror \ --define="absl=1" \ + --distdir="/bazel-distdir" \ --keep_going \ --show_timestamps \ --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \ diff --git a/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh index 59647031f8..0555ecedb5 100644..100755 --- a/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh +++ b/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh @@ -20,28 +20,29 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${STD:-} ]; then - STD="c++11 c++14 c++17" +if [[ -z ${STD:-} ]]; then + STD="c++11 c++14 c++17 c++20" fi -if [ -z ${COMPILATION_MODE:-} ]; then +if [[ -z ${COMPILATION_MODE:-} ]]; then COMPILATION_MODE="fastbuild opt" fi -if [ -z ${EXCEPTIONS_MODE:-} ]; then +if [[ -z ${EXCEPTIONS_MODE:-} ]]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20200106" +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER} # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. -if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then - DOCKER_EXTRA_ARGS="--volume=${KOKORO_KEYSTORE_DIR}:/keystore:ro ${DOCKER_EXTRA_ARGS:-}" +if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_KEYSTORE_DIR},target=/keystore,readonly ${DOCKER_EXTRA_ARGS:-}" # Bazel doesn't track changes to tools outside of the workspace # (e.g. /usr/bin/gcc), so by appending the docker container to the # remote_http_cache url, we make changes to the container part of @@ -50,12 +51,20 @@ if [ ${USE_BAZEL_CACHE:-0} -ne 0 ]; then BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}" fi +# Avoid depending on external sites like GitHub by checking --distdir for +# external dependencies first. +# https://docs.bazel.build/versions/master/guide.html#distdir +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then + DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}" + BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}" +fi + for std in ${STD}; do for compilation_mode in ${COMPILATION_MODE}; do for exceptions_mode in ${EXCEPTIONS_MODE}; do echo "--------------------------------------------------------------------" time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp-ro:ro" \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp-ro,readonly \ --tmpfs=/abseil-cpp \ --workdir=/abseil-cpp \ --cap-add=SYS_PTRACE \ @@ -74,6 +83,7 @@ for std in ${STD}; do --copt=\"${exceptions_mode}\" \ --copt=-Werror \ --define=\"absl=1\" \ + --distdir=\"/bazel-distdir\" \ --keep_going \ --show_timestamps \ --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \ diff --git a/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh b/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh index 38ad99f7ef..ab06aa0574 100644..100755 --- a/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh +++ b/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh @@ -14,48 +14,52 @@ # See the License for the specific language governing permissions and # limitations under the License. -# TODO(absl-team): This script isn't fully hermetic because -# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed -# version of GoogleTest. This means that an upstream change to GoogleTest could -# break this test. Fix this by allowing this script to pin to a known-good -# version of GoogleTest. - set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]; then - ABSL_CMAKE_CXX_STANDARDS="11 14 17" +source "${ABSEIL_ROOT}/ci/cmake_common.sh" + +if [[ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]]; then + ABSL_CMAKE_CXX_STANDARDS="11 14 17 20" fi -if [ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]; then +if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then ABSL_CMAKE_BUILD_TYPES="Debug Release" fi +if [[ -z ${ABSL_CMAKE_BUILD_SHARED:-} ]]; then + ABSL_CMAKE_BUILD_SHARED="OFF ON" +fi + +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_GCC_LATEST_CONTAINER} + for std in ${ABSL_CMAKE_CXX_STANDARDS}; do for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do - echo "--------------------------------------------------------------------" - echo "Testing with CMAKE_BUILD_TYPE=${compilation_mode} and -std=c++${std}" - - time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ - --workdir=/abseil-cpp \ - --tmpfs=/buildfs:exec \ - --cap-add=SYS_PTRACE \ - --rm \ - -e CFLAGS="-Werror" \ - -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20200106 \ - /bin/bash -c " - cd /buildfs && \ - cmake /abseil-cpp \ - -DABSL_USE_GOOGLETEST_HEAD=ON \ - -DABSL_RUN_TESTS=ON \ - -DCMAKE_BUILD_TYPE=${compilation_mode} \ - -DCMAKE_CXX_STANDARD=${std} && \ - make -j$(nproc) && \ - ctest -j$(nproc) --output-on-failure" + for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do + time docker run \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \ + --tmpfs=/buildfs:exec \ + --workdir=/buildfs \ + --cap-add=SYS_PTRACE \ + --rm \ + -e CFLAGS="-Werror" \ + -e CXXFLAGS="-Werror" \ + ${DOCKER_EXTRA_ARGS:-} \ + "${DOCKER_CONTAINER}" \ + /bin/bash -c " + cmake /abseil-cpp \ + -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \ + -DBUILD_SHARED_LIBS=${build_shared} \ + -DBUILD_TESTING=ON \ + -DCMAKE_BUILD_TYPE=${compilation_mode} \ + -DCMAKE_CXX_STANDARD=${std} \ + -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \ + make -j$(nproc) && \ + ctest -j$(nproc) --output-on-failure" + done done done diff --git a/third_party/abseil-cpp/ci/linux_gcc_alpine_cmake.sh b/third_party/abseil-cpp/ci/linux_gcc_alpine_cmake.sh index 830a1360a3..bce27d295f 100644..100755 --- a/third_party/abseil-cpp/ci/linux_gcc_alpine_cmake.sh +++ b/third_party/abseil-cpp/ci/linux_gcc_alpine_cmake.sh @@ -14,50 +14,51 @@ # See the License for the specific language governing permissions and # limitations under the License. -# TODO(absl-team): This script isn't fully hermetic because -# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed -# version of GoogleTest. This means that an upstream change to GoogleTest could -# break this test. Fix this by allowing this script to pin to a known-good -# version of GoogleTest. - set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi -if [ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]; then +source "${ABSEIL_ROOT}/ci/cmake_common.sh" + +if [[ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]]; then ABSL_CMAKE_CXX_STANDARDS="11 14 17" fi -if [ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]; then +if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then ABSL_CMAKE_BUILD_TYPES="Debug Release" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/alpine:20191016" +if [[ -z ${ABSL_CMAKE_BUILD_SHARED:-} ]]; then + ABSL_CMAKE_BUILD_SHARED="OFF ON" +fi + +source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh" +readonly DOCKER_CONTAINER=${LINUX_ALPINE_CONTAINER} for std in ${ABSL_CMAKE_CXX_STANDARDS}; do for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do - echo "--------------------------------------------------------------------" - echo "Testing with CMAKE_BUILD_TYPE=${compilation_mode} and -std=c++${std}" - - time docker run \ - --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ - --workdir=/abseil-cpp \ - --tmpfs=/buildfs:exec \ - --cap-add=SYS_PTRACE \ - --rm \ - -e CFLAGS="-Werror" \ - -e CXXFLAGS="-Werror" \ - "${DOCKER_CONTAINER}" \ - /bin/sh -c " - cd /buildfs && \ - cmake /abseil-cpp \ - -DABSL_USE_GOOGLETEST_HEAD=ON \ - -DABSL_RUN_TESTS=ON \ - -DCMAKE_BUILD_TYPE=${compilation_mode} \ - -DCMAKE_CXX_STANDARD=${std} && \ - make -j$(nproc) && \ - ctest -j$(nproc) --output-on-failure" + for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do + time docker run \ + --mount type=bind,source="${ABSEIL_ROOT}",target=/abseil-cpp,readonly \ + --tmpfs=/buildfs:exec \ + --workdir=/buildfs \ + --cap-add=SYS_PTRACE \ + --rm \ + -e CFLAGS="-Werror" \ + -e CXXFLAGS="-Werror" \ + ${DOCKER_EXTRA_ARGS:-} \ + "${DOCKER_CONTAINER}" \ + /bin/sh -c " + cmake /abseil-cpp \ + -DABSL_GOOGLETEST_DOWNLOAD_URL=${ABSL_GOOGLETEST_DOWNLOAD_URL} \ + -DBUILD_TESTING=ON \ + -DCMAKE_BUILD_TYPE=${compilation_mode} \ + -DCMAKE_CXX_STANDARD=${std} \ + -DCMAKE_MODULE_LINKER_FLAGS=\"-Wl,--no-undefined\" && \ + make -j$(nproc) && \ + ctest -j$(nproc) --output-on-failure" + done done done diff --git a/third_party/abseil-cpp/ci/macos_xcode_bazel.sh b/third_party/abseil-cpp/ci/macos_xcode_bazel.sh index f5f2d759cf..9e14e66039 100644..100755 --- a/third_party/abseil-cpp/ci/macos_xcode_bazel.sh +++ b/third_party/abseil-cpp/ci/macos_xcode_bazel.sh @@ -19,13 +19,13 @@ set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" fi # If we are running on Kokoro, check for a versioned Bazel binary. -KOKORO_GFILE_BAZEL_BIN="bazel-2.0.0-darwin-x86_64" -if [ ${KOKORO_GFILE_DIR:-} ] && [ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]; then +KOKORO_GFILE_BAZEL_BIN="bazel-3.7.0-darwin-x86_64" +if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}" chmod +x ${BAZEL_BIN} else @@ -41,7 +41,7 @@ echo "---------------" cd ${ABSEIL_ROOT} -if [ -n "${ALTERNATE_OPTIONS:-}" ]; then +if [[ -n "${ALTERNATE_OPTIONS:-}" ]]; then cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1 fi diff --git a/third_party/abseil-cpp/ci/macos_xcode_cmake.sh b/third_party/abseil-cpp/ci/macos_xcode_cmake.sh index a1f4a857be..2a870cf4e9 100644..100755 --- a/third_party/abseil-cpp/ci/macos_xcode_cmake.sh +++ b/third_party/abseil-cpp/ci/macos_xcode_cmake.sh @@ -14,31 +14,43 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This script is invoked on Kokoro to test Abseil on macOS. -# It is not hermetic and may break when Kokoro is updated. - set -euox pipefail -if [ -z ${ABSEIL_ROOT:-} ]; then +if [[ -z ${ABSEIL_ROOT:-} ]]; then ABSEIL_ROOT="$(dirname ${0})/.." fi ABSEIL_ROOT=$(realpath ${ABSEIL_ROOT}) -if [ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]; then +source "${ABSEIL_ROOT}/ci/cmake_common.sh" + +# The MacOS build doesn't run in a docker container, so we have to override ABSL_GOOGLETEST_DOWNLOAD_URL. +if [[ -r "${KOKORO_GFILE_DIR}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" ]]; then + ABSL_GOOGLETEST_DOWNLOAD_URL="file://${KOKORO_GFILE_DIR}/distdir/${ABSL_GOOGLETEST_COMMIT}.zip" +fi + +if [[ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]]; then ABSL_CMAKE_BUILD_TYPES="Debug" fi +if [[ -z ${ABSL_CMAKE_BUILD_SHARED:-} ]]; then + ABSL_CMAKE_BUILD_SHARED="OFF ON" +fi + for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do - BUILD_DIR=$(mktemp -d ${compilation_mode}.XXXXXXXX) - cd ${BUILD_DIR} - - # TODO(absl-team): Enable -Werror once all warnings are fixed. - time cmake ${ABSEIL_ROOT} \ - -GXcode \ - -DCMAKE_BUILD_TYPE=${compilation_mode} \ - -DCMAKE_CXX_STANDARD=11 \ - -DABSL_USE_GOOGLETEST_HEAD=ON \ - -DABSL_RUN_TESTS=ON - time cmake --build . - time ctest -C ${compilation_mode} --output-on-failure + for build_shared in ${ABSL_CMAKE_BUILD_SHARED}; do + BUILD_DIR=$(mktemp -d ${compilation_mode}.XXXXXXXX) + cd ${BUILD_DIR} + + # TODO(absl-team): Enable -Werror once all warnings are fixed. + time cmake ${ABSEIL_ROOT} \ + -GXcode \ + -DBUILD_SHARED_LIBS=${build_shared} \ + -DBUILD_TESTING=ON \ + -DCMAKE_BUILD_TYPE=${compilation_mode} \ + -DCMAKE_CXX_STANDARD=11 \ + -DCMAKE_MODULE_LINKER_FLAGS="-Wl,--no-undefined" \ + -DABSL_GOOGLETEST_DOWNLOAD_URL="${ABSL_GOOGLETEST_DOWNLOAD_URL}" + time cmake --build . + time ctest -C ${compilation_mode} --output-on-failure + done done diff --git a/third_party/abseil-cpp/conanfile.py b/third_party/abseil-cpp/conanfile.py index 926ec5ccd6..926ec5ccd6 100644..100755 --- a/third_party/abseil-cpp/conanfile.py +++ b/third_party/abseil-cpp/conanfile.py diff --git a/third_party/abseil-cpp/create_lts.py b/third_party/abseil-cpp/create_lts.py new file mode 100755 index 0000000000..561708063f --- /dev/null +++ b/third_party/abseil-cpp/create_lts.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# +# Copyright 2021 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. +"""A script to do source transformations to create a new LTS release. + + Usage: ./create_lts.py YYYYMMDD +""" + +import sys + + +def ReplaceStringsInFile(filename, replacement_dict): + """Performs textual replacements in a file. + + Rewrites filename with the keys in replacement_dict replaced with + their values. This function assumes the file can fit in memory. + + Args: + filename: the filename to perform the replacement on + replacement_dict: a dictionary of key strings to be replaced with their + values + + Raises: + Exception: A failure occured + """ + f = open(filename, 'r') + content = f.read() + f.close() + + for key, value in replacement_dict.items(): + original = content + content = content.replace(key, value) + if content == original: + raise Exception('Failed to find {} in {}'.format(key, filename)) + + f = open(filename, 'w') + f.write(content) + f.close() + + +def StripContentBetweenTags(filename, strip_begin_tag, strip_end_tag): + """Strip contents from a file. + + Rewrites filename with by removing all content between + strip_begin_tag and strip_end_tag, including the tags themselves. + + Args: + filename: the filename to perform the replacement on + strip_begin_tag: the start of the content to be removed + strip_end_tag: the end of the content to be removed + + Raises: + Exception: A failure occured + """ + f = open(filename, 'r') + content = f.read() + f.close() + + while True: + begin = content.find(strip_begin_tag) + if begin == -1: + break + end = content.find(strip_end_tag, begin + len(strip_begin_tag)) + if end == -1: + raise Exception('{}: imbalanced strip begin ({}) and ' + 'end ({}) tags'.format(filename, strip_begin_tag, + strip_end_tag)) + content = content.replace(content[begin:end + len(strip_end_tag)], '') + + f = open(filename, 'w') + f.write(content) + f.close() + + +def main(argv): + if len(argv) != 2: + print('Usage: {} YYYYMMDD'.format(sys.argv[0], file=sys.stderr)) + sys.exit(1) + + datestamp = sys.argv[1] + if len(datestamp) != 8 or not datestamp.isdigit(): + raise Exception( + 'datestamp={} is not in the YYYYMMDD format'.format(datestamp)) + + # Replacement directives go here. + ReplaceStringsInFile( + 'absl/base/config.h', { + '#undef ABSL_LTS_RELEASE_VERSION': + '#define ABSL_LTS_RELEASE_VERSION {}'.format(datestamp), + '#undef ABSL_LTS_RELEASE_PATCH_LEVEL': + '#define ABSL_LTS_RELEASE_PATCH_LEVEL 0' + }) + ReplaceStringsInFile( + 'absl/base/options.h', { + '#define ABSL_OPTION_USE_INLINE_NAMESPACE 0': + '#define ABSL_OPTION_USE_INLINE_NAMESPACE 1', + '#define ABSL_OPTION_INLINE_NAMESPACE_NAME head': + '#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_{}'.format( + datestamp) + }) + ReplaceStringsInFile( + 'CMakeLists.txt', { + 'project(absl LANGUAGES CXX)': + 'project(absl LANGUAGES CXX VERSION {})'.format(datestamp) + }) + # Set the SOVERSION to YYMM.0.0 - The first 0 means we only have ABI + # compatible changes, and the second 0 means we can increment it to + # mark changes as ABI-compatible, for patch releases. Note that we + # only use the last two digits of the year and the month because the + # MacOS linker requires the first part of the SOVERSION to fit into + # 16 bits. + # https://www.sicpers.info/2013/03/how-to-version-a-mach-o-library/ + ReplaceStringsInFile( + 'CMake/AbseilHelpers.cmake', + {'SOVERSION 0': 'SOVERSION "{}.0.0"'.format(datestamp[2:6])}) + StripContentBetweenTags('CMakeLists.txt', '# absl:lts-remove-begin', + '# absl:lts-remove-end') + + +if __name__ == '__main__': + main(sys.argv) |