aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-04-19 18:08:55 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-04-19 18:08:55 +0000
commita5cf6f737aabc6bf3dbb4b628e37080fa292f323 (patch)
tree2338e093e11bde9ecd3f1fa7dd94b5a963169a89
parent50d13d4adc7c1901718d082e8dad9352726c1376 (diff)
parent4af5b57dd17f345a9b8936d12bdc743b0e0bf7e5 (diff)
downloadabseil-cpp-androidx-lifecycle-release.tar.gz
Snap for 11739378 from 4af5b57dd17f345a9b8936d12bdc743b0e0bf7e5 to androidx-lifecycle-releaseandroidx-lifecycle-release
Change-Id: Ie18fb4a4db8925af61bade824fb11f71fbede6a3
-rw-r--r--.gitignore2
-rw-r--r--Android.bp9
-rw-r--r--CMake/AbseilDll.cmake51
-rw-r--r--CMake/AbseilHelpers.cmake8
-rw-r--r--CMakeLists.txt44
-rw-r--r--METADATA4
-rw-r--r--MODULE.bazel39
l---------NOTICE1
-rw-r--r--PrivacyInfo.xcprivacy14
-rw-r--r--WORKSPACE41
-rw-r--r--absl/BUILD.bazel11
-rwxr-xr-xabsl/abseil.podspec.gen.py3
-rw-r--r--absl/algorithm/BUILD.bazel26
-rw-r--r--absl/algorithm/CMakeLists.txt1
-rw-r--r--absl/algorithm/algorithm.h111
-rw-r--r--absl/algorithm/algorithm_test.cc141
-rw-r--r--absl/algorithm/container.h128
-rw-r--r--absl/algorithm/container_test.cc22
-rw-r--r--absl/algorithm/equal_benchmark.cc126
-rw-r--r--absl/base/BUILD.bazel83
-rw-r--r--absl/base/CMakeLists.txt33
-rw-r--r--absl/base/attributes.h63
-rw-r--r--absl/base/call_once.h24
-rw-r--r--absl/base/casts.h2
-rw-r--r--absl/base/config.h117
-rw-r--r--absl/base/internal/endian.h25
-rw-r--r--absl/base/internal/identity.h6
-rw-r--r--absl/base/internal/inline_variable.h37
-rw-r--r--absl/base/internal/low_level_alloc.cc2
-rw-r--r--absl/base/internal/prefetch.h137
-rw-r--r--absl/base/internal/prefetch_test.cc43
-rw-r--r--absl/base/internal/raw_logging.cc20
-rw-r--r--absl/base/internal/raw_logging.h2
-rw-r--r--absl/base/internal/spinlock.h21
-rw-r--r--absl/base/internal/spinlock_benchmark.cc34
-rw-r--r--absl/base/internal/sysinfo.cc20
-rw-r--r--absl/base/internal/thread_annotations.h280
-rw-r--r--absl/base/internal/thread_identity.cc14
-rw-r--r--absl/base/internal/unaligned_access.h19
-rw-r--r--absl/base/log_severity.cc1
-rw-r--r--absl/base/log_severity.h33
-rw-r--r--absl/base/log_severity_test.cc10
-rw-r--r--absl/base/no_destructor.h217
-rw-r--r--absl/base/no_destructor_benchmark.cc165
-rw-r--r--absl/base/no_destructor_test.cc209
-rw-r--r--absl/base/optimization.h1
-rw-r--r--absl/base/options.h28
-rw-r--r--absl/base/prefetch.h39
-rw-r--r--absl/base/spinlock_test_common.cc13
-rw-r--r--absl/base/thread_annotations.h2
-rw-r--r--absl/cleanup/BUILD.bazel10
-rw-r--r--absl/container/BUILD.bazel70
-rw-r--r--absl/container/CMakeLists.txt32
-rw-r--r--absl/container/btree_map.h4
-rw-r--r--absl/container/btree_test.cc219
-rw-r--r--absl/container/fixed_array_test.cc2
-rw-r--r--absl/container/flat_hash_map.h6
-rw-r--r--absl/container/flat_hash_map_test.cc38
-rw-r--r--absl/container/flat_hash_set.h2
-rw-r--r--absl/container/flat_hash_set_test.cc65
-rw-r--r--absl/container/inlined_vector_test.cc2
-rw-r--r--absl/container/internal/btree.h49
-rw-r--r--absl/container/internal/common_policy_traits.h6
-rw-r--r--absl/container/internal/common_policy_traits_test.cc18
-rw-r--r--absl/container/internal/container_memory.h22
-rw-r--r--absl/container/internal/container_memory_test.cc35
-rw-r--r--absl/container/internal/counting_allocator.h122
-rw-r--r--absl/container/internal/hash_generator_testing.cc12
-rw-r--r--absl/container/internal/hashtable_debug.h8
-rw-r--r--absl/container/internal/hashtablez_sampler.h14
-rw-r--r--absl/container/internal/hashtablez_sampler_test.cc8
-rw-r--r--absl/container/internal/inlined_vector.h13
-rw-r--r--absl/container/internal/layout.h27
-rw-r--r--absl/container/internal/layout_test.cc7
-rw-r--r--absl/container/internal/node_slot_policy.h5
-rw-r--r--absl/container/internal/node_slot_policy_test.cc2
-rw-r--r--absl/container/internal/raw_hash_map.h13
-rw-r--r--absl/container/internal/raw_hash_set.cc179
-rw-r--r--absl/container/internal/raw_hash_set.h995
-rw-r--r--absl/container/internal/raw_hash_set_allocator_test.cc119
-rw-r--r--absl/container/internal/raw_hash_set_benchmark.cc56
-rw-r--r--absl/container/internal/raw_hash_set_probe_benchmark.cc16
-rw-r--r--absl/container/internal/raw_hash_set_test.cc358
-rw-r--r--absl/container/internal/test_allocator.h387
-rw-r--r--absl/crc/BUILD.bazel18
-rw-r--r--absl/crc/CMakeLists.txt5
-rw-r--r--absl/crc/internal/cpu_detect.cc28
-rw-r--r--absl/crc/internal/cpu_detect.h6
-rw-r--r--absl/crc/internal/crc32_x86_arm_combined_simd.h39
-rw-r--r--absl/crc/internal/crc_memcpy.h9
-rw-r--r--absl/crc/internal/crc_memcpy_fallback.cc6
-rw-r--r--absl/crc/internal/crc_memcpy_test.cc28
-rw-r--r--absl/crc/internal/crc_memcpy_x86_arm_combined.cc (renamed from absl/crc/internal/crc_memcpy_x86_64.cc)112
-rw-r--r--absl/crc/internal/crc_x86_arm_combined.cc12
-rw-r--r--absl/debugging/BUILD.bazel15
-rw-r--r--absl/debugging/failure_signal_handler.cc2
-rw-r--r--absl/debugging/internal/address_is_readable.cc6
-rw-r--r--absl/debugging/internal/demangle.cc24
-rw-r--r--absl/debugging/internal/demangle.h68
-rw-r--r--absl/debugging/internal/demangle_test.cc30
-rw-r--r--absl/debugging/internal/stack_consumption.cc18
-rw-r--r--absl/debugging/internal/stacktrace_aarch64-inl.inc58
-rw-r--r--absl/debugging/symbolize_elf.inc152
-rw-r--r--absl/flags/BUILD.bazel27
-rw-r--r--absl/flags/CMakeLists.txt5
-rw-r--r--absl/flags/declare.h5
-rw-r--r--absl/flags/flag.h11
-rw-r--r--absl/flags/flag_test.cc11
-rw-r--r--absl/flags/internal/flag.h5
-rw-r--r--absl/flags/internal/flag_msvc.inc116
-rw-r--r--absl/flags/internal/usage.cc5
-rw-r--r--absl/flags/marshalling.cc11
-rw-r--r--absl/flags/parse.cc2
-rw-r--r--absl/flags/reflection.cc3
-rw-r--r--absl/functional/BUILD.bazel40
-rw-r--r--absl/functional/CMakeLists.txt24
-rw-r--r--absl/functional/function_ref.h8
-rw-r--r--absl/functional/function_ref_test.cc5
-rw-r--r--absl/functional/internal/any_invocable.h4
-rw-r--r--absl/functional/overload.h75
-rw-r--r--absl/functional/overload_test.cc130
-rw-r--r--absl/hash/BUILD.bazel17
-rw-r--r--absl/hash/CMakeLists.txt18
-rw-r--r--absl/hash/hash_benchmark.cc14
-rw-r--r--absl/hash/hash_test.cc65
-rw-r--r--absl/hash/internal/hash.h51
-rw-r--r--absl/log/BUILD.bazel85
-rw-r--r--absl/log/CMakeLists.txt156
-rw-r--r--absl/log/absl_log.h12
-rw-r--r--absl/log/absl_vlog_is_on.h93
-rw-r--r--absl/log/check.h2
-rw-r--r--absl/log/flags.cc23
-rw-r--r--absl/log/globals.h23
-rw-r--r--absl/log/globals_test.cc43
-rw-r--r--absl/log/initialize.cc8
-rw-r--r--absl/log/internal/BUILD.bazel94
-rw-r--r--absl/log/internal/check_op.h60
-rw-r--r--absl/log/internal/conditions.h55
-rw-r--r--absl/log/internal/flags.h8
-rw-r--r--absl/log/internal/fnmatch.cc73
-rw-r--r--absl/log/internal/fnmatch.h (renamed from absl/flags/flag.cc)35
-rw-r--r--absl/log/internal/fnmatch_benchmark.cc29
-rw-r--r--absl/log/internal/fnmatch_test.cc59
-rw-r--r--absl/log/internal/log_impl.h66
-rw-r--r--absl/log/internal/log_message.h16
-rw-r--r--absl/log/internal/log_sink_set.cc16
-rw-r--r--absl/log/internal/strip.h7
-rw-r--r--absl/log/internal/vlog_config.cc340
-rw-r--r--absl/log/internal/vlog_config.h163
-rw-r--r--absl/log/internal/vlog_config_benchmark.cc187
-rw-r--r--absl/log/log.h41
-rw-r--r--absl/log/log_basic_test_impl.inc90
-rw-r--r--absl/log/log_entry.cc12
-rw-r--r--absl/log/log_entry.h3
-rw-r--r--absl/log/log_streamer.h10
-rw-r--r--absl/log/log_streamer_test.cc51
-rw-r--r--absl/log/stripping_test.cc128
-rw-r--r--absl/log/vlog_is_on.h72
-rw-r--r--absl/log/vlog_is_on_test.cc176
-rw-r--r--absl/memory/BUILD.bazel10
-rw-r--r--absl/meta/BUILD.bazel10
-rw-r--r--absl/numeric/BUILD.bazel13
-rw-r--r--absl/numeric/bits.h55
-rw-r--r--absl/profiling/BUILD.bazel13
-rw-r--r--absl/random/BUILD.bazel26
-rw-r--r--absl/random/distributions.h2
-rw-r--r--absl/random/internal/BUILD.bazel27
-rw-r--r--absl/status/BUILD.bazel24
-rw-r--r--absl/status/CMakeLists.txt17
-rw-r--r--absl/status/internal/status_internal.cc248
-rw-r--r--absl/status/internal/status_internal.h69
-rw-r--r--absl/status/internal/statusor_internal.h55
-rw-r--r--absl/status/status.cc274
-rw-r--r--absl/status/status.h148
-rw-r--r--absl/status/status_payload_printer.cc4
-rw-r--r--absl/status/status_payload_printer.h5
-rw-r--r--absl/status/status_test.cc73
-rw-r--r--absl/status/statusor.cc7
-rw-r--r--absl/status/statusor.h46
-rw-r--r--absl/status/statusor_test.cc41
-rw-r--r--absl/strings/BUILD.bazel238
-rw-r--r--absl/strings/CMakeLists.txt138
-rw-r--r--absl/strings/ascii.cc96
-rw-r--r--absl/strings/ascii.h14
-rw-r--r--absl/strings/ascii_benchmark.cc20
-rw-r--r--absl/strings/ascii_test.cc2
-rw-r--r--absl/strings/charconv.cc31
-rw-r--r--absl/strings/charconv.h9
-rw-r--r--absl/strings/charconv_test.cc7
-rw-r--r--absl/strings/charset.h164
-rw-r--r--absl/strings/charset_benchmark.cc (renamed from absl/strings/internal/char_map_benchmark.cc)24
-rw-r--r--absl/strings/charset_test.cc181
-rw-r--r--absl/strings/cord.cc335
-rw-r--r--absl/strings/cord.h230
-rw-r--r--absl/strings/cord_analysis.cc52
-rw-r--r--absl/strings/cord_analysis.h7
-rw-r--r--absl/strings/cord_buffer_test.cc4
-rw-r--r--absl/strings/cord_ring_reader_test.cc180
-rw-r--r--absl/strings/cord_ring_test.cc1454
-rw-r--r--absl/strings/cord_test.cc125
-rw-r--r--absl/strings/cordz_test.cc10
-rw-r--r--absl/strings/cordz_test_helpers.h10
-rw-r--r--absl/strings/escaping.cc9
-rw-r--r--absl/strings/escaping_benchmark.cc4
-rw-r--r--absl/strings/escaping_test.cc7
-rw-r--r--absl/strings/has_absl_stringify.h63
-rw-r--r--absl/strings/has_absl_stringify_test.cc40
-rw-r--r--absl/strings/has_ostream_operator.h42
-rw-r--r--absl/strings/has_ostream_operator_test.cc41
-rw-r--r--absl/strings/internal/char_map.h158
-rw-r--r--absl/strings/internal/char_map_test.cc172
-rw-r--r--absl/strings/internal/cord_internal.cc6
-rw-r--r--absl/strings/internal/cord_internal.h64
-rw-r--r--absl/strings/internal/cord_rep_ring.cc773
-rw-r--r--absl/strings/internal/cord_rep_ring.h607
-rw-r--r--absl/strings/internal/cord_rep_ring_reader.h118
-rw-r--r--absl/strings/internal/cordz_info.cc51
-rw-r--r--absl/strings/internal/cordz_info_statistics_test.cc103
-rw-r--r--absl/strings/internal/has_absl_stringify.h41
-rw-r--r--absl/strings/internal/memutil.cc16
-rw-r--r--absl/strings/internal/str_format/arg.cc153
-rw-r--r--absl/strings/internal/str_format/arg.h96
-rw-r--r--absl/strings/internal/str_format/arg_test.cc34
-rw-r--r--absl/strings/internal/str_format/bind.cc18
-rw-r--r--absl/strings/internal/str_format/bind.h11
-rw-r--r--absl/strings/internal/str_format/constexpr_parser.h11
-rw-r--r--absl/strings/internal/str_format/convert_test.cc352
-rw-r--r--absl/strings/internal/str_format/extension.h14
-rw-r--r--absl/strings/internal/str_format/parser.h9
-rw-r--r--absl/strings/internal/str_format/parser_test.cc10
-rw-r--r--absl/strings/match.cc3
-rw-r--r--absl/strings/match_test.cc3
-rw-r--r--absl/strings/numbers.cc549
-rw-r--r--absl/strings/numbers.h228
-rw-r--r--absl/strings/numbers_benchmark.cc2
-rw-r--r--absl/strings/numbers_test.cc32
-rw-r--r--absl/strings/str_cat.cc172
-rw-r--r--absl/strings/str_cat.h152
-rw-r--r--absl/strings/str_cat_benchmark.cc110
-rw-r--r--absl/strings/str_cat_test.cc67
-rw-r--r--absl/strings/str_format.h50
-rw-r--r--absl/strings/str_format_test.cc12
-rw-r--r--absl/strings/str_join.h32
-rw-r--r--absl/strings/str_join_test.cc3
-rw-r--r--absl/strings/str_replace.cc15
-rw-r--r--absl/strings/str_replace.h13
-rw-r--r--absl/strings/str_replace_test.cc6
-rw-r--r--absl/strings/str_split.cc14
-rw-r--r--absl/strings/str_split.h18
-rw-r--r--absl/strings/str_split_benchmark.cc1
-rw-r--r--absl/strings/str_split_test.cc48
-rw-r--r--absl/strings/string_view.cc31
-rw-r--r--absl/strings/string_view.h117
-rw-r--r--absl/strings/string_view_benchmark.cc1
-rw-r--r--absl/strings/string_view_test.cc79
-rw-r--r--absl/strings/strip.h7
-rw-r--r--absl/strings/substitute.cc16
-rw-r--r--absl/strings/substitute.h194
-rw-r--r--absl/strings/substitute_test.cc3
-rw-r--r--absl/synchronization/BUILD.bazel20
-rw-r--r--absl/synchronization/blocking_counter_benchmark.cc5
-rw-r--r--absl/synchronization/internal/kernel_timeout_test.cc9
-rw-r--r--absl/synchronization/internal/pthread_waiter.h4
-rw-r--r--absl/synchronization/internal/waiter.h2
-rw-r--r--absl/synchronization/internal/win32_waiter.h6
-rw-r--r--absl/synchronization/mutex.cc628
-rw-r--r--absl/synchronization/mutex.h123
-rw-r--r--absl/synchronization/mutex_benchmark.cc37
-rw-r--r--absl/synchronization/mutex_test.cc145
-rw-r--r--absl/time/BUILD.bazel11
-rw-r--r--absl/time/civil_time.h26
-rw-r--r--absl/time/civil_time_benchmark.cc13
-rw-r--r--absl/time/civil_time_test.cc21
-rw-r--r--absl/time/clock.h6
-rw-r--r--absl/time/duration.cc6
-rw-r--r--absl/time/internal/cctz/BUILD.bazel9
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time_detail.h4
-rw-r--r--absl/time/internal/cctz/src/cctz_benchmark.cc113
-rw-r--r--absl/time/internal/cctz/src/time_zone_format.cc2
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.cc23
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc112
-rw-r--r--absl/time/internal/cctz/src/tzfile.h8
-rw-r--r--absl/time/internal/cctz/testdata/README.zoneinfo2
-rw-r--r--absl/time/internal/cctz/testdata/version2
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Ensenadabin1025 -> 1025 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Godthabbin965 -> 965 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Baybin1580 -> 1580 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamacbin612 -> 603 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Matamorosbin437 -> 437 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatlabin595 -> 586 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Monctonbin1493 -> 1493 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Nuukbin965 -> 965 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Ojinagabin709 -> 718 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabelbin1025 -> 1025 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysundbin479 -> 984 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/St_Johnsbin1878 -> 1878 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Tijuanabin1025 -> 1025 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Caseybin243 -> 287 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquariebin976 -> 976 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Trollbin177 -> 158 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostokbin133 -> 170 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Gazabin2518 -> 2968 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebronbin2536 -> 2986 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosiabin597 -> 597 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundlandbin1878 -> 1878 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfastbin1599 -> 1599 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharestbin661 -> 661 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinaubin755 -> 755 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernseybin1611 -> 1611 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Manbin1599 -> 1599 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Jerseybin1611 -> 1611 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Kievbin558 -> 558 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyivbin558 -> 558 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Londonbin1599 -> 1599 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosiabin597 -> 597 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Rigabin694 -> 694 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofiabin592 -> 592 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinnbin675 -> 675 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspolbin755 -> 755 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorodbin558 -> 558 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilniusbin676 -> 676 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhyebin558 -> 558 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GBbin1599 -> 1599 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/GB-Eirebin1599 -> 1599 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNortebin1025 -> 1025 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolkbin247 -> 237 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab17
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab29
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab301
-rw-r--r--absl/types/BUILD.bazel57
-rw-r--r--absl/types/CMakeLists.txt56
-rw-r--r--absl/types/bad_any_cast.cc18
-rw-r--r--absl/types/bad_optional_access.cc18
-rw-r--r--absl/types/bad_variant_access.cc18
-rw-r--r--absl/types/compare.h160
-rw-r--r--absl/types/compare_test.cc89
-rw-r--r--absl/types/internal/conformance_aliases.h447
-rw-r--r--absl/types/internal/conformance_archetype.h978
-rw-r--r--absl/types/internal/conformance_profile.h933
-rw-r--r--absl/types/internal/conformance_testing.h1386
-rw-r--r--absl/types/internal/conformance_testing_helpers.h391
-rw-r--r--absl/types/internal/conformance_testing_test.cc1556
-rw-r--r--absl/types/internal/parentheses.h34
-rw-r--r--absl/types/internal/transform_args.h246
-rw-r--r--absl/types/internal/variant.h6
-rw-r--r--absl/types/optional.h5
-rw-r--r--absl/types/span.h13
-rw-r--r--absl/utility/BUILD.bazel11
-rw-r--r--absl/utility/utility.h104
-rw-r--r--absl/utility/utility_test.cc129
-rw-r--r--ci/absl_alternate_options.h1
-rw-r--r--ci/cmake_common.sh6
-rwxr-xr-xci/linux_arm_clang-latest_libcxx_bazel.sh100
-rwxr-xr-xci/linux_clang-latest_libcxx_asan_bazel.sh8
-rwxr-xr-xci/linux_clang-latest_libcxx_bazel.sh8
-rwxr-xr-xci/linux_clang-latest_libcxx_tsan_bazel.sh8
-rwxr-xr-xci/linux_clang-latest_libstdcxx_bazel.sh4
-rw-r--r--ci/linux_docker_containers.sh5
-rwxr-xr-xci/linux_gcc-floor_libstdcxx_bazel.sh3
-rwxr-xr-xci/linux_gcc-latest_libstdcxx_bazel.sh4
-rwxr-xr-xci/macos_xcode_bazel.sh3
-rwxr-xr-xci/windows_clangcl_bazel.bat6
-rwxr-xr-xci/windows_msvc_bazel.bat4
-rwxr-xr-xci/windows_msvc_cmake.bat7
-rwxr-xr-xcreate_lts.py5
365 files changed, 12257 insertions, 14680 deletions
diff --git a/.gitignore b/.gitignore
index d54fa5a9..3a729f41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+# Bzlmod lockfile
+MODULE.bazel.lock
# Ignore all bazel-* symlinks.
/bazel-*
# Ignore Bazel verbose explanations
diff --git a/Android.bp b/Android.bp
index de126595..e6ca9e0f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -51,12 +51,3 @@ cc_library_host_static {
export_include_dirs: ["."],
stl: "libc++",
}
-
-// Targets below are deprecated.
-
-cc_library_headers {
- name: "libabsl_headers",
- device_supported: false,
- host_supported: true,
- export_include_dirs: ["."],
-}
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake
index f0d984ae..47f3beeb 100644
--- a/CMake/AbseilDll.cmake
+++ b/CMake/AbseilDll.cmake
@@ -28,7 +28,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/low_level_scheduling.h"
"base/internal/nullability_impl.h"
"base/internal/per_thread_tls.h"
- "base/internal/prefetch.h"
"base/prefetch.h"
"base/internal/pretty_function.h"
"base/internal/raw_logging.cc"
@@ -44,7 +43,6 @@ set(ABSL_INTERNAL_DLL_FILES
"base/internal/spinlock_wait.h"
"base/internal/sysinfo.cc"
"base/internal/sysinfo.h"
- "base/internal/thread_annotations.h"
"base/internal/thread_identity.cc"
"base/internal/thread_identity.h"
"base/internal/throw_delegate.cc"
@@ -57,6 +55,7 @@ set(ABSL_INTERNAL_DLL_FILES
"base/log_severity.cc"
"base/log_severity.h"
"base/macros.h"
+ "base/no_destructor.h"
"base/nullability.h"
"base/optimization.h"
"base/options.h"
@@ -77,7 +76,6 @@ set(ABSL_INTERNAL_DLL_FILES
"container/internal/common_policy_traits.h"
"container/internal/compressed_tuple.h"
"container/internal/container_memory.h"
- "container/internal/counting_allocator.h"
"container/internal/hash_function_defaults.h"
"container/internal/hash_policy_traits.h"
"container/internal/hashtable_debug.h"
@@ -109,7 +107,7 @@ set(ABSL_INTERNAL_DLL_FILES
"crc/internal/crc_x86_arm_combined.cc"
"crc/internal/crc_memcpy_fallback.cc"
"crc/internal/crc_memcpy.h"
- "crc/internal/crc_memcpy_x86_64.cc"
+ "crc/internal/crc_memcpy_x86_arm_combined.cc"
"crc/internal/crc_non_temporal_memcpy.cc"
"crc/internal/crc_x86_arm_combined.cc"
"crc/internal/non_temporal_arm_intrinsics.h"
@@ -141,6 +139,7 @@ set(ABSL_INTERNAL_DLL_FILES
"functional/function_ref.h"
"functional/internal/any_invocable.h"
"functional/internal/function_ref.h"
+ "functional/overload.h"
"hash/hash.h"
"hash/internal/city.h"
"hash/internal/city.cc"
@@ -151,6 +150,7 @@ set(ABSL_INTERNAL_DLL_FILES
"hash/internal/low_level_hash.cc"
"log/absl_check.h"
"log/absl_log.h"
+ "log/absl_vlog_is_on.h"
"log/check.h"
"log/die_if_null.cc"
"log/die_if_null.h"
@@ -163,6 +163,8 @@ set(ABSL_INTERNAL_DLL_FILES
"log/internal/conditions.cc"
"log/internal/conditions.h"
"log/internal/config.h"
+ "log/internal/fnmatch.h"
+ "log/internal/fnmatch.cc"
"log/internal/globals.cc"
"log/internal/globals.h"
"log/internal/log_format.cc"
@@ -179,6 +181,8 @@ set(ABSL_INTERNAL_DLL_FILES
"log/internal/proto.cc"
"log/internal/strip.h"
"log/internal/structured.h"
+ "log/internal/vlog_config.cc"
+ "log/internal/vlog_config.h"
"log/internal/voidify.h"
"log/initialize.cc"
"log/initialize.h"
@@ -190,6 +194,7 @@ set(ABSL_INTERNAL_DLL_FILES
"log/log_sink_registry.h"
"log/log_streamer.h"
"log/structured.h"
+ "log/vlog_is_on.h"
"memory/memory.h"
"meta/type_traits.h"
"numeric/bits.h"
@@ -250,6 +255,7 @@ set(ABSL_INTERNAL_DLL_FILES
"random/uniform_real_distribution.h"
"random/zipf_distribution.h"
"status/internal/status_internal.h"
+ "status/internal/status_internal.cc"
"status/internal/statusor_internal.h"
"status/status.h"
"status/status.cc"
@@ -261,6 +267,7 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/ascii.h"
"strings/charconv.cc"
"strings/charconv.h"
+ "strings/charset.h"
"strings/cord.cc"
"strings/cord.h"
"strings/cord_analysis.cc"
@@ -287,9 +294,6 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/internal/cord_rep_consume.h"
"strings/internal/cord_rep_consume.cc"
"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"
@@ -308,6 +312,8 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/internal/stringify_sink.h"
"strings/internal/stringify_sink.cc"
"strings/internal/has_absl_stringify.h"
+ "strings/has_absl_stringify.h"
+ "strings/has_ostream_operator.h"
"strings/match.cc"
"strings/match.h"
"strings/numbers.cc"
@@ -325,7 +331,6 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/strip.h"
"strings/substitute.cc"
"strings/substitute.h"
- "strings/internal/char_map.h"
"strings/internal/escaping.h"
"strings/internal/escaping.cc"
"strings/internal/memutil.cc"
@@ -421,11 +426,6 @@ set(ABSL_INTERNAL_DLL_FILES
"types/bad_variant_access.cc"
"types/bad_variant_access.h"
"types/compare.h"
- "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"
@@ -627,17 +627,32 @@ include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
[==[
#ifdef _MSC_VER
-# if _MSVC_LANG < 201700L
+# if _MSVC_LANG < 201703L
# error "The compiler defaults or is configured for C++ < 17"
# endif
-#elif __cplusplus < 201700L
+#elif __cplusplus < 201703L
# error "The compiler defaults or is configured for C++ < 17"
#endif
int main() { return 0; }
]==]
ABSL_INTERNAL_AT_LEAST_CXX17)
-if(ABSL_INTERNAL_AT_LEAST_CXX17)
+check_cxx_source_compiles(
+ [==[
+#ifdef _MSC_VER
+# if _MSVC_LANG < 202002L
+# error "The compiler defaults or is configured for C++ < 20"
+# endif
+#elif __cplusplus < 202002L
+# error "The compiler defaults or is configured for C++ < 20"
+#endif
+int main() { return 0; }
+]==]
+ ABSL_INTERNAL_AT_LEAST_CXX20)
+
+if(ABSL_INTERNAL_AT_LEAST_CXX20)
+ set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_20)
+elseif(ABSL_INTERNAL_AT_LEAST_CXX17)
set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_17)
else()
set(ABSL_INTERNAL_CXX_STD_FEATURE cxx_std_14)
@@ -807,8 +822,8 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
if(ABSL_PROPAGATE_CXX_STD)
# Abseil libraries require C++14 as the current minimum standard. When
- # compiled with C++17 (either because it is the compiler's default or
- # explicitly requested), then Abseil requires C++17.
+ # compiled with a higher minimum (either because it is the compiler's
+ # default or explicitly requested), then Abseil requires that standard.
target_compile_features(${_dll} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
endif()
diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake
index 3bd33ce8..c53b3584 100644
--- a/CMake/AbseilHelpers.cmake
+++ b/CMake/AbseilHelpers.cmake
@@ -80,7 +80,7 @@ endif()
# absl::fantastic_lib
# )
#
-# TODO: Implement "ALWAYSLINK"
+# TODO(b/320467376): Implement "ALWAYSLINK".
function(absl_cc_library)
cmake_parse_arguments(ABSL_CC_LIB
"DISABLE_INSTALL;PUBLIC;TESTONLY"
@@ -287,8 +287,8 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
if(ABSL_PROPAGATE_CXX_STD)
# Abseil libraries require C++14 as the current minimum standard. When
- # compiled with C++17 (either because it is the compiler's default or
- # explicitly requested), then Abseil requires C++17.
+ # compiled with a higher standard (either because it is the compiler's
+ # default or explicitly requested), then Abseil requires that standard.
target_compile_features(${_NAME} PUBLIC ${ABSL_INTERNAL_CXX_STD_FEATURE})
endif()
@@ -298,7 +298,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n")
if(ABSL_ENABLE_INSTALL)
set_target_properties(${_NAME} PROPERTIES
OUTPUT_NAME "absl_${_NAME}"
- SOVERSION "2308.0.0"
+ SOVERSION "2401.0.0"
)
endif()
else()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eef66267..194f8708 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,11 @@ if (POLICY CMP0048)
cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)
+# Honor the GTest_ROOT variable if specified
+if (POLICY CMP0074)
+ cmake_policy(SET CMP0074 NEW)
+endif (POLICY CMP0074)
+
# option() honor variables
if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
@@ -53,7 +58,7 @@ if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
endif (POLICY CMP0141)
-project(absl LANGUAGES CXX VERSION 20230802)
+project(absl LANGUAGES CXX VERSION 20240116)
include(CTest)
# Output directory is correct by default for most build setups. However, when
@@ -221,20 +226,41 @@ if(ABSL_ENABLE_INSTALL)
PATTERN "testdata" EXCLUDE
)
+ # Rewrite options.h to use the compiled ABI.
file(READ "absl/base/options.h" ABSL_INTERNAL_OPTIONS_H_CONTENTS)
- if (ABSL_INTERNAL_AT_LEAST_CXX17)
- string(REGEX REPLACE
- "#define ABSL_OPTION_USE_STD_([^ ]*) 2"
- "#define ABSL_OPTION_USE_STD_\\1 1"
+
+ # Handle features that require at least C++20.
+ if (ABSL_INTERNAL_AT_LEAST_CXX20)
+ foreach(FEATURE "ORDERING")
+ string(REPLACE
+ "#define ABSL_OPTION_USE_STD_${FEATURE} 2"
+ "#define ABSL_OPTION_USE_STD_${FEATURE} 1"
ABSL_INTERNAL_OPTIONS_H_PINNED
"${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
- else()
- string(REGEX REPLACE
- "#define ABSL_OPTION_USE_STD_([^ ]*) 2"
- "#define ABSL_OPTION_USE_STD_\\1 0"
+ set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
+ endforeach()
+ endif()
+
+ # Handle features that require at least C++17.
+ if (ABSL_INTERNAL_AT_LEAST_CXX17)
+ foreach(FEATURE "ANY" "OPTIONAL" "STRING_VIEW" "VARIANT")
+ string(REPLACE
+ "#define ABSL_OPTION_USE_STD_${FEATURE} 2"
+ "#define ABSL_OPTION_USE_STD_${FEATURE} 1"
ABSL_INTERNAL_OPTIONS_H_PINNED
"${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
+ set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
+ endforeach()
endif()
+
+ # Any feature that still has the value of 2 (because it was not handled above)
+ # should be set to 0.
+ string(REGEX REPLACE
+ "#define ABSL_OPTION_USE_STD_([^ ]*) 2"
+ "#define ABSL_OPTION_USE_STD_\\1 0"
+ ABSL_INTERNAL_OPTIONS_H_PINNED
+ "${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
+
file(WRITE "${CMAKE_BINARY_DIR}/options-pinned.h" "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
install(FILES "${CMAKE_BINARY_DIR}/options-pinned.h"
diff --git a/METADATA b/METADATA
index ebc58f77..6c196d6f 100644
--- a/METADATA
+++ b/METADATA
@@ -12,7 +12,7 @@ third_party {
type: GIT
value: "https://github.com/abseil/abseil-cpp"
}
- version: "20230802.2"
- last_upgrade_date { year: 2023 month: 8 day: 18 }
+ version: "20240116.1"
+ last_upgrade_date { year: 2024 month: 2 day: 28 }
license_type: NOTICE
}
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 00000000..efbc88b2
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,39 @@
+# Copyright 2024 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.
+
+# https://bazel.build/external/overview#bzlmod
+
+module(
+ name = "abseil-cpp",
+ version = "20240116.0",
+ compatibility_level = 1,
+)
+
+# Only direct dependencies need to be listed below.
+# Please keep the versions in sync with the versions in the WORKSPACE file.
+
+bazel_dep(name = "bazel_skylib",
+ version = "1.5.0")
+
+bazel_dep(name = "google_benchmark",
+ version = "1.8.3",
+ repo_name = "com_github_google_benchmark",
+ dev_dependency = True)
+
+bazel_dep(name = "googletest",
+ version = "1.14.0.bcr.1",
+ repo_name = "com_google_googletest")
+
+bazel_dep(name = "platforms",
+ version = "0.0.8")
diff --git a/NOTICE b/NOTICE
deleted file mode 120000
index 7a694c96..00000000
--- a/NOTICE
+++ /dev/null
@@ -1 +0,0 @@
-LICENSE \ No newline at end of file
diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy
new file mode 100644
index 00000000..6af16412
--- /dev/null
+++ b/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSPrivacyTracking</key>
+ <false/>
+ <key>NSPrivacyCollectedDataTypes</key>
+ <array/>
+ <key>NSPrivacyTrackingDomains</key>
+ <array/>
+ <key>NSPrivacyAccessedAPITypes</key>
+ <array/>
+</dict>
+</plist>
diff --git a/WORKSPACE b/WORKSPACE
index fdb615f3..0d886091 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -20,41 +20,40 @@ 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", # 2023-08-02T16:45:10Z
- sha256 = "1f357c27ca988c3f7c6b4bf68a9395005ac6761f034046e9dde0896e3aba00e4",
- strip_prefix = "googletest-1.14.0",
- # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh.
- urls = ["https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip"],
+ name = "com_google_googletest",
+ sha256 = "8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7",
+ strip_prefix = "googletest-1.14.0",
+ # Keep this URL in sync with ABSL_GOOGLETEST_COMMIT in ci/cmake_common.sh and
+ # ci/windows_msvc_cmake.bat.
+ urls = ["https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz"],
)
# RE2 (the regular expression library used by GoogleTest)
http_archive(
- name = "com_googlesource_code_re2", # 2023-03-17T11:36:51Z
- sha256 = "cb8b5312a65f2598954545a76e8bce913f35fbb3a21a5c88797a4448e9f9b9d9",
- strip_prefix = "re2-578843a516fd1da7084ae46209a75f3613b6065e",
- urls = ["https://github.com/google/re2/archive/578843a516fd1da7084ae46209a75f3613b6065e.zip"],
+ name = "com_googlesource_code_re2",
+ sha256 = "828341ad08524618a626167bd320b0c2acc97bd1c28eff693a9ea33a7ed2a85f",
+ strip_prefix = "re2-2023-11-01",
+ urls = ["https://github.com/google/re2/releases/download/2023-11-01/re2-2023-11-01.zip"],
)
# Google benchmark.
http_archive(
- name = "com_github_google_benchmark", # 2023-08-01T07:47:09Z
- sha256 = "db1e39ee71dc38aa7e57ed007f2c8b3bb59e13656435974781a9dc0617d75cc9",
- strip_prefix = "benchmark-02a354f3f323ae8256948e1dc77ddcb1dfc297da",
- urls = ["https://github.com/google/benchmark/archive/02a354f3f323ae8256948e1dc77ddcb1dfc297da.zip"],
+ name = "com_github_google_benchmark",
+ sha256 = "6bc180a57d23d4d9515519f92b0c83d61b05b5bab188961f36ac7b06b0d9e9ce",
+ strip_prefix = "benchmark-1.8.3",
+ urls = ["https://github.com/google/benchmark/archive/refs/tags/v1.8.3.tar.gz"],
)
# Bazel Skylib.
http_archive(
- name = "bazel_skylib", # 2023-05-31T19:24:07Z
- sha256 = "08c0386f45821ce246bbbf77503c973246ed6ee5c3463e41efc197fa9bc3a7f4",
- strip_prefix = "bazel-skylib-288731ef9f7f688932bd50e704a91a45ec185f9b",
- urls = ["https://github.com/bazelbuild/bazel-skylib/archive/288731ef9f7f688932bd50e704a91a45ec185f9b.zip"],
+ name = "bazel_skylib",
+ sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94",
+ urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"],
)
# Bazel platform rules.
http_archive(
- name = "platforms", # 2023-07-28T19:44:27Z
- sha256 = "40eb313613ff00a5c03eed20aba58890046f4d38dec7344f00bb9a8867853526",
- strip_prefix = "platforms-4ad40ef271da8176d4fc0194d2089b8a76e19d7b",
- urls = ["https://github.com/bazelbuild/platforms/archive/4ad40ef271da8176d4fc0194d2089b8a76e19d7b.zip"],
+ name = "platforms",
+ sha256 = "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74",
+ urls = ["https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz"],
)
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 253c0aef..14c30b38 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -68,6 +68,17 @@ config_setting(
visibility = [":__subpackages__"],
)
+# x64_windows-clang-cl - used for selecting clang-cl for CI builds
+platform(
+ name = "x64_windows-clang-cl",
+ constraint_values = [
+ "@platforms//cpu:x86_64",
+ "@platforms//os:windows",
+ "@bazel_tools//tools/cpp:clang-cl",
+ ],
+ visibility = [":__subpackages__"],
+)
+
config_setting(
name = "osx",
constraint_values = [
diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py
index 63752980..c83edbfe 100755
--- a/absl/abseil.podspec.gen.py
+++ b/absl/abseil.podspec.gen.py
@@ -30,6 +30,9 @@ Pod::Spec.new do |s|
:git => 'https://github.com/abseil/abseil-cpp.git',
:tag => '${tag}',
}
+ s.resource_bundles = {
+ s.module_name => 'PrivacyInfo.xcprivacy',
+ }
s.module_name = 'absl'
s.header_mappings_dir = 'absl'
s.header_dir = 'absl'
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel
index 3a9ab013..ddf9e11f 100644
--- a/absl/algorithm/BUILD.bazel
+++ b/absl/algorithm/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -44,24 +51,11 @@ cc_test(
deps = [
":algorithm",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
-cc_binary(
- name = "algorithm_benchmark",
- testonly = 1,
- srcs = ["equal_benchmark.cc"],
- copts = ABSL_TEST_COPTS,
- linkopts = ABSL_DEFAULT_LINKOPTS,
- tags = ["benchmark"],
- deps = [
- ":algorithm",
- "//absl/base:core_headers",
- "@com_github_google_benchmark//:benchmark_main",
- ],
-)
-
cc_library(
name = "container",
hdrs = [
@@ -72,6 +66,7 @@ cc_library(
deps = [
":algorithm",
"//absl/base:core_headers",
+ "//absl/base:nullability",
"//absl/meta:type_traits",
],
)
@@ -87,6 +82,7 @@ cc_test(
"//absl/base:core_headers",
"//absl/memory",
"//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt
index 181b49ca..5577164d 100644
--- a/absl/algorithm/CMakeLists.txt
+++ b/absl/algorithm/CMakeLists.txt
@@ -50,6 +50,7 @@ absl_cc_library(
absl::algorithm
absl::core_headers
absl::meta
+ absl::nullability
PUBLIC
)
diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h
index e9b47338..59aeed7d 100644
--- a/absl/algorithm/algorithm.h
+++ b/absl/algorithm/algorithm.h
@@ -31,92 +31,17 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
-namespace algorithm_internal {
-
-// Performs comparisons with operator==, similar to C++14's `std::equal_to<>`.
-struct EqualTo {
- template <typename T, typename U>
- bool operator()(const T& a, const U& b) const {
- return a == b;
- }
-};
-
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
- InputIter2 last2, Pred pred, std::input_iterator_tag,
- std::input_iterator_tag) {
- while (true) {
- if (first1 == last1) return first2 == last2;
- if (first2 == last2) return false;
- if (!pred(*first1, *first2)) return false;
- ++first1;
- ++first2;
- }
-}
-
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
- InputIter2 last2, Pred&& pred, std::random_access_iterator_tag,
- std::random_access_iterator_tag) {
- return (last1 - first1 == last2 - first2) &&
- std::equal(first1, last1, first2, std::forward<Pred>(pred));
-}
-
-// When we are using our own internal predicate that just applies operator==, we
-// forward to the non-predicate form of std::equal. This enables an optimization
-// in libstdc++ that can result in std::memcmp being used for integer types.
-template <typename InputIter1, typename InputIter2>
-bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2,
- InputIter2 last2, algorithm_internal::EqualTo /* unused */,
- std::random_access_iterator_tag,
- std::random_access_iterator_tag) {
- return (last1 - first1 == last2 - first2) &&
- std::equal(first1, last1, first2);
-}
-
-template <typename It>
-It RotateImpl(It first, It middle, It last, std::true_type) {
- return std::rotate(first, middle, last);
-}
-
-template <typename It>
-It RotateImpl(It first, It middle, It last, std::false_type) {
- std::rotate(first, middle, last);
- return std::next(first, std::distance(middle, last));
-}
-
-} // namespace algorithm_internal
-
// equal()
+// rotate()
//
-// Compares the equality of two ranges specified by pairs of iterators, using
-// the given predicate, returning true iff for each corresponding iterator i1
-// and i2 in the first and second range respectively, pred(*i1, *i2) == true
-//
-// This comparison takes at most min(`last1` - `first1`, `last2` - `first2`)
-// invocations of the predicate. Additionally, if InputIter1 and InputIter2 are
-// both random-access iterators, and `last1` - `first1` != `last2` - `first2`,
-// then the predicate is never invoked and the function returns false.
+// Historical note: Abseil once provided implementations of these algorithms
+// prior to their adoption in C++14. New code should prefer to use the std
+// variants.
//
-// This is a C++11-compatible implementation of C++14 `std::equal`. See
-// https://en.cppreference.com/w/cpp/algorithm/equal for more information.
-template <typename InputIter1, typename InputIter2, typename Pred>
-bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
- InputIter2 last2, Pred&& pred) {
- return algorithm_internal::EqualImpl(
- first1, last1, first2, last2, std::forward<Pred>(pred),
- typename std::iterator_traits<InputIter1>::iterator_category{},
- typename std::iterator_traits<InputIter2>::iterator_category{});
-}
-
-// Overload of equal() that performs comparison of two ranges specified by pairs
-// of iterators using operator==.
-template <typename InputIter1, typename InputIter2>
-bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
- InputIter2 last2) {
- return absl::equal(first1, last1, first2, last2,
- algorithm_internal::EqualTo{});
-}
+// See the documentation for the STL <algorithm> header for more information:
+// https://en.cppreference.com/w/cpp/header/algorithm
+using std::equal;
+using std::rotate;
// linear_search()
//
@@ -133,26 +58,6 @@ bool linear_search(InputIterator first, InputIterator last,
return std::find(first, last, value) != last;
}
-// rotate()
-//
-// Performs a left rotation on a range of elements (`first`, `last`) such that
-// `middle` is now the first element. `rotate()` returns an iterator pointing to
-// the first element before rotation. This function is exactly the same as
-// `std::rotate`, but fixes a bug in gcc
-// <= 4.9 where `std::rotate` returns `void` instead of an iterator.
-//
-// The complexity of this algorithm is the same as that of `std::rotate`, but if
-// `ForwardIterator` is not a random-access iterator, then `absl::rotate`
-// performs an additional pass over the range to construct the return value.
-template <typename ForwardIterator>
-ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
- ForwardIterator last) {
- return algorithm_internal::RotateImpl(
- first, middle, last,
- std::is_same<decltype(std::rotate(first, middle, last)),
- ForwardIterator>());
-}
-
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/algorithm/algorithm_test.cc b/absl/algorithm/algorithm_test.cc
index d18df024..e6ee4695 100644
--- a/absl/algorithm/algorithm_test.cc
+++ b/absl/algorithm/algorithm_test.cc
@@ -24,137 +24,6 @@
namespace {
-TEST(EqualTest, DefaultComparisonRandomAccess) {
- std::vector<int> v1{1, 2, 3};
- std::vector<int> v2 = v1;
- std::vector<int> v3 = {1, 2};
- std::vector<int> v4 = {1, 2, 4};
-
- EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, DefaultComparison) {
- std::list<int> lst1{1, 2, 3};
- std::list<int> lst2 = lst1;
- std::list<int> lst3{1, 2};
- std::list<int> lst4{1, 2, 4};
-
- EXPECT_TRUE(absl::equal(lst1.begin(), lst1.end(), lst2.begin(), lst2.end()));
- EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst3.begin(), lst3.end()));
- EXPECT_FALSE(absl::equal(lst1.begin(), lst1.end(), lst4.begin(), lst4.end()));
-}
-
-TEST(EqualTest, EmptyRange) {
- std::vector<int> v1{1, 2, 3};
- std::vector<int> empty1;
- std::vector<int> empty2;
-
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105705
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnonnull"
-#endif
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), empty1.begin(), empty1.end()));
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic pop
-#endif
- EXPECT_FALSE(absl::equal(empty1.begin(), empty1.end(), v1.begin(), v1.end()));
- EXPECT_TRUE(
- absl::equal(empty1.begin(), empty1.end(), empty2.begin(), empty2.end()));
-}
-
-TEST(EqualTest, MixedIterTypes) {
- std::vector<int> v1{1, 2, 3};
- std::list<int> lst1{v1.begin(), v1.end()};
- std::list<int> lst2{1, 2, 4};
- std::list<int> lst3{1, 2};
-
- EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), lst1.begin(), lst1.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst2.begin(), lst2.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), lst3.begin(), lst3.end()));
-}
-
-TEST(EqualTest, MixedValueTypes) {
- std::vector<int> v1{1, 2, 3};
- std::vector<char> v2{1, 2, 3};
- std::vector<char> v3{1, 2};
- std::vector<char> v4{1, 2, 4};
-
- EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, WeirdIterators) {
- std::vector<bool> v1{true, false};
- std::vector<bool> v2 = v1;
- std::vector<bool> v3{true};
- std::vector<bool> v4{true, true, true};
-
- EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end()));
-}
-
-TEST(EqualTest, CustomComparison) {
- int n[] = {1, 2, 3, 4};
- std::vector<int*> v1{&n[0], &n[1], &n[2]};
- std::vector<int*> v2 = v1;
- std::vector<int*> v3{&n[0], &n[1], &n[3]};
- std::vector<int*> v4{&n[0], &n[1]};
-
- auto eq = [](int* a, int* b) { return *a == *b; };
-
- EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), eq));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(), eq));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v4.begin(), v4.end(), eq));
-}
-
-TEST(EqualTest, MoveOnlyPredicate) {
- std::vector<int> v1{1, 2, 3};
- std::vector<int> v2{4, 5, 6};
-
- // move-only equality predicate
- struct Eq {
- Eq() = default;
- Eq(Eq &&) = default;
- Eq(const Eq &) = delete;
- Eq &operator=(const Eq &) = delete;
- bool operator()(const int a, const int b) const { return a == b; }
- };
-
- EXPECT_TRUE(absl::equal(v1.begin(), v1.end(), v1.begin(), v1.end(), Eq()));
- EXPECT_FALSE(absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(), Eq()));
-}
-
-struct CountingTrivialPred {
- int* count;
- bool operator()(int, int) const {
- ++*count;
- return true;
- }
-};
-
-TEST(EqualTest, RandomAccessComplexity) {
- std::vector<int> v1{1, 1, 3};
- std::vector<int> v2 = v1;
- std::vector<int> v3{1, 2};
-
- do {
- int count = 0;
- absl::equal(v1.begin(), v1.end(), v2.begin(), v2.end(),
- CountingTrivialPred{&count});
- EXPECT_LE(count, 3);
- } while (std::next_permutation(v2.begin(), v2.end()));
-
- int count = 0;
- absl::equal(v1.begin(), v1.end(), v3.begin(), v3.end(),
- CountingTrivialPred{&count});
- EXPECT_EQ(count, 0);
-}
-
class LinearSearchTest : public testing::Test {
protected:
LinearSearchTest() : container_{1, 2, 3} {}
@@ -178,14 +47,4 @@ TEST_F(LinearSearchTest, linear_searchConst) {
absl::linear_search(const_container->begin(), const_container->end(), 4));
}
-TEST(RotateTest, Rotate) {
- std::vector<int> v{0, 1, 2, 3, 4};
- EXPECT_EQ(*absl::rotate(v.begin(), v.begin() + 2, v.end()), 0);
- EXPECT_THAT(v, testing::ElementsAreArray({2, 3, 4, 0, 1}));
-
- std::list<int> l{0, 1, 2, 3, 4};
- EXPECT_EQ(*absl::rotate(l.begin(), std::next(l.begin(), 3), l.end()), 0);
- EXPECT_THAT(l, testing::ElementsAreArray({3, 4, 0, 1, 2}));
-}
-
} // namespace
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 679e0267..c7bafae1 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -52,6 +52,7 @@
#include "absl/algorithm/algorithm.h"
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
#include "absl/meta/type_traits.h"
namespace absl {
@@ -116,18 +117,6 @@ template <class Key, class Hash, class KeyEqual, class Allocator>
struct IsUnorderedContainer<std::unordered_set<Key, Hash, KeyEqual, Allocator>>
: std::true_type {};
-// container_algorithm_internal::c_size. It is meant for internal use only.
-
-template <class C>
-auto c_size(C& c) -> decltype(c.size()) {
- return c.size();
-}
-
-template <class T, std::size_t N>
-constexpr std::size_t c_size(T (&)[N]) {
- return N;
-}
-
} // namespace container_algorithm_internal
// PUBLIC API
@@ -348,20 +337,10 @@ container_algorithm_internal::ContainerDifferenceType<const C> c_count_if(
template <typename C1, typename C2>
container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(C1& c1,
C2& 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);
+ return std::mismatch(container_algorithm_internal::c_begin(c1),
+ container_algorithm_internal::c_end(c1),
+ container_algorithm_internal::c_begin(c2),
+ container_algorithm_internal::c_end(c2));
}
// Overload of c_mismatch() for using a predicate evaluation other than `==` as
@@ -370,56 +349,33 @@ container_algorithm_internal::ContainerIterPairType<C1, C2> c_mismatch(C1& c1,
template <typename C1, typename C2, typename BinaryPredicate>
container_algorithm_internal::ContainerIterPairType<C1, C2> 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);
+ return std::mismatch(container_algorithm_internal::c_begin(c1),
+ container_algorithm_internal::c_end(c1),
+ container_algorithm_internal::c_begin(c2),
+ container_algorithm_internal::c_end(c2), pred);
}
// c_equal()
//
// Container-based version of the <algorithm> `std::equal()` function to
// test whether two containers are equal.
-//
-// NOTE: the semantics of c_equal() are slightly different than those of
-// equal(): while the latter iterates over the second container only up to the
-// size of the first container, c_equal() also checks whether the container
-// sizes are equal. This better matches expectations about c_equal() based on
-// its signature.
-//
-// Example:
-// vector v1 = <1, 2, 3>;
-// vector v2 = <1, 2, 3, 4>;
-// equal(std::begin(v1), std::end(v1), std::begin(v2)) returns true
-// c_equal(v1, v2) returns false
-
template <typename C1, typename C2>
bool c_equal(const C1& c1, const C2& c2) {
- return ((container_algorithm_internal::c_size(c1) ==
- container_algorithm_internal::c_size(c2)) &&
- std::equal(container_algorithm_internal::c_begin(c1),
- container_algorithm_internal::c_end(c1),
- container_algorithm_internal::c_begin(c2)));
+ return std::equal(container_algorithm_internal::c_begin(c1),
+ container_algorithm_internal::c_end(c1),
+ container_algorithm_internal::c_begin(c2),
+ container_algorithm_internal::c_end(c2));
}
// Overload of c_equal() for using a predicate evaluation other than `==` as
// the function's test condition.
template <typename C1, typename C2, typename BinaryPredicate>
bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
- return ((container_algorithm_internal::c_size(c1) ==
- container_algorithm_internal::c_size(c2)) &&
- std::equal(container_algorithm_internal::c_begin(c1),
- container_algorithm_internal::c_end(c1),
- container_algorithm_internal::c_begin(c2),
- std::forward<BinaryPredicate>(pred)));
+ return std::equal(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<BinaryPredicate>(pred));
}
// c_is_permutation()
@@ -428,20 +384,20 @@ bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
// to test whether a container is a permutation of another.
template <typename C1, typename C2>
bool c_is_permutation(const C1& c1, const C2& c2) {
- using std::begin;
- using std::end;
- return c1.size() == c2.size() &&
- std::is_permutation(begin(c1), end(c1), begin(c2));
+ return std::is_permutation(container_algorithm_internal::c_begin(c1),
+ container_algorithm_internal::c_end(c1),
+ container_algorithm_internal::c_begin(c2),
+ container_algorithm_internal::c_end(c2));
}
// Overload of c_is_permutation() for using a predicate evaluation other than
// `==` as the function's test condition.
template <typename C1, typename C2, typename BinaryPredicate>
bool c_is_permutation(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
- using std::begin;
- using std::end;
- return c1.size() == c2.size() &&
- std::is_permutation(begin(c1), end(c1), begin(c2),
+ return std::is_permutation(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<BinaryPredicate>(pred));
}
@@ -818,6 +774,36 @@ void c_shuffle(RandomAccessContainer& c, UniformRandomBitGenerator&& gen) {
std::forward<UniformRandomBitGenerator>(gen));
}
+// c_sample()
+//
+// Container-based version of the <algorithm> `std::sample()` function to
+// randomly sample elements from the container without replacement using a
+// `gen()` uniform random number generator and write them to an iterator range.
+template <typename C, typename OutputIterator, typename Distance,
+ typename UniformRandomBitGenerator>
+OutputIterator c_sample(const C& c, OutputIterator result, Distance n,
+ UniformRandomBitGenerator&& gen) {
+#if defined(__cpp_lib_sample) && __cpp_lib_sample >= 201603L
+ return std::sample(container_algorithm_internal::c_begin(c),
+ container_algorithm_internal::c_end(c), result, n,
+ std::forward<UniformRandomBitGenerator>(gen));
+#else
+ // Fall back to a stable selection-sampling implementation.
+ auto first = container_algorithm_internal::c_begin(c);
+ Distance unsampled_elements = c_distance(c);
+ n = (std::min)(n, unsampled_elements);
+ for (; n != 0; ++first) {
+ Distance r =
+ std::uniform_int_distribution<Distance>(0, --unsampled_elements)(gen);
+ if (r < n) {
+ *result++ = *first;
+ --n;
+ }
+ }
+ return result;
+#endif
+}
+
//------------------------------------------------------------------------------
// <algorithm> Partition functions
//------------------------------------------------------------------------------
@@ -1657,7 +1643,7 @@ bool c_prev_permutation(C& c, LessThan&& comp) {
//
// Container-based version of the <numeric> `std::iota()` function
// to compute successive values of `value`, as if incremented with `++value`
-// after each element is written. and write them to the container.
+// after each element is written, and write them to the container.
template <typename Sequence, typename T>
void c_iota(Sequence& sequence, const T& value) {
std::iota(container_algorithm_internal::c_begin(sequence),
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc
index 0fbc7773..c01f5fc0 100644
--- a/absl/algorithm/container_test.cc
+++ b/absl/algorithm/container_test.cc
@@ -14,6 +14,7 @@
#include "absl/algorithm/container.h"
+#include <algorithm>
#include <functional>
#include <initializer_list>
#include <iterator>
@@ -40,8 +41,10 @@ using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::Gt;
using ::testing::IsNull;
+using ::testing::IsSubsetOf;
using ::testing::Lt;
using ::testing::Pointee;
+using ::testing::SizeIs;
using ::testing::Truly;
using ::testing::UnorderedElementsAre;
@@ -963,12 +966,29 @@ TEST(MutatingTest, RotateCopy) {
EXPECT_THAT(actual, ElementsAre(3, 4, 1, 2, 5));
}
+template <typename T>
+T RandomlySeededPrng() {
+ std::random_device rdev;
+ std::seed_seq::result_type data[T::state_size];
+ std::generate_n(data, T::state_size, std::ref(rdev));
+ std::seed_seq prng_seed(data, data + T::state_size);
+ return T(prng_seed);
+}
+
TEST(MutatingTest, Shuffle) {
std::vector<int> actual = {1, 2, 3, 4, 5};
- absl::c_shuffle(actual, std::random_device());
+ absl::c_shuffle(actual, RandomlySeededPrng<std::mt19937_64>());
EXPECT_THAT(actual, UnorderedElementsAre(1, 2, 3, 4, 5));
}
+TEST(MutatingTest, Sample) {
+ std::vector<int> actual;
+ absl::c_sample(std::vector<int>{1, 2, 3, 4, 5}, std::back_inserter(actual), 3,
+ RandomlySeededPrng<std::mt19937_64>());
+ EXPECT_THAT(actual, IsSubsetOf({1, 2, 3, 4, 5}));
+ EXPECT_THAT(actual, SizeIs(3));
+}
+
TEST(MutatingTest, PartialSort) {
std::vector<int> sequence{5, 3, 42, 0};
absl::c_partial_sort(sequence, sequence.begin() + 2);
diff --git a/absl/algorithm/equal_benchmark.cc b/absl/algorithm/equal_benchmark.cc
deleted file mode 100644
index 948cd65c..00000000
--- a/absl/algorithm/equal_benchmark.cc
+++ /dev/null
@@ -1,126 +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 <cstdint>
-#include <cstring>
-
-#include "absl/algorithm/algorithm.h"
-#include "benchmark/benchmark.h"
-
-namespace {
-
-// The range of sequence sizes to benchmark.
-constexpr int kMinBenchmarkSize = 1024;
-constexpr int kMaxBenchmarkSize = 8 * 1024 * 1024;
-
-// A user-defined type for use in equality benchmarks. Note that we expect
-// std::memcmp to win for this type: libstdc++'s std::equal only defers to
-// memcmp for integral types. This is because it is not straightforward to
-// guarantee that std::memcmp would produce a result "as-if" compared by
-// operator== for other types (example gotchas: NaN floats, structs with
-// padding).
-struct EightBits {
- explicit EightBits(int /* unused */) : data(0) {}
- bool operator==(const EightBits& rhs) const { return data == rhs.data; }
- uint8_t data;
-};
-
-template <typename T>
-void BM_absl_equal_benchmark(benchmark::State& state) {
- std::vector<T> xs(state.range(0), T(0));
- std::vector<T> ys = xs;
- while (state.KeepRunning()) {
- const bool same = absl::equal(xs.begin(), xs.end(), ys.begin(), ys.end());
- benchmark::DoNotOptimize(same);
- }
-}
-
-template <typename T>
-void BM_std_equal_benchmark(benchmark::State& state) {
- std::vector<T> xs(state.range(0), T(0));
- std::vector<T> ys = xs;
- while (state.KeepRunning()) {
- const bool same = std::equal(xs.begin(), xs.end(), ys.begin());
- benchmark::DoNotOptimize(same);
- }
-}
-
-template <typename T>
-void BM_memcmp_benchmark(benchmark::State& state) {
- std::vector<T> xs(state.range(0), T(0));
- std::vector<T> ys = xs;
- while (state.KeepRunning()) {
- const bool same =
- std::memcmp(xs.data(), ys.data(), xs.size() * sizeof(T)) == 0;
- benchmark::DoNotOptimize(same);
- }
-}
-
-// The expectation is that the compiler should be able to elide the equality
-// comparison altogether for sufficiently simple types.
-template <typename T>
-void BM_absl_equal_self_benchmark(benchmark::State& state) {
- std::vector<T> xs(state.range(0), T(0));
- while (state.KeepRunning()) {
- const bool same = absl::equal(xs.begin(), xs.end(), xs.begin(), xs.end());
- benchmark::DoNotOptimize(same);
- }
-}
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint8_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint8_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint8_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint8_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint16_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint16_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint16_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint16_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint32_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint32_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint32_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint32_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, uint64_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, uint64_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, uint64_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, uint64_t)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-BENCHMARK_TEMPLATE(BM_absl_equal_benchmark, EightBits)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_std_equal_benchmark, EightBits)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_memcmp_benchmark, EightBits)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-BENCHMARK_TEMPLATE(BM_absl_equal_self_benchmark, EightBits)
- ->Range(kMinBenchmarkSize, kMaxBenchmarkSize);
-
-} // namespace
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index fb008db3..0eb735da 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -63,6 +70,14 @@ cc_library(
)
cc_library(
+ name = "no_destructor",
+ hdrs = ["no_destructor.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [":config"],
+)
+
+cc_library(
name = "nullability",
srcs = ["internal/nullability_impl.h"],
hdrs = ["nullability.h"],
@@ -160,9 +175,6 @@ cc_library(
cc_library(
name = "core_headers",
- srcs = [
- "internal/thread_annotations.h",
- ],
hdrs = [
"attributes.h",
"const_init.h",
@@ -273,6 +285,7 @@ cc_library(
":cycleclock_internal",
":dynamic_annotations",
":log_severity",
+ ":nullability",
":raw_logging_internal",
":spinlock_wait",
"//absl/meta:type_traits",
@@ -302,6 +315,7 @@ cc_test(
":atomic_hook",
":atomic_hook_test_helper",
":core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -317,6 +331,7 @@ cc_test(
deps = [
":base",
":core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -344,6 +359,7 @@ cc_test(
deps = [
":config",
":throw_delegate",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -357,6 +373,7 @@ cc_test(
deps = [
":errno_saver",
":strerror",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -380,7 +397,9 @@ cc_library(
name = "pretty_function",
hdrs = ["internal/pretty_function.h"],
linkopts = ABSL_DEFAULT_LINKOPTS,
- visibility = ["//absl:__subpackages__"],
+ visibility = [
+ "//absl:__subpackages__",
+ ],
)
cc_library(
@@ -409,6 +428,7 @@ cc_test(
deps = [
":exception_safety_testing",
"//absl/memory",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -426,6 +446,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":base_internal",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -440,6 +461,7 @@ cc_test(
":base_internal",
"//absl/memory",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -478,6 +500,7 @@ cc_test(
":config",
":core_headers",
"//absl/synchronization",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -494,6 +517,7 @@ cc_library(
deps = [
":base",
":base_internal",
+ ":no_destructor",
":raw_logging_internal",
"//absl/synchronization",
"@com_github_google_benchmark//:benchmark_main",
@@ -525,6 +549,7 @@ cc_library(
":base",
":config",
":core_headers",
+ ":nullability",
],
)
@@ -535,6 +560,7 @@ cc_test(
deps = [
":config",
":endian",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -547,6 +573,7 @@ cc_test(
deps = [
":config",
"//absl/synchronization:thread_pool",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -560,16 +587,47 @@ cc_test(
":base",
":core_headers",
"//absl/synchronization",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "no_destructor_test",
+ srcs = ["no_destructor_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":config",
+ ":no_destructor",
+ ":raw_logging_internal",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
+cc_binary(
+ name = "no_destructor_benchmark",
+ testonly = 1,
+ srcs = ["no_destructor_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":no_destructor",
+ ":raw_logging_internal",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
cc_test(
name = "nullability_test",
srcs = ["nullability_test.cc"],
deps = [
":core_headers",
":nullability",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -582,6 +640,7 @@ cc_test(
deps = [
":raw_logging_internal",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -595,6 +654,7 @@ cc_test(
deps = [
":base",
"//absl/synchronization",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -628,6 +688,7 @@ cc_test(
":base",
":core_headers",
"//absl/synchronization",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -643,6 +704,7 @@ cc_test(
":base",
"//absl/synchronization",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -669,6 +731,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":scoped_set_env",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -684,6 +747,7 @@ cc_test(
"//absl/flags:flag_internal",
"//absl/flags:marshalling",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -713,6 +777,7 @@ cc_test(
deps = [
":strerror",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -752,6 +817,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":fast_type_id",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -759,14 +825,13 @@ cc_test(
cc_library(
name = "prefetch",
hdrs = [
- "internal/prefetch.h",
"prefetch.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
- ":core_headers", # TODO(b/265984188): remove
+ ":core_headers",
],
)
@@ -774,13 +839,13 @@ cc_test(
name = "prefetch_test",
size = "small",
srcs = [
- "internal/prefetch_test.cc",
"prefetch_test.cc",
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":prefetch",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -795,6 +860,7 @@ cc_test(
deps = [
":core_headers",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -808,6 +874,7 @@ cc_test(
deps = [
":core_headers",
"//absl/types:optional",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index 76c4ff1d..4cfc2285 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -49,6 +49,7 @@ absl_cc_library(
SRCS
"log_severity.cc"
DEPS
+ absl::config
absl::core_headers
COPTS
${ABSL_DEFAULT_COPTS}
@@ -56,6 +57,17 @@ absl_cc_library(
absl_cc_library(
NAME
+ no_destructor
+ HDRS
+ "no_destructor.h"
+ DEPS
+ absl::config
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+)
+
+absl_cc_library(
+ NAME
nullability
HDRS
"nullability.h"
@@ -155,7 +167,6 @@ absl_cc_library(
"optimization.h"
"port.h"
"thread_annotations.h"
- "internal/thread_annotations.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
@@ -236,6 +247,7 @@ absl_cc_library(
absl::core_headers
absl::dynamic_annotations
absl::log_severity
+ absl::nullability
absl::raw_logging_internal
absl::spinlock_wait
absl::type_traits
@@ -464,6 +476,7 @@ absl_cc_library(
absl::base
absl::config
absl::core_headers
+ absl::nullability
PUBLIC
)
@@ -510,6 +523,20 @@ absl_cc_test(
absl_cc_test(
NAME
+ no_destructor_test
+ SRCS
+ "no_destructor_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::no_destructor
+ absl::config
+ absl::raw_logging_internal
+ GTest::gtest_main
+)
+
+absl_cc_test(
+ NAME
raw_logging_test
SRCS
"raw_logging_test.cc"
@@ -677,14 +704,13 @@ absl_cc_library(
prefetch
HDRS
"prefetch.h"
- "internal/prefetch.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::config
- absl::core_headers # TODO(b/265984188): remove
+ absl::core_headers
)
absl_cc_test(
@@ -692,7 +718,6 @@ absl_cc_test(
prefetch_test
SRCS
"prefetch_test.cc"
- "internal/prefetch_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index a7f279a0..d4f67a12 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -687,7 +687,7 @@
// When deprecating Abseil code, it is sometimes necessary to turn off the
// warning within Abseil, until the deprecated code is actually removed. The
-// deprecated code can be surrounded with these directives to acheive that
+// deprecated code can be surrounded with these directives to achieve that
// result.
//
// class ABSL_DEPRECATED("Use Bar instead") Foo;
@@ -747,9 +747,52 @@
#define ABSL_CONST_INIT
#endif
-// These annotations are not available yet due to fear of breaking code.
-#define ABSL_ATTRIBUTE_PURE_FUNCTION
-#define ABSL_ATTRIBUTE_CONST_FUNCTION
+// 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 std::string FormatTime(Time t);
+#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
+// If the attribute isn't defined, we'll fallback to ABSL_MUST_USE_RESULT since
+// pure functions are useless if its return is ignored.
+#define ABSL_ATTRIBUTE_PURE_FUNCTION ABSL_MUST_USE_RESULT
+#endif
+
+// ABSL_ATTRIBUTE_CONST_FUNCTION
+//
+// ABSL_ATTRIBUTE_CONST_FUNCTION is used to annotate declarations of "const"
+// functions. A const function is similar to a pure function, with one
+// exception: Pure functions may return value that depend on a non-volatile
+// object that isn't provided as a function argument, while the const function
+// is guaranteed to return the same result given the same arguments.
+//
+// Example:
+//
+// ABSL_ATTRIBUTE_CONST_FUNCTION int64_t ToInt64Milliseconds(Duration d);
+#if defined(_MSC_VER) && !defined(__clang__)
+// Put the MSVC case first since MSVC seems to parse const as a C++ keyword.
+#define ABSL_ATTRIBUTE_CONST_FUNCTION ABSL_ATTRIBUTE_PURE_FUNCTION
+#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::const)
+#define ABSL_ATTRIBUTE_CONST_FUNCTION [[gnu::const]]
+#elif ABSL_HAVE_ATTRIBUTE(const)
+#define ABSL_ATTRIBUTE_CONST_FUNCTION __attribute__((const))
+#else
+// Since const functions are more restrictive pure function, we'll fallback to a
+// pure function if the const attribute is not handled.
+#define ABSL_ATTRIBUTE_CONST_FUNCTION 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
@@ -800,15 +843,11 @@
// See also the upstream documentation:
// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
//
-#if ABSL_HAVE_CPP_ATTRIBUTE(clang::trivial_abi)
-#define ABSL_ATTRIBUTE_TRIVIAL_ABI [[clang::trivial_abi]]
-#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
-#elif ABSL_HAVE_ATTRIBUTE(trivial_abi)
-#define ABSL_ATTRIBUTE_TRIVIAL_ABI __attribute__((trivial_abi))
-#define ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI 1
-#else
+// b/321691395 - This is currently disabled in open-source builds since
+// compiler support differs. If system libraries compiled with GCC are mixed
+// with libraries compiled with Clang, types will have different ideas about
+// their ABI, leading to hard to debug crashes.
#define ABSL_ATTRIBUTE_TRIVIAL_ABI
-#endif
// ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS
//
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 08436bac..7b0e69cc 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -37,6 +37,7 @@
#include "absl/base/internal/scheduling_mode.h"
#include "absl/base/internal/spinlock_wait.h"
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
@@ -46,7 +47,8 @@ ABSL_NAMESPACE_BEGIN
class once_flag;
namespace base_internal {
-std::atomic<uint32_t>* ControlWord(absl::once_flag* flag);
+absl::Nonnull<std::atomic<uint32_t>*> ControlWord(
+ absl::Nonnull<absl::once_flag*> flag);
} // namespace base_internal
// call_once()
@@ -89,7 +91,8 @@ class once_flag {
once_flag& operator=(const once_flag&) = delete;
private:
- friend std::atomic<uint32_t>* base_internal::ControlWord(once_flag* flag);
+ friend absl::Nonnull<std::atomic<uint32_t>*> base_internal::ControlWord(
+ absl::Nonnull<once_flag*> flag);
std::atomic<uint32_t> control_;
};
@@ -103,7 +106,8 @@ namespace base_internal {
// Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to
// initialize entities used by the scheduler implementation.
template <typename Callable, typename... Args>
-void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args);
+void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn,
+ Args&&... args);
// Disables scheduling while on stack when scheduling mode is non-cooperative.
// No effect for cooperative scheduling modes.
@@ -143,10 +147,10 @@ enum {
};
template <typename Callable, typename... Args>
-ABSL_ATTRIBUTE_NOINLINE
-void CallOnceImpl(std::atomic<uint32_t>* control,
- base_internal::SchedulingMode scheduling_mode, Callable&& fn,
- Args&&... args) {
+ABSL_ATTRIBUTE_NOINLINE void CallOnceImpl(
+ absl::Nonnull<std::atomic<uint32_t>*> control,
+ base_internal::SchedulingMode scheduling_mode, Callable&& fn,
+ Args&&... args) {
#ifndef NDEBUG
{
uint32_t old_control = control->load(std::memory_order_relaxed);
@@ -185,12 +189,14 @@ void CallOnceImpl(std::atomic<uint32_t>* control,
} // else *control is already kOnceDone
}
-inline std::atomic<uint32_t>* ControlWord(once_flag* flag) {
+inline absl::Nonnull<std::atomic<uint32_t>*> ControlWord(
+ absl::Nonnull<once_flag*> flag) {
return &flag->control_;
}
template <typename Callable, typename... Args>
-void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) {
+void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn,
+ Args&&... args) {
std::atomic<uint32_t>* once = base_internal::ControlWord(flag);
uint32_t s = once->load(std::memory_order_acquire);
if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
diff --git a/absl/base/casts.h b/absl/base/casts.h
index d1958885..e0b11bbe 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -90,7 +90,7 @@ ABSL_NAMESPACE_BEGIN
//
// Such implicit cast chaining may be useful within template logic.
template <typename To>
-constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
+constexpr To implicit_cast(typename absl::internal::type_identity_t<To> to) {
return to;
}
diff --git a/absl/base/config.h b/absl/base/config.h
index 1de79930..c9165acd 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -75,6 +75,12 @@
#define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus
#endif
+#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+ ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+// Include library feature test macros.
+#include <version>
+#endif
+
#if defined(__APPLE__)
// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED,
// __IPHONE_8_0.
@@ -111,8 +117,8 @@
//
// LTS releases can be obtained from
// https://github.com/abseil/abseil-cpp/releases.
-#define ABSL_LTS_RELEASE_VERSION 20230802
-#define ABSL_LTS_RELEASE_PATCH_LEVEL 0
+#define ABSL_LTS_RELEASE_VERSION 20240116
+#define ABSL_LTS_RELEASE_PATCH_LEVEL 1
// Helper macro to convert a CPP variable to a string literal.
#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x
@@ -332,8 +338,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#ifdef ABSL_HAVE_INTRINSIC_INT128
#error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set
#elif defined(__SIZEOF_INT128__)
-#if (defined(__clang__) && !defined(_WIN32)) || \
- (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
+#if (defined(__clang__) && !defined(_WIN32)) || \
+ (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
(defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
#define ABSL_HAVE_INTRINSIC_INT128 1
#elif defined(__CUDACC__)
@@ -395,7 +401,7 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// Windows _WIN32
// NaCL __native_client__
// AsmJS __asmjs__
-// WebAssembly __wasm__
+// WebAssembly (Emscripten) __EMSCRIPTEN__
// Fuchsia __Fuchsia__
//
// Note that since Android defines both __ANDROID__ and __linux__, one
@@ -407,11 +413,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(_AIX) || defined(__ros__) || defined(__native_client__) || \
- defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \
- defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \
- defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+ defined(_AIX) || defined(__ros__) || defined(__native_client__) || \
+ defined(__asmjs__) || defined(__EMSCRIPTEN__) || defined(__Fuchsia__) || \
+ defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \
+ defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
defined(__QNX__) || defined(__VXWORKS__) || defined(__hexagon__)
#define ABSL_HAVE_MMAP 1
#endif
@@ -484,6 +490,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c
#elif defined(__EMSCRIPTEN__)
// emscripten doesn't support signals
+#elif defined(__wasi__)
+// WASI doesn't support signals
#elif defined(__Fuchsia__)
// Signals don't exist on fuchsia.
#elif defined(__native_client__)
@@ -536,14 +544,14 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// and
// https://github.com/llvm/llvm-project/commit/0bc451e7e137c4ccadcd3377250874f641ca514a
// The second has the actually correct versions, thus, is what we copy here.
-#if defined(__APPLE__) && \
- ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) || \
- (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__ < 50000) || \
- (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \
+#if defined(__APPLE__) && \
+ ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
+ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) || \
+ (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__ < 50000) || \
+ (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \
__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000))
#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1
#else
@@ -555,6 +563,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// Checks whether C++17 std::any is available.
#ifdef ABSL_HAVE_STD_ANY
#error "ABSL_HAVE_STD_ANY cannot be directly set."
+#elif defined(__cpp_lib_any) && __cpp_lib_any >= 201606L
+#define ABSL_HAVE_STD_ANY 1
#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
!ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
@@ -566,7 +576,9 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// Checks whether C++17 std::optional is available.
#ifdef ABSL_HAVE_STD_OPTIONAL
#error "ABSL_HAVE_STD_OPTIONAL cannot be directly set."
-#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+#elif defined(__cpp_lib_optional) && __cpp_lib_optional >= 202106L
+#define ABSL_HAVE_STD_OPTIONAL 1
+#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
!ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
#define ABSL_HAVE_STD_OPTIONAL 1
@@ -577,6 +589,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// Checks whether C++17 std::variant is available.
#ifdef ABSL_HAVE_STD_VARIANT
#error "ABSL_HAVE_STD_VARIANT cannot be directly set."
+#elif defined(__cpp_lib_variant) && __cpp_lib_variant >= 201606L
+#define ABSL_HAVE_STD_VARIANT 1
#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
!ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE
@@ -588,11 +602,29 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// Checks whether C++17 std::string_view is available.
#ifdef ABSL_HAVE_STD_STRING_VIEW
#error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
+#elif defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
+#define ABSL_HAVE_STD_STRING_VIEW 1
#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
#define ABSL_HAVE_STD_STRING_VIEW 1
#endif
+// ABSL_HAVE_STD_ORDERING
+//
+// Checks whether C++20 std::{partial,weak,strong}_ordering are available.
+//
+// __cpp_lib_three_way_comparison is missing on libc++
+// (https://github.com/llvm/llvm-project/issues/73953) so treat it as defined
+// when building in C++20 mode.
+#ifdef ABSL_HAVE_STD_ORDERING
+#error "ABSL_HAVE_STD_ORDERING cannot be directly set."
+#elif (defined(__cpp_lib_three_way_comparison) && \
+ __cpp_lib_three_way_comparison >= 201907L) || \
+ (defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+ ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L)
+#define ABSL_HAVE_STD_ORDERING 1
+#endif
+
// ABSL_USES_STD_ANY
//
// Indicates whether absl::any is an alias for std::any.
@@ -655,6 +687,22 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#error options.h is misconfigured.
#endif
+// ABSL_USES_STD_ORDERING
+//
+// Indicates whether absl::{partial,weak,strong}_ordering are aliases for the
+// std:: ordering types.
+#if !defined(ABSL_OPTION_USE_STD_ORDERING)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_ORDERING == 0 || \
+ (ABSL_OPTION_USE_STD_ORDERING == 2 && !defined(ABSL_HAVE_STD_ORDERING))
+#undef ABSL_USES_STD_ORDERING
+#elif ABSL_OPTION_USE_STD_ORDERING == 1 || \
+ (ABSL_OPTION_USE_STD_ORDERING == 2 && defined(ABSL_HAVE_STD_ORDERING))
+#define ABSL_USES_STD_ORDERING 1
+#else
+#error options.h is misconfigured.
+#endif
+
// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
// SEH exception from emplace for variant<SomeStruct> when constructing the
// struct can throw. This defeats some of variant_test and
@@ -837,11 +885,30 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
// RTTI support.
#ifdef ABSL_INTERNAL_HAS_RTTI
#error ABSL_INTERNAL_HAS_RTTI cannot be directly set
-#elif (defined(__GNUC__) && defined(__GXX_RTTI)) || \
- (defined(_MSC_VER) && defined(_CPPRTTI)) || \
- (!defined(__GNUC__) && !defined(_MSC_VER))
+#elif ABSL_HAVE_FEATURE(cxx_rtti)
#define ABSL_INTERNAL_HAS_RTTI 1
-#endif // !defined(__GNUC__) || defined(__GXX_RTTI)
+#elif defined(__GNUC__) && defined(__GXX_RTTI)
+#define ABSL_INTERNAL_HAS_RTTI 1
+#elif defined(_MSC_VER) && defined(_CPPRTTI)
+#define ABSL_INTERNAL_HAS_RTTI 1
+#elif !defined(__GNUC__) && !defined(_MSC_VER)
+// Unknown compiler, default to RTTI
+#define ABSL_INTERNAL_HAS_RTTI 1
+#endif
+
+// `ABSL_INTERNAL_HAS_CXA_DEMANGLE` determines whether `abi::__cxa_demangle` is
+// available.
+#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#error ABSL_INTERNAL_HAS_CXA_DEMANGLE cannot be directly set
+#elif defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__))
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 0
+#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+ (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \
+ !defined(__mips__)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
+#elif defined(__clang__) && !defined(_MSC_VER)
+#define ABSL_INTERNAL_HAS_CXA_DEMANGLE 1
+#endif
// ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support.
// See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of
@@ -928,8 +995,8 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#if __EMSCRIPTEN_tiny__ >= 1000
#error __EMSCRIPTEN_tiny__ is too big to fit in ABSL_INTERNAL_EMSCRIPTEN_VERSION
#endif
-#define ABSL_INTERNAL_EMSCRIPTEN_VERSION \
- ((__EMSCRIPTEN_major__)*1000000 + (__EMSCRIPTEN_minor__)*1000 + \
+#define ABSL_INTERNAL_EMSCRIPTEN_VERSION \
+ ((__EMSCRIPTEN_major__) * 1000000 + (__EMSCRIPTEN_minor__) * 1000 + \
(__EMSCRIPTEN_tiny__))
#endif
#endif
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h
index 50747d75..943f3d97 100644
--- a/absl/base/internal/endian.h
+++ b/absl/base/internal/endian.h
@@ -22,6 +22,7 @@
#include "absl/base/casts.h"
#include "absl/base/config.h"
#include "absl/base/internal/unaligned_access.h"
+#include "absl/base/nullability.h"
#include "absl/base/port.h"
namespace absl {
@@ -160,27 +161,27 @@ inline int64_t ToHost(int64_t x) {
}
// Functions to do unaligned loads and stores in little-endian order.
-inline uint16_t Load16(const void *p) {
+inline uint16_t Load16(absl::Nonnull<const void *> p) {
return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
}
-inline void Store16(void *p, uint16_t v) {
+inline void Store16(absl::Nonnull<void *> p, uint16_t v) {
ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
}
-inline uint32_t Load32(const void *p) {
+inline uint32_t Load32(absl::Nonnull<const void *> p) {
return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
}
-inline void Store32(void *p, uint32_t v) {
+inline void Store32(absl::Nonnull<void *> p, uint32_t v) {
ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
}
-inline uint64_t Load64(const void *p) {
+inline uint64_t Load64(absl::Nonnull<const void *> p) {
return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
}
-inline void Store64(void *p, uint64_t v) {
+inline void Store64(absl::Nonnull<void *> p, uint64_t v) {
ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
}
@@ -250,27 +251,27 @@ inline int64_t ToHost(int64_t x) {
}
// Functions to do unaligned loads and stores in big-endian order.
-inline uint16_t Load16(const void *p) {
+inline uint16_t Load16(absl::Nonnull<const void *> p) {
return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p));
}
-inline void Store16(void *p, uint16_t v) {
+inline void Store16(absl::Nonnull<void *> p, uint16_t v) {
ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
}
-inline uint32_t Load32(const void *p) {
+inline uint32_t Load32(absl::Nonnull<const void *> p) {
return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p));
}
-inline void Store32(void *p, uint32_t v) {
+inline void Store32(absl::Nonnull<void *>p, uint32_t v) {
ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
}
-inline uint64_t Load64(const void *p) {
+inline uint64_t Load64(absl::Nonnull<const void *> p) {
return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p));
}
-inline void Store64(void *p, uint64_t v) {
+inline void Store64(absl::Nonnull<void *> p, uint64_t v) {
ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
}
diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h
index a3154ed7..365207b7 100644
--- a/absl/base/internal/identity.h
+++ b/absl/base/internal/identity.h
@@ -22,13 +22,15 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace internal {
+// This is a back-fill of C++20's `std::type_identity`.
template <typename T>
-struct identity {
+struct type_identity {
typedef T type;
};
+// This is a back-fill of C++20's `std::type_identity_t`.
template <typename T>
-using identity_t = typename identity<T>::type;
+using type_identity_t = typename type_identity<T>::type;
} // namespace internal
ABSL_NAMESPACE_END
diff --git a/absl/base/internal/inline_variable.h b/absl/base/internal/inline_variable.h
index df933faf..09daf0f5 100644
--- a/absl/base/internal/inline_variable.h
+++ b/absl/base/internal/inline_variable.h
@@ -63,12 +63,12 @@
// Bug: https://bugs.llvm.org/show_bug.cgi?id=35862
//
// Note:
-// identity_t is used here so that the const and name are in the
+// type_identity_t is used here so that the const and name are in the
// appropriate place for pointer types, reference types, function pointer
// types, etc..
#if defined(__clang__)
#define ABSL_INTERNAL_EXTERN_DECL(type, name) \
- extern const ::absl::internal::identity_t<type> name;
+ extern const ::absl::internal::type_identity_t<type> name;
#else // Otherwise, just define the macro to do nothing.
#define ABSL_INTERNAL_EXTERN_DECL(type, name)
#endif // defined(__clang__)
@@ -76,30 +76,31 @@
// See above comment at top of file for details.
#define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \
ABSL_INTERNAL_EXTERN_DECL(type, name) \
- inline constexpr ::absl::internal::identity_t<type> name = init
+ inline constexpr ::absl::internal::type_identity_t<type> name = init
#else
// See above comment at top of file for details.
//
// Note:
-// identity_t is used here so that the const and name are in the
+// type_identity_t is used here so that the const and name are in the
// appropriate place for pointer types, reference types, function pointer
// types, etc..
-#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \
- template <class /*AbslInternalDummy*/ = void> \
- struct AbslInternalInlineVariableHolder##name { \
- static constexpr ::absl::internal::identity_t<var_type> kInstance = init; \
- }; \
- \
- template <class AbslInternalDummy> \
- constexpr ::absl::internal::identity_t<var_type> \
- AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance; \
- \
- static constexpr const ::absl::internal::identity_t<var_type>& \
- name = /* NOLINT */ \
- AbslInternalInlineVariableHolder##name<>::kInstance; \
- static_assert(sizeof(void (*)(decltype(name))) != 0, \
+#define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \
+ template <class /*AbslInternalDummy*/ = void> \
+ struct AbslInternalInlineVariableHolder##name { \
+ static constexpr ::absl::internal::type_identity_t<var_type> kInstance = \
+ init; \
+ }; \
+ \
+ template <class AbslInternalDummy> \
+ constexpr ::absl::internal::type_identity_t<var_type> \
+ AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance; \
+ \
+ static constexpr const ::absl::internal::type_identity_t<var_type>& \
+ name = /* NOLINT */ \
+ AbslInternalInlineVariableHolder##name<>::kInstance; \
+ static_assert(sizeof(void (*)(decltype(name))) != 0, \
"Silence unused variable warnings.")
#endif // __cpp_inline_variables
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 6d2cfeac..a563f7b9 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -329,7 +329,7 @@ size_t GetPageSize() {
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
return std::max(system_info.dwPageSize, system_info.dwAllocationGranularity);
-#elif defined(__wasm__) || defined(__asmjs__)
+#elif defined(__wasm__) || defined(__asmjs__) || defined(__hexagon__)
return getpagesize();
#else
return static_cast<size_t>(sysconf(_SC_PAGESIZE));
diff --git a/absl/base/internal/prefetch.h b/absl/base/internal/prefetch.h
deleted file mode 100644
index aecfd877..00000000
--- a/absl/base/internal/prefetch.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2022 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.
-
-// TODO(b/265984188): remove all uses and delete this header.
-
-#ifndef ABSL_BASE_INTERNAL_PREFETCH_H_
-#define ABSL_BASE_INTERNAL_PREFETCH_H_
-
-#include "absl/base/attributes.h"
-#include "absl/base/config.h"
-#include "absl/base/prefetch.h"
-
-#ifdef __SSE__
-#include <xmmintrin.h>
-#endif
-
-#if defined(_MSC_VER) && defined(ABSL_INTERNAL_HAVE_SSE)
-#include <intrin.h>
-#pragma intrinsic(_mm_prefetch)
-#endif
-
-// Compatibility wrappers around __builtin_prefetch, to prefetch data
-// for read if supported by the toolchain.
-
-// Move data into the cache before it is read, or "prefetch" it.
-//
-// The value of `addr` is the address of the memory to prefetch. If
-// the target and compiler support it, data prefetch instructions are
-// generated. If the prefetch is done some time before the memory is
-// read, it may be in the cache by the time the read occurs.
-//
-// The function names specify the temporal locality heuristic applied,
-// using the names of Intel prefetch instructions:
-//
-// T0 - high degree of temporal locality; data should be left in as
-// many levels of the cache possible
-// T1 - moderate degree of temporal locality
-// T2 - low degree of temporal locality
-// Nta - no temporal locality, data need not be left in the cache
-// after the read
-//
-// Incorrect or gratuitous use of these functions can degrade
-// performance, so use them only when representative benchmarks show
-// an improvement.
-//
-// Example usage:
-//
-// absl::base_internal::PrefetchT0(addr);
-//
-// Currently, the different prefetch calls behave on some Intel
-// architectures as follows:
-//
-// SNB..SKL SKX
-// PrefetchT0() L1/L2/L3 L1/L2
-// PrefetchT1() L2/L3 L2
-// PrefetchT2() L2/L3 L2
-// PrefetchNta() L1/--/L3 L1*
-//
-// * On SKX PrefetchNta() will bring the line into L1 but will evict
-// from L3 cache. This might result in surprising behavior.
-//
-// SNB = Sandy Bridge, SKL = Skylake, SKX = Skylake Xeon.
-//
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace base_internal {
-
-ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead")
-inline void PrefetchT0(const void* address) {
- absl::PrefetchToLocalCache(address);
-}
-
-ABSL_DEPRECATED("Use absl::PrefetchToLocalCache() instead")
-inline void PrefetchNta(const void* address) {
- absl::PrefetchToLocalCacheNta(address);
-}
-
-ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead")
-void PrefetchT1(const void* addr);
-
-ABSL_DEPRECATED("Use __builtin_prefetch() for advanced prefetch logic instead")
-void PrefetchT2(const void* addr);
-
-// Implementation details follow.
-
-#if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__)
-
-#define ABSL_INTERNAL_HAVE_PREFETCH 1
-
-// See __builtin_prefetch:
-// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.
-//
-// These functions speculatively load for read only. This is
-// safe for all currently supported platforms. However, prefetch for
-// store may have problems depending on the target platform.
-//
-inline void PrefetchT1(const void* addr) {
- // Note: this uses prefetcht1 on Intel.
- __builtin_prefetch(addr, 0, 2);
-}
-inline void PrefetchT2(const void* addr) {
- // Note: this uses prefetcht2 on Intel.
- __builtin_prefetch(addr, 0, 1);
-}
-
-#elif defined(ABSL_INTERNAL_HAVE_SSE)
-
-#define ABSL_INTERNAL_HAVE_PREFETCH 1
-
-inline void PrefetchT1(const void* addr) {
- _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T1);
-}
-inline void PrefetchT2(const void* addr) {
- _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T2);
-}
-
-#else
-inline void PrefetchT1(const void*) {}
-inline void PrefetchT2(const void*) {}
-#endif
-
-} // namespace base_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-#endif // ABSL_BASE_INTERNAL_PREFETCH_H_
diff --git a/absl/base/internal/prefetch_test.cc b/absl/base/internal/prefetch_test.cc
deleted file mode 100644
index 7c1dae46..00000000
--- a/absl/base/internal/prefetch_test.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2022 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/prefetch.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-int number = 42;
-
-TEST(Prefetch, TemporalLocalityNone) {
- absl::base_internal::PrefetchNta(&number);
- EXPECT_EQ(number, 42);
-}
-
-TEST(Prefetch, TemporalLocalityLow) {
- absl::base_internal::PrefetchT2(&number);
- EXPECT_EQ(number, 42);
-}
-
-TEST(Prefetch, TemporalLocalityMedium) {
- absl::base_internal::PrefetchT1(&number);
- EXPECT_EQ(number, 42);
-}
-
-TEST(Prefetch, TemporalLocalityHigh) {
- absl::base_internal::PrefetchT0(&number);
- EXPECT_EQ(number, 42);
-}
-
-} // namespace
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index 4c922ccf..d32b40a8 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -42,8 +42,9 @@
// This preprocessor token is also defined in raw_io.cc. If you need to copy
// this, consider moving both to config.h instead.
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
- defined(__Fuchsia__) || defined(__native_client__) || \
- defined(__OpenBSD__) || defined(__EMSCRIPTEN__) || defined(__ASYLO__)
+ defined(__hexagon__) || defined(__Fuchsia__) || \
+ defined(__native_client__) || defined(__OpenBSD__) || \
+ defined(__EMSCRIPTEN__) || defined(__ASYLO__)
#include <unistd.h>
@@ -56,8 +57,7 @@
// ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall
// syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len);
// for low level operations that want to avoid libc.
-#if (defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && \
- !defined(__ANDROID__)
+#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__)
#include <sys/syscall.h>
#define ABSL_HAVE_SYSCALL_WRITE 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
@@ -93,8 +93,7 @@ constexpr char kTruncated[] = " ... (message truncated)\n";
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) {
- if (*size < 0)
- return false;
+ if (*size < 0) return false;
int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap);
bool result = true;
if (n < 0 || n > *size) {
@@ -122,8 +121,7 @@ constexpr int kLogBufSize = 3000;
bool DoRawLog(char** buf, int* size, const char* format, ...)
ABSL_PRINTF_ATTRIBUTE(3, 4);
bool DoRawLog(char** buf, int* size, const char* format, ...) {
- if (*size < 0)
- return false;
+ if (*size < 0) return false;
va_list ap;
va_start(ap, format);
int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap);
@@ -242,8 +240,8 @@ void AsyncSignalSafeWriteError(const char* s, size_t len) {
_write(/* stderr */ 2, s, static_cast<unsigned>(len));
#else
// stderr logging unsupported on this platform
- (void) s;
- (void) len;
+ (void)s;
+ (void)len;
#endif
}
@@ -258,7 +256,7 @@ void RawLog(absl::LogSeverity severity, const char* file, int line,
bool RawLoggingFullySupported() {
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
return true;
-#else // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
+#else // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
return false;
#endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
}
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index b79550b2..d7cfbc57 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -108,6 +108,7 @@
#define ABSL_RAW_LOG_INTERNAL_WARNING ::absl::LogSeverity::kWarning
#define ABSL_RAW_LOG_INTERNAL_ERROR ::absl::LogSeverity::kError
#define ABSL_RAW_LOG_INTERNAL_FATAL ::absl::LogSeverity::kFatal
+#define ABSL_RAW_LOG_INTERNAL_DFATAL ::absl::kLogDebugFatal
#define ABSL_RAW_LOG_INTERNAL_LEVEL(severity) \
::absl::NormalizeLogSeverity(severity)
@@ -115,6 +116,7 @@
#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_WARNING
#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_ERROR
#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_FATAL ABSL_UNREACHABLE()
+#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_DFATAL
#define ABSL_RAW_LOG_INTERNAL_MAYBE_UNREACHABLE_LEVEL(severity)
namespace absl {
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index 09ba5824..2929cd6f 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -19,10 +19,10 @@
// - 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.
+// SpinLock with a base_internal::SchedulingMode::SCHEDULE_KERNEL_ONLY 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.
@@ -41,6 +41,14 @@
#include "absl/base/internal/tsan_mutex_interface.h"
#include "absl/base/thread_annotations.h"
+namespace tcmalloc {
+namespace tcmalloc_internal {
+
+class AllocationGuardSpinLockHolder;
+
+} // namespace tcmalloc_internal
+} // namespace tcmalloc
+
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
@@ -137,6 +145,7 @@ class ABSL_LOCKABLE SpinLock {
// Provide access to protected method above. Use for testing only.
friend struct SpinLockTest;
+ friend class tcmalloc::tcmalloc_internal::AllocationGuardSpinLockHolder;
private:
// lockword_ is used to store the following:
@@ -171,6 +180,10 @@ class ABSL_LOCKABLE SpinLock {
return scheduling_mode == base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL;
}
+ bool IsCooperative() const {
+ return lockword_.load(std::memory_order_relaxed) & kSpinLockCooperative;
+ }
+
uint32_t TryLockInternal(uint32_t lock_value, uint32_t wait_cycles);
void SlowLock() ABSL_ATTRIBUTE_COLD;
void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD;
diff --git a/absl/base/internal/spinlock_benchmark.cc b/absl/base/internal/spinlock_benchmark.cc
index 0451c65f..1790d967 100644
--- a/absl/base/internal/spinlock_benchmark.cc
+++ b/absl/base/internal/spinlock_benchmark.cc
@@ -18,22 +18,39 @@
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/scheduling_mode.h"
#include "absl/base/internal/spinlock.h"
+#include "absl/base/no_destructor.h"
#include "absl/synchronization/internal/create_thread_identity.h"
#include "benchmark/benchmark.h"
namespace {
template <absl::base_internal::SchedulingMode scheduling_mode>
+static void BM_TryLock(benchmark::State& state) {
+ // Ensure a ThreadIdentity is installed so that KERNEL_ONLY has an effect.
+ ABSL_INTERNAL_CHECK(
+ absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() !=
+ nullptr,
+ "GetOrCreateCurrentThreadIdentity() failed");
+
+ static absl::NoDestructor<absl::base_internal::SpinLock> spinlock(
+ scheduling_mode);
+ for (auto _ : state) {
+ if (spinlock->TryLock()) spinlock->Unlock();
+ }
+}
+
+template <absl::base_internal::SchedulingMode scheduling_mode>
static void BM_SpinLock(benchmark::State& state) {
- // Ensure a ThreadIdentity is installed.
+ // Ensure a ThreadIdentity is installed so that KERNEL_ONLY has an effect.
ABSL_INTERNAL_CHECK(
absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() !=
nullptr,
"GetOrCreateCurrentThreadIdentity() failed");
- static auto* spinlock = new absl::base_internal::SpinLock(scheduling_mode);
+ static absl::NoDestructor<absl::base_internal::SpinLock> spinlock(
+ scheduling_mode);
for (auto _ : state) {
- absl::base_internal::SpinLockHolder holder(spinlock);
+ absl::base_internal::SpinLockHolder holder(spinlock.get());
}
}
@@ -49,4 +66,15 @@ BENCHMARK_TEMPLATE(BM_SpinLock,
->Threads(1)
->ThreadPerCpu();
+BENCHMARK_TEMPLATE(BM_TryLock, absl::base_internal::SCHEDULE_KERNEL_ONLY)
+ ->UseRealTime()
+ ->Threads(1)
+ ->ThreadPerCpu();
+
+BENCHMARK_TEMPLATE(BM_TryLock,
+ absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL)
+ ->UseRealTime()
+ ->Threads(1)
+ ->ThreadPerCpu();
+
} // namespace
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc
index 8bcc4faf..79eaba3e 100644
--- a/absl/base/internal/sysinfo.cc
+++ b/absl/base/internal/sysinfo.cc
@@ -34,6 +34,14 @@
#include <sys/sysctl.h>
#endif
+#ifdef __FreeBSD__
+#include <pthread_np.h>
+#endif
+
+#ifdef __NetBSD__
+#include <lwp.h>
+#endif
+
#if defined(__myriad2__)
#include <rtems.h>
#endif
@@ -432,6 +440,18 @@ pid_t GetTID() {
return static_cast<pid_t>(tid);
}
+#elif defined(__FreeBSD__)
+
+pid_t GetTID() { return static_cast<pid_t>(pthread_getthreadid_np()); }
+
+#elif defined(__OpenBSD__)
+
+pid_t GetTID() { return getthrid(); }
+
+#elif defined(__NetBSD__)
+
+pid_t GetTID() { return static_cast<pid_t>(_lwp_self()); }
+
#elif defined(__native_client__)
pid_t GetTID() {
diff --git a/absl/base/internal/thread_annotations.h b/absl/base/internal/thread_annotations.h
deleted file mode 100644
index 8c5c67e0..00000000
--- a/absl/base/internal/thread_annotations.h
+++ /dev/null
@@ -1,280 +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.
-//
-// -----------------------------------------------------------------------------
-// File: thread_annotations.h
-// -----------------------------------------------------------------------------
-//
-// WARNING: This is a backwards compatible header and it will be removed after
-// the migration to prefixed thread annotations is finished; please include
-// "absl/base/thread_annotations.h".
-//
-// This header file contains macro definitions for thread safety annotations
-// that allow developers to document the locking policies of multi-threaded
-// code. The annotations can also help program analysis tools to identify
-// potential thread safety issues.
-//
-// These annotations are implemented using compiler attributes. Using the macros
-// defined here instead of raw attributes allow for portability and future
-// compatibility.
-//
-// When referring to mutexes in the arguments of the attributes, you should
-// use variable names or more complex expressions (e.g. my_object->mutex_)
-// that evaluate to a concrete mutex object whenever possible. If the mutex
-// you want to refer to is not in scope, you may use a member pointer
-// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object.
-
-#ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
-#define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
-
-// ABSL_LEGACY_THREAD_ANNOTATIONS is a *temporary* compatibility macro that can
-// be defined on the compile command-line to restore the legacy spellings of the
-// thread annotations macros/functions. The macros in this file are available
-// under ABSL_ prefixed spellings in absl/base/thread_annotations.h. This macro
-// and the legacy spellings will be removed in the future.
-#ifdef ABSL_LEGACY_THREAD_ANNOTATIONS
-
-#if defined(__clang__)
-#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
-#else
-#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
-#endif
-
-// GUARDED_BY()
-//
-// Documents if a shared field or global variable needs to be protected by a
-// mutex. GUARDED_BY() allows the user to specify a particular mutex that
-// should be held when accessing the annotated variable.
-//
-// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to
-// local variables, a local variable and its associated mutex can often be
-// combined into a small class or struct, thereby allowing the annotation.
-//
-// Example:
-//
-// class Foo {
-// Mutex mu_;
-// int p1_ GUARDED_BY(mu_);
-// ...
-// };
-#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
-
-// PT_GUARDED_BY()
-//
-// Documents if the memory location pointed to by a pointer should be guarded
-// by a mutex when dereferencing the pointer.
-//
-// Example:
-// class Foo {
-// Mutex mu_;
-// int *p1_ PT_GUARDED_BY(mu_);
-// ...
-// };
-//
-// Note that a pointer variable to a shared memory location could itself be a
-// shared variable.
-//
-// Example:
-//
-// // `q_`, guarded by `mu1_`, points to a shared memory location that is
-// // guarded by `mu2_`:
-// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_);
-#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
-
-// ACQUIRED_AFTER() / ACQUIRED_BEFORE()
-//
-// Documents the acquisition order between locks that can be held
-// simultaneously by a thread. For any two locks that need to be annotated
-// to establish an acquisition order, only one of them needs the annotation.
-// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
-// and ACQUIRED_BEFORE.)
-//
-// As with GUARDED_BY, this is only applicable to mutexes that are shared
-// fields or global variables.
-//
-// Example:
-//
-// Mutex m1_;
-// Mutex m2_ ACQUIRED_AFTER(m1_);
-#define ACQUIRED_AFTER(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
-
-#define ACQUIRED_BEFORE(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
-
-// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED()
-//
-// Documents a function that expects a mutex to be held prior to entry.
-// The mutex is expected to be held both on entry to, and exit from, the
-// function.
-//
-// An exclusive lock allows read-write access to the guarded data member(s), and
-// only one thread can acquire a lock exclusively at any one time. A shared lock
-// allows read-only access, and any number of threads can acquire a shared lock
-// concurrently.
-//
-// Generally, non-const methods should be annotated with
-// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with
-// SHARED_LOCKS_REQUIRED.
-//
-// Example:
-//
-// Mutex mu1, mu2;
-// int a GUARDED_BY(mu1);
-// int b GUARDED_BY(mu2);
-//
-// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
-// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
-#define EXCLUSIVE_LOCKS_REQUIRED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
-
-#define SHARED_LOCKS_REQUIRED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
-
-// 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 LOCKS_EXCLUDED(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
-
-// 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 LOCK_RETURNED.
-#define LOCK_RETURNED(x) \
- THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
-
-// LOCKABLE
-//
-// Documents if a class/type is a lockable type (such as the `Mutex` class).
-#define LOCKABLE \
- THREAD_ANNOTATION_ATTRIBUTE__(lockable)
-
-// SCOPED_LOCKABLE
-//
-// Documents if a class does RAII locking (such as the `MutexLock` class).
-// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is
-// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no
-// arguments; the analysis will assume that the destructor unlocks whatever the
-// constructor locked.
-#define SCOPED_LOCKABLE \
- THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
-
-// EXCLUSIVE_LOCK_FUNCTION()
-//
-// Documents functions that acquire a lock in the body of a function, and do
-// not release it.
-#define EXCLUSIVE_LOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
-
-// SHARED_LOCK_FUNCTION()
-//
-// Documents functions that acquire a shared (reader) lock in the body of a
-// function, and do not release it.
-#define SHARED_LOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
-
-// 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 UNLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
-
-// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION()
-//
-// Documents functions that try to acquire a lock, and return success or failure
-// (or a non-boolean value that can be interpreted as a boolean).
-// The first argument should be `true` for functions that return `true` on
-// 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`.
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
-
-#define SHARED_TRYLOCK_FUNCTION(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
-
-// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK()
-//
-// Documents functions that dynamically check to see if a lock is held, and fail
-// if it is not held.
-#define ASSERT_EXCLUSIVE_LOCK(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
-
-#define ASSERT_SHARED_LOCK(...) \
- THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
-
-// 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.
-#define NO_THREAD_SAFETY_ANALYSIS \
- THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
-
-//------------------------------------------------------------------------------
-// Tool-Supplied Annotations
-//------------------------------------------------------------------------------
-
-// TS_UNCHECKED should be placed around lock expressions that are not valid
-// C++ syntax, but which are present for documentation purposes. These
-// annotations will be ignored by the analysis.
-#define TS_UNCHECKED(x) ""
-
-// TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
-// It is used by automated tools to mark and disable invalid expressions.
-// The annotation should either be fixed, or changed to TS_UNCHECKED.
-#define TS_FIXME(x) ""
-
-// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of
-// a particular function. However, this attribute is used to mark functions
-// that are incorrect and need to be fixed. It is used by automated tools to
-// avoid breaking the build when the analysis is updated.
-// Code owners are expected to eventually fix the routine.
-#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS
-
-// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY
-// annotation that needs to be fixed, because it is producing thread safety
-// warning. It disables the GUARDED_BY.
-#define GUARDED_BY_FIXME(x)
-
-// Disables warnings for a single read operation. This can be used to avoid
-// warnings when it is known that the read is not actually involved in a race,
-// but the compiler cannot confirm that.
-#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x)
-
-
-namespace thread_safety_analysis {
-
-// Takes a reference to a guarded data member, and returns an unguarded
-// reference.
-template <typename T>
-inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS {
- return v;
-}
-
-template <typename T>
-inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS {
- return v;
-}
-
-} // namespace thread_safety_analysis
-
-#endif // defined(ABSL_LEGACY_THREAD_ANNOTATIONS)
-
-#endif // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_
diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc
index 252443eb..0471a25d 100644
--- a/absl/base/internal/thread_identity.cc
+++ b/absl/base/internal/thread_identity.cc
@@ -16,8 +16,12 @@
#if !defined(_WIN32) || defined(__MINGW32__)
#include <pthread.h>
+#ifndef __wasi__
+// WASI does not provide this header, either way we disable use
+// of signals with it below.
#include <signal.h>
#endif
+#endif
#include <atomic>
#include <cassert>
@@ -80,10 +84,12 @@ void SetCurrentThreadIdentity(ThreadIdentity* identity,
absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
reclaimer);
-#if defined(__EMSCRIPTEN__) || defined(__MINGW32__) || defined(__hexagon__)
- // Emscripten and MinGW pthread implementations does not support signals.
- // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
- // for more information.
+#if defined(__wasi__) || defined(__EMSCRIPTEN__) || defined(__MINGW32__) || \
+ defined(__hexagon__)
+ // Emscripten, WASI and MinGW pthread implementations does not support
+ // signals. See
+ // https://kripken.github.io/emscripten-site/docs/porting/pthreads.html for
+ // more information.
pthread_setspecific(thread_identity_pthread_key,
reinterpret_cast<void*>(identity));
#else
diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h
index 093dd9b4..4fea4574 100644
--- a/absl/base/internal/unaligned_access.h
+++ b/absl/base/internal/unaligned_access.h
@@ -23,6 +23,7 @@
#include "absl/base/attributes.h"
#include "absl/base/config.h"
+#include "absl/base/nullability.h"
// unaligned APIs
@@ -35,29 +36,35 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace base_internal {
-inline uint16_t UnalignedLoad16(const void *p) {
+inline uint16_t UnalignedLoad16(absl::Nonnull<const void *> p) {
uint16_t t;
memcpy(&t, p, sizeof t);
return t;
}
-inline uint32_t UnalignedLoad32(const void *p) {
+inline uint32_t UnalignedLoad32(absl::Nonnull<const void *> p) {
uint32_t t;
memcpy(&t, p, sizeof t);
return t;
}
-inline uint64_t UnalignedLoad64(const void *p) {
+inline uint64_t UnalignedLoad64(absl::Nonnull<const void *> p) {
uint64_t t;
memcpy(&t, p, sizeof t);
return t;
}
-inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); }
+inline void UnalignedStore16(absl::Nonnull<void *> p, uint16_t v) {
+ memcpy(p, &v, sizeof v);
+}
-inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
+inline void UnalignedStore32(absl::Nonnull<void *> p, uint32_t v) {
+ memcpy(p, &v, sizeof v);
+}
-inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
+inline void UnalignedStore64(absl::Nonnull<void *> p, uint64_t v) {
+ memcpy(p, &v, sizeof v);
+}
} // namespace base_internal
ABSL_NAMESPACE_END
diff --git a/absl/base/log_severity.cc b/absl/base/log_severity.cc
index 60a8fc1f..8e7bbbc9 100644
--- a/absl/base/log_severity.cc
+++ b/absl/base/log_severity.cc
@@ -17,6 +17,7 @@
#include <ostream>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h
index 8bdca38b..de9702ab 100644
--- a/absl/base/log_severity.h
+++ b/absl/base/log_severity.h
@@ -64,6 +64,8 @@ ABSL_NAMESPACE_BEGIN
// --my_log_level=info
// --my_log_level=0
//
+// `DFATAL` and `kLogDebugFatal` are similarly accepted.
+//
// Unparsing a flag produces the same result as `absl::LogSeverityName()` for
// the standard levels and a base-ten integer otherwise.
enum class LogSeverity : int {
@@ -82,18 +84,28 @@ constexpr std::array<absl::LogSeverity, 4> LogSeverities() {
absl::LogSeverity::kError, absl::LogSeverity::kFatal}};
}
+// `absl::kLogDebugFatal` equals `absl::LogSeverity::kFatal` in debug builds
+// (i.e. when `NDEBUG` is not defined) and `absl::LogSeverity::kError`
+// otherwise. Avoid ODR-using this variable as it has internal linkage and thus
+// distinct storage in different TUs.
+#ifdef NDEBUG
+static constexpr absl::LogSeverity kLogDebugFatal = absl::LogSeverity::kError;
+#else
+static constexpr absl::LogSeverity kLogDebugFatal = absl::LogSeverity::kFatal;
+#endif
+
// LogSeverityName()
//
// Returns the all-caps string representation (e.g. "INFO") of the specified
// severity level if it is one of the standard levels and "UNKNOWN" otherwise.
constexpr const char* LogSeverityName(absl::LogSeverity s) {
- return s == absl::LogSeverity::kInfo
- ? "INFO"
- : s == absl::LogSeverity::kWarning
- ? "WARNING"
- : s == absl::LogSeverity::kError
- ? "ERROR"
- : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN";
+ switch (s) {
+ case absl::LogSeverity::kInfo: return "INFO";
+ case absl::LogSeverity::kWarning: return "WARNING";
+ case absl::LogSeverity::kError: return "ERROR";
+ case absl::LogSeverity::kFatal: return "FATAL";
+ }
+ return "UNKNOWN";
}
// NormalizeLogSeverity()
@@ -101,9 +113,10 @@ constexpr const char* LogSeverityName(absl::LogSeverity s) {
// Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal`
// normalize to `kError` (**NOT** `kFatal`).
constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) {
- return s < absl::LogSeverity::kInfo
- ? absl::LogSeverity::kInfo
- : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s;
+ absl::LogSeverity n = s;
+ if (n < absl::LogSeverity::kInfo) n = absl::LogSeverity::kInfo;
+ if (n > absl::LogSeverity::kFatal) n = absl::LogSeverity::kError;
+ return n;
}
constexpr absl::LogSeverity NormalizeLogSeverity(int s) {
return absl::NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc
index 16091a5b..3394ecd7 100644
--- a/absl/base/log_severity_test.cc
+++ b/absl/base/log_severity_test.cc
@@ -146,7 +146,12 @@ INSTANTIATE_TEST_SUITE_P(
std::make_tuple("fatal", absl::LogSeverity::kFatal),
std::make_tuple("kFatal", absl::LogSeverity::kFatal),
std::make_tuple("FaTaL", absl::LogSeverity::kFatal),
- std::make_tuple("KfAtAl", absl::LogSeverity::kFatal)));
+ std::make_tuple("KfAtAl", absl::LogSeverity::kFatal),
+ std::make_tuple("DFATAL", absl::kLogDebugFatal),
+ std::make_tuple("dfatal", absl::kLogDebugFatal),
+ std::make_tuple("kLogDebugFatal", absl::kLogDebugFatal),
+ std::make_tuple("dFaTaL", absl::kLogDebugFatal),
+ std::make_tuple("kLoGdEbUgFaTaL", absl::kLogDebugFatal)));
TEST_P(ParseFlagFromEnumeratorTest, YieldsExpectedValue) {
const absl::string_view to_parse = std::get<0>(GetParam());
const absl::LogSeverity expected = std::get<1>(GetParam());
@@ -158,7 +163,8 @@ TEST_P(ParseFlagFromEnumeratorTest, YieldsExpectedValue) {
using ParseFlagFromGarbageTest = TestWithParam<absl::string_view>;
INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromGarbageTest,
- Values("", "\0", " ", "garbage", "kkinfo", "I"));
+ Values("", "\0", " ", "garbage", "kkinfo", "I",
+ "kDFATAL", "LogDebugFatal", "lOgDeBuGfAtAl"));
TEST_P(ParseFlagFromGarbageTest, ReturnsError) {
const absl::string_view to_parse = GetParam();
absl::LogSeverity value;
diff --git a/absl/base/no_destructor.h b/absl/base/no_destructor.h
new file mode 100644
index 00000000..d4b16a6e
--- /dev/null
+++ b/absl/base/no_destructor.h
@@ -0,0 +1,217 @@
+// Copyright 2023 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: no_destructor.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the absl::NoDestructor<T> wrapper for defining a
+// static type that does not need to be destructed upon program exit. Instead,
+// such an object survives during program exit (and can be safely accessed at
+// any time).
+//
+// Objects of such type, if constructed safely and under the right conditions,
+// provide two main benefits over other alternatives:
+//
+// * Global objects not normally allowed due to concerns of destruction order
+// (i.e. no "complex globals") can be safely allowed, provided that such
+// objects can be constant initialized.
+// * Function scope static objects can be optimized to avoid heap allocation,
+// pointer chasing, and allow lazy construction.
+//
+// See below for complete details.
+
+
+#ifndef ABSL_BASE_NO_DESTRUCTOR_H_
+#define ABSL_BASE_NO_DESTRUCTOR_H_
+
+#include <new>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// absl::NoDestructor<T>
+//
+// NoDestructor<T> is a wrapper around an object of type T that behaves as an
+// object of type T but never calls T's destructor. NoDestructor<T> makes it
+// safer and/or more efficient to use such objects in static storage contexts:
+// as global or function scope static variables.
+//
+// An instance of absl::NoDestructor<T> has similar type semantics to an
+// instance of T:
+//
+// * Constructs in the same manner as an object of type T through perfect
+// forwarding.
+// * Provides pointer/reference semantic access to the object of type T via
+// `->`, `*`, and `get()`.
+// (Note that `const NoDestructor<T>` works like a pointer to const `T`.)
+//
+// An object of type NoDestructor<T> should be defined in static storage:
+// as either a global static object, or as a function scope static variable.
+//
+// Additionally, NoDestructor<T> provides the following benefits:
+//
+// * Never calls T's destructor for the object
+// * If the object is a function-local static variable, the type can be
+// lazily constructed.
+//
+// An object of type NoDestructor<T> is "trivially destructible" in the notion
+// that its destructor is never run. Provided that an object of this type can be
+// safely initialized and does not need to be cleaned up on program shutdown,
+// NoDestructor<T> allows you to define global static variables, since Google's
+// C++ style guide ban on such objects doesn't apply to objects that are
+// trivially destructible.
+//
+// Usage as Global Static Variables
+//
+// NoDestructor<T> allows declaration of a global object with a non-trivial
+// constructor in static storage without needing to add a destructor.
+// However, such objects still need to worry about initialization order, so
+// such objects should be const initialized:
+//
+// // Global or namespace scope.
+// ABSL_CONST_INIT absl::NoDestructor<MyRegistry> reg{"foo", "bar", 8008};
+//
+// Note that if your object already has a trivial destructor, you don't need to
+// use NoDestructor<T>.
+//
+// Usage as Function Scope Static Variables
+//
+// Function static objects will be lazily initialized within static storage:
+//
+// // Function scope.
+// const std::string& MyString() {
+// static const absl::NoDestructor<std::string> x("foo");
+// return *x;
+// }
+//
+// For function static variables, NoDestructor avoids heap allocation and can be
+// inlined in static storage, resulting in exactly-once, thread-safe
+// construction of an object, and very fast access thereafter (the cost is a few
+// extra cycles).
+//
+// Using NoDestructor<T> in this manner is generally better than other patterns
+// which require pointer chasing:
+//
+// // Prefer using absl::NoDestructor<T> instead for the static variable.
+// const std::string& MyString() {
+// static const std::string* x = new std::string("foo");
+// return *x;
+// }
+//
+template <typename T>
+class NoDestructor {
+ public:
+ // Forwards arguments to the T's constructor: calls T(args...).
+ template <typename... Ts,
+ // Disable this overload when it might collide with copy/move.
+ typename std::enable_if<!std::is_same<void(std::decay_t<Ts>&...),
+ void(NoDestructor&)>::value,
+ int>::type = 0>
+ explicit constexpr NoDestructor(Ts&&... args)
+ : impl_(std::forward<Ts>(args)...) {}
+
+ // Forwards copy and move construction for T. Enables usage like this:
+ // static NoDestructor<std::array<string, 3>> x{{{"1", "2", "3"}}};
+ // static NoDestructor<std::vector<int>> x{{1, 2, 3}};
+ explicit constexpr NoDestructor(const T& x) : impl_(x) {}
+ explicit constexpr NoDestructor(T&& x)
+ : impl_(std::move(x)) {}
+
+ // No copying.
+ NoDestructor(const NoDestructor&) = delete;
+ NoDestructor& operator=(const NoDestructor&) = delete;
+
+ // Pretend to be a smart pointer to T with deep constness.
+ // Never returns a null pointer.
+ T& operator*() { return *get(); }
+ T* operator->() { return get(); }
+ T* get() { return impl_.get(); }
+ const T& operator*() const { return *get(); }
+ const T* operator->() const { return get(); }
+ const T* get() const { return impl_.get(); }
+
+ private:
+ class DirectImpl {
+ public:
+ template <typename... Args>
+ explicit constexpr DirectImpl(Args&&... args)
+ : value_(std::forward<Args>(args)...) {}
+ const T* get() const { return &value_; }
+ T* get() { return &value_; }
+
+ private:
+ T value_;
+ };
+
+ class PlacementImpl {
+ public:
+ template <typename... Args>
+ explicit PlacementImpl(Args&&... args) {
+ new (&space_) T(std::forward<Args>(args)...);
+ }
+ const T* get() const {
+ return Launder(reinterpret_cast<const T*>(&space_));
+ }
+ T* get() { return Launder(reinterpret_cast<T*>(&space_)); }
+
+ private:
+ template <typename P>
+ static P* Launder(P* p) {
+#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
+ return std::launder(p);
+#elif ABSL_HAVE_BUILTIN(__builtin_launder)
+ return __builtin_launder(p);
+#else
+ // When `std::launder` or equivalent are not available, we rely on
+ // undefined behavior, which works as intended on Abseil's officially
+ // supported platforms as of Q3 2023.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif
+ return p;
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+#endif
+ }
+
+ alignas(T) unsigned char space_[sizeof(T)];
+ };
+
+ // If the object is trivially destructible we use a member directly to avoid
+ // potential once-init runtime initialization. It somewhat defeats the
+ // purpose of NoDestructor in this case, but this makes the class more
+ // friendly to generic code.
+ std::conditional_t<std::is_trivially_destructible<T>::value, DirectImpl,
+ PlacementImpl>
+ impl_;
+};
+
+#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+// Provide 'Class Template Argument Deduction': the type of NoDestructor's T
+// will be the same type as the argument passed to NoDestructor's constructor.
+template <typename T>
+NoDestructor(T) -> NoDestructor<T>;
+#endif // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_BASE_NO_DESTRUCTOR_H_
diff --git a/absl/base/no_destructor_benchmark.cc b/absl/base/no_destructor_benchmark.cc
new file mode 100644
index 00000000..5fc88f1d
--- /dev/null
+++ b/absl/base/no_destructor_benchmark.cc
@@ -0,0 +1,165 @@
+// Copyright 2023 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 "absl/base/internal/raw_logging.h"
+#include "absl/base/no_destructor.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+// Number of static-NoDestructor-in-a-function to exercise.
+// This must be low enough not to hit template instantiation limits
+// (happens around 1000).
+constexpr int kNumObjects = 1; // set to 512 when doing benchmarks
+ // 1 is faster to compile: just one templated
+ // function instantiation
+
+// Size of individual objects to benchmark static-NoDestructor-in-a-function
+// usage with.
+constexpr int kObjSize = sizeof(void*)*1;
+
+// Simple object of kObjSize bytes (rounded to int).
+// We benchmark complete reading of its state via Verify().
+class BM_Blob {
+ public:
+ BM_Blob(int val) { for (auto& d : data_) d = val; }
+ BM_Blob() : BM_Blob(-1) {}
+ void Verify(int val) const { // val must be the c-tor argument
+ for (auto& d : data_) ABSL_INTERNAL_CHECK(d == val, "");
+ }
+ private:
+ int data_[kObjSize / sizeof(int) > 0 ? kObjSize / sizeof(int) : 1];
+};
+
+// static-NoDestructor-in-a-function pattern instances.
+// We'll instantiate kNumObjects of them.
+template<int i>
+const BM_Blob& NoDestrBlobFunc() {
+ static absl::NoDestructor<BM_Blob> x(i);
+ return *x;
+}
+
+// static-heap-ptr-in-a-function pattern instances
+// We'll instantiate kNumObjects of them.
+template<int i>
+const BM_Blob& OnHeapBlobFunc() {
+ static BM_Blob* x = new BM_Blob(i);
+ return *x;
+}
+
+// Type for NoDestrBlobFunc or OnHeapBlobFunc.
+typedef const BM_Blob& (*FuncType)();
+
+// ========================================================================= //
+// Simple benchmarks that read a single BM_Blob over and over, hence
+// all they touch fits into L1 CPU cache:
+
+// Direct non-POD global variable (style guide violation) as a baseline.
+static BM_Blob direct_blob(0);
+
+void BM_Direct(benchmark::State& state) {
+ for (auto s : state) {
+ direct_blob.Verify(0);
+ }
+}
+BENCHMARK(BM_Direct);
+
+void BM_NoDestr(benchmark::State& state) {
+ for (auto s : state) {
+ NoDestrBlobFunc<0>().Verify(0);
+ }
+}
+BENCHMARK(BM_NoDestr);
+
+void BM_OnHeap(benchmark::State& state) {
+ for (auto s : state) {
+ OnHeapBlobFunc<0>().Verify(0);
+ }
+}
+BENCHMARK(BM_OnHeap);
+
+// ========================================================================= //
+// Benchmarks that read kNumObjects of BM_Blob over and over, hence with
+// appropriate values of sizeof(BM_Blob) and kNumObjects their working set
+// can exceed a given layer of CPU cache.
+
+// Type of benchmark to select between NoDestrBlobFunc and OnHeapBlobFunc.
+enum BM_Type { kNoDestr, kOnHeap, kDirect };
+
+// BlobFunc<n>(t, i) returns the i-th function of type t.
+// n must be larger than i (we'll use kNumObjects for n).
+template<int n>
+FuncType BlobFunc(BM_Type t, int i) {
+ if (i == n) {
+ switch (t) {
+ case kNoDestr: return &NoDestrBlobFunc<n>;
+ case kOnHeap: return &OnHeapBlobFunc<n>;
+ case kDirect: return nullptr;
+ }
+ }
+ return BlobFunc<n-1>(t, i);
+}
+
+template<>
+FuncType BlobFunc<0>(BM_Type t, int i) {
+ ABSL_INTERNAL_CHECK(i == 0, "");
+ switch (t) {
+ case kNoDestr: return &NoDestrBlobFunc<0>;
+ case kOnHeap: return &OnHeapBlobFunc<0>;
+ case kDirect: return nullptr;
+ }
+ return nullptr;
+}
+
+// Direct non-POD global variables (style guide violation) as a baseline.
+static BM_Blob direct_blobs[kNumObjects];
+
+// Helper that cheaply maps benchmark iteration to randomish index in
+// [0, kNumObjects).
+int RandIdx(int i) {
+ // int64 is to avoid overflow and generating negative return values:
+ return (static_cast<int64_t>(i) * 13) % kNumObjects;
+}
+
+// Generic benchmark working with kNumObjects for any of the possible BM_Type.
+template <BM_Type t>
+void BM_Many(benchmark::State& state) {
+ FuncType funcs[kNumObjects];
+ for (int i = 0; i < kNumObjects; ++i) {
+ funcs[i] = BlobFunc<kNumObjects-1>(t, i);
+ }
+ if (t == kDirect) {
+ for (auto s : state) {
+ int idx = RandIdx(state.iterations());
+ direct_blobs[idx].Verify(-1);
+ }
+ } else {
+ for (auto s : state) {
+ int idx = RandIdx(state.iterations());
+ funcs[idx]().Verify(idx);
+ }
+ }
+}
+
+void BM_DirectMany(benchmark::State& state) { BM_Many<kDirect>(state); }
+void BM_NoDestrMany(benchmark::State& state) { BM_Many<kNoDestr>(state); }
+void BM_OnHeapMany(benchmark::State& state) { BM_Many<kOnHeap>(state); }
+
+BENCHMARK(BM_DirectMany);
+BENCHMARK(BM_NoDestrMany);
+BENCHMARK(BM_OnHeapMany);
+
+} // namespace
diff --git a/absl/base/no_destructor_test.cc b/absl/base/no_destructor_test.cc
new file mode 100644
index 00000000..71693c7e
--- /dev/null
+++ b/absl/base/no_destructor_test.cc
@@ -0,0 +1,209 @@
+// Copyright 2023 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/no_destructor.h"
+
+#include <array>
+#include <initializer_list>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/raw_logging.h"
+
+namespace {
+
+struct Blob {
+ Blob() : val(42) {}
+ Blob(int x, int y) : val(x + y) {}
+ Blob(std::initializer_list<int> xs) {
+ val = 0;
+ for (auto& x : xs) val += x;
+ }
+
+ Blob(const Blob& /*b*/) = delete;
+ Blob(Blob&& b) noexcept : val(b.val) {
+ b.moved_out = true;
+ } // moving is fine
+
+ // no crash: NoDestructor indeed does not destruct (the moved-out Blob
+ // temporaries do get destroyed though)
+ ~Blob() { ABSL_INTERNAL_CHECK(moved_out, "~Blob"); }
+
+ int val;
+ bool moved_out = false;
+};
+
+struct TypeWithDeletedDestructor {
+ ~TypeWithDeletedDestructor() = delete;
+};
+
+TEST(NoDestructorTest, DestructorNeverCalled) {
+ absl::NoDestructor<TypeWithDeletedDestructor> a;
+ (void)a;
+}
+
+TEST(NoDestructorTest, Noncopyable) {
+ using T = absl::NoDestructor<int>;
+
+ EXPECT_FALSE((std::is_constructible<T, T>::value));
+ EXPECT_FALSE((std::is_constructible<T, const T>::value));
+ EXPECT_FALSE((std::is_constructible<T, T&>::value));
+ EXPECT_FALSE((std::is_constructible<T, const T&>::value));
+
+ EXPECT_FALSE((std::is_assignable<T&, T>::value));
+ EXPECT_FALSE((std::is_assignable<T&, const T>::value));
+ EXPECT_FALSE((std::is_assignable<T&, T&>::value));
+ EXPECT_FALSE((std::is_assignable<T&, const T&>::value));
+}
+
+TEST(NoDestructorTest, Interface) {
+ EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<Blob>>::value);
+ EXPECT_TRUE(
+ std::is_trivially_destructible<absl::NoDestructor<const Blob>>::value);
+ {
+ absl::NoDestructor<Blob> b; // default c-tor
+ // access: *, ->, get()
+ EXPECT_EQ(42, (*b).val);
+ (*b).val = 55;
+ EXPECT_EQ(55, b->val);
+ b->val = 66;
+ EXPECT_EQ(66, b.get()->val);
+ b.get()->val = 42; // NOLINT
+ EXPECT_EQ(42, (*b).val);
+ }
+ {
+ absl::NoDestructor<const Blob> b(70, 7); // regular c-tor, const
+ EXPECT_EQ(77, (*b).val);
+ EXPECT_EQ(77, b->val);
+ EXPECT_EQ(77, b.get()->val);
+ }
+ {
+ const absl::NoDestructor<Blob> b{
+ {20, 28, 40}}; // init-list c-tor, deep const
+ // This only works in clang, not in gcc:
+ // const absl::NoDestructor<Blob> b({20, 28, 40});
+ EXPECT_EQ(88, (*b).val);
+ EXPECT_EQ(88, b->val);
+ EXPECT_EQ(88, b.get()->val);
+ }
+}
+
+TEST(NoDestructorTest, SfinaeRegressionAbstractArg) {
+ struct Abstract {
+ virtual ~Abstract() = default;
+ virtual int foo() const = 0;
+ };
+
+ struct Concrete : Abstract {
+ int foo() const override { return 17; }
+ };
+
+ struct UsesAbstractInConstructor {
+ explicit UsesAbstractInConstructor(const Abstract& abstract)
+ : i(abstract.foo()) {}
+ int i;
+ };
+
+ Concrete input;
+ absl::NoDestructor<UsesAbstractInConstructor> foo1(input);
+ EXPECT_EQ(foo1->i, 17);
+ absl::NoDestructor<UsesAbstractInConstructor> foo2(
+ static_cast<const Abstract&>(input));
+ EXPECT_EQ(foo2->i, 17);
+}
+
+// ========================================================================= //
+
+std::string* Str0() {
+ static absl::NoDestructor<std::string> x;
+ return x.get();
+}
+
+extern const std::string& Str2();
+
+const char* Str1() {
+ static absl::NoDestructor<std::string> x(Str2() + "_Str1");
+ return x->c_str();
+}
+
+const std::string& Str2() {
+ static absl::NoDestructor<std::string> x("Str2");
+ return *x;
+}
+
+const std::string& Str2Copy() {
+ // Exercise copy construction
+ static absl::NoDestructor<std::string> x(Str2());
+ return *x;
+}
+
+typedef std::array<std::string, 3> MyArray;
+const MyArray& Array() {
+ static absl::NoDestructor<MyArray> x{{{"foo", "bar", "baz"}}};
+ // This only works in clang, not in gcc:
+ // static absl::NoDestructor<MyArray> x({{"foo", "bar", "baz"}});
+ return *x;
+}
+
+typedef std::vector<int> MyVector;
+const MyVector& Vector() {
+ static absl::NoDestructor<MyVector> x{{1, 2, 3}};
+ return *x;
+}
+
+const int& Int() {
+ static absl::NoDestructor<int> x;
+ return *x;
+}
+
+TEST(NoDestructorTest, StaticPattern) {
+ EXPECT_TRUE(
+ std::is_trivially_destructible<absl::NoDestructor<std::string>>::value);
+ EXPECT_TRUE(
+ std::is_trivially_destructible<absl::NoDestructor<MyArray>>::value);
+ EXPECT_TRUE(
+ std::is_trivially_destructible<absl::NoDestructor<MyVector>>::value);
+ EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<int>>::value);
+
+ EXPECT_EQ(*Str0(), "");
+ Str0()->append("foo");
+ EXPECT_EQ(*Str0(), "foo");
+
+ EXPECT_EQ(std::string(Str1()), "Str2_Str1");
+
+ EXPECT_EQ(Str2(), "Str2");
+ EXPECT_EQ(Str2Copy(), "Str2");
+
+ EXPECT_THAT(Array(), testing::ElementsAre("foo", "bar", "baz"));
+
+ EXPECT_THAT(Vector(), testing::ElementsAre(1, 2, 3));
+
+ EXPECT_EQ(0, Int()); // should get zero-initialized
+}
+
+#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
+// This would fail to compile if Class Template Argument Deduction was not
+// provided for absl::NoDestructor.
+TEST(NoDestructorTest, ClassTemplateArgumentDeduction) {
+ absl::NoDestructor i(1);
+ static_assert(std::is_same<decltype(i), absl::NoDestructor<int>>::value,
+ "Expected deduced type to be int.");
+}
+#endif
+
+} // namespace
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index ad0121ad..f9859958 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -25,6 +25,7 @@
#include <assert.h>
#include "absl/base/config.h"
+#include "absl/base/options.h"
// ABSL_BLOCK_TAIL_CALL_OPTIMIZATION
//
diff --git a/absl/base/options.h b/absl/base/options.h
index 09b98ee2..67cbf456 100644
--- a/absl/base/options.h
+++ b/absl/base/options.h
@@ -176,6 +176,32 @@
#define ABSL_OPTION_USE_STD_VARIANT 1
+// ABSL_OPTION_USE_STD_ORDERING
+//
+// This option controls whether absl::{partial,weak,strong}_ordering are
+// implemented as aliases to the std:: ordering types, or as an independent
+// implementation.
+//
+// A value of 0 means to use Abseil's implementation. This requires only C++11
+// support, and is expected to work on every toolchain we support.
+//
+// A value of 1 means to use aliases. This requires that all code using Abseil
+// is built in C++20 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if working std:: ordering types are available. This
+// option is useful when you are building your program from source. It should
+// not be used otherwise -- for example, if you are distributing Abseil in a
+// binary package manager -- since in mode 2, they will name different types,
+// with different mangled names and binary layout, depending on the compiler
+// flags passed by the end user. For more info, see
+// https://abseil.io/about/design/dropin-types.
+//
+// User code should not inspect this macro. To check in the preprocessor if
+// the ordering types are aliases of std:: ordering types, use the feature macro
+// ABSL_USES_STD_ORDERING.
+
+#define ABSL_OPTION_USE_STD_ORDERING 2
// ABSL_OPTION_USE_INLINE_NAMESPACE
// ABSL_OPTION_INLINE_NAMESPACE_NAME
@@ -200,7 +226,7 @@
// allowed.
#define ABSL_OPTION_USE_INLINE_NAMESPACE 1
-#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20230802
+#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20240116
// ABSL_OPTION_HARDENED
//
diff --git a/absl/base/prefetch.h b/absl/base/prefetch.h
index de7a180d..eb40a445 100644
--- a/absl/base/prefetch.h
+++ b/absl/base/prefetch.h
@@ -24,17 +24,19 @@
#ifndef ABSL_BASE_PREFETCH_H_
#define ABSL_BASE_PREFETCH_H_
+#include "absl/base/attributes.h"
#include "absl/base/config.h"
#if defined(ABSL_INTERNAL_HAVE_SSE)
#include <xmmintrin.h>
#endif
-#if defined(_MSC_VER) && _MSC_VER >= 1900 && \
- (defined(_M_X64) || defined(_M_IX86))
+#if defined(_MSC_VER)
#include <intrin.h>
+#if defined(ABSL_INTERNAL_HAVE_SSE)
#pragma intrinsic(_mm_prefetch)
#endif
+#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -140,21 +142,24 @@ void PrefetchToLocalCacheForWrite(const void* addr);
// See __builtin_prefetch:
// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.
//
-inline void PrefetchToLocalCache(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCache(
+ const void* addr) {
__builtin_prefetch(addr, 0, 3);
}
-inline void PrefetchToLocalCacheNta(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheNta(
+ const void* addr) {
__builtin_prefetch(addr, 0, 0);
}
-inline void PrefetchToLocalCacheForWrite(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheForWrite(
+ const void* addr) {
// [x86] gcc/clang don't generate PREFETCHW for __builtin_prefetch(.., 1)
// unless -march=broadwell or newer; this is not generally the default, so we
// manually emit prefetchw. PREFETCHW is recognized as a no-op on older Intel
// processors and has been present on AMD processors since the K6-2.
-#if defined(__x86_64__)
- asm("prefetchw (%0)" : : "r"(addr));
+#if defined(__x86_64__) && !defined(__PRFCHW__)
+ asm("prefetchw %0" : : "m"(*reinterpret_cast<const char*>(addr)));
#else
__builtin_prefetch(addr, 1, 3);
#endif
@@ -164,15 +169,18 @@ inline void PrefetchToLocalCacheForWrite(const void* addr) {
#define ABSL_HAVE_PREFETCH 1
-inline void PrefetchToLocalCache(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCache(
+ const void* addr) {
_mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T0);
}
-inline void PrefetchToLocalCacheNta(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheNta(
+ const void* addr) {
_mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_NTA);
}
-inline void PrefetchToLocalCacheForWrite(const void* addr) {
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheForWrite(
+ const void* addr) {
#if defined(_MM_HINT_ET0)
_mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_ET0);
#elif !defined(_MSC_VER) && defined(__x86_64__)
@@ -180,15 +188,18 @@ inline void PrefetchToLocalCacheForWrite(const void* addr) {
// up, PREFETCHW is recognized as a no-op on older Intel processors
// and has been present on AMD processors since the K6-2. We have this
// disabled for MSVC compilers as this miscompiles on older MSVC compilers.
- asm("prefetchw (%0)" : : "r"(addr));
+ asm("prefetchw %0" : : "m"(*reinterpret_cast<const char*>(addr)));
#endif
}
#else
-inline void PrefetchToLocalCache(const void* addr) {}
-inline void PrefetchToLocalCacheNta(const void* addr) {}
-inline void PrefetchToLocalCacheForWrite(const void* addr) {}
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCache(
+ const void* addr) {}
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheNta(
+ const void* addr) {}
+ABSL_ATTRIBUTE_ALWAYS_INLINE inline void PrefetchToLocalCacheForWrite(
+ const void* addr) {}
#endif
diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc
index 52ecf580..e9047158 100644
--- a/absl/base/spinlock_test_common.cc
+++ b/absl/base/spinlock_test_common.cc
@@ -51,6 +51,8 @@ struct SpinLockTest {
static int64_t DecodeWaitCycles(uint32_t lock_value) {
return SpinLock::DecodeWaitCycles(lock_value);
}
+
+ static bool IsCooperative(const SpinLock& l) { return l.IsCooperative(); }
};
namespace {
@@ -266,6 +268,17 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) {
base_internal::NumCPUs() * 2);
}
+TEST(SpinLockTest, IsCooperative) {
+ SpinLock default_constructor;
+ EXPECT_TRUE(SpinLockTest::IsCooperative(default_constructor));
+
+ SpinLock cooperative(base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
+ EXPECT_TRUE(SpinLockTest::IsCooperative(cooperative));
+
+ SpinLock kernel_only(base_internal::SCHEDULE_KERNEL_ONLY);
+ EXPECT_FALSE(SpinLockTest::IsCooperative(kernel_only));
+}
+
} // namespace
} // namespace base_internal
ABSL_NAMESPACE_END
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index bc8a6203..4a3f3e33 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -36,8 +36,6 @@
#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
// ABSL_GUARDED_BY()
//
diff --git a/absl/cleanup/BUILD.bazel b/absl/cleanup/BUILD.bazel
index 2154d9f1..984d5714 100644
--- a/absl/cleanup/BUILD.bazel
+++ b/absl/cleanup/BUILD.bazel
@@ -19,7 +19,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -60,6 +67,7 @@ cc_test(
":cleanup",
"//absl/base:config",
"//absl/utility",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index f22da59a..0ba2fa76 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -47,6 +54,7 @@ cc_test(
"//absl/types:any",
"//absl/types:optional",
"//absl/utility",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -73,12 +81,13 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":counting_allocator",
":fixed_array",
+ ":test_allocator",
"//absl/base:config",
"//absl/base:exception_testing",
"//absl/hash:hash_testing",
"//absl/memory",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -92,6 +101,7 @@ cc_test(
":fixed_array",
"//absl/base:config",
"//absl/base:exception_safety_testing",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -116,6 +126,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":compressed_tuple",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/memory",
"//absl/meta:type_traits",
@@ -139,13 +150,12 @@ cc_library(
)
cc_library(
- name = "counting_allocator",
+ name = "test_allocator",
testonly = 1,
- hdrs = ["internal/counting_allocator.h"],
- copts = ABSL_DEFAULT_COPTS,
+ copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ textual_hdrs = ["internal/test_allocator.h"],
visibility = ["//visibility:private"],
- deps = ["//absl/base:config"],
)
cc_test(
@@ -154,8 +164,8 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":counting_allocator",
":inlined_vector",
+ ":test_allocator",
":test_instance_tracker",
"//absl/base:config",
"//absl/base:core_headers",
@@ -164,6 +174,7 @@ cc_test(
"//absl/log:check",
"//absl/memory",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -192,6 +203,7 @@ cc_test(
":inlined_vector",
"//absl/base:config",
"//absl/base:exception_safety_testing",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -216,6 +228,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":test_instance_tracker",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -256,7 +269,9 @@ cc_test(
":unordered_map_members_test",
":unordered_map_modifiers_test",
"//absl/log:check",
+ "//absl/meta:type_traits",
"//absl/types:any",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -283,15 +298,18 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
tags = ["no_test_loonix"],
deps = [
+ ":container_memory",
":flat_hash_set",
":hash_generator_testing",
":unordered_set_constructor_test",
":unordered_set_lookup_test",
":unordered_set_members_test",
":unordered_set_modifiers_test",
+ "//absl/base:config",
"//absl/log:check",
"//absl/memory",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -326,6 +344,7 @@ cc_test(
":unordered_map_lookup_test",
":unordered_map_members_test",
":unordered_map_modifiers_test",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -357,6 +376,7 @@ cc_test(
":unordered_set_lookup_test",
":unordered_set_members_test",
":unordered_set_modifiers_test",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -383,7 +403,10 @@ cc_test(
deps = [
":container_memory",
":test_instance_tracker",
+ "//absl/base:no_destructor",
+ "//absl/meta:type_traits",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -417,6 +440,7 @@ cc_test(
"//absl/strings",
"//absl/strings:cord",
"//absl/strings:cord_test_helpers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -430,6 +454,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":hash_policy_testing",
+ "//absl/base:no_destructor",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/strings",
@@ -455,6 +480,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":hash_policy_testing",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -477,6 +503,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":hash_policy_traits",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -497,6 +524,8 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":common_policy_traits",
+ "//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -560,6 +589,7 @@ cc_test(
"//absl/synchronization",
"//absl/synchronization:thread_pool",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -580,6 +610,8 @@ cc_test(
deps = [
":hash_policy_traits",
":node_slot_policy",
+ "//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -592,6 +624,8 @@ cc_library(
deps = [
":container_memory",
":raw_hash_set",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/base:throw_delegate",
],
)
@@ -651,13 +685,19 @@ cc_test(
":hash_function_defaults",
":hash_policy_testing",
":hashtable_debug",
+ ":hashtablez_sampler",
":raw_hash_set",
+ ":test_allocator",
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:prefetch",
+ "//absl/hash",
"//absl/log",
+ "//absl/memory",
+ "//absl/meta:type_traits",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -694,10 +734,12 @@ cc_binary(
":hash_function_defaults",
":hashtable_debug",
":raw_hash_set",
+ "//absl/base:no_destructor",
"//absl/random",
"//absl/random:distributions",
"//absl/strings",
"//absl/strings:str_format",
+ "//absl/types:optional",
],
)
@@ -710,7 +752,8 @@ cc_test(
deps = [
":raw_hash_set",
":tracked",
- "//absl/base:core_headers",
+ "//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -723,6 +766,7 @@ cc_library(
deps = [
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/debugging:demangle_internal",
"//absl/meta:type_traits",
"//absl/strings",
"//absl/types:span",
@@ -741,9 +785,10 @@ cc_test(
deps = [
":layout",
"//absl/base:config",
- "//absl/base:core_headers",
"//absl/log:check",
"//absl/types:span",
+ "//absl/utility",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -889,6 +934,7 @@ cc_test(
":unordered_set_lookup_test",
":unordered_set_members_test",
":unordered_set_modifiers_test",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -904,6 +950,7 @@ cc_test(
":unordered_map_lookup_test",
":unordered_map_members_test",
":unordered_map_modifiers_test",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -920,6 +967,7 @@ cc_test(
":flat_hash_set",
":node_hash_map",
":node_hash_set",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -989,7 +1037,7 @@ cc_test(
deps = [
":btree",
":btree_test_common",
- ":counting_allocator",
+ ":test_allocator",
":test_instance_tracker",
"//absl/algorithm:container",
"//absl/base:core_headers",
@@ -1001,6 +1049,7 @@ cc_test(
"//absl/strings",
"//absl/types:compare",
"//absl/types:optional",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1031,5 +1080,6 @@ cc_binary(
"//absl/strings:str_format",
"//absl/time",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 39d95e02..128cc0e9 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -77,13 +77,13 @@ absl_cc_test(
absl::btree_test_common
absl::compare
absl::core_headers
- absl::counting_allocator
absl::flags
absl::hash_testing
absl::optional
absl::random_random
absl::raw_logging_internal
absl::strings
+ absl::test_allocator
absl::test_instance_tracker
GTest::gmock_main
)
@@ -145,11 +145,11 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::fixed_array
- absl::counting_allocator
absl::config
absl::exception_testing
absl::hash_testing
absl::memory
+ absl::test_allocator
GTest::gmock_main
)
@@ -177,6 +177,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::compressed_tuple
+ absl::config
absl::core_headers
absl::memory
absl::span
@@ -204,13 +205,14 @@ absl_cc_library(
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
- counting_allocator
+ test_allocator
HDRS
- "internal/counting_allocator.h"
+ "internal/test_allocator.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::config
+ GTest::gmock
)
absl_cc_test(
@@ -224,12 +226,12 @@ absl_cc_test(
absl::check
absl::config
absl::core_headers
- absl::counting_allocator
absl::exception_testing
absl::hash_testing
absl::inlined_vector
absl::memory
absl::strings
+ absl::test_allocator
absl::test_instance_tracker
GTest::gmock_main
)
@@ -304,6 +306,7 @@ absl_cc_test(
absl::check
absl::flat_hash_map
absl::hash_generator_testing
+ absl::type_traits
absl::unordered_map_constructor_test
absl::unordered_map_lookup_test
absl::unordered_map_members_test
@@ -338,6 +341,8 @@ absl_cc_test(
"-DUNORDERED_SET_CXX17"
DEPS
absl::check
+ absl::config
+ absl::container_memory
absl::flat_hash_set
absl::hash_generator_testing
absl::memory
@@ -445,8 +450,10 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::container_memory
+ absl::no_destructor
absl::strings
absl::test_instance_tracker
+ absl::type_traits
GTest::gmock_main
)
@@ -497,6 +504,7 @@ absl_cc_library(
absl::hash_policy_testing
absl::memory
absl::meta
+ absl::no_destructor
absl::strings
TESTONLY
)
@@ -575,6 +583,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::common_policy_traits
+ absl::config
GTest::gmock_main
)
@@ -658,6 +667,7 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::config
absl::hash_policy_traits
absl::node_slot_policy
GTest::gmock_main
@@ -672,7 +682,9 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::config
absl::container_memory
+ absl::core_headers
absl::raw_hash_set
absl::throw_delegate
PUBLIC
@@ -736,13 +748,18 @@ absl_cc_test(
absl::core_headers
absl::flat_hash_map
absl::flat_hash_set
+ absl::hash
absl::hash_function_defaults
absl::hash_policy_testing
absl::hashtable_debug
+ absl::hashtablez_sampler
absl::log
+ absl::memory
absl::prefetch
absl::raw_hash_set
absl::strings
+ absl::test_allocator
+ absl::type_traits
GTest::gmock_main
)
@@ -754,9 +771,9 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::config
absl::raw_hash_set
absl::tracked
- absl::core_headers
GTest::gmock_main
)
@@ -771,6 +788,7 @@ absl_cc_library(
DEPS
absl::config
absl::core_headers
+ absl::debugging_internal
absl::meta
absl::strings
absl::span
@@ -789,8 +807,8 @@ absl_cc_test(
absl::layout
absl::check
absl::config
- absl::core_headers
absl::span
+ absl::utility
GTest::gmock_main
)
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h
index cd3ee2b4..0f62f0bd 100644
--- a/absl/container/btree_map.h
+++ b/absl/container/btree_map.h
@@ -53,6 +53,7 @@
#ifndef ABSL_CONTAINER_BTREE_MAP_H_
#define ABSL_CONTAINER_BTREE_MAP_H_
+#include "absl/base/attributes.h"
#include "absl/container/internal/btree.h" // IWYU pragma: export
#include "absl/container/internal/btree_container.h" // IWYU pragma: export
@@ -864,7 +865,8 @@ struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, IsMulti,
using init_type = typename super_type::init_type;
template <typename V>
- static auto key(const V &value) -> decltype(value.first) {
+ static auto key(const V &value ABSL_ATTRIBUTE_LIFETIME_BOUND)
+ -> decltype((value.first)) {
return value.first;
}
static const Key &key(const slot_type *s) { return slot_policy::key(s); }
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
index 72f446b2..d7102fe4 100644
--- a/absl/container/btree_test.cc
+++ b/absl/container/btree_test.cc
@@ -37,7 +37,7 @@
#include "absl/base/macros.h"
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
-#include "absl/container/internal/counting_allocator.h"
+#include "absl/container/internal/test_allocator.h"
#include "absl/container/internal/test_instance_tracker.h"
#include "absl/flags/flag.h"
#include "absl/hash/hash_testing.h"
@@ -76,16 +76,6 @@ void CheckPairEquals(const std::pair<T, U> &x, const std::pair<V, W> &y) {
CheckPairEquals(x.first, y.first);
CheckPairEquals(x.second, y.second);
}
-
-bool IsAssertEnabled() {
- // Use an assert with side-effects to figure out if they are actually enabled.
- bool assert_enabled = false;
- assert([&]() { // NOLINT
- assert_enabled = true;
- return true;
- }());
- return assert_enabled;
-}
} // namespace
// The base class for a sorted associative container checker. TreeType is the
@@ -668,111 +658,6 @@ void BtreeMultiTest() {
}
template <typename T>
-struct PropagatingCountingAlloc : public CountingAllocator<T> {
- using propagate_on_container_copy_assignment = std::true_type;
- using propagate_on_container_move_assignment = std::true_type;
- using propagate_on_container_swap = std::true_type;
-
- using Base = CountingAllocator<T>;
- using Base::Base;
-
- template <typename U>
- explicit PropagatingCountingAlloc(const PropagatingCountingAlloc<U> &other)
- : Base(other.bytes_used_) {}
-
- template <typename U>
- struct rebind {
- using other = PropagatingCountingAlloc<U>;
- };
-};
-
-template <typename T>
-void BtreeAllocatorTest() {
- using value_type = typename T::value_type;
-
- int64_t bytes1 = 0, bytes2 = 0;
- PropagatingCountingAlloc<T> allocator1(&bytes1);
- PropagatingCountingAlloc<T> allocator2(&bytes2);
- Generator<value_type> generator(1000);
-
- // Test that we allocate properly aligned memory. If we don't, then Layout
- // will assert fail.
- auto unused1 = allocator1.allocate(1);
- auto unused2 = allocator2.allocate(1);
-
- // Test copy assignment
- {
- T b1(typename T::key_compare(), allocator1);
- T b2(typename T::key_compare(), allocator2);
-
- int64_t original_bytes1 = bytes1;
- b1.insert(generator(0));
- EXPECT_GT(bytes1, original_bytes1);
-
- // This should propagate the allocator.
- b1 = b2;
- EXPECT_EQ(b1.size(), 0);
- EXPECT_EQ(b2.size(), 0);
- EXPECT_EQ(bytes1, original_bytes1);
-
- for (int i = 1; i < 1000; i++) {
- b1.insert(generator(i));
- }
-
- // We should have allocated out of allocator2.
- EXPECT_GT(bytes2, bytes1);
- }
-
- // Test move assignment
- {
- T b1(typename T::key_compare(), allocator1);
- T b2(typename T::key_compare(), allocator2);
-
- int64_t original_bytes1 = bytes1;
- b1.insert(generator(0));
- EXPECT_GT(bytes1, original_bytes1);
-
- // This should propagate the allocator.
- b1 = std::move(b2);
- EXPECT_EQ(b1.size(), 0);
- EXPECT_EQ(bytes1, original_bytes1);
-
- for (int i = 1; i < 1000; i++) {
- b1.insert(generator(i));
- }
-
- // We should have allocated out of allocator2.
- EXPECT_GT(bytes2, bytes1);
- }
-
- // Test swap
- {
- T b1(typename T::key_compare(), allocator1);
- T b2(typename T::key_compare(), allocator2);
-
- int64_t original_bytes1 = bytes1;
- b1.insert(generator(0));
- EXPECT_GT(bytes1, original_bytes1);
-
- // This should swap the allocators.
- swap(b1, b2);
- EXPECT_EQ(b1.size(), 0);
- EXPECT_EQ(b2.size(), 1);
- EXPECT_GT(bytes1, original_bytes1);
-
- for (int i = 1; i < 1000; i++) {
- b1.insert(generator(i));
- }
-
- // We should have allocated out of allocator2.
- EXPECT_GT(bytes2, bytes1);
- }
-
- allocator1.deallocate(unused1, 1);
- allocator2.deallocate(unused2, 1);
-}
-
-template <typename T>
void BtreeMapTest() {
using value_type = typename T::value_type;
using mapped_type = typename T::mapped_type;
@@ -811,10 +696,7 @@ void SetTest() {
sizeof(absl::btree_set<K>),
2 * sizeof(void *) + sizeof(typename absl::btree_set<K>::size_type));
using BtreeSet = absl::btree_set<K>;
- using CountingBtreeSet =
- absl::btree_set<K, std::less<K>, PropagatingCountingAlloc<K>>;
BtreeTest<BtreeSet, std::set<K>>();
- BtreeAllocatorTest<CountingBtreeSet>();
}
template <typename K, int N = 256>
@@ -823,24 +705,16 @@ void MapTest() {
sizeof(absl::btree_map<K, K>),
2 * sizeof(void *) + sizeof(typename absl::btree_map<K, K>::size_type));
using BtreeMap = absl::btree_map<K, K>;
- using CountingBtreeMap =
- absl::btree_map<K, K, std::less<K>,
- PropagatingCountingAlloc<std::pair<const K, K>>>;
BtreeTest<BtreeMap, std::map<K, K>>();
- BtreeAllocatorTest<CountingBtreeMap>();
BtreeMapTest<BtreeMap>();
}
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>
void MultiSetTest() {
@@ -848,10 +722,7 @@ void MultiSetTest() {
sizeof(absl::btree_multiset<K>),
2 * sizeof(void *) + sizeof(typename absl::btree_multiset<K>::size_type));
using BtreeMSet = absl::btree_multiset<K>;
- using CountingBtreeMSet =
- absl::btree_multiset<K, std::less<K>, PropagatingCountingAlloc<K>>;
BtreeMultiTest<BtreeMSet, std::multiset<K>>();
- BtreeAllocatorTest<CountingBtreeMSet>();
}
template <typename K, int N = 256>
@@ -860,24 +731,16 @@ void MultiMapTest() {
2 * sizeof(void *) +
sizeof(typename absl::btree_multimap<K, K>::size_type));
using BtreeMMap = absl::btree_multimap<K, K>;
- using CountingBtreeMMap =
- absl::btree_multimap<K, K, std::less<K>,
- PropagatingCountingAlloc<std::pair<const K, K>>>;
BtreeMultiTest<BtreeMMap, std::multimap<K, K>>();
BtreeMultiMapTest<BtreeMMap>();
- BtreeAllocatorTest<CountingBtreeMMap>();
}
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 {
bool operator()(const std::string &a, const std::string &b) const {
@@ -1483,7 +1346,8 @@ void ExpectOperationCounts(const int expected_moves,
tracker->ResetCopiesMovesSwaps();
}
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+ defined(ABSL_HAVE_HWADDRESS_SANITIZER)
constexpr bool kAsan = true;
#else
constexpr bool kAsan = false;
@@ -2526,50 +2390,23 @@ TEST(Btree, TryEmplaceWithHintAndMultipleValueArgsWorks) {
EXPECT_EQ(std::string(10, 'a'), m[1]);
}
-TEST(Btree, MoveAssignmentAllocatorPropagation) {
- InstanceTracker tracker;
-
- int64_t bytes1 = 0, bytes2 = 0;
- PropagatingCountingAlloc<MovableOnlyInstance> allocator1(&bytes1);
- PropagatingCountingAlloc<MovableOnlyInstance> allocator2(&bytes2);
- std::less<MovableOnlyInstance> cmp;
+template <typename Alloc>
+using BtreeSetAlloc = absl::btree_set<int, std::less<int>, Alloc>;
- // Test propagating allocator_type.
- {
- absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
- PropagatingCountingAlloc<MovableOnlyInstance>>
- set1(cmp, allocator1), set2(cmp, allocator2);
-
- for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
+TEST(Btree, AllocatorPropagation) {
+ TestAllocPropagation<BtreeSetAlloc>();
+}
- tracker.ResetCopiesMovesSwaps();
- set2 = std::move(set1);
- EXPECT_EQ(tracker.moves(), 0);
- }
- // Test non-propagating allocator_type with equal allocators.
- {
- absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
- CountingAllocator<MovableOnlyInstance>>
- set1(cmp, allocator1), set2(cmp, allocator1);
+TEST(Btree, MinimumAlignmentAllocator) {
+ absl::btree_set<int8_t, std::less<int8_t>, MinimumAlignmentAlloc<int8_t>> set;
- for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
+ // Do some basic operations. Test that everything is fine when allocator uses
+ // minimal alignment.
+ for (int8_t i = 0; i < 100; ++i) set.insert(i);
+ set.erase(set.find(50), set.end());
+ for (int8_t i = 51; i < 101; ++i) set.insert(i);
- tracker.ResetCopiesMovesSwaps();
- set2 = std::move(set1);
- EXPECT_EQ(tracker.moves(), 0);
- }
- // Test non-propagating allocator_type with different allocators.
- {
- absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
- CountingAllocator<MovableOnlyInstance>>
- set1(cmp, allocator1), set2(cmp, allocator2);
-
- for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
-
- tracker.ResetCopiesMovesSwaps();
- set2 = std::move(set1);
- EXPECT_GE(tracker.moves(), 100);
- }
+ EXPECT_EQ(set.size(), 100);
}
TEST(Btree, EmptyTree) {
@@ -2965,6 +2802,20 @@ TYPED_TEST(BtreeMultiKeyTest, Count) {
EXPECT_EQ(set.count(2), 2);
}
+TEST(Btree, SetIteratorsAreConst) {
+ using Set = absl::btree_set<int>;
+ EXPECT_TRUE(
+ (std::is_same<typename Set::iterator::reference, const int &>::value));
+ EXPECT_TRUE(
+ (std::is_same<typename Set::iterator::pointer, const int *>::value));
+
+ using MSet = absl::btree_multiset<int>;
+ EXPECT_TRUE(
+ (std::is_same<typename MSet::iterator::reference, const int &>::value));
+ EXPECT_TRUE(
+ (std::is_same<typename MSet::iterator::pointer, const int *>::value));
+}
+
TEST(Btree, AllocConstructor) {
using Alloc = CountingAllocator<int>;
using Set = absl::btree_set<int, std::less<int>, Alloc>;
@@ -3229,10 +3080,10 @@ TEST(Btree, InvalidIteratorUse) {
if (!BtreeGenerationsEnabled())
GTEST_SKIP() << "Generation validation for iterators is disabled.";
- // Invalid memory use can trigger heap-use-after-free in ASan or invalidated
- // iterator assertions.
+ // Invalid memory use can trigger use-after-free in ASan, HWASAN or
+ // invalidated iterator assertions.
constexpr const char *kInvalidMemoryDeathMessage =
- "heap-use-after-free|invalidated iterator";
+ "use-after-free|invalidated iterator";
{
absl::btree_set<int> set;
@@ -3561,12 +3412,12 @@ TEST(Btree, InvalidPointerUse) {
set.insert(0);
const int *ptr = &*set.begin();
set.insert(1);
- EXPECT_DEATH(std::cout << *ptr, "heap-use-after-free");
+ EXPECT_DEATH(std::cout << *ptr, "use-after-free");
size_t slots_per_node = BtreeNodePeer::GetNumSlotsPerNode<decltype(set)>();
for (int i = 2; i < slots_per_node - 1; ++i) set.insert(i);
ptr = &*set.begin();
set.insert(static_cast<int>(slots_per_node));
- EXPECT_DEATH(std::cout << *ptr, "heap-use-after-free");
+ EXPECT_DEATH(std::cout << *ptr, "use-after-free");
}
template<typename Set>
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc
index 9dbf2a84..2421b5fc 100644
--- a/absl/container/fixed_array_test.cc
+++ b/absl/container/fixed_array_test.cc
@@ -30,7 +30,7 @@
#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/container/internal/test_allocator.h"
#include "absl/hash/hash_testing.h"
#include "absl/memory/memory.h"
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h
index 8f4d9939..acd013b0 100644
--- a/absl/container/flat_hash_map.h
+++ b/absl/container/flat_hash_map.h
@@ -64,7 +64,7 @@ struct FlatHashMapPolicy;
// `insert()`, provided that the map is provided a compatible heterogeneous
// hashing function and equality operator.
// * Invalidates any references and pointers to elements within the table after
-// `rehash()`.
+// `rehash()` and when the table is moved.
// * Contains a `capacity()` member function indicating the number of element
// slots (open, deleted, and empty) within the hash map.
// * Returns `void` from the `erase(iterator)` overload.
@@ -579,9 +579,9 @@ struct FlatHashMapPolicy {
}
template <class Allocator>
- static void transfer(Allocator* alloc, slot_type* new_slot,
+ static auto transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) {
- slot_policy::transfer(alloc, new_slot, old_slot);
+ return slot_policy::transfer(alloc, new_slot, old_slot);
}
template <class F, class... Args>
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
index e6acbea2..d90fe9d5 100644
--- a/absl/container/flat_hash_map_test.cc
+++ b/absl/container/flat_hash_map_test.cc
@@ -14,14 +14,20 @@
#include "absl/container/flat_hash_map.h"
+#include <cstddef>
#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "gtest/gtest.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"
#include "absl/container/internal/unordered_map_members_test.h"
#include "absl/container/internal/unordered_map_modifiers_test.h"
#include "absl/log/check.h"
+#include "absl/meta/type_traits.h"
#include "absl/types/any.h"
namespace absl {
@@ -102,6 +108,34 @@ TEST(FlatHashMap, StandardLayout) {
}
}
+TEST(FlatHashMap, Relocatability) {
+ static_assert(absl::is_trivially_relocatable<int>::value, "");
+ static_assert(
+ absl::is_trivially_relocatable<std::pair<const int, int>>::value, "");
+ static_assert(
+ std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
+ int, int>::transfer<std::allocator<char>>(nullptr,
+ nullptr,
+ nullptr)),
+ std::true_type>::value,
+ "");
+
+ struct NonRelocatable {
+ NonRelocatable() = default;
+ NonRelocatable(NonRelocatable&&) {}
+ NonRelocatable& operator=(NonRelocatable&&) { return *this; }
+ void* self = nullptr;
+ };
+
+ EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
+ EXPECT_TRUE(
+ (std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
+ int, NonRelocatable>::
+ transfer<std::allocator<char>>(nullptr, nullptr,
+ nullptr)),
+ std::false_type>::value));
+}
+
// gcc becomes unhappy if this is inside the method, so pull it out here.
struct balast {};
@@ -150,9 +184,7 @@ struct Hash {
struct Eq {
using is_transparent = void;
- bool operator()(size_t lhs, size_t rhs) const {
- return lhs == rhs;
- }
+ bool operator()(size_t lhs, size_t rhs) const { return lhs == rhs; }
bool operator()(size_t lhs, const LazyInt& rhs) const {
return lhs == rhs.value;
}
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index c789c7ef..a94a82a0 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -60,7 +60,7 @@ struct FlatHashSetPolicy;
// that the set is provided a compatible heterogeneous hashing function and
// equality operator.
// * Invalidates any references and pointers to elements within the table after
-// `rehash()`.
+// `rehash()` and when the table is moved.
// * Contains a `capacity()` member function indicating the number of element
// slots (open, deleted, and empty) within the hash set.
// * Returns `void` from the `erase(iterator)` overload.
diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc
index 20130f91..a60b4bf5 100644
--- a/absl/container/flat_hash_set_test.cc
+++ b/absl/container/flat_hash_set_test.cc
@@ -14,8 +14,15 @@
#include "absl/container/flat_hash_set.h"
+#include <cstdint>
+#include <memory>
+#include <utility>
#include <vector>
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/container/internal/container_memory.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"
@@ -172,6 +179,64 @@ TEST(FlatHashSet, EraseIf) {
}
}
+class PoisonInline {
+ int64_t data_;
+
+ public:
+ explicit PoisonInline(int64_t d) : data_(d) {
+ SanitizerPoisonObject(&data_);
+ }
+ PoisonInline(const PoisonInline& that) : PoisonInline(*that) {}
+ ~PoisonInline() { SanitizerUnpoisonObject(&data_); }
+
+ int64_t operator*() const {
+ SanitizerUnpoisonObject(&data_);
+ const int64_t ret = data_;
+ SanitizerPoisonObject(&data_);
+ return ret;
+ }
+ template <typename H>
+ friend H AbslHashValue(H h, const PoisonInline& pi) {
+ return H::combine(std::move(h), *pi);
+ }
+ bool operator==(const PoisonInline& rhs) const { return **this == *rhs; }
+};
+
+// Tests that we don't touch the poison_ member of PoisonInline.
+TEST(FlatHashSet, PoisonInline) {
+ PoisonInline a(0), b(1);
+ { // basic usage
+ flat_hash_set<PoisonInline> set;
+ set.insert(a);
+ EXPECT_THAT(set, UnorderedElementsAre(a));
+ set.insert(b);
+ EXPECT_THAT(set, UnorderedElementsAre(a, b));
+ set.erase(a);
+ EXPECT_THAT(set, UnorderedElementsAre(b));
+ set.rehash(0); // shrink to inline
+ EXPECT_THAT(set, UnorderedElementsAre(b));
+ }
+ { // test move constructor from inline to inline
+ flat_hash_set<PoisonInline> set;
+ set.insert(a);
+ flat_hash_set<PoisonInline> set2(std::move(set));
+ EXPECT_THAT(set2, UnorderedElementsAre(a));
+ }
+ { // test move assignment from inline to inline
+ flat_hash_set<PoisonInline> set, set2;
+ set.insert(a);
+ set2 = std::move(set);
+ EXPECT_THAT(set2, UnorderedElementsAre(a));
+ }
+ { // test alloc move constructor from inline to inline
+ flat_hash_set<PoisonInline> set;
+ set.insert(a);
+ flat_hash_set<PoisonInline> set2(std::move(set),
+ std::allocator<PoisonInline>());
+ EXPECT_THAT(set2, UnorderedElementsAre(a));
+ }
+}
+
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index b9a79f5b..241389ae 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -33,7 +33,7 @@
#include "absl/base/internal/exception_testing.h"
#include "absl/base/macros.h"
#include "absl/base/options.h"
-#include "absl/container/internal/counting_allocator.h"
+#include "absl/container/internal/test_allocator.h"
#include "absl/container/internal/test_instance_tracker.h"
#include "absl/hash/hash_testing.h"
#include "absl/log/check.h"
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h
index 569faa07..91df57a3 100644
--- a/absl/container/internal/btree.h
+++ b/absl/container/internal/btree.h
@@ -79,6 +79,7 @@ namespace container_internal {
#ifdef ABSL_BTREE_ENABLE_GENERATIONS
#error ABSL_BTREE_ENABLE_GENERATIONS cannot be directly set
#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+ defined(ABSL_HAVE_HWADDRESS_SANITIZER) || \
defined(ABSL_HAVE_MEMORY_SANITIZER)
// When compiled in sanitizer mode, we add generation integers to the nodes and
// iterators. When iterators are used, we validate that the container has not
@@ -572,13 +573,6 @@ class btree_node {
btree_node(btree_node const &) = delete;
btree_node &operator=(btree_node const &) = delete;
- // Public for EmptyNodeType.
- constexpr static size_type Alignment() {
- static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(),
- "Alignment of all nodes must be equal.");
- return InternalLayout().Alignment();
- }
-
protected:
btree_node() = default;
@@ -653,6 +647,12 @@ class btree_node {
return InternalLayout().AllocSize();
}
+ constexpr static size_type Alignment() {
+ static_assert(LeafLayout(1).Alignment() == InternalLayout().Alignment(),
+ "Alignment of all nodes must be equal.");
+ return InternalLayout().Alignment();
+ }
+
// N is the index of the type in the Layout definition.
// ElementType<N> is the Nth type in the Layout definition.
template <size_type N>
@@ -1122,8 +1122,11 @@ class btree_iterator : private btree_iterator_generation_info {
using const_reference = typename params_type::const_reference;
using slot_type = typename params_type::slot_type;
- using iterator =
- btree_iterator<normal_node, normal_reference, normal_pointer>;
+ // In sets, all iterators are const.
+ using iterator = absl::conditional_t<
+ is_map_container::value,
+ btree_iterator<normal_node, normal_reference, normal_pointer>,
+ btree_iterator<normal_node, const_reference, const_pointer>>;
using const_iterator =
btree_iterator<const_node, const_reference, const_pointer>;
@@ -1318,7 +1321,7 @@ class btree {
// We use a static empty node for the root/leftmost/rightmost of empty btrees
// in order to avoid branching in begin()/end().
- struct alignas(node_type::Alignment()) EmptyNodeType : node_type {
+ struct EmptyNodeType : node_type {
using field_type = typename node_type::field_type;
node_type *parent;
#ifdef ABSL_BTREE_ENABLE_GENERATIONS
@@ -1331,25 +1334,12 @@ class btree {
// as a leaf node). max_count() is never called when the tree is empty.
field_type max_count = node_type::kInternalNodeMaxCount + 1;
-#ifdef _MSC_VER
- // MSVC has constexpr code generations bugs here.
- EmptyNodeType() : parent(this) {}
-#else
- explicit constexpr EmptyNodeType(node_type *p) : parent(p) {}
-#endif
+ constexpr EmptyNodeType() : parent(this) {}
};
static node_type *EmptyNode() {
-#ifdef _MSC_VER
- static EmptyNodeType *empty_node = new EmptyNodeType;
- // This assert fails on some other construction methods.
- assert(empty_node->parent == empty_node);
- return empty_node;
-#else
- static constexpr EmptyNodeType empty_node(
- const_cast<EmptyNodeType *>(&empty_node));
+ alignas(node_type::Alignment()) static constexpr EmptyNodeType empty_node;
return const_cast<EmptyNodeType *>(&empty_node);
-#endif
}
enum : uint32_t {
@@ -2420,7 +2410,7 @@ auto btree<P>::operator=(btree &&other) noexcept -> btree & {
using std::swap;
if (absl::allocator_traits<
- allocator_type>::propagate_on_container_copy_assignment::value) {
+ allocator_type>::propagate_on_container_move_assignment::value) {
swap(root_, other.root_);
// Note: `rightmost_` also contains the allocator and the key comparator.
swap(rightmost_, other.rightmost_);
@@ -2534,6 +2524,10 @@ auto btree<P>::rebalance_after_delete(iterator iter) -> iterator {
return res;
}
+// Note: we tried implementing this more efficiently by erasing all of the
+// elements in [begin, end) at once and then doing rebalancing once at the end
+// (rather than interleaving deletion and rebalancing), but that adds a lot of
+// complexity, which seems to outweigh the performance win.
template <typename P>
auto btree<P>::erase_range(iterator begin, iterator end)
-> std::pair<size_type, iterator> {
@@ -2863,7 +2857,8 @@ inline auto btree<P>::internal_emplace(iterator iter, Args &&...args)
}
}
(void)replaced_node;
-#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+ defined(ABSL_HAVE_HWADDRESS_SANITIZER)
if (!replaced_node) {
assert(iter.node_->is_leaf());
if (iter.node_->is_root()) {
diff --git a/absl/container/internal/common_policy_traits.h b/absl/container/internal/common_policy_traits.h
index 3558a543..57eac678 100644
--- a/absl/container/internal/common_policy_traits.h
+++ b/absl/container/internal/common_policy_traits.h
@@ -93,11 +93,13 @@ struct common_policy_traits {
struct Rank0 : Rank1 {};
// Use auto -> decltype as an enabler.
+ // P::transfer returns std::true_type if transfer uses memcpy (e.g. in
+ // node_slot_policy).
template <class Alloc, class P = Policy>
static auto transfer_impl(Alloc* alloc, slot_type* new_slot,
slot_type* old_slot, Rank0)
- -> decltype((void)P::transfer(alloc, new_slot, old_slot)) {
- P::transfer(alloc, new_slot, old_slot);
+ -> decltype(P::transfer(alloc, new_slot, old_slot)) {
+ return P::transfer(alloc, new_slot, old_slot);
}
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
// This overload returns true_type for the trait below.
diff --git a/absl/container/internal/common_policy_traits_test.cc b/absl/container/internal/common_policy_traits_test.cc
index 5eaa4aae..faee3e7a 100644
--- a/absl/container/internal/common_policy_traits_test.cc
+++ b/absl/container/internal/common_policy_traits_test.cc
@@ -16,10 +16,12 @@
#include <functional>
#include <memory>
-#include <new>
+#include <type_traits>
+#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -51,9 +53,14 @@ std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::element;
struct PolicyWithOptionalOps : PolicyWithoutOptionalOps {
static std::function<void(void*, Slot*, Slot*)> transfer;
};
-
std::function<void(void*, Slot*, Slot*)> PolicyWithOptionalOps::transfer;
+struct PolicyWithMemcpyTransfer : PolicyWithoutOptionalOps {
+ static std::function<std::true_type(void*, Slot*, Slot*)> transfer;
+};
+std::function<std::true_type(void*, Slot*, Slot*)>
+ PolicyWithMemcpyTransfer::transfer;
+
struct Test : ::testing::Test {
Test() {
PolicyWithoutOptionalOps::construct = [&](void* a1, Slot* a2, Slot a3) {
@@ -114,6 +121,13 @@ TEST_F(Test, with_transfer) {
common_policy_traits<PolicyWithOptionalOps>::transfer(&alloc, &a, &b);
}
+TEST(TransferUsesMemcpy, Basic) {
+ EXPECT_FALSE(
+ common_policy_traits<PolicyWithOptionalOps>::transfer_uses_memcpy());
+ EXPECT_TRUE(
+ common_policy_traits<PolicyWithMemcpyTransfer>::transfer_uses_memcpy());
+}
+
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
index f59ca4ee..3262d4eb 100644
--- a/absl/container/internal/container_memory.h
+++ b/absl/container/internal/container_memory.h
@@ -122,10 +122,10 @@ auto TupleRefImpl(T&& t, absl::index_sequence<Is...>)
// Returns a tuple of references to the elements of the input tuple. T must be a
// tuple.
template <class T>
-auto TupleRef(T&& t) -> decltype(
- TupleRefImpl(std::forward<T>(t),
- absl::make_index_sequence<
- std::tuple_size<typename std::decay<T>::type>::value>())) {
+auto TupleRef(T&& t) -> decltype(TupleRefImpl(
+ std::forward<T>(t),
+ absl::make_index_sequence<
+ std::tuple_size<typename std::decay<T>::type>::value>())) {
return TupleRefImpl(
std::forward<T>(t),
absl::make_index_sequence<
@@ -156,8 +156,8 @@ void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) {
// Constructs T using the args specified in the tuple and calls F with the
// constructed value.
template <class T, class Tuple, class F>
-decltype(std::declval<F>()(std::declval<T>())) WithConstructed(
- Tuple&& t, F&& f) {
+decltype(std::declval<F>()(std::declval<T>())) WithConstructed(Tuple&& t,
+ F&& f) {
return memory_internal::WithConstructedImpl<T>(
std::forward<Tuple>(t),
absl::make_index_sequence<
@@ -423,16 +423,19 @@ struct map_slot_policy {
}
template <class Allocator>
- static void transfer(Allocator* alloc, slot_type* new_slot,
+ static auto transfer(Allocator* alloc, slot_type* new_slot,
slot_type* old_slot) {
+ auto is_relocatable =
+ typename absl::is_trivially_relocatable<value_type>::type();
+
emplace(new_slot);
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
- if (absl::is_trivially_relocatable<value_type>()) {
+ if (is_relocatable) {
// TODO(b/247130232,b/251814870): remove casts after fixing warnings.
std::memcpy(static_cast<void*>(std::launder(&new_slot->value)),
static_cast<const void*>(&old_slot->value),
sizeof(value_type));
- return;
+ return is_relocatable;
}
#endif
@@ -444,6 +447,7 @@ struct map_slot_policy {
std::move(old_slot->value));
}
destroy(alloc, old_slot);
+ return is_relocatable;
}
};
diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc
index fb9c4dde..90d64bf5 100644
--- a/absl/container/internal/container_memory_test.cc
+++ b/absl/container/internal/container_memory_test.cc
@@ -14,15 +14,20 @@
#include "absl/container/internal/container_memory.h"
+#include <cstddef>
#include <cstdint>
+#include <memory>
#include <tuple>
+#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/no_destructor.h"
#include "absl/container/internal/test_instance_tracker.h"
+#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
namespace absl {
@@ -54,7 +59,7 @@ TEST(Memory, AlignmentSmallerThanBase) {
}
std::map<std::type_index, int>& AllocationMap() {
- static auto* map = new std::map<std::type_index, int>;
+ static absl::NoDestructor<std::map<std::type_index, int>> map;
return *map;
}
@@ -219,8 +224,7 @@ TEST(DecomposePair, NotDecomposable) {
ADD_FAILURE() << "Must not be called";
return 'A';
};
- EXPECT_STREQ("not decomposable",
- TryDecomposePair(f));
+ EXPECT_STREQ("not decomposable", TryDecomposePair(f));
EXPECT_STREQ("not decomposable",
TryDecomposePair(f, std::piecewise_construct, std::make_tuple(),
std::make_tuple(0.5)));
@@ -251,6 +255,31 @@ TEST(MapSlotPolicy, ConstKeyAndValue) {
EXPECT_EQ(tracker.copies(), 0);
}
+TEST(MapSlotPolicy, TransferReturnsTrue) {
+ {
+ using slot_policy = map_slot_policy<int, float>;
+ EXPECT_TRUE(
+ (std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
+ nullptr, nullptr, nullptr)),
+ std::true_type>::value));
+ }
+ {
+ struct NonRelocatable {
+ NonRelocatable() = default;
+ NonRelocatable(NonRelocatable&&) {}
+ NonRelocatable& operator=(NonRelocatable&&) { return *this; }
+ void* self = nullptr;
+ };
+
+ EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
+ using slot_policy = map_slot_policy<int, NonRelocatable>;
+ EXPECT_TRUE(
+ (std::is_same<decltype(slot_policy::transfer<std::allocator<char>>(
+ nullptr, nullptr, nullptr)),
+ std::false_type>::value));
+ }
+}
+
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END
diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h
deleted file mode 100644
index 66068a5a..00000000
--- a/absl/container/internal/counting_allocator.h
+++ /dev/null
@@ -1,122 +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_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
-#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
-
-#include <cstdint>
-#include <memory>
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace container_internal {
-
-// 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:
- 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() = 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)
- : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {}
-
- 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) {
- Allocator allocator;
- AllocatorTraits::deallocate(allocator, p, n);
- if (bytes_used_ != nullptr) {
- *bytes_used_ -= n * sizeof(T);
- }
- }
-
- 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;
- // Ignore GCC warning bug.
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wuse-after-free"
-#endif
- AllocatorTraits::destroy(allocator, p);
-#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
-#pragma GCC diagnostic pop
-#endif
- if (instance_count_ != nullptr) {
- *instance_count_ -= 1;
- }
- }
-
- template <typename U>
- class rebind {
- public:
- using other = CountingAllocator<U>;
- };
-
- friend bool operator==(const CountingAllocator& a,
- const CountingAllocator& b) {
- return a.bytes_used_ == b.bytes_used_ &&
- a.instance_count_ == b.instance_count_;
- }
-
- friend bool operator!=(const CountingAllocator& a,
- const CountingAllocator& b) {
- return !(a == b);
- }
-
- int64_t* bytes_used_ = nullptr;
- int64_t* instance_count_ = nullptr;
-};
-
-} // namespace container_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-#endif // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc
index 59cc5aac..e89dfdb5 100644
--- a/absl/container/internal/hash_generator_testing.cc
+++ b/absl/container/internal/hash_generator_testing.cc
@@ -16,6 +16,8 @@
#include <deque>
+#include "absl/base/no_destructor.h"
+
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace container_internal {
@@ -41,11 +43,11 @@ class RandomDeviceSeedSeq {
} // namespace
std::mt19937_64* GetSharedRng() {
- static auto* rng = [] {
+ static absl::NoDestructor<std::mt19937_64> rng([] {
RandomDeviceSeedSeq seed_seq;
- return new std::mt19937_64(seed_seq);
- }();
- return rng;
+ return std::mt19937_64(seed_seq);
+ }());
+ return rng.get();
}
std::string Generator<std::string>::operator()() const {
@@ -59,7 +61,7 @@ std::string Generator<std::string>::operator()() const {
}
absl::string_view Generator<absl::string_view>::operator()() const {
- static auto* arena = new std::deque<std::string>();
+ static absl::NoDestructor<std::deque<std::string>> arena;
// NOLINTNEXTLINE(runtime/int)
std::uniform_int_distribution<short> chars(0x20, 0x7E);
arena->emplace_back();
diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h
index 19d52121..c79c1a98 100644
--- a/absl/container/internal/hashtable_debug.h
+++ b/absl/container/internal/hashtable_debug.h
@@ -95,14 +95,6 @@ size_t AllocatedByteSize(const C& c) {
HashtableDebugAccess<C>::AllocatedByteSize(c);
}
-// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C`
-// and `c.size()` is equal to `num_elements`.
-template <typename C>
-size_t LowerBoundAllocatedByteSize(size_t num_elements) {
- return absl::container_internal::hashtable_debug_internal::
- HashtableDebugAccess<C>::LowerBoundAllocatedByteSize(num_elements);
-}
-
} // namespace container_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index d8fd8f34..e41ee2d7 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -137,18 +137,7 @@ class HashtablezInfoHandle {
UnsampleSlow(info_);
}
- HashtablezInfoHandle(const HashtablezInfoHandle&) = delete;
- HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete;
-
- HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept
- : info_(absl::exchange(o.info_, nullptr)) {}
- HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept {
- if (ABSL_PREDICT_FALSE(info_ != nullptr)) {
- UnsampleSlow(info_);
- }
- info_ = absl::exchange(o.info_, nullptr);
- return *this;
- }
+ inline bool IsSampled() const { return ABSL_PREDICT_FALSE(info_ != nullptr); }
inline void RecordStorageChanged(size_t size, size_t capacity) {
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
@@ -198,6 +187,7 @@ class HashtablezInfoHandle {
explicit HashtablezInfoHandle(std::nullptr_t) {}
inline void Unregister() {}
+ inline bool IsSampled() const { return false; }
inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {}
inline void RecordRehash(size_t /*total_probe_length*/) {}
inline void RecordReservation(size_t /*target_capacity*/) {}
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
index 665d518f..8ebb08da 100644
--- a/absl/container/internal/hashtablez_sampler_test.cc
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -42,16 +42,11 @@ namespace container_internal {
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
class HashtablezInfoHandlePeer {
public:
- static bool IsSampled(const HashtablezInfoHandle& h) {
- return h.info_ != nullptr;
- }
-
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)
@@ -267,7 +262,7 @@ TEST(HashtablezSamplerTest, Sample) {
for (int i = 0; i < 1000000; ++i) {
HashtablezInfoHandle h = Sample(test_element_size);
++total;
- if (HashtablezInfoHandlePeer::IsSampled(h)) {
+ if (h.IsSampled()) {
++num_sampled;
}
sample_rate = static_cast<double>(num_sampled) / total;
@@ -294,6 +289,7 @@ TEST(HashtablezSamplerTest, Handle) {
});
EXPECT_TRUE(found);
+ h.Unregister();
h = HashtablezInfoHandle();
found = false;
sampler.Iterate([&](const HashtablezInfo& h) {
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index b2a602db..0eb9c34d 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -26,6 +26,7 @@
#include <utility>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/container/internal/compressed_tuple.h"
#include "absl/memory/memory.h"
@@ -384,7 +385,17 @@ class Storage {
bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; }
- Pointer<A> GetAllocatedData() { return data_.allocated.allocated_data; }
+ Pointer<A> GetAllocatedData() {
+ // GCC 12 has a false-positive -Wmaybe-uninitialized warning here.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+ return data_.allocated.allocated_data;
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic pop
+#endif
+ }
ConstPointer<A> GetAllocatedData() const {
return data_.allocated.allocated_data;
diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h
index a59a2430..a4ba6101 100644
--- a/absl/container/internal/layout.h
+++ b/absl/container/internal/layout.h
@@ -55,7 +55,7 @@
// `Partial()` comes in handy when the array sizes are embedded into the
// allocation.
//
-// // size_t[1] containing N, size_t[1] containing M, double[N], int[M].
+// // size_t[0] containing N, size_t[1] containing M, double[N], int[M].
// using L = Layout<size_t, size_t, double, int>;
//
// unsigned char* Allocate(size_t n, size_t m) {
@@ -172,6 +172,7 @@
#include <utility>
#include "absl/base/config.h"
+#include "absl/debugging/internal/demangle.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
@@ -181,14 +182,6 @@
#include <sanitizer/asan_interface.h>
#endif
-#if defined(__GXX_RTTI)
-#define ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#endif
-
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
-#include <cxxabi.h>
-#endif
-
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace container_internal {
@@ -294,19 +287,11 @@ constexpr size_t Max(size_t a, size_t b, Ts... rest) {
template <class T>
std::string TypeName() {
std::string out;
- int status = 0;
- char* demangled = nullptr;
-#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE
- demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status);
-#endif
- if (status == 0 && demangled != nullptr) { // Demangling succeeded.
- absl::StrAppend(&out, "<", demangled, ">");
- free(demangled);
- } else {
-#if defined(__GXX_RTTI) || defined(_CPPRTTI)
- absl::StrAppend(&out, "<", typeid(T).name(), ">");
+#if ABSL_INTERNAL_HAS_RTTI
+ absl::StrAppend(&out, "<",
+ absl::debugging_internal::DemangleString(typeid(T).name()),
+ ">");
#endif
- }
return out;
}
diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc
index ce599ce7..ae55cf7e 100644
--- a/absl/container/internal/layout_test.cc
+++ b/absl/container/internal/layout_test.cc
@@ -19,8 +19,12 @@
#include <stddef.h>
#include <cstdint>
+#include <cstring>
+#include <initializer_list>
#include <memory>
-#include <sstream>
+#include <ostream>
+#include <string>
+#include <tuple>
#include <type_traits>
#include "gmock/gmock.h"
@@ -28,6 +32,7 @@
#include "absl/base/config.h"
#include "absl/log/check.h"
#include "absl/types/span.h"
+#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
diff --git a/absl/container/internal/node_slot_policy.h b/absl/container/internal/node_slot_policy.h
index baba5743..3f1874d4 100644
--- a/absl/container/internal/node_slot_policy.h
+++ b/absl/container/internal/node_slot_policy.h
@@ -62,9 +62,12 @@ struct node_slot_policy {
Policy::delete_element(alloc, *slot);
}
+ // Returns true_type to indicate that transfer can use memcpy.
template <class Alloc>
- static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) {
+ static std::true_type transfer(Alloc*, slot_type* new_slot,
+ slot_type* old_slot) {
*new_slot = *old_slot;
+ return {};
}
static size_t space_used(const slot_type* slot) {
diff --git a/absl/container/internal/node_slot_policy_test.cc b/absl/container/internal/node_slot_policy_test.cc
index 51b7467b..d4ea919d 100644
--- a/absl/container/internal/node_slot_policy_test.cc
+++ b/absl/container/internal/node_slot_policy_test.cc
@@ -18,6 +18,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
#include "absl/container/internal/hash_policy_traits.h"
namespace absl {
@@ -61,6 +62,7 @@ TEST_F(NodeTest, transfer) {
int* b = &s;
NodePolicy::transfer(&alloc, &a, &b);
EXPECT_EQ(&s, a);
+ EXPECT_TRUE(NodePolicy::transfer_uses_memcpy());
}
} // namespace
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index 2d5a8710..97182bc7 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -19,6 +19,8 @@
#include <type_traits>
#include <utility>
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
@@ -175,13 +177,20 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> {
template <class K = key_type, class P = Policy, K* = nullptr>
MappedReference<P> operator[](key_arg<K>&& key)
ABSL_ATTRIBUTE_LIFETIME_BOUND {
- return Policy::value(&*try_emplace(std::forward<K>(key)).first);
+ // It is safe to use unchecked_deref here because try_emplace
+ // will always return an iterator pointing to a valid item in the table,
+ // since it inserts if nothing is found for the given key.
+ return Policy::value(
+ &this->unchecked_deref(try_emplace(std::forward<K>(key)).first));
}
template <class K = key_type, class P = Policy>
MappedReference<P> operator[](const key_arg<K>& key)
ABSL_ATTRIBUTE_LIFETIME_BOUND {
- return Policy::value(&*try_emplace(key).first);
+ // It is safe to use unchecked_deref here because try_emplace
+ // will always return an iterator pointing to a valid item in the table,
+ // since it inserts if nothing is found for the given key.
+ return Policy::value(&this->unchecked_deref(try_emplace(key).first));
}
private:
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index 2ff95b61..9f8ea519 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -17,11 +17,13 @@
#include <atomic>
#include <cassert>
#include <cstddef>
+#include <cstdint>
#include <cstring>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/dynamic_annotations.h"
+#include "absl/container/internal/container_memory.h"
#include "absl/hash/hash.h"
namespace absl {
@@ -67,6 +69,16 @@ inline size_t RandomSeed() {
return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
}
+bool ShouldRehashForBugDetection(const ctrl_t* ctrl, size_t capacity) {
+ // Note: we can't use the abseil-random library because abseil-random
+ // depends on swisstable. We want to return true with probability
+ // `min(1, RehashProbabilityConstant() / capacity())`. In order to do this,
+ // we probe based on a random hash and see if the offset is less than
+ // RehashProbabilityConstant().
+ return probe(ctrl, capacity, absl::HashOf(RandomSeed())).offset() <
+ RehashProbabilityConstant();
+}
+
} // namespace
GenerationType* EmptyGeneration() {
@@ -84,13 +96,12 @@ bool CommonFieldsGenerationInfoEnabled::
size_t capacity) const {
if (reserved_growth_ == kReservedGrowthJustRanOut) return true;
if (reserved_growth_ > 0) return false;
- // Note: we can't use the abseil-random library because abseil-random
- // depends on swisstable. We want to return true with probability
- // `min(1, RehashProbabilityConstant() / capacity())`. In order to do this,
- // we probe based on a random hash and see if the offset is less than
- // RehashProbabilityConstant().
- return probe(ctrl, capacity, absl::HashOf(RandomSeed())).offset() <
- RehashProbabilityConstant();
+ return ShouldRehashForBugDetection(ctrl, capacity);
+}
+
+bool CommonFieldsGenerationInfoEnabled::should_rehash_for_bug_detection_on_move(
+ const ctrl_t* ctrl, size_t capacity) const {
+ return ShouldRehashForBugDetection(ctrl, capacity);
}
bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) {
@@ -117,14 +128,6 @@ FindInfo find_first_non_full_outofline(const CommonFields& common,
return find_first_non_full(common, hash);
}
-// Returns the address of the ith slot in slots where each slot occupies
-// slot_size.
-static inline void* SlotAddress(void* slot_array, size_t slot,
- size_t slot_size) {
- return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot_array) +
- (slot * slot_size));
-}
-
// Returns the address of the slot just after slot assuming each slot has the
// specified size.
static inline void* NextSlot(void* slot, size_t slot_size) {
@@ -218,26 +221,35 @@ void DropDeletesWithoutResize(CommonFields& common,
common.infoz().RecordRehash(total_probe_length);
}
-void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size) {
- assert(IsFull(*it) && "erasing a dangling iterator");
- c.set_size(c.size() - 1);
- const auto index = static_cast<size_t>(it - c.control());
+static bool WasNeverFull(CommonFields& c, size_t index) {
+ if (is_single_group(c.capacity())) {
+ return true;
+ }
const size_t index_before = (index - Group::kWidth) & c.capacity();
- const auto empty_after = Group(it).MaskEmpty();
+ const auto empty_after = Group(c.control() + index).MaskEmpty();
const auto empty_before = Group(c.control() + index_before).MaskEmpty();
// We count how many consecutive non empties we have to the right and to the
// left of `it`. If the sum is >= kWidth then there is at least one probe
// window that might have seen a full group.
- bool was_never_full = empty_before && empty_after &&
- static_cast<size_t>(empty_after.TrailingZeros()) +
- empty_before.LeadingZeros() <
- Group::kWidth;
-
- SetCtrl(c, index, was_never_full ? ctrl_t::kEmpty : ctrl_t::kDeleted,
- slot_size);
- c.set_growth_left(c.growth_left() + (was_never_full ? 1 : 0));
+ return empty_before && empty_after &&
+ static_cast<size_t>(empty_after.TrailingZeros()) +
+ empty_before.LeadingZeros() <
+ Group::kWidth;
+}
+
+void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size) {
+ assert(IsFull(c.control()[index]) && "erasing a dangling iterator");
+ c.decrement_size();
c.infoz().RecordErase();
+
+ if (WasNeverFull(c, index)) {
+ SetCtrl(c, index, ctrl_t::kEmpty, slot_size);
+ c.set_growth_left(c.growth_left() + 1);
+ return;
+ }
+
+ SetCtrl(c, index, ctrl_t::kDeleted, slot_size);
}
void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
@@ -245,19 +257,124 @@ void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
c.set_size(0);
if (reuse) {
ResetCtrl(c, policy.slot_size);
+ ResetGrowthLeft(c);
c.infoz().RecordStorageChanged(0, c.capacity());
} else {
+ // We need to record infoz before calling dealloc, which will unregister
+ // infoz.
+ c.infoz().RecordClearedReservation();
+ c.infoz().RecordStorageChanged(0, 0);
(*policy.dealloc)(c, policy);
c.set_control(EmptyGroup());
c.set_generation_ptr(EmptyGeneration());
c.set_slots(nullptr);
c.set_capacity(0);
- c.infoz().RecordClearedReservation();
- assert(c.size() == 0);
- c.infoz().RecordStorageChanged(0, 0);
}
}
+void HashSetResizeHelper::GrowIntoSingleGroupShuffleControlBytes(
+ ctrl_t* new_ctrl, size_t new_capacity) const {
+ assert(is_single_group(new_capacity));
+ constexpr size_t kHalfWidth = Group::kWidth / 2;
+ assert(old_capacity_ < kHalfWidth);
+
+ const size_t half_old_capacity = old_capacity_ / 2;
+
+ // NOTE: operations are done with compile time known size = kHalfWidth.
+ // Compiler optimizes that into single ASM operation.
+
+ // Copy second half of bytes to the beginning.
+ // We potentially copy more bytes in order to have compile time known size.
+ // Mirrored bytes from the old_ctrl_ will also be copied.
+ // In case of old_capacity_ == 3, we will copy 1st element twice.
+ // Examples:
+ // old_ctrl = 0S0EEEEEEE...
+ // new_ctrl = S0EEEEEEEE...
+ //
+ // old_ctrl = 01S01EEEEE...
+ // new_ctrl = 1S01EEEEEE...
+ //
+ // old_ctrl = 0123456S0123456EE...
+ // new_ctrl = 456S0123?????????...
+ std::memcpy(new_ctrl, old_ctrl_ + half_old_capacity + 1, kHalfWidth);
+ // Clean up copied kSentinel from old_ctrl.
+ new_ctrl[half_old_capacity] = ctrl_t::kEmpty;
+
+ // Clean up damaged or uninitialized bytes.
+
+ // Clean bytes after the intended size of the copy.
+ // Example:
+ // new_ctrl = 1E01EEEEEEE????
+ // *new_ctrl= 1E0EEEEEEEE????
+ // position /
+ std::memset(new_ctrl + old_capacity_ + 1, static_cast<int8_t>(ctrl_t::kEmpty),
+ kHalfWidth);
+ // Clean non-mirrored bytes that are not initialized.
+ // For small old_capacity that may be inside of mirrored bytes zone.
+ // Examples:
+ // new_ctrl = 1E0EEEEEEEE??????????....
+ // *new_ctrl= 1E0EEEEEEEEEEEEE?????....
+ // position /
+ //
+ // new_ctrl = 456E0123???????????...
+ // *new_ctrl= 456E0123EEEEEEEE???...
+ // position /
+ std::memset(new_ctrl + kHalfWidth, static_cast<int8_t>(ctrl_t::kEmpty),
+ kHalfWidth);
+ // Clean last mirrored bytes that are not initialized
+ // and will not be overwritten by mirroring.
+ // Examples:
+ // new_ctrl = 1E0EEEEEEEEEEEEE????????
+ // *new_ctrl= 1E0EEEEEEEEEEEEEEEEEEEEE
+ // position S /
+ //
+ // new_ctrl = 456E0123EEEEEEEE???????????????
+ // *new_ctrl= 456E0123EEEEEEEE???????EEEEEEEE
+ // position S /
+ std::memset(new_ctrl + new_capacity + kHalfWidth,
+ static_cast<int8_t>(ctrl_t::kEmpty), kHalfWidth);
+
+ // Create mirrored bytes. old_capacity_ < kHalfWidth
+ // Example:
+ // new_ctrl = 456E0123EEEEEEEE???????EEEEEEEE
+ // *new_ctrl= 456E0123EEEEEEEE456E0123EEEEEEE
+ // position S/
+ ctrl_t g[kHalfWidth];
+ std::memcpy(g, new_ctrl, kHalfWidth);
+ std::memcpy(new_ctrl + new_capacity + 1, g, kHalfWidth);
+
+ // Finally set sentinel to its place.
+ new_ctrl[new_capacity] = ctrl_t::kSentinel;
+}
+
+void HashSetResizeHelper::GrowIntoSingleGroupShuffleTransferableSlots(
+ void* old_slots, void* new_slots, size_t slot_size) const {
+ assert(old_capacity_ > 0);
+ const size_t half_old_capacity = old_capacity_ / 2;
+
+ SanitizerUnpoisonMemoryRegion(old_slots, slot_size * old_capacity_);
+ std::memcpy(new_slots,
+ SlotAddress(old_slots, half_old_capacity + 1, slot_size),
+ slot_size * half_old_capacity);
+ std::memcpy(SlotAddress(new_slots, half_old_capacity + 1, slot_size),
+ old_slots, slot_size * (half_old_capacity + 1));
+}
+
+void HashSetResizeHelper::GrowSizeIntoSingleGroupTransferable(
+ CommonFields& c, void* old_slots, size_t slot_size) {
+ assert(old_capacity_ < Group::kWidth / 2);
+ assert(is_single_group(c.capacity()));
+ assert(IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity()));
+
+ GrowIntoSingleGroupShuffleControlBytes(c.control(), c.capacity());
+ GrowIntoSingleGroupShuffleTransferableSlots(old_slots, c.slot_array(),
+ slot_size);
+
+ // We poison since GrowIntoSingleGroupShuffleTransferableSlots
+ // may leave empty slots unpoisoned.
+ PoisonSingleGroupEmptySlots(c, slot_size);
+}
+
} // namespace container_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 5f89d8ef..3518bc34 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -62,6 +62,9 @@
// pseudo-struct:
//
// struct BackingArray {
+// // Sampling handler. This field isn't present when the sampling is
+// // disabled or this allocation hasn't been selected for sampling.
+// HashtablezInfoHandle infoz_;
// // The number of elements we can insert before growing the capacity.
// size_t growth_left;
// // Control bytes for the "real" slots.
@@ -175,25 +178,29 @@
#define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
#include <algorithm>
+#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
+#include <initializer_list>
#include <iterator>
#include <limits>
#include <memory>
-#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
+#include "absl/base/attributes.h"
#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/base/optimization.h"
+#include "absl/base/options.h"
#include "absl/base/port.h"
#include "absl/base/prefetch.h"
-#include "absl/container/internal/common.h"
+#include "absl/container/internal/common.h" // IWYU pragma: export // for node_handle
#include "absl/container/internal/compressed_tuple.h"
#include "absl/container/internal/container_memory.h"
#include "absl/container/internal/hash_policy_traits.h"
@@ -227,6 +234,7 @@ namespace container_internal {
#ifdef ABSL_SWISSTABLE_ENABLE_GENERATIONS
#error ABSL_SWISSTABLE_ENABLE_GENERATIONS cannot be directly set
#elif defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+ defined(ABSL_HAVE_HWADDRESS_SANITIZER) || \
defined(ABSL_HAVE_MEMORY_SANITIZER)
// When compiled in sanitizer mode, we add generation integers to the backing
// array and iterators. In the backing array, we store the generation between
@@ -262,8 +270,21 @@ void SwapAlloc(AllocType& lhs, AllocType& rhs,
swap(lhs, rhs);
}
template <typename AllocType>
-void SwapAlloc(AllocType& /*lhs*/, AllocType& /*rhs*/,
- std::false_type /* propagate_on_container_swap */) {}
+void SwapAlloc(AllocType& lhs, AllocType& rhs,
+ std::false_type /* propagate_on_container_swap */) {
+ (void)lhs;
+ (void)rhs;
+ assert(lhs == rhs &&
+ "It's UB to call swap with unequal non-propagating allocators.");
+}
+
+template <typename AllocType>
+void CopyAlloc(AllocType& lhs, AllocType& rhs,
+ std::true_type /* propagate_alloc */) {
+ lhs = rhs;
+}
+template <typename AllocType>
+void CopyAlloc(AllocType&, AllocType&, std::false_type /* propagate_alloc */) {}
// The state for a probe sequence.
//
@@ -361,7 +382,7 @@ uint32_t TrailingZeros(T x) {
// width of an abstract bit in the representation.
// This mask provides operations for any number of real bits set in an abstract
// bit. To add iteration on top of that, implementation must guarantee no more
-// than one real bit is set in an abstract bit.
+// than the most significant real bit is set in a set abstract bit.
template <class T, int SignificantBits, int Shift = 0>
class NonIterableBitMask {
public:
@@ -388,7 +409,9 @@ class NonIterableBitMask {
uint32_t LeadingZeros() const {
constexpr int total_significant_bits = SignificantBits << Shift;
constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
- return static_cast<uint32_t>(countl_zero(mask_ << extra_bits)) >> Shift;
+ return static_cast<uint32_t>(
+ countl_zero(static_cast<T>(mask_ << extra_bits))) >>
+ Shift;
}
T mask_;
@@ -418,6 +441,10 @@ class BitMask : public NonIterableBitMask<T, SignificantBits, Shift> {
using const_iterator = BitMask;
BitMask& operator++() {
+ if (Shift == 3) {
+ constexpr uint64_t msbs = 0x8080808080808080ULL;
+ this->mask_ &= msbs;
+ }
this->mask_ &= (this->mask_ - 1);
return *this;
}
@@ -590,29 +617,39 @@ struct GroupSse2Impl {
}
// Returns a bitmask representing the positions of slots that match hash.
- BitMask<uint32_t, kWidth> Match(h2_t hash) const {
+ BitMask<uint16_t, kWidth> Match(h2_t hash) const {
auto match = _mm_set1_epi8(static_cast<char>(hash));
- return BitMask<uint32_t, kWidth>(
- static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
+ BitMask<uint16_t, kWidth> result = BitMask<uint16_t, kWidth>(0);
+ result = BitMask<uint16_t, kWidth>(
+ static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
+ return result;
}
// Returns a bitmask representing the positions of empty slots.
- NonIterableBitMask<uint32_t, kWidth> MaskEmpty() const {
+ NonIterableBitMask<uint16_t, kWidth> MaskEmpty() const {
#ifdef ABSL_INTERNAL_HAVE_SSSE3
// This only works because ctrl_t::kEmpty is -128.
- return NonIterableBitMask<uint32_t, kWidth>(
- static_cast<uint32_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))));
+ return NonIterableBitMask<uint16_t, kWidth>(
+ static_cast<uint16_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))));
#else
auto match = _mm_set1_epi8(static_cast<char>(ctrl_t::kEmpty));
- return NonIterableBitMask<uint32_t, kWidth>(
- static_cast<uint32_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
+ return NonIterableBitMask<uint16_t, kWidth>(
+ static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
#endif
}
+ // Returns a bitmask representing the positions of full slots.
+ // Note: for `is_small()` tables group may contain the "same" slot twice:
+ // original and mirrored.
+ BitMask<uint16_t, kWidth> MaskFull() const {
+ return BitMask<uint16_t, kWidth>(
+ static_cast<uint16_t>(_mm_movemask_epi8(ctrl) ^ 0xffff));
+ }
+
// Returns a bitmask representing the positions of empty or deleted slots.
- NonIterableBitMask<uint32_t, kWidth> MaskEmptyOrDeleted() const {
+ NonIterableBitMask<uint16_t, kWidth> MaskEmptyOrDeleted() const {
auto special = _mm_set1_epi8(static_cast<char>(ctrl_t::kSentinel));
- return NonIterableBitMask<uint32_t, kWidth>(static_cast<uint32_t>(
+ return NonIterableBitMask<uint16_t, kWidth>(static_cast<uint16_t>(
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))));
}
@@ -651,9 +688,8 @@ struct GroupAArch64Impl {
BitMask<uint64_t, kWidth, 3> Match(h2_t hash) const {
uint8x8_t dup = vdup_n_u8(hash);
auto mask = vceq_u8(ctrl, dup);
- constexpr uint64_t msbs = 0x8080808080808080ULL;
return BitMask<uint64_t, kWidth, 3>(
- vget_lane_u64(vreinterpret_u64_u8(mask), 0) & msbs);
+ vget_lane_u64(vreinterpret_u64_u8(mask), 0));
}
NonIterableBitMask<uint64_t, kWidth, 3> MaskEmpty() const {
@@ -665,6 +701,17 @@ struct GroupAArch64Impl {
return NonIterableBitMask<uint64_t, kWidth, 3>(mask);
}
+ // Returns a bitmask representing the positions of full slots.
+ // Note: for `is_small()` tables group may contain the "same" slot twice:
+ // original and mirrored.
+ BitMask<uint64_t, kWidth, 3> MaskFull() const {
+ uint64_t mask = vget_lane_u64(
+ vreinterpret_u64_u8(vcge_s8(vreinterpret_s8_u8(ctrl),
+ vdup_n_s8(static_cast<int8_t>(0)))),
+ 0);
+ return BitMask<uint64_t, kWidth, 3>(mask);
+ }
+
NonIterableBitMask<uint64_t, kWidth, 3> MaskEmptyOrDeleted() const {
uint64_t mask =
vget_lane_u64(vreinterpret_u64_u8(vcgt_s8(
@@ -729,13 +776,21 @@ struct GroupPortableImpl {
NonIterableBitMask<uint64_t, kWidth, 3> MaskEmpty() const {
constexpr uint64_t msbs = 0x8080808080808080ULL;
- return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 6)) &
+ return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & ~(ctrl << 6)) &
msbs);
}
+ // Returns a bitmask representing the positions of full slots.
+ // Note: for `is_small()` tables group may contain the "same" slot twice:
+ // original and mirrored.
+ BitMask<uint64_t, kWidth, 3> MaskFull() const {
+ constexpr uint64_t msbs = 0x8080808080808080ULL;
+ return BitMask<uint64_t, kWidth, 3>((ctrl ^ msbs) & msbs);
+ }
+
NonIterableBitMask<uint64_t, kWidth, 3> MaskEmptyOrDeleted() const {
constexpr uint64_t msbs = 0x8080808080808080ULL;
- return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & (~ctrl << 7)) &
+ return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & ~(ctrl << 7)) &
msbs);
}
@@ -760,10 +815,21 @@ struct GroupPortableImpl {
#ifdef ABSL_INTERNAL_HAVE_SSE2
using Group = GroupSse2Impl;
+using GroupEmptyOrDeleted = GroupSse2Impl;
#elif defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN)
using Group = GroupAArch64Impl;
+// For Aarch64, we use the portable implementation for counting and masking
+// empty or deleted group elements. This is to avoid the latency of moving
+// between data GPRs and Neon registers when it does not provide a benefit.
+// Using Neon is profitable when we call Match(), but is not when we don't,
+// which is the case when we do *EmptyOrDeleted operations. It is difficult to
+// make a similar approach beneficial on other architectures such as x86 since
+// they have much lower GPR <-> vector register transfer latency and 16-wide
+// Groups.
+using GroupEmptyOrDeleted = GroupPortableImpl;
#else
using Group = GroupPortableImpl;
+using GroupEmptyOrDeleted = GroupPortableImpl;
#endif
// When there is an insertion with no reserved growth, we rehash with
@@ -802,15 +868,19 @@ class CommonFieldsGenerationInfoEnabled {
// whenever reserved_growth_ is zero.
bool should_rehash_for_bug_detection_on_insert(const ctrl_t* ctrl,
size_t capacity) const;
+ // Similar to above, except that we don't depend on reserved_growth_.
+ bool should_rehash_for_bug_detection_on_move(const ctrl_t* ctrl,
+ size_t capacity) const;
void maybe_increment_generation_on_insert() {
if (reserved_growth_ == kReservedGrowthJustRanOut) reserved_growth_ = 0;
if (reserved_growth_ > 0) {
if (--reserved_growth_ == 0) reserved_growth_ = kReservedGrowthJustRanOut;
} else {
- *generation_ = NextGeneration(*generation_);
+ increment_generation();
}
}
+ void increment_generation() { *generation_ = NextGeneration(*generation_); }
void reset_reserved_growth(size_t reservation, size_t size) {
reserved_growth_ = reservation - size;
}
@@ -856,7 +926,11 @@ class CommonFieldsGenerationInfoDisabled {
bool should_rehash_for_bug_detection_on_insert(const ctrl_t*, size_t) const {
return false;
}
+ bool should_rehash_for_bug_detection_on_move(const ctrl_t*, size_t) const {
+ return false;
+ }
void maybe_increment_generation_on_insert() {}
+ void increment_generation() {}
void reset_reserved_growth(size_t, size_t) {}
size_t reserved_growth() const { return 0; }
void set_reserved_growth(size_t) {}
@@ -909,9 +983,11 @@ using HashSetIteratorGenerationInfo = HashSetIteratorGenerationInfoDisabled;
// A valid capacity is a non-zero integer `2^m - 1`.
inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
-// Computes the offset from the start of the backing allocation of the control
-// bytes. growth_left is stored at the beginning of the backing array.
-inline size_t ControlOffset() { return sizeof(size_t); }
+// Computes the offset from the start of the backing allocation of control.
+// infoz and growth_left are stored at the beginning of the backing array.
+inline size_t ControlOffset(bool has_infoz) {
+ return (has_infoz ? sizeof(HashtablezInfoHandle) : 0) + sizeof(size_t);
+}
// Returns the number of "cloned control bytes".
//
@@ -922,24 +998,26 @@ constexpr size_t NumClonedBytes() { return Group::kWidth - 1; }
// Given the capacity of a table, computes the offset (from the start of the
// backing allocation) of the generation counter (if it exists).
-inline size_t GenerationOffset(size_t capacity) {
+inline size_t GenerationOffset(size_t capacity, bool has_infoz) {
assert(IsValidCapacity(capacity));
const size_t num_control_bytes = capacity + 1 + NumClonedBytes();
- return ControlOffset() + num_control_bytes;
+ return ControlOffset(has_infoz) + num_control_bytes;
}
// Given the capacity of a table, computes the offset (from the start of the
// backing allocation) at which the slots begin.
-inline size_t SlotOffset(size_t capacity, size_t slot_align) {
+inline size_t SlotOffset(size_t capacity, size_t slot_align, bool has_infoz) {
assert(IsValidCapacity(capacity));
- return (GenerationOffset(capacity) + NumGenerationBytes() + slot_align - 1) &
+ return (GenerationOffset(capacity, has_infoz) + NumGenerationBytes() +
+ slot_align - 1) &
(~slot_align + 1);
}
// Given the capacity of a table, computes the total size of the backing
// array.
-inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align) {
- return SlotOffset(capacity, slot_align) + capacity * slot_size;
+inline size_t AllocSize(size_t capacity, size_t slot_size, size_t slot_align,
+ bool has_infoz) {
+ return SlotOffset(capacity, slot_align, has_infoz) + capacity * slot_size;
}
// CommonFields hold the fields in raw_hash_set that do not depend
@@ -954,28 +1032,15 @@ class CommonFields : public CommonFieldsGenerationInfo {
CommonFields& operator=(const CommonFields&) = delete;
// Movable
- CommonFields(CommonFields&& that)
- : CommonFieldsGenerationInfo(
- std::move(static_cast<CommonFieldsGenerationInfo&&>(that))),
- // Explicitly copying fields into "this" and then resetting "that"
- // fields generates less code then calling absl::exchange per field.
- control_(that.control()),
- slots_(that.slot_array()),
- capacity_(that.capacity()),
- compressed_tuple_(that.size(), std::move(that.infoz())) {
- that.set_control(EmptyGroup());
- that.set_slots(nullptr);
- that.set_capacity(0);
- that.set_size(0);
- }
+ CommonFields(CommonFields&& that) = default;
CommonFields& operator=(CommonFields&&) = default;
ctrl_t* control() const { return control_; }
void set_control(ctrl_t* c) { control_ = c; }
void* backing_array_start() const {
- // growth_left is stored before control bytes.
+ // growth_left (and maybe infoz) is stored before control bytes.
assert(reinterpret_cast<uintptr_t>(control()) % alignof(size_t) == 0);
- return control() - sizeof(size_t);
+ return control() - ControlOffset(has_infoz());
}
// Note: we can't use slots() because Qt defines "slots" as a macro.
@@ -983,8 +1048,18 @@ class CommonFields : public CommonFieldsGenerationInfo {
void set_slots(void* s) { slots_ = s; }
// The number of filled slots.
- size_t size() const { return compressed_tuple_.template get<0>(); }
- void set_size(size_t s) { compressed_tuple_.template get<0>() = s; }
+ size_t size() const { return size_ >> HasInfozShift(); }
+ void set_size(size_t s) {
+ size_ = (s << HasInfozShift()) | (size_ & HasInfozMask());
+ }
+ void increment_size() {
+ assert(size() < capacity());
+ size_ += size_t{1} << HasInfozShift();
+ }
+ void decrement_size() {
+ assert(size() > 0);
+ size_ -= size_t{1} << HasInfozShift();
+ }
// The total number of available slots.
size_t capacity() const { return capacity_; }
@@ -996,28 +1071,52 @@ class CommonFields : public CommonFieldsGenerationInfo {
// The number of slots we can still fill without needing to rehash.
// This is stored in the heap allocation before the control bytes.
size_t growth_left() const {
- return *reinterpret_cast<size_t*>(backing_array_start());
+ const size_t* gl_ptr = reinterpret_cast<size_t*>(control()) - 1;
+ assert(reinterpret_cast<uintptr_t>(gl_ptr) % alignof(size_t) == 0);
+ return *gl_ptr;
}
void set_growth_left(size_t gl) {
- *reinterpret_cast<size_t*>(backing_array_start()) = gl;
+ size_t* gl_ptr = reinterpret_cast<size_t*>(control()) - 1;
+ assert(reinterpret_cast<uintptr_t>(gl_ptr) % alignof(size_t) == 0);
+ *gl_ptr = gl;
}
- HashtablezInfoHandle& infoz() { return compressed_tuple_.template get<1>(); }
- const HashtablezInfoHandle& infoz() const {
- return compressed_tuple_.template get<1>();
+ bool has_infoz() const {
+ return ABSL_PREDICT_FALSE((size_ & HasInfozMask()) != 0);
+ }
+ void set_has_infoz(bool has_infoz) {
+ size_ = (size() << HasInfozShift()) | static_cast<size_t>(has_infoz);
+ }
+
+ HashtablezInfoHandle infoz() {
+ return has_infoz()
+ ? *reinterpret_cast<HashtablezInfoHandle*>(backing_array_start())
+ : HashtablezInfoHandle();
+ }
+ void set_infoz(HashtablezInfoHandle infoz) {
+ assert(has_infoz());
+ *reinterpret_cast<HashtablezInfoHandle*>(backing_array_start()) = infoz;
}
bool should_rehash_for_bug_detection_on_insert() const {
return CommonFieldsGenerationInfo::
should_rehash_for_bug_detection_on_insert(control(), capacity());
}
+ bool should_rehash_for_bug_detection_on_move() const {
+ return CommonFieldsGenerationInfo::
+ should_rehash_for_bug_detection_on_move(control(), capacity());
+ }
+ void maybe_increment_generation_on_move() {
+ if (capacity() == 0) return;
+ increment_generation();
+ }
void reset_reserved_growth(size_t reservation) {
CommonFieldsGenerationInfo::reset_reserved_growth(reservation, size());
}
// The size of the backing array allocation.
size_t alloc_size(size_t slot_size, size_t slot_align) const {
- return AllocSize(capacity(), slot_size, slot_align);
+ return AllocSize(capacity(), slot_size, slot_align, has_infoz());
}
// Returns the number of control bytes set to kDeleted. For testing only.
@@ -1027,9 +1126,14 @@ class CommonFields : public CommonFieldsGenerationInfo {
}
private:
- // TODO(b/259599413): Investigate removing some of these fields:
+ // We store the has_infoz bit in the lowest bit of size_.
+ static constexpr size_t HasInfozShift() { return 1; }
+ static constexpr size_t HasInfozMask() {
+ return (size_t{1} << HasInfozShift()) - 1;
+ }
+
+ // TODO(b/182800944): Investigate removing some of these fields:
// - control/slots can be derived from each other
- // - we can use 6 bits for capacity since it's always a power of two minus 1
// The control bytes (and, also, a pointer near to the base of the backing
// array).
@@ -1044,12 +1148,16 @@ class CommonFields : public CommonFieldsGenerationInfo {
// `control`. May be null for empty tables.
void* slots_ = nullptr;
+ // The number of slots in the backing array. This is always 2^N-1 for an
+ // integer N. NOTE: we tried experimenting with compressing the capacity and
+ // storing it together with size_: (a) using 6 bits to store the corresponding
+ // power (N in 2^N-1), and (b) storing 2^N as the most significant bit of
+ // size_ and storing size in the low bits. Both of these experiments were
+ // regressions, presumably because we need capacity to do find operations.
size_t capacity_ = 0;
- // Bundle together size and HashtablezInfoHandle to ensure EBO for
- // HashtablezInfoHandle when sampling is turned off.
- absl::container_internal::CompressedTuple<size_t, HashtablezInfoHandle>
- compressed_tuple_{0u, HashtablezInfoHandle{}};
+ // The size and also has one bit that stores whether we have infoz.
+ size_t size_ = 0;
};
template <class Policy, class Hash, class Eq, class Alloc>
@@ -1139,35 +1247,39 @@ inline void AssertIsFull(const ctrl_t* ctrl, GenerationType generation,
const GenerationType* generation_ptr,
const char* operation) {
if (!SwisstableDebugEnabled()) return;
- if (ctrl == nullptr) {
- ABSL_INTERNAL_LOG(FATAL,
- std::string(operation) + " called on end() iterator.");
- }
- if (ctrl == EmptyGroup()) {
- ABSL_INTERNAL_LOG(FATAL, std::string(operation) +
- " called on default-constructed iterator.");
+ // `SwisstableDebugEnabled()` is also true for release builds with hardening
+ // enabled. To minimize their impact in those builds:
+ // - use `ABSL_PREDICT_FALSE()` to provide a compiler hint for code layout
+ // - use `ABSL_RAW_LOG()` with a format string to reduce code size and improve
+ // the chances that the hot paths will be inlined.
+ if (ABSL_PREDICT_FALSE(ctrl == nullptr)) {
+ ABSL_RAW_LOG(FATAL, "%s called on end() iterator.", operation);
+ }
+ if (ABSL_PREDICT_FALSE(ctrl == EmptyGroup())) {
+ ABSL_RAW_LOG(FATAL, "%s called on default-constructed iterator.",
+ operation);
}
if (SwisstableGenerationsEnabled()) {
- if (generation != *generation_ptr) {
- ABSL_INTERNAL_LOG(FATAL,
- std::string(operation) +
- " called on invalid iterator. The table could have "
- "rehashed since this iterator was initialized.");
+ if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) {
+ ABSL_RAW_LOG(FATAL,
+ "%s called on invalid iterator. The table could have "
+ "rehashed or moved since this iterator was initialized.",
+ operation);
}
- if (!IsFull(*ctrl)) {
- ABSL_INTERNAL_LOG(
+ if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) {
+ ABSL_RAW_LOG(
FATAL,
- std::string(operation) +
- " called on invalid iterator. The element was likely erased.");
+ "%s called on invalid iterator. The element was likely erased.",
+ operation);
}
} else {
- if (!IsFull(*ctrl)) {
- ABSL_INTERNAL_LOG(
+ if (ABSL_PREDICT_FALSE(!IsFull(*ctrl))) {
+ ABSL_RAW_LOG(
FATAL,
- std::string(operation) +
- " called on invalid iterator. The element might have been erased "
- "or the table might have rehashed. Consider running with "
- "--config=asan to diagnose rehashing issues.");
+ "%s called on invalid iterator. The element might have been erased "
+ "or the table might have rehashed. Consider running with "
+ "--config=asan to diagnose rehashing issues.",
+ operation);
}
}
}
@@ -1180,13 +1292,13 @@ inline void AssertIsValidForComparison(const ctrl_t* ctrl,
const bool ctrl_is_valid_for_comparison =
ctrl == nullptr || ctrl == EmptyGroup() || IsFull(*ctrl);
if (SwisstableGenerationsEnabled()) {
- if (generation != *generation_ptr) {
- ABSL_INTERNAL_LOG(FATAL,
- "Invalid iterator comparison. The table could have "
- "rehashed since this iterator was initialized.");
+ if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) {
+ ABSL_RAW_LOG(FATAL,
+ "Invalid iterator comparison. The table could have rehashed "
+ "or moved since this iterator was initialized.");
}
- if (!ctrl_is_valid_for_comparison) {
- ABSL_INTERNAL_LOG(
+ if (ABSL_PREDICT_FALSE(!ctrl_is_valid_for_comparison)) {
+ ABSL_RAW_LOG(
FATAL, "Invalid iterator comparison. The element was likely erased.");
}
} else {
@@ -1226,10 +1338,15 @@ inline void AssertSameContainer(const ctrl_t* ctrl_a, const ctrl_t* ctrl_b,
const GenerationType* generation_ptr_a,
const GenerationType* generation_ptr_b) {
if (!SwisstableDebugEnabled()) return;
+ // `SwisstableDebugEnabled()` is also true for release builds with hardening
+ // enabled. To minimize their impact in those builds:
+ // - use `ABSL_PREDICT_FALSE()` to provide a compiler hint for code layout
+ // - use `ABSL_RAW_LOG()` with a format string to reduce code size and improve
+ // the chances that the hot paths will be inlined.
const bool a_is_default = ctrl_a == EmptyGroup();
const bool b_is_default = ctrl_b == EmptyGroup();
- if (a_is_default != b_is_default) {
- ABSL_INTERNAL_LOG(
+ if (ABSL_PREDICT_FALSE(a_is_default != b_is_default)) {
+ ABSL_RAW_LOG(
FATAL,
"Invalid iterator comparison. Comparing default-constructed iterator "
"with non-default-constructed iterator.");
@@ -1237,36 +1354,36 @@ inline void AssertSameContainer(const ctrl_t* ctrl_a, const ctrl_t* ctrl_b,
if (a_is_default && b_is_default) return;
if (SwisstableGenerationsEnabled()) {
- if (generation_ptr_a == generation_ptr_b) return;
+ if (ABSL_PREDICT_TRUE(generation_ptr_a == generation_ptr_b)) return;
const bool a_is_empty = IsEmptyGeneration(generation_ptr_a);
const bool b_is_empty = IsEmptyGeneration(generation_ptr_b);
if (a_is_empty != b_is_empty) {
- ABSL_INTERNAL_LOG(FATAL,
- "Invalid iterator comparison. Comparing iterator from "
- "a non-empty hashtable with an iterator from an empty "
- "hashtable.");
+ ABSL_RAW_LOG(FATAL,
+ "Invalid iterator comparison. Comparing iterator from a "
+ "non-empty hashtable with an iterator from an empty "
+ "hashtable.");
}
if (a_is_empty && b_is_empty) {
- ABSL_INTERNAL_LOG(FATAL,
- "Invalid iterator comparison. Comparing iterators from "
- "different empty hashtables.");
+ ABSL_RAW_LOG(FATAL,
+ "Invalid iterator comparison. Comparing iterators from "
+ "different empty hashtables.");
}
const bool a_is_end = ctrl_a == nullptr;
const bool b_is_end = ctrl_b == nullptr;
if (a_is_end || b_is_end) {
- ABSL_INTERNAL_LOG(FATAL,
- "Invalid iterator comparison. Comparing iterator with "
- "an end() iterator from a different hashtable.");
+ ABSL_RAW_LOG(FATAL,
+ "Invalid iterator comparison. Comparing iterator with an "
+ "end() iterator from a different hashtable.");
}
- ABSL_INTERNAL_LOG(FATAL,
- "Invalid iterator comparison. Comparing non-end() "
- "iterators from different hashtables.");
+ ABSL_RAW_LOG(FATAL,
+ "Invalid iterator comparison. Comparing non-end() iterators "
+ "from different hashtables.");
} else {
ABSL_HARDENING_ASSERT(
AreItersFromSameContainer(ctrl_a, ctrl_b, slot_a, slot_b) &&
"Invalid iterator comparison. The iterators may be from different "
- "containers or the container might have rehashed. Consider running "
- "with --config=asan to diagnose rehashing issues.");
+ "containers or the container might have rehashed or moved. Consider "
+ "running with --config=asan to diagnose issues.");
}
}
@@ -1289,6 +1406,12 @@ struct FindInfo {
// `ShouldInsertBackwards()` for small tables.
inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
+// Whether a table fits entirely into a probing group.
+// Arbitrary order of elements in such tables is correct.
+inline bool is_single_group(size_t capacity) {
+ return capacity <= Group::kWidth;
+}
+
// Begins a probing operation on `common.control`, using `hash`.
inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, const size_t capacity,
size_t hash) {
@@ -1310,7 +1433,7 @@ inline FindInfo find_first_non_full(const CommonFields& common, size_t hash) {
auto seq = probe(common, hash);
const ctrl_t* ctrl = common.control();
while (true) {
- Group g{ctrl + seq.offset()};
+ GroupEmptyOrDeleted g{ctrl + seq.offset()};
auto mask = g.MaskEmptyOrDeleted();
if (mask) {
#if !defined(NDEBUG)
@@ -1351,7 +1474,6 @@ inline void ResetCtrl(CommonFields& common, size_t slot_size) {
capacity + 1 + NumClonedBytes());
ctrl[capacity] = ctrl_t::kSentinel;
SanitizerPoisonMemoryRegion(common.slot_array(), slot_size * capacity);
- ResetGrowthLeft(common);
}
// Sets `ctrl[i]` to `h`.
@@ -1386,38 +1508,263 @@ constexpr size_t BackingArrayAlignment(size_t align_of_slot) {
return (std::max)(align_of_slot, alignof(size_t));
}
-template <typename Alloc, size_t SizeOfSlot, size_t AlignOfSlot>
-ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c, Alloc alloc) {
- assert(c.capacity());
- // Folks with custom allocators often make unwarranted assumptions about the
- // behavior of their classes vis-a-vis trivial destructability and what
- // calls they will or won't make. Avoid sampling for people with custom
- // allocators to get us out of this mess. This is not a hard guarantee but
- // a workaround while we plan the exact guarantee we want to provide.
- const size_t sample_size =
- (std::is_same<Alloc, std::allocator<char>>::value &&
- c.slot_array() == nullptr)
- ? SizeOfSlot
- : 0;
-
- const size_t cap = c.capacity();
- const size_t alloc_size = AllocSize(cap, SizeOfSlot, AlignOfSlot);
- // growth_left (which is a size_t) is stored with the backing array.
- char* mem = static_cast<char*>(
- Allocate<BackingArrayAlignment(AlignOfSlot)>(&alloc, alloc_size));
- const GenerationType old_generation = c.generation();
- c.set_generation_ptr(
- reinterpret_cast<GenerationType*>(mem + GenerationOffset(cap)));
- c.set_generation(NextGeneration(old_generation));
- c.set_control(reinterpret_cast<ctrl_t*>(mem + ControlOffset()));
- c.set_slots(mem + SlotOffset(cap, AlignOfSlot));
- ResetCtrl(c, SizeOfSlot);
- if (sample_size) {
- c.infoz() = Sample(sample_size);
- }
- c.infoz().RecordStorageChanged(c.size(), cap);
+// Returns the address of the ith slot in slots where each slot occupies
+// slot_size.
+inline void* SlotAddress(void* slot_array, size_t slot, size_t slot_size) {
+ return reinterpret_cast<void*>(reinterpret_cast<char*>(slot_array) +
+ (slot * slot_size));
}
+// Helper class to perform resize of the hash set.
+//
+// It contains special optimizations for small group resizes.
+// See GrowIntoSingleGroupShuffleControlBytes for details.
+class HashSetResizeHelper {
+ public:
+ explicit HashSetResizeHelper(CommonFields& c)
+ : old_ctrl_(c.control()),
+ old_capacity_(c.capacity()),
+ had_infoz_(c.has_infoz()) {}
+
+ // Optimized for small groups version of `find_first_non_full` applicable
+ // only right after calling `raw_hash_set::resize`.
+ // It has implicit assumption that `resize` will call
+ // `GrowSizeIntoSingleGroup*` in case `IsGrowingIntoSingleGroupApplicable`.
+ // Falls back to `find_first_non_full` in case of big groups, so it is
+ // safe to use after `rehash_and_grow_if_necessary`.
+ static FindInfo FindFirstNonFullAfterResize(const CommonFields& c,
+ size_t old_capacity,
+ size_t hash) {
+ if (!IsGrowingIntoSingleGroupApplicable(old_capacity, c.capacity())) {
+ return find_first_non_full(c, hash);
+ }
+ // Find a location for the new element non-deterministically.
+ // Note that any position is correct.
+ // It will located at `half_old_capacity` or one of the other
+ // empty slots with approximately 50% probability each.
+ size_t offset = probe(c, hash).offset();
+
+ // Note that we intentionally use unsigned int underflow.
+ if (offset - (old_capacity + 1) >= old_capacity) {
+ // Offset fall on kSentinel or into the mostly occupied first half.
+ offset = old_capacity / 2;
+ }
+ assert(IsEmpty(c.control()[offset]));
+ return FindInfo{offset, 0};
+ }
+
+ ctrl_t* old_ctrl() const { return old_ctrl_; }
+ size_t old_capacity() const { return old_capacity_; }
+
+ // Allocates a backing array for the hashtable.
+ // Reads `capacity` and updates all other fields based on the result of
+ // the allocation.
+ //
+ // It also may do the folowing actions:
+ // 1. initialize control bytes
+ // 2. initialize slots
+ // 3. deallocate old slots.
+ //
+ // We are bundling a lot of functionality
+ // in one ABSL_ATTRIBUTE_NOINLINE function in order to minimize binary code
+ // duplication in raw_hash_set<>::resize.
+ //
+ // `c.capacity()` must be nonzero.
+ // POSTCONDITIONS:
+ // 1. CommonFields is initialized.
+ //
+ // if IsGrowingIntoSingleGroupApplicable && TransferUsesMemcpy
+ // Both control bytes and slots are fully initialized.
+ // old_slots are deallocated.
+ // infoz.RecordRehash is called.
+ //
+ // if IsGrowingIntoSingleGroupApplicable && !TransferUsesMemcpy
+ // Control bytes are fully initialized.
+ // infoz.RecordRehash is called.
+ // GrowSizeIntoSingleGroup must be called to finish slots initialization.
+ //
+ // if !IsGrowingIntoSingleGroupApplicable
+ // Control bytes are initialized to empty table via ResetCtrl.
+ // raw_hash_set<>::resize must insert elements regularly.
+ // infoz.RecordRehash is called if old_capacity == 0.
+ //
+ // Returns IsGrowingIntoSingleGroupApplicable result to avoid recomputation.
+ template <typename Alloc, size_t SizeOfSlot, bool TransferUsesMemcpy,
+ size_t AlignOfSlot>
+ ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, void* old_slots,
+ Alloc alloc) {
+ assert(c.capacity());
+ // Folks with custom allocators often make unwarranted assumptions about the
+ // behavior of their classes vis-a-vis trivial destructability and what
+ // calls they will or won't make. Avoid sampling for people with custom
+ // allocators to get us out of this mess. This is not a hard guarantee but
+ // a workaround while we plan the exact guarantee we want to provide.
+ const size_t sample_size =
+ (std::is_same<Alloc, std::allocator<char>>::value &&
+ c.slot_array() == nullptr)
+ ? SizeOfSlot
+ : 0;
+ HashtablezInfoHandle infoz =
+ sample_size > 0 ? Sample(sample_size) : c.infoz();
+
+ const bool has_infoz = infoz.IsSampled();
+ const size_t cap = c.capacity();
+ const size_t alloc_size =
+ AllocSize(cap, SizeOfSlot, AlignOfSlot, has_infoz);
+ char* mem = static_cast<char*>(
+ Allocate<BackingArrayAlignment(AlignOfSlot)>(&alloc, alloc_size));
+ const GenerationType old_generation = c.generation();
+ c.set_generation_ptr(reinterpret_cast<GenerationType*>(
+ mem + GenerationOffset(cap, has_infoz)));
+ c.set_generation(NextGeneration(old_generation));
+ c.set_control(reinterpret_cast<ctrl_t*>(mem + ControlOffset(has_infoz)));
+ c.set_slots(mem + SlotOffset(cap, AlignOfSlot, has_infoz));
+ ResetGrowthLeft(c);
+
+ const bool grow_single_group =
+ IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity());
+ if (old_capacity_ != 0 && grow_single_group) {
+ if (TransferUsesMemcpy) {
+ GrowSizeIntoSingleGroupTransferable(c, old_slots, SizeOfSlot);
+ DeallocateOld<AlignOfSlot>(alloc, SizeOfSlot, old_slots);
+ } else {
+ GrowIntoSingleGroupShuffleControlBytes(c.control(), c.capacity());
+ }
+ } else {
+ ResetCtrl(c, SizeOfSlot);
+ }
+
+ c.set_has_infoz(has_infoz);
+ if (has_infoz) {
+ infoz.RecordStorageChanged(c.size(), cap);
+ if (grow_single_group || old_capacity_ == 0) {
+ infoz.RecordRehash(0);
+ }
+ c.set_infoz(infoz);
+ }
+ return grow_single_group;
+ }
+
+ // Relocates slots into new single group consistent with
+ // GrowIntoSingleGroupShuffleControlBytes.
+ //
+ // PRECONDITIONS:
+ // 1. GrowIntoSingleGroupShuffleControlBytes was already called.
+ template <class PolicyTraits, class Alloc>
+ void GrowSizeIntoSingleGroup(CommonFields& c, Alloc& alloc_ref,
+ typename PolicyTraits::slot_type* old_slots) {
+ assert(old_capacity_ < Group::kWidth / 2);
+ assert(IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity()));
+ using slot_type = typename PolicyTraits::slot_type;
+ assert(is_single_group(c.capacity()));
+
+ auto* new_slots = reinterpret_cast<slot_type*>(c.slot_array());
+
+ size_t shuffle_bit = old_capacity_ / 2 + 1;
+ for (size_t i = 0; i < old_capacity_; ++i) {
+ if (IsFull(old_ctrl_[i])) {
+ size_t new_i = i ^ shuffle_bit;
+ SanitizerUnpoisonMemoryRegion(new_slots + new_i, sizeof(slot_type));
+ PolicyTraits::transfer(&alloc_ref, new_slots + new_i, old_slots + i);
+ }
+ }
+ PoisonSingleGroupEmptySlots(c, sizeof(slot_type));
+ }
+
+ // Deallocates old backing array.
+ template <size_t AlignOfSlot, class CharAlloc>
+ void DeallocateOld(CharAlloc alloc_ref, size_t slot_size, void* old_slots) {
+ SanitizerUnpoisonMemoryRegion(old_slots, slot_size * old_capacity_);
+ Deallocate<BackingArrayAlignment(AlignOfSlot)>(
+ &alloc_ref, old_ctrl_ - ControlOffset(had_infoz_),
+ AllocSize(old_capacity_, slot_size, AlignOfSlot, had_infoz_));
+ }
+
+ private:
+ // Returns true if `GrowSizeIntoSingleGroup` can be used for resizing.
+ static bool IsGrowingIntoSingleGroupApplicable(size_t old_capacity,
+ size_t new_capacity) {
+ // NOTE that `old_capacity < new_capacity` in order to have
+ // `old_capacity < Group::kWidth / 2` to make faster copies of 8 bytes.
+ return is_single_group(new_capacity) && old_capacity < new_capacity;
+ }
+
+ // Relocates control bytes and slots into new single group for
+ // transferable objects.
+ // Must be called only if IsGrowingIntoSingleGroupApplicable returned true.
+ void GrowSizeIntoSingleGroupTransferable(CommonFields& c, void* old_slots,
+ size_t slot_size);
+
+ // Shuffle control bits deterministically to the next capacity.
+ // Returns offset for newly added element with given hash.
+ //
+ // PRECONDITIONs:
+ // 1. new_ctrl is allocated for new_capacity,
+ // but not initialized.
+ // 2. new_capacity is a single group.
+ //
+ // All elements are transferred into the first `old_capacity + 1` positions
+ // of the new_ctrl. Elements are rotated by `old_capacity_ / 2 + 1` positions
+ // in order to change an order and keep it non deterministic.
+ // Although rotation itself deterministic, position of the new added element
+ // will be based on `H1` and is not deterministic.
+ //
+ // Examples:
+ // S = kSentinel, E = kEmpty
+ //
+ // old_ctrl = SEEEEEEEE...
+ // new_ctrl = ESEEEEEEE...
+ //
+ // old_ctrl = 0SEEEEEEE...
+ // new_ctrl = E0ESE0EEE...
+ //
+ // old_ctrl = 012S012EEEEEEEEE...
+ // new_ctrl = 2E01EEES2E01EEE...
+ //
+ // old_ctrl = 0123456S0123456EEEEEEEEEEE...
+ // new_ctrl = 456E0123EEEEEES456E0123EEE...
+ void GrowIntoSingleGroupShuffleControlBytes(ctrl_t* new_ctrl,
+ size_t new_capacity) const;
+
+ // Shuffle trivially transferable slots in the way consistent with
+ // GrowIntoSingleGroupShuffleControlBytes.
+ //
+ // PRECONDITIONs:
+ // 1. old_capacity must be non-zero.
+ // 2. new_ctrl is fully initialized using
+ // GrowIntoSingleGroupShuffleControlBytes.
+ // 3. new_slots is allocated and *not* poisoned.
+ //
+ // POSTCONDITIONS:
+ // 1. new_slots are transferred from old_slots_ consistent with
+ // GrowIntoSingleGroupShuffleControlBytes.
+ // 2. Empty new_slots are *not* poisoned.
+ void GrowIntoSingleGroupShuffleTransferableSlots(void* old_slots,
+ void* new_slots,
+ size_t slot_size) const;
+
+ // Poison empty slots that were transferred using the deterministic algorithm
+ // described above.
+ // PRECONDITIONs:
+ // 1. new_ctrl is fully initialized using
+ // GrowIntoSingleGroupShuffleControlBytes.
+ // 2. new_slots is fully initialized consistent with
+ // GrowIntoSingleGroupShuffleControlBytes.
+ void PoisonSingleGroupEmptySlots(CommonFields& c, size_t slot_size) const {
+ // poison non full items
+ for (size_t i = 0; i < c.capacity(); ++i) {
+ if (!IsFull(c.control()[i])) {
+ SanitizerPoisonMemoryRegion(SlotAddress(c.slot_array(), i, slot_size),
+ slot_size);
+ }
+ }
+ }
+
+ ctrl_t* old_ctrl_;
+ size_t old_capacity_;
+ bool had_infoz_;
+};
+
// PolicyFunctions bundles together some information for a particular
// raw_hash_set<T, ...> instantiation. This information is passed to
// type-erased functions that want to do small amounts of type-specific
@@ -1442,7 +1789,7 @@ void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
bool reuse);
// Type-erased version of raw_hash_set::erase_meta_only.
-void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size);
+void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size);
// Function to place in PolicyFunctions::dealloc for raw_hash_sets
// that are using std::allocator. This allows us to share the same
@@ -1456,6 +1803,7 @@ ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard(CommonFields& common,
policy.slot_size * common.capacity());
std::allocator<char> alloc;
+ common.infoz().Unregister();
Deallocate<BackingArrayAlignment(AlignOfSlot)>(
&alloc, common.backing_array_start(),
common.alloc_size(policy.slot_size, AlignOfSlot));
@@ -1534,6 +1882,11 @@ class raw_hash_set {
using AllocTraits = absl::allocator_traits<allocator_type>;
using SlotAlloc = typename absl::allocator_traits<
allocator_type>::template rebind_alloc<slot_type>;
+ // People are often sloppy with the exact type of their allocator (sometimes
+ // it has an extra const or is missing the pair, but rebinds made it work
+ // anyway).
+ using CharAlloc =
+ typename absl::allocator_traits<Alloc>::template rebind_alloc<char>;
using SlotAllocTraits = typename absl::allocator_traits<
allocator_type>::template rebind_traits<slot_type>;
@@ -1590,7 +1943,7 @@ class raw_hash_set {
// PRECONDITION: not an end() iterator.
reference operator*() const {
AssertIsFull(ctrl_, generation(), generation_ptr(), "operator*()");
- return PolicyTraits::element(slot_);
+ return unchecked_deref();
}
// PRECONDITION: not an end() iterator.
@@ -1645,13 +1998,17 @@ class raw_hash_set {
// If a sentinel is reached, we null `ctrl_` out instead.
void skip_empty_or_deleted() {
while (IsEmptyOrDeleted(*ctrl_)) {
- uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted();
+ uint32_t shift =
+ GroupEmptyOrDeleted{ctrl_}.CountLeadingEmptyOrDeleted();
ctrl_ += shift;
slot_ += shift;
}
if (ABSL_PREDICT_FALSE(*ctrl_ == ctrl_t::kSentinel)) ctrl_ = nullptr;
}
+ ctrl_t* control() const { return ctrl_; }
+ slot_type* slot() const { return slot_; }
+
// We use EmptyGroup() for default-constructed iterators so that they can
// be distinguished from end iterators, which have nullptr ctrl_.
ctrl_t* ctrl_ = EmptyGroup();
@@ -1660,10 +2017,23 @@ class raw_hash_set {
union {
slot_type* slot_;
};
+
+ // An equality check which skips ABSL Hardening iterator invalidation
+ // checks.
+ // Should be used when the lifetimes of the iterators are well-enough
+ // understood to prove that they cannot be invalid.
+ bool unchecked_equals(const iterator& b) { return ctrl_ == b.control(); }
+
+ // Dereferences the iterator without ABSL Hardening iterator invalidation
+ // checks.
+ reference unchecked_deref() const { return PolicyTraits::element(slot_); }
};
class const_iterator {
friend class raw_hash_set;
+ template <class Container, typename Enabler>
+ friend struct absl::container_internal::hashtable_debug_internal::
+ HashtableDebugAccess;
public:
using iterator_category = typename iterator::iterator_category;
@@ -1697,8 +2067,14 @@ class raw_hash_set {
const GenerationType* gen)
: inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot), gen) {
}
+ ctrl_t* control() const { return inner_.control(); }
+ slot_type* slot() const { return inner_.slot(); }
iterator inner_;
+
+ bool unchecked_equals(const const_iterator& b) {
+ return inner_.unchecked_equals(b.inner_);
+ }
};
using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
@@ -1717,8 +2093,7 @@ class raw_hash_set {
const allocator_type& alloc = allocator_type())
: settings_(CommonFields{}, hash, eq, alloc) {
if (bucket_count) {
- common().set_capacity(NormalizeCapacity(bucket_count));
- initialize_slots();
+ resize(NormalizeCapacity(bucket_count));
}
}
@@ -1843,28 +2218,35 @@ class raw_hash_set {
: // 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_(absl::exchange(that.common(), CommonFields{}),
- that.hash_ref(), that.eq_ref(), that.alloc_ref()) {}
+ // TODO(b/296061262): move instead of copying hash/eq/alloc.
+ // Note: we avoid using exchange for better generated code.
+ settings_(std::move(that.common()), that.hash_ref(), that.eq_ref(),
+ that.alloc_ref()) {
+ that.common() = CommonFields{};
+ maybe_increment_generation_or_rehash_on_move();
+ }
raw_hash_set(raw_hash_set&& that, const allocator_type& a)
: settings_(CommonFields{}, that.hash_ref(), that.eq_ref(), a) {
if (a == that.alloc_ref()) {
std::swap(common(), that.common());
+ maybe_increment_generation_or_rehash_on_move();
} else {
- reserve(that.size());
- // Note: this will copy elements of dense_set and unordered_set instead of
- // moving them. This can be fixed if it ever becomes an issue.
- for (auto& elem : that) insert(std::move(elem));
+ move_elements_allocs_unequal(std::move(that));
}
}
raw_hash_set& operator=(const raw_hash_set& that) {
- raw_hash_set tmp(that,
- AllocTraits::propagate_on_container_copy_assignment::value
- ? that.alloc_ref()
- : alloc_ref());
- swap(tmp);
- return *this;
+ if (ABSL_PREDICT_FALSE(this == &that)) return *this;
+ constexpr bool propagate_alloc =
+ AllocTraits::propagate_on_container_copy_assignment::value;
+ // TODO(ezb): maybe avoid allocating a new backing array if this->capacity()
+ // is an exact match for that.size(). If this->capacity() is too big, then
+ // it would make iteration very slow to reuse the allocation. Maybe we can
+ // do the same heuristic as clear() and reuse if it's small enough.
+ raw_hash_set tmp(that, propagate_alloc ? that.alloc_ref() : alloc_ref());
+ // NOLINTNEXTLINE: not returning *this for performance.
+ return assign_impl<propagate_alloc>(std::move(tmp));
}
raw_hash_set& operator=(raw_hash_set&& that) noexcept(
@@ -1879,19 +2261,7 @@ class raw_hash_set {
typename AllocTraits::propagate_on_container_move_assignment());
}
- ~raw_hash_set() {
- const size_t cap = capacity();
- if (!cap) return;
- destroy_slots();
-
- // Unpoison before returning the memory to the allocator.
- SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * cap);
- Deallocate<BackingArrayAlignment(alignof(slot_type))>(
- &alloc_ref(), common().backing_array_start(),
- AllocSize(cap, sizeof(slot_type), alignof(slot_type)));
-
- infoz().Unregister();
- }
+ ~raw_hash_set() { destructor_impl(); }
iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
auto it = iterator_at(0);
@@ -1937,17 +2307,6 @@ class raw_hash_set {
common().set_reservation_size(0);
}
- inline void destroy_slots() {
- const size_t cap = capacity();
- const ctrl_t* ctrl = control();
- slot_type* slot = slot_array();
- for (size_t i = 0; i != cap; ++i) {
- if (IsFull(ctrl[i])) {
- PolicyTraits::destroy(&alloc_ref(), slot + i);
- }
- }
- }
-
// This overload kicks in when the argument is an rvalue of insertable and
// decomposable type other than init_type.
//
@@ -2075,7 +2434,7 @@ class raw_hash_set {
alignas(slot_type) unsigned char raw[sizeof(slot_type)];
slot_type* slot = reinterpret_cast<slot_type*>(&raw);
- PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
+ construct(slot, std::forward<Args>(args)...);
const auto& elem = PolicyTraits::element(slot);
return PolicyTraits::apply(InsertSlot<true>{*this, std::move(*slot)}, elem);
}
@@ -2179,8 +2538,8 @@ 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) {
- AssertIsFull(it.ctrl_, it.generation(), it.generation_ptr(), "erase()");
- PolicyTraits::destroy(&alloc_ref(), it.slot_);
+ AssertIsFull(it.control(), it.generation(), it.generation_ptr(), "erase()");
+ destroy(it.slot());
erase_meta_only(it);
}
@@ -2211,8 +2570,8 @@ class raw_hash_set {
assert(this != &src);
for (auto it = src.begin(), e = src.end(); it != e;) {
auto next = std::next(it);
- if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot_)},
- PolicyTraits::element(it.slot_))
+ if (PolicyTraits::apply(InsertSlot<false>{*this, std::move(*it.slot())},
+ PolicyTraits::element(it.slot()))
.second) {
src.erase_meta_only(it);
}
@@ -2226,10 +2585,9 @@ class raw_hash_set {
}
node_type extract(const_iterator position) {
- AssertIsFull(position.inner_.ctrl_, position.inner_.generation(),
+ AssertIsFull(position.control(), position.inner_.generation(),
position.inner_.generation_ptr(), "extract()");
- auto node =
- CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_);
+ auto node = CommonAccess::Transfer<node_type>(alloc_ref(), position.slot());
erase_meta_only(position);
return node;
}
@@ -2364,7 +2722,11 @@ class raw_hash_set {
template <class K = key_type>
bool contains(const key_arg<K>& key) const {
- return find(key) != end();
+ // Here neither the iterator returned by `find()` nor `end()` can be invalid
+ // outside of potential thread-safety issues.
+ // `find()`'s return value is constructed, used, and then destructed
+ // all in this context.
+ return !find(key).unchecked_equals(end());
}
template <class K = key_type>
@@ -2400,8 +2762,10 @@ class raw_hash_set {
const raw_hash_set* outer = &a;
const raw_hash_set* inner = &b;
if (outer->capacity() > inner->capacity()) std::swap(outer, inner);
- for (const value_type& elem : *outer)
- if (!inner->has_element(elem)) return false;
+ for (const value_type& elem : *outer) {
+ auto it = PolicyTraits::apply(FindElement{*inner}, elem);
+ if (it == inner->end() || !(*it == elem)) return false;
+ }
return true;
}
@@ -2471,10 +2835,9 @@ class raw_hash_set {
std::pair<iterator, bool> operator()(const K& key, Args&&...) && {
auto res = s.find_or_prepare_insert(key);
if (res.second) {
- PolicyTraits::transfer(&s.alloc_ref(), s.slot_array() + res.first,
- &slot);
+ s.transfer(s.slot_array() + res.first, &slot);
} else if (do_destroy) {
- PolicyTraits::destroy(&s.alloc_ref(), &slot);
+ s.destroy(&slot);
}
return {s.iterator_at(res.first), res.second};
}
@@ -2483,58 +2846,111 @@ class raw_hash_set {
slot_type&& slot;
};
+ // TODO(b/303305702): re-enable reentrant validation.
+ template <typename... Args>
+ inline void construct(slot_type* slot, Args&&... args) {
+ PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
+ }
+ inline void destroy(slot_type* slot) {
+ PolicyTraits::destroy(&alloc_ref(), slot);
+ }
+ inline void transfer(slot_type* to, slot_type* from) {
+ PolicyTraits::transfer(&alloc_ref(), to, from);
+ }
+
+ inline void destroy_slots() {
+ const size_t cap = capacity();
+ const ctrl_t* ctrl = control();
+ slot_type* slot = slot_array();
+ for (size_t i = 0; i != cap; ++i) {
+ if (IsFull(ctrl[i])) {
+ destroy(slot + i);
+ }
+ }
+ }
+
+ inline void dealloc() {
+ assert(capacity() != 0);
+ // Unpoison before returning the memory to the allocator.
+ SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * capacity());
+ infoz().Unregister();
+ Deallocate<BackingArrayAlignment(alignof(slot_type))>(
+ &alloc_ref(), common().backing_array_start(),
+ common().alloc_size(sizeof(slot_type), alignof(slot_type)));
+ }
+
+ inline void destructor_impl() {
+ if (capacity() == 0) return;
+ destroy_slots();
+ dealloc();
+ }
+
// Erases, but does not destroy, the value pointed to by `it`.
//
// This merely updates the pertinent control byte. This can be used in
// conjunction with Policy::transfer to move the object to another place.
void erase_meta_only(const_iterator it) {
- EraseMetaOnly(common(), it.inner_.ctrl_, sizeof(slot_type));
+ EraseMetaOnly(common(), static_cast<size_t>(it.control() - control()),
+ sizeof(slot_type));
}
- // Allocates a backing array for `self` and initializes its control bytes.
- // This reads `capacity` and updates all other fields based on the result of
- // the allocation.
+ // Resizes table to the new capacity and move all elements to the new
+ // positions accordingly.
//
- // This does not free the currently held array; `capacity` must be nonzero.
- inline void initialize_slots() {
- // People are often sloppy with the exact type of their allocator (sometimes
- // it has an extra const or is missing the pair, but rebinds made it work
- // anyway).
- using CharAlloc =
- typename absl::allocator_traits<Alloc>::template rebind_alloc<char>;
- InitializeSlots<CharAlloc, sizeof(slot_type), alignof(slot_type)>(
- common(), CharAlloc(alloc_ref()));
- }
-
+ // Note that for better performance instead of
+ // find_first_non_full(common(), hash),
+ // HashSetResizeHelper::FindFirstNonFullAfterResize(
+ // common(), old_capacity, hash)
+ // can be called right after `resize`.
ABSL_ATTRIBUTE_NOINLINE void resize(size_t new_capacity) {
assert(IsValidCapacity(new_capacity));
- auto* old_ctrl = control();
+ HashSetResizeHelper resize_helper(common());
auto* old_slots = slot_array();
- const size_t old_capacity = common().capacity();
common().set_capacity(new_capacity);
- initialize_slots();
-
- auto* new_slots = slot_array();
- size_t total_probe_length = 0;
- for (size_t i = 0; i != old_capacity; ++i) {
- if (IsFull(old_ctrl[i])) {
- size_t hash = PolicyTraits::apply(HashElement{hash_ref()},
- PolicyTraits::element(old_slots + i));
- auto target = find_first_non_full(common(), hash);
- size_t new_i = target.offset;
- total_probe_length += target.probe_length;
- SetCtrl(common(), new_i, H2(hash), sizeof(slot_type));
- PolicyTraits::transfer(&alloc_ref(), new_slots + new_i, old_slots + i);
- }
+ // Note that `InitializeSlots` does different number initialization steps
+ // depending on the values of `transfer_uses_memcpy` and capacities.
+ // Refer to the comment in `InitializeSlots` for more details.
+ const bool grow_single_group =
+ resize_helper.InitializeSlots<CharAlloc, sizeof(slot_type),
+ PolicyTraits::transfer_uses_memcpy(),
+ alignof(slot_type)>(
+ common(), const_cast<std::remove_const_t<slot_type>*>(old_slots),
+ CharAlloc(alloc_ref()));
+
+ if (resize_helper.old_capacity() == 0) {
+ // InitializeSlots did all the work including infoz().RecordRehash().
+ return;
}
- if (old_capacity) {
- SanitizerUnpoisonMemoryRegion(old_slots,
- sizeof(slot_type) * old_capacity);
- Deallocate<BackingArrayAlignment(alignof(slot_type))>(
- &alloc_ref(), old_ctrl - ControlOffset(),
- AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type)));
+
+ if (grow_single_group) {
+ if (PolicyTraits::transfer_uses_memcpy()) {
+ // InitializeSlots did all the work.
+ return;
+ }
+ // We want GrowSizeIntoSingleGroup to be called here in order to make
+ // InitializeSlots not depend on PolicyTraits.
+ resize_helper.GrowSizeIntoSingleGroup<PolicyTraits>(common(), alloc_ref(),
+ old_slots);
+ } else {
+ // InitializeSlots prepares control bytes to correspond to empty table.
+ auto* new_slots = slot_array();
+ size_t total_probe_length = 0;
+ for (size_t i = 0; i != resize_helper.old_capacity(); ++i) {
+ if (IsFull(resize_helper.old_ctrl()[i])) {
+ size_t hash = PolicyTraits::apply(
+ HashElement{hash_ref()}, PolicyTraits::element(old_slots + i));
+ auto target = find_first_non_full(common(), hash);
+ size_t new_i = target.offset;
+ total_probe_length += target.probe_length;
+ SetCtrl(common(), new_i, H2(hash), sizeof(slot_type));
+ transfer(new_slots + new_i, old_slots + i);
+ }
+ }
+ infoz().RecordRehash(total_probe_length);
}
- infoz().RecordRehash(total_probe_length);
+ resize_helper.DeallocateOld<alignof(slot_type)>(
+ CharAlloc(alloc_ref()), sizeof(slot_type),
+ const_cast<std::remove_const_t<slot_type>*>(old_slots));
}
// Prunes control bytes to remove as many tombstones as possible.
@@ -2604,36 +3020,64 @@ class raw_hash_set {
}
}
- bool has_element(const value_type& elem) const {
- size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem);
- auto seq = probe(common(), hash);
- const ctrl_t* ctrl = control();
- while (true) {
- Group g{ctrl + seq.offset()};
- for (uint32_t i : g.Match(H2(hash))) {
- if (ABSL_PREDICT_TRUE(
- PolicyTraits::element(slot_array() + seq.offset(i)) == elem))
- return true;
- }
- if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return false;
- seq.next();
- assert(seq.index() <= capacity() && "full table!");
+ void maybe_increment_generation_or_rehash_on_move() {
+ common().maybe_increment_generation_on_move();
+ if (!empty() && common().should_rehash_for_bug_detection_on_move()) {
+ resize(capacity());
}
- return false;
}
- // 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));
- swap(tmp);
+ template<bool propagate_alloc>
+ raw_hash_set& assign_impl(raw_hash_set&& that) {
+ // We don't bother checking for this/that aliasing. We just need to avoid
+ // breaking the invariants in that case.
+ destructor_impl();
+ common() = std::move(that.common());
+ // TODO(b/296061262): move instead of copying hash/eq/alloc.
+ hash_ref() = that.hash_ref();
+ eq_ref() = that.eq_ref();
+ CopyAlloc(alloc_ref(), that.alloc_ref(),
+ std::integral_constant<bool, propagate_alloc>());
+ that.common() = CommonFields{};
+ maybe_increment_generation_or_rehash_on_move();
return *this;
}
- raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) {
- raw_hash_set tmp(std::move(that), alloc_ref());
- swap(tmp);
+
+ raw_hash_set& move_elements_allocs_unequal(raw_hash_set&& that) {
+ const size_t size = that.size();
+ if (size == 0) return *this;
+ reserve(size);
+ for (iterator it = that.begin(); it != that.end(); ++it) {
+ insert(std::move(PolicyTraits::element(it.slot())));
+ that.destroy(it.slot());
+ }
+ that.dealloc();
+ that.common() = CommonFields{};
+ maybe_increment_generation_or_rehash_on_move();
return *this;
}
+ raw_hash_set& move_assign(raw_hash_set&& that,
+ std::true_type /*propagate_alloc*/) {
+ return assign_impl<true>(std::move(that));
+ }
+ raw_hash_set& move_assign(raw_hash_set&& that,
+ std::false_type /*propagate_alloc*/) {
+ if (alloc_ref() == that.alloc_ref()) {
+ return assign_impl<false>(std::move(that));
+ }
+ // Aliasing can't happen here because allocs would compare equal above.
+ assert(this != &that);
+ destructor_impl();
+ // We can't take over that's memory so we need to move each element.
+ // While moving elements, this should have that's hash/eq so copy hash/eq
+ // before moving elements.
+ // TODO(b/296061262): move instead of copying hash/eq.
+ hash_ref() = that.hash_ref();
+ eq_ref() = that.eq_ref();
+ return move_elements_allocs_unequal(std::move(that));
+ }
+
protected:
// Attempts to find `key` in the table; if it isn't found, returns a slot that
// the value can be inserted into, with the control byte already set to
@@ -2675,10 +3119,19 @@ class raw_hash_set {
if (!rehash_for_bug_detection &&
ABSL_PREDICT_FALSE(growth_left() == 0 &&
!IsDeleted(control()[target.offset]))) {
+ size_t old_capacity = capacity();
rehash_and_grow_if_necessary();
- target = find_first_non_full(common(), hash);
+ // NOTE: It is safe to use `FindFirstNonFullAfterResize`.
+ // `FindFirstNonFullAfterResize` must be called right after resize.
+ // `rehash_and_grow_if_necessary` may *not* call `resize`
+ // and perform `drop_deletes_without_resize` instead. But this
+ // could happen only on big tables.
+ // For big tables `FindFirstNonFullAfterResize` will always
+ // fallback to normal `find_first_non_full`, so it is safe to use it.
+ target = HashSetResizeHelper::FindFirstNonFullAfterResize(
+ common(), old_capacity, hash);
}
- common().set_size(common().size() + 1);
+ common().increment_size();
set_growth_left(growth_left() - IsEmpty(control()[target.offset]));
SetCtrl(common(), target.offset, H2(hash), sizeof(slot_type));
common().maybe_increment_generation_on_insert();
@@ -2696,8 +3149,7 @@ class raw_hash_set {
// POSTCONDITION: *m.iterator_at(i) == value_type(forward<Args>(args)...).
template <class... Args>
void emplace_at(size_t i, Args&&... args) {
- PolicyTraits::construct(&alloc_ref(), slot_array() + i,
- std::forward<Args>(args)...);
+ construct(slot_array() + i, std::forward<Args>(args)...);
assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) ==
iterator_at(i) &&
@@ -2711,6 +3163,8 @@ class raw_hash_set {
return {control() + i, slot_array() + i, common().generation_ptr()};
}
+ reference unchecked_deref(iterator it) { return it.unchecked_deref(); }
+
private:
friend struct RawHashSetTestOnlyAccess;
@@ -2743,7 +3197,7 @@ class raw_hash_set {
slot_type* slot_array() const {
return static_cast<slot_type*>(common().slot_array());
}
- HashtablezInfoHandle& infoz() { return common().infoz(); }
+ HashtablezInfoHandle infoz() { return common().infoz(); }
hasher& hash_ref() { return settings_.template get<1>(); }
const hasher& hash_ref() const { return settings_.template get<1>(); }
@@ -2763,8 +3217,7 @@ class raw_hash_set {
}
static void transfer_slot_fn(void* set, void* dst, void* src) {
auto* h = static_cast<raw_hash_set*>(set);
- PolicyTraits::transfer(&h->alloc_ref(), static_cast<slot_type*>(dst),
- static_cast<slot_type*>(src));
+ h->transfer(static_cast<slot_type*>(dst), static_cast<slot_type*>(src));
}
// Note: dealloc_fn will only be used if we have a non-standard allocator.
static void dealloc_fn(CommonFields& common, const PolicyFunctions&) {
@@ -2774,6 +3227,7 @@ class raw_hash_set {
SanitizerUnpoisonMemoryRegion(common.slot_array(),
sizeof(slot_type) * common.capacity());
+ common.infoz().Unregister();
Deallocate<BackingArrayAlignment(alignof(slot_type))>(
&set->alloc_ref(), common.backing_array_start(),
common.alloc_size(sizeof(slot_type), alignof(slot_type)));
@@ -2847,33 +3301,18 @@ 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;
- size_t m = AllocSize(capacity, sizeof(Slot), alignof(Slot));
+ size_t m = c.common().alloc_size(sizeof(Slot), alignof(Slot));
size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr));
if (per_slot != ~size_t{}) {
m += per_slot * c.size();
} else {
- const ctrl_t* ctrl = c.control();
- for (size_t i = 0; i != capacity; ++i) {
- if (container_internal::IsFull(ctrl[i])) {
- m += Traits::space_used(c.slot_array() + i);
- }
+ for (auto it = c.begin(); it != c.end(); ++it) {
+ m += Traits::space_used(it.slot());
}
}
return m;
}
-
- static size_t LowerBoundAllocatedByteSize(size_t size) {
- size_t capacity = GrowthToLowerboundCapacity(size);
- if (capacity == 0) return 0;
- 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;
- }
- return m;
- }
};
} // namespace hashtable_debug_internal
diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc
index e73f53fd..05dcfaa6 100644
--- a/absl/container/internal/raw_hash_set_allocator_test.cc
+++ b/absl/container/internal/raw_hash_set_allocator_test.cc
@@ -12,10 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <cstddef>
+#include <cstdint>
+#include <functional>
#include <limits>
-#include <scoped_allocator>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <type_traits>
+#include <utility>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
#include "absl/container/internal/raw_hash_set.h"
#include "absl/container/internal/tracked.h"
@@ -23,6 +32,7 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
+using ::testing::AnyOf;
enum AllocSpec {
kPropagateOnCopy = 1,
@@ -276,34 +286,38 @@ TEST_F(NoPropagateOnCopy, CopyConstructorWithDifferentAlloc) {
}
TEST_F(PropagateOnAll, MoveConstructor) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(std::move(t1));
- EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ auto it = u.begin();
+ EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(0, it->num_copies());
}
TEST_F(NoPropagateOnMove, MoveConstructor) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(std::move(t1));
- EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ auto it = u.begin();
+ EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(0, it->num_copies());
}
TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(std::move(t1), a1);
- EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ auto it = u.begin();
+ EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(0, it->num_copies());
}
TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(std::move(t1), a1);
- EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ auto it = u.begin();
+ EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(0, it->num_copies());
}
@@ -313,8 +327,8 @@ TEST_F(PropagateOnAll, MoveConstructorWithDifferentAlloc) {
it = u.find(0);
EXPECT_EQ(a2, u.get_allocator());
EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(1, a2.num_allocs());
- EXPECT_EQ(1, it->num_moves());
+ EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
EXPECT_EQ(0, it->num_copies());
}
@@ -324,8 +338,8 @@ TEST_F(NoPropagateOnMove, MoveConstructorWithDifferentAlloc) {
it = u.find(0);
EXPECT_EQ(a2, u.get_allocator());
EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(1, a2.num_allocs());
- EXPECT_EQ(1, it->num_moves());
+ EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
EXPECT_EQ(0, it->num_copies());
}
@@ -333,8 +347,8 @@ TEST_F(PropagateOnAll, CopyAssignmentWithSameAlloc) {
auto it = t1.insert(0).first;
Table u(0, a1);
u = t1;
- EXPECT_EQ(2, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(1, it->num_copies());
}
@@ -342,8 +356,8 @@ TEST_F(NoPropagateOnCopy, CopyAssignmentWithSameAlloc) {
auto it = t1.insert(0).first;
Table u(0, a1);
u = t1;
- EXPECT_EQ(2, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(1, it->num_copies());
}
@@ -352,9 +366,9 @@ TEST_F(PropagateOnAll, CopyAssignmentWithDifferentAlloc) {
Table u(0, a2);
u = t1;
EXPECT_EQ(a1, u.get_allocator());
- EXPECT_EQ(2, a1.num_allocs());
+ EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
EXPECT_EQ(0, a2.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(1, it->num_copies());
}
@@ -364,51 +378,54 @@ TEST_F(NoPropagateOnCopy, CopyAssignmentWithDifferentAlloc) {
u = t1;
EXPECT_EQ(a2, u.get_allocator());
EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(1, a2.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(1, it->num_copies());
}
TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(0, a1);
u = std::move(t1);
+ auto it = u.begin();
EXPECT_EQ(a1, u.get_allocator());
- EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(0, it->num_copies());
}
TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(0, a1);
u = std::move(t1);
+ auto it = u.begin();
EXPECT_EQ(a1, u.get_allocator());
- EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(0, it->num_copies());
}
TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(0, a2);
u = std::move(t1);
+ auto it = u.begin();
EXPECT_EQ(a1, u.get_allocator());
- EXPECT_EQ(1, a1.num_allocs());
+ EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
EXPECT_EQ(0, a2.num_allocs());
- EXPECT_EQ(0, it->num_moves());
+ EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
EXPECT_EQ(0, it->num_copies());
}
TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) {
- auto it = t1.insert(0).first;
+ t1.insert(0);
Table u(0, a2);
u = std::move(t1);
- it = u.find(0);
+ auto it = u.find(0);
EXPECT_EQ(a2, u.get_allocator());
EXPECT_EQ(1, a1.num_allocs());
- EXPECT_EQ(1, a2.num_allocs());
- EXPECT_EQ(1, it->num_moves());
+ EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
+ EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
EXPECT_EQ(0, it->num_copies());
}
@@ -435,9 +452,6 @@ class PAlloc {
// 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;
@@ -466,37 +480,32 @@ class PAlloc {
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) {
+TEST(NoPropagateDeletedAssignment, CopyConstruct) {
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);
+ Table t1(PA{1}), t2(t1);
EXPECT_EQ(t1.get_allocator(), PA(1));
- EXPECT_EQ(t2.get_allocator(), PA(2));
+ EXPECT_EQ(t2.get_allocator(), PA());
}
-#endif
-TEST(NoPropagateOn, CopyConstruct) {
+TEST(NoPropagateDeletedAssignment, CopyAssignment) {
using PA = PAlloc<char>;
using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
- Table t1(PA{1}), t2(t1);
+ Table t1(PA{1}), t2(PA{2});
+ t1 = t2;
EXPECT_EQ(t1.get_allocator(), PA(1));
- EXPECT_EQ(t2.get_allocator(), PA());
+ EXPECT_EQ(t2.get_allocator(), PA(2));
}
-TEST(NoPropagateOn, Assignment) {
+TEST(NoPropagateDeletedAssignment, MoveAssignment) {
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;
+ t1 = std::move(t2);
EXPECT_EQ(t1.get_allocator(), PA(1));
- EXPECT_EQ(t2.get_allocator(), PA(2));
}
} // namespace
diff --git a/absl/container/internal/raw_hash_set_benchmark.cc b/absl/container/internal/raw_hash_set_benchmark.cc
index a3647890..88b07373 100644
--- a/absl/container/internal/raw_hash_set_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_benchmark.cc
@@ -14,6 +14,8 @@
#include <array>
#include <cmath>
+#include <cstddef>
+#include <cstdint>
#include <numeric>
#include <random>
#include <tuple>
@@ -205,6 +207,22 @@ void CacheInSteadyStateArgs(Benchmark* bm) {
}
BENCHMARK(BM_CacheInSteadyState)->Apply(CacheInSteadyStateArgs);
+void BM_EraseEmplace(benchmark::State& state) {
+ IntTable t;
+ int64_t size = state.range(0);
+ for (int64_t i = 0; i < size; ++i) {
+ t.emplace(i);
+ }
+ while (state.KeepRunningBatch(size)) {
+ for (int64_t i = 0; i < size; ++i) {
+ benchmark::DoNotOptimize(t);
+ t.erase(i);
+ t.emplace(i);
+ }
+ }
+}
+BENCHMARK(BM_EraseEmplace)->Arg(1)->Arg(2)->Arg(4)->Arg(8)->Arg(16)->Arg(100);
+
void BM_EndComparison(benchmark::State& state) {
StringTable t = {{"a", "a"}, {"b", "b"}};
auto it = t.begin();
@@ -369,28 +387,42 @@ void BM_NoOpReserveStringTable(benchmark::State& state) {
BENCHMARK(BM_NoOpReserveStringTable);
void BM_ReserveIntTable(benchmark::State& state) {
- int reserve_size = state.range(0);
- for (auto _ : state) {
+ constexpr size_t kBatchSize = 1024;
+ size_t reserve_size = static_cast<size_t>(state.range(0));
+
+ std::vector<IntTable> tables;
+ while (state.KeepRunningBatch(kBatchSize)) {
state.PauseTiming();
- IntTable t;
+ tables.clear();
+ tables.resize(kBatchSize);
state.ResumeTiming();
- benchmark::DoNotOptimize(t);
- t.reserve(reserve_size);
+ for (auto& t : tables) {
+ benchmark::DoNotOptimize(t);
+ t.reserve(reserve_size);
+ benchmark::DoNotOptimize(t);
+ }
}
}
-BENCHMARK(BM_ReserveIntTable)->Range(128, 4096);
+BENCHMARK(BM_ReserveIntTable)->Range(1, 64);
void BM_ReserveStringTable(benchmark::State& state) {
- int reserve_size = state.range(0);
- for (auto _ : state) {
+ constexpr size_t kBatchSize = 1024;
+ size_t reserve_size = static_cast<size_t>(state.range(0));
+
+ std::vector<StringTable> tables;
+ while (state.KeepRunningBatch(kBatchSize)) {
state.PauseTiming();
- StringTable t;
+ tables.clear();
+ tables.resize(kBatchSize);
state.ResumeTiming();
- benchmark::DoNotOptimize(t);
- t.reserve(reserve_size);
+ for (auto& t : tables) {
+ benchmark::DoNotOptimize(t);
+ t.reserve(reserve_size);
+ benchmark::DoNotOptimize(t);
+ }
}
}
-BENCHMARK(BM_ReserveStringTable)->Range(128, 4096);
+BENCHMARK(BM_ReserveStringTable)->Range(1, 64);
// Like std::iota, except that ctrl_t doesn't support operator++.
template <typename CtrlIter>
diff --git a/absl/container/internal/raw_hash_set_probe_benchmark.cc b/absl/container/internal/raw_hash_set_probe_benchmark.cc
index 7169a2e2..5d4184b2 100644
--- a/absl/container/internal/raw_hash_set_probe_benchmark.cc
+++ b/absl/container/internal/raw_hash_set_probe_benchmark.cc
@@ -19,6 +19,7 @@
#include <regex> // NOLINT
#include <vector>
+#include "absl/base/no_destructor.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/internal/hash_function_defaults.h"
#include "absl/container/internal/hashtable_debug.h"
@@ -29,6 +30,7 @@
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
+#include "absl/types/optional.h"
namespace {
@@ -71,7 +73,7 @@ struct Policy {
};
absl::BitGen& GlobalBitGen() {
- static auto* value = new absl::BitGen;
+ static absl::NoDestructor<absl::BitGen> value;
return *value;
}
@@ -112,7 +114,7 @@ class RandomizedAllocator {
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*>>();
+ static absl::NoDestructor<absl::flat_hash_map<size_t, std::vector<T*>>> m;
return (*m)[n];
}
};
@@ -460,12 +462,12 @@ constexpr int kNameWidth = 15;
constexpr int kDistWidth = 16;
bool CanRunBenchmark(absl::string_view name) {
- static std::regex* const filter = []() -> std::regex* {
+ static const absl::NoDestructor<absl::optional<std::regex>> filter([] {
return benchmarks.empty() || benchmarks == "all"
- ? nullptr
- : new std::regex(std::string(benchmarks));
- }();
- return filter == nullptr || std::regex_search(std::string(name), *filter);
+ ? absl::nullopt
+ : absl::make_optional(std::regex(std::string(benchmarks)));
+ }());
+ return !filter->has_value() || std::regex_search(std::string(name), **filter);
}
struct Result {
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index 242a97cb..f9797f56 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -30,6 +30,7 @@
#include <ostream>
#include <random>
#include <string>
+#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
@@ -48,7 +49,12 @@
#include "absl/container/internal/hash_function_defaults.h"
#include "absl/container/internal/hash_policy_testing.h"
#include "absl/container/internal/hashtable_debug.h"
+#include "absl/container/internal/hashtablez_sampler.h"
+#include "absl/container/internal/test_allocator.h"
+#include "absl/hash/hash.h"
#include "absl/log/log.h"
+#include "absl/memory/memory.h"
+#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
namespace absl {
@@ -230,6 +236,25 @@ TEST(Group, MaskEmpty) {
}
}
+TEST(Group, MaskFull) {
+ if (Group::kWidth == 16) {
+ 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), ctrl_t::kDeleted, CtrlT(1),
+ CtrlT(1), ctrl_t::kSentinel, ctrl_t::kEmpty, CtrlT(1)};
+ EXPECT_THAT(Group{group}.MaskFull(),
+ ElementsAre(1, 3, 5, 7, 8, 9, 11, 12, 15));
+ } else if (Group::kWidth == 8) {
+ ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty,
+ ctrl_t::kDeleted, CtrlT(2), ctrl_t::kSentinel,
+ ctrl_t::kSentinel, CtrlT(1)};
+ EXPECT_THAT(Group{group}.MaskFull(), ElementsAre(1, 4, 7));
+ } else {
+ FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
+ }
+}
+
TEST(Group, MaskEmptyOrDeleted) {
if (Group::kWidth == 16) {
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty, CtrlT(3),
@@ -292,13 +317,13 @@ TEST(Group, CountLeadingEmptyOrDeleted) {
std::vector<ctrl_t> f(Group::kWidth, empty);
f[Group::kWidth * 2 / 3] = full;
f[Group::kWidth / 2] = full;
- EXPECT_EQ(
- Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted());
+ EXPECT_EQ(Group::kWidth / 2,
+ Group{f.data()}.CountLeadingEmptyOrDeleted());
}
}
}
-template <class T>
+template <class T, bool kTransferable = false>
struct ValuePolicy {
using slot_type = T;
using key_type = T;
@@ -316,10 +341,11 @@ struct ValuePolicy {
}
template <class Allocator>
- static void transfer(Allocator* alloc, slot_type* new_slot,
- slot_type* old_slot) {
+ static std::integral_constant<bool, kTransferable> transfer(
+ Allocator* alloc, slot_type* new_slot, slot_type* old_slot) {
construct(alloc, new_slot, std::move(*old_slot));
destroy(alloc, old_slot);
+ return {};
}
static T& element(slot_type* slot) { return *slot; }
@@ -336,6 +362,8 @@ struct ValuePolicy {
using IntPolicy = ValuePolicy<int64_t>;
using Uint8Policy = ValuePolicy<uint8_t>;
+using TranferableIntPolicy = ValuePolicy<int64_t, /*kTransferable=*/true>;
+
class StringPolicy {
template <class F, class K, class V,
class = typename std::enable_if<
@@ -408,19 +436,18 @@ struct 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;
+template <typename T, bool kTransferable = false>
+struct ValueTable
+ : raw_hash_set<ValuePolicy<T, kTransferable>, hash_default_hash<T>,
+ std::equal_to<T>, std::allocator<T>> {
+ using Base = typename ValueTable::raw_hash_set;
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;
-};
+using IntTable = ValueTable<int64_t>;
+using Uint8Table = ValueTable<uint8_t>;
+
+using TransferableIntTable = ValueTable<int64_t, /*kTransferable=*/true>;
template <typename T>
struct CustomAlloc : std::allocator<T> {
@@ -429,48 +456,21 @@ struct CustomAlloc : std::allocator<T> {
template <typename U>
explicit CustomAlloc(const CustomAlloc<U>& /*other*/) {}
- template<class U> struct rebind {
+ template <class U>
+ struct rebind {
using other = CustomAlloc<U>;
};
};
struct CustomAllocIntTable
- : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
+ : raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
std::equal_to<int64_t>, CustomAlloc<int64_t>> {
using Base = typename CustomAllocIntTable::raw_hash_set;
using Base::Base;
};
-// Tries to allocate memory at the minimum alignment even when the default
-// allocator uses a higher alignment.
-template <typename T>
-struct MinimumAlignmentAlloc : std::allocator<T> {
- MinimumAlignmentAlloc() = default;
-
- template <typename U>
- explicit MinimumAlignmentAlloc(const MinimumAlignmentAlloc<U>& /*other*/) {}
-
- template <class U>
- struct rebind {
- using other = MinimumAlignmentAlloc<U>;
- };
-
- T* allocate(size_t n) {
- T* ptr = std::allocator<T>::allocate(n + 1);
- char* cptr = reinterpret_cast<char*>(ptr);
- cptr += alignof(T);
- return reinterpret_cast<T*>(cptr);
- }
-
- void deallocate(T* ptr, size_t n) {
- char* cptr = reinterpret_cast<char*>(ptr);
- cptr -= alignof(T);
- std::allocator<T>::deallocate(reinterpret_cast<T*>(cptr), n + 1);
- }
-};
-
struct MinimumAlignmentUint8Table
- : raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>,
+ : raw_hash_set<Uint8Policy, hash_default_hash<uint8_t>,
std::equal_to<uint8_t>, MinimumAlignmentAlloc<uint8_t>> {
using Base = typename MinimumAlignmentUint8Table::raw_hash_set;
using Base::Base;
@@ -524,13 +524,6 @@ TEST(Table, EmptyFunctorOptimization) {
static_assert(std::is_empty<std::allocator<int>>::value, "");
struct MockTable {
- void* infoz;
- void* ctrl;
- void* slots;
- size_t size;
- size_t capacity;
- };
- struct MockTableInfozDisabled {
void* ctrl;
void* slots;
size_t size;
@@ -555,9 +548,7 @@ TEST(Table, EmptyFunctorOptimization) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
#endif
- constexpr size_t mock_size = std::is_empty<HashtablezInfoHandle>()
- ? sizeof(MockTableInfozDisabled)
- : sizeof(MockTable);
+ constexpr size_t mock_size = sizeof(MockTable);
constexpr size_t generation_size =
SwisstableGenerationsEnabled() ? sizeof(GenerationData) : 0;
#if defined(__clang__)
@@ -662,6 +653,23 @@ TEST(Table, InsertCollisionAndFindAfterDelete) {
EXPECT_TRUE(t.empty());
}
+TEST(Table, EraseInSmallTables) {
+ for (int64_t size = 0; size < 64; ++size) {
+ IntTable t;
+ for (int64_t i = 0; i < size; ++i) {
+ t.insert(i);
+ }
+ for (int64_t i = 0; i < size; ++i) {
+ t.erase(i);
+ EXPECT_EQ(t.size(), size - i - 1);
+ for (int64_t j = i + 1; j < size; ++j) {
+ EXPECT_THAT(*t.find(j), j);
+ }
+ }
+ EXPECT_TRUE(t.empty());
+ }
+}
+
TEST(Table, InsertWithinCapacity) {
IntTable t;
t.reserve(10);
@@ -693,6 +701,68 @@ TEST(Table, InsertWithinCapacity) {
EXPECT_THAT(addr(0), original_addr_0);
}
+template <class TableType>
+class SmallTableResizeTest : public testing::Test {};
+
+TYPED_TEST_SUITE_P(SmallTableResizeTest);
+
+TYPED_TEST_P(SmallTableResizeTest, InsertIntoSmallTable) {
+ TypeParam t;
+ for (int i = 0; i < 32; ++i) {
+ t.insert(i);
+ ASSERT_EQ(t.size(), i + 1);
+ for (int j = 0; j < i + 1; ++j) {
+ EXPECT_TRUE(t.find(j) != t.end());
+ EXPECT_EQ(*t.find(j), j);
+ }
+ }
+}
+
+TYPED_TEST_P(SmallTableResizeTest, ResizeGrowSmallTables) {
+ TypeParam t;
+ for (size_t source_size = 0; source_size < 32; ++source_size) {
+ for (size_t target_size = source_size; target_size < 32; ++target_size) {
+ for (bool rehash : {false, true}) {
+ for (size_t i = 0; i < source_size; ++i) {
+ t.insert(static_cast<int>(i));
+ }
+ if (rehash) {
+ t.rehash(target_size);
+ } else {
+ t.reserve(target_size);
+ }
+ for (size_t i = 0; i < source_size; ++i) {
+ EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end());
+ EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i));
+ }
+ }
+ }
+ }
+}
+
+TYPED_TEST_P(SmallTableResizeTest, ResizeReduceSmallTables) {
+ TypeParam t;
+ for (size_t source_size = 0; source_size < 32; ++source_size) {
+ for (size_t target_size = 0; target_size <= source_size; ++target_size) {
+ size_t inserted_count = std::min<size_t>(source_size, 5);
+ for (size_t i = 0; i < inserted_count; ++i) {
+ t.insert(static_cast<int>(i));
+ }
+ t.rehash(target_size);
+ for (size_t i = 0; i < inserted_count; ++i) {
+ EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end());
+ EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i));
+ }
+ }
+ }
+}
+
+REGISTER_TYPED_TEST_SUITE_P(SmallTableResizeTest, InsertIntoSmallTable,
+ ResizeGrowSmallTables, ResizeReduceSmallTables);
+using SmallTableTypes = ::testing::Types<IntTable, TransferableIntTable>;
+INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSmallTableResizeTest,
+ SmallTableResizeTest, SmallTableTypes);
+
TEST(Table, LazyEmplace) {
StringTable t;
bool called = false;
@@ -1111,7 +1181,7 @@ TEST(Table, Erase) {
TEST(Table, EraseMaintainsValidIterator) {
IntTable t;
const int kNumElements = 100;
- for (int i = 0; i < kNumElements; i ++) {
+ for (int i = 0; i < kNumElements; i++) {
EXPECT_TRUE(t.emplace(i).second);
}
EXPECT_EQ(t.size(), kNumElements);
@@ -1332,15 +1402,15 @@ ExpectedStats XorSeedExpectedStats() {
switch (container_internal::Group::kWidth) {
case 8:
if (kRandomizesInserts) {
- return {0.05,
- 1.0,
- {{0.95, 0.5}},
- {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
+ return {0.05,
+ 1.0,
+ {{0.95, 0.5}},
+ {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
} else {
- return {0.05,
- 2.0,
- {{0.95, 0.1}},
- {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
+ return {0.05,
+ 2.0,
+ {{0.95, 0.1}},
+ {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
}
case 16:
if (kRandomizesInserts) {
@@ -1386,7 +1456,7 @@ ProbeStats CollectProbeStatsOnLinearlyTransformedKeys(
std::random_device rd;
std::mt19937 rng(rd());
auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; };
- std::uniform_int_distribution<size_t> dist(0, keys.size()-1);
+ std::uniform_int_distribution<size_t> dist(0, keys.size() - 1);
while (num_iters--) {
IntTable t1;
size_t num_keys = keys.size() / 10;
@@ -1638,7 +1708,7 @@ TEST(Table, CopyConstructWithAlloc) {
}
struct ExplicitAllocIntTable
- : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
+ : raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
std::equal_to<int64_t>, Alloc<int64_t>> {
ExplicitAllocIntTable() = default;
};
@@ -1716,6 +1786,14 @@ TEST(Table, MoveAssign) {
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
}
+TEST(Table, MoveSelfAssign) {
+ StringTable t;
+ t.emplace("a", "b");
+ EXPECT_EQ(1, t.size());
+ t = std::move(*&t);
+ // As long as we don't crash, it's fine.
+}
+
TEST(Table, Equality) {
StringTable t;
std::vector<std::pair<std::string, std::string>> v = {{"a", "b"},
@@ -1894,11 +1972,9 @@ TEST(Table, HeterogeneousLookupOverloads) {
EXPECT_FALSE((VerifyResultOf<CallPrefetch, NonTransparentTable>()));
EXPECT_FALSE((VerifyResultOf<CallCount, NonTransparentTable>()));
- using TransparentTable = raw_hash_set<
- StringPolicy,
- absl::container_internal::hash_default_hash<absl::string_view>,
- absl::container_internal::hash_default_eq<absl::string_view>,
- std::allocator<int>>;
+ using TransparentTable =
+ raw_hash_set<StringPolicy, hash_default_hash<absl::string_view>,
+ hash_default_eq<absl::string_view>, std::allocator<int>>;
EXPECT_TRUE((VerifyResultOf<CallFind, TransparentTable>()));
EXPECT_TRUE((VerifyResultOf<CallErase, TransparentTable>()));
@@ -2120,16 +2196,6 @@ TEST(Table, UnstablePointers) {
EXPECT_NE(old_ptr, addr(0));
}
-bool IsAssertEnabled() {
- // Use an assert with side-effects to figure out if they are actually enabled.
- bool assert_enabled = false;
- assert([&]() { // NOLINT
- assert_enabled = true;
- return true;
- }());
- return assert_enabled;
-}
-
TEST(TableDeathTest, InvalidIteratorAsserts) {
if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
GTEST_SKIP() << "Assertions not enabled.";
@@ -2152,10 +2218,10 @@ TEST(TableDeathTest, InvalidIteratorAsserts) {
EXPECT_DEATH_IF_SUPPORTED(++iter, kErasedDeathMessage);
}
-// Invalid iterator use can trigger heap-use-after-free in asan,
+// Invalid iterator use can trigger use-after-free in asan/hwasan,
// use-of-uninitialized-value in msan, or invalidated iterator assertions.
constexpr const char* kInvalidIteratorDeathMessage =
- "heap-use-after-free|use-of-uninitialized-value|invalidated "
+ "use-after-free|use-of-uninitialized-value|invalidated "
"iterator|Invalid iterator|invalid iterator";
// MSVC doesn't support | in regex.
@@ -2302,21 +2368,34 @@ TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
}
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
-TEST(Sanitizer, PoisoningUnused) {
- IntTable t;
- t.reserve(5);
- // Insert something to force an allocation.
- int64_t& v1 = *t.insert(0).first;
+template <class TableType>
+class SanitizerTest : public testing::Test {};
- // Make sure there is something to test.
- ASSERT_GT(t.capacity(), 1);
+TYPED_TEST_SUITE_P(SanitizerTest);
- int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
- for (size_t i = 0; i < t.capacity(); ++i) {
- EXPECT_EQ(slots + i != &v1, __asan_address_is_poisoned(slots + i));
+TYPED_TEST_P(SanitizerTest, PoisoningUnused) {
+ TypeParam t;
+ for (size_t reserve_size = 2; reserve_size < 1024;
+ reserve_size = reserve_size * 3 / 2) {
+ t.reserve(reserve_size);
+ // Insert something to force an allocation.
+ int64_t& v = *t.insert(0).first;
+
+ // Make sure there is something to test.
+ ASSERT_GT(t.capacity(), 1);
+
+ int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
+ for (size_t i = 0; i < t.capacity(); ++i) {
+ EXPECT_EQ(slots + i != &v, __asan_address_is_poisoned(slots + i)) << i;
+ }
}
}
+REGISTER_TYPED_TEST_SUITE_P(SanitizerTest, PoisoningUnused);
+using SanitizerTableTypes = ::testing::Types<IntTable, TransferableIntTable>;
+INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSanitizerTest, SanitizerTest,
+ SanitizerTableTypes);
+
TEST(Sanitizer, PoisoningOnErase) {
IntTable t;
int64_t& v = *t.insert(0).first;
@@ -2410,6 +2489,26 @@ TEST(Iterator, InvalidUseWithReserveCrashesWithSanitizers) {
#endif
}
+TEST(Iterator, InvalidUseWithMoveCrashesWithSanitizers) {
+ if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
+ if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
+
+ IntTable t1, t2;
+ t1.insert(1);
+ auto it = t1.begin();
+ // ptr will become invalidated on rehash.
+ const int64_t* ptr = &*it;
+ (void)ptr;
+
+ t2 = std::move(t1);
+ EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
+ EXPECT_DEATH_IF_SUPPORTED(void(it == t2.begin()),
+ kInvalidIteratorDeathMessage);
+#ifdef ABSL_HAVE_ADDRESS_SANITIZER
+ EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free");
+#endif
+}
+
TEST(Table, ReservedGrowthUpdatesWhenTableDoesntGrow) {
IntTable t;
for (int i = 0; i < 8; ++i) t.insert(i);
@@ -2480,7 +2579,7 @@ TEST(Table, InvalidReferenceUseCrashesWithSanitizers) {
// ptr will become invalidated on rehash.
const int64_t* ptr = &*t.begin();
t.insert(++i);
- EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free") << i;
+ EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "use-after-free") << i;
}
}
@@ -2510,6 +2609,75 @@ TEST(Iterator, InvalidComparisonDifferentTables) {
"Invalid iterator comparison.*non-end");
}
+template <typename Alloc>
+using RawHashSetAlloc = raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
+ std::equal_to<int64_t>, Alloc>;
+
+TEST(Table, AllocatorPropagation) { TestAllocPropagation<RawHashSetAlloc>(); }
+
+struct CountedHash {
+ size_t operator()(int value) const {
+ ++count;
+ return static_cast<size_t>(value);
+ }
+ mutable int count = 0;
+};
+
+struct CountedHashIntTable
+ : raw_hash_set<IntPolicy, CountedHash, std::equal_to<int>,
+ std::allocator<int>> {
+ using Base = typename CountedHashIntTable::raw_hash_set;
+ using Base::Base;
+};
+
+TEST(Table, CountedHash) {
+ // Verify that raw_hash_set does not compute redundant hashes.
+#ifdef NDEBUG
+ constexpr bool kExpectMinimumHashes = true;
+#else
+ constexpr bool kExpectMinimumHashes = false;
+#endif
+ if (!kExpectMinimumHashes) {
+ GTEST_SKIP() << "Only run under NDEBUG: `assert` statements may cause "
+ "redundant hashing.";
+ }
+
+ using Table = CountedHashIntTable;
+ auto HashCount = [](const Table& t) { return t.hash_function().count; };
+ {
+ Table t;
+ EXPECT_EQ(HashCount(t), 0);
+ }
+ {
+ Table t;
+ t.insert(1);
+ EXPECT_EQ(HashCount(t), 1);
+ t.erase(1);
+ EXPECT_EQ(HashCount(t), 2);
+ }
+ {
+ Table t;
+ t.insert(3);
+ EXPECT_EQ(HashCount(t), 1);
+ auto node = t.extract(3);
+ EXPECT_EQ(HashCount(t), 2);
+ t.insert(std::move(node));
+ EXPECT_EQ(HashCount(t), 3);
+ }
+ {
+ Table t;
+ t.emplace(5);
+ EXPECT_EQ(HashCount(t), 1);
+ }
+ {
+ Table src;
+ src.insert(7);
+ Table dst;
+ dst.merge(src);
+ EXPECT_EQ(HashCount(dst), 1);
+ }
+}
+
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END
diff --git a/absl/container/internal/test_allocator.h b/absl/container/internal/test_allocator.h
new file mode 100644
index 00000000..8e365a3c
--- /dev/null
+++ b/absl/container/internal/test_allocator.h
@@ -0,0 +1,387 @@
+// 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_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
+#define ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <type_traits>
+
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+
+// 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:
+ 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() = 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)
+ : bytes_used_(x.bytes_used_), instance_count_(x.instance_count_) {}
+
+ 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) {
+ Allocator allocator;
+ AllocatorTraits::deallocate(allocator, p, n);
+ if (bytes_used_ != nullptr) {
+ *bytes_used_ -= n * sizeof(T);
+ }
+ }
+
+ 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;
+ // Ignore GCC warning bug.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuse-after-free"
+#endif
+ AllocatorTraits::destroy(allocator, p);
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic pop
+#endif
+ if (instance_count_ != nullptr) {
+ *instance_count_ -= 1;
+ }
+ }
+
+ template <typename U>
+ class rebind {
+ public:
+ using other = CountingAllocator<U>;
+ };
+
+ friend bool operator==(const CountingAllocator& a,
+ const CountingAllocator& b) {
+ return a.bytes_used_ == b.bytes_used_ &&
+ a.instance_count_ == b.instance_count_;
+ }
+
+ friend bool operator!=(const CountingAllocator& a,
+ const CountingAllocator& b) {
+ return !(a == b);
+ }
+
+ int64_t* bytes_used_ = nullptr;
+ int64_t* instance_count_ = nullptr;
+};
+
+template <typename T>
+struct CopyAssignPropagatingCountingAlloc : public CountingAllocator<T> {
+ using propagate_on_container_copy_assignment = std::true_type;
+
+ using Base = CountingAllocator<T>;
+ using Base::Base;
+
+ template <typename U>
+ explicit CopyAssignPropagatingCountingAlloc(
+ const CopyAssignPropagatingCountingAlloc<U>& other)
+ : Base(other.bytes_used_, other.instance_count_) {}
+
+ template <typename U>
+ struct rebind {
+ using other = CopyAssignPropagatingCountingAlloc<U>;
+ };
+};
+
+template <typename T>
+struct MoveAssignPropagatingCountingAlloc : public CountingAllocator<T> {
+ using propagate_on_container_move_assignment = std::true_type;
+
+ using Base = CountingAllocator<T>;
+ using Base::Base;
+
+ template <typename U>
+ explicit MoveAssignPropagatingCountingAlloc(
+ const MoveAssignPropagatingCountingAlloc<U>& other)
+ : Base(other.bytes_used_, other.instance_count_) {}
+
+ template <typename U>
+ struct rebind {
+ using other = MoveAssignPropagatingCountingAlloc<U>;
+ };
+};
+
+template <typename T>
+struct SwapPropagatingCountingAlloc : public CountingAllocator<T> {
+ using propagate_on_container_swap = std::true_type;
+
+ using Base = CountingAllocator<T>;
+ using Base::Base;
+
+ template <typename U>
+ explicit SwapPropagatingCountingAlloc(
+ const SwapPropagatingCountingAlloc<U>& other)
+ : Base(other.bytes_used_, other.instance_count_) {}
+
+ template <typename U>
+ struct rebind {
+ using other = SwapPropagatingCountingAlloc<U>;
+ };
+};
+
+// Tries to allocate memory at the minimum alignment even when the default
+// allocator uses a higher alignment.
+template <typename T>
+struct MinimumAlignmentAlloc : std::allocator<T> {
+ MinimumAlignmentAlloc() = default;
+
+ template <typename U>
+ explicit MinimumAlignmentAlloc(const MinimumAlignmentAlloc<U>& /*other*/) {}
+
+ template <class U>
+ struct rebind {
+ using other = MinimumAlignmentAlloc<U>;
+ };
+
+ T* allocate(size_t n) {
+ T* ptr = std::allocator<T>::allocate(n + 1);
+ char* cptr = reinterpret_cast<char*>(ptr);
+ cptr += alignof(T);
+ return reinterpret_cast<T*>(cptr);
+ }
+
+ void deallocate(T* ptr, size_t n) {
+ char* cptr = reinterpret_cast<char*>(ptr);
+ cptr -= alignof(T);
+ std::allocator<T>::deallocate(reinterpret_cast<T*>(cptr), n + 1);
+ }
+};
+
+inline bool IsAssertEnabled() {
+ // Use an assert with side-effects to figure out if they are actually enabled.
+ bool assert_enabled = false;
+ assert([&]() { // NOLINT
+ assert_enabled = true;
+ return true;
+ }());
+ return assert_enabled;
+}
+
+template <template <class Alloc> class Container>
+void TestCopyAssignAllocPropagation() {
+ int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
+ CopyAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
+ CopyAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
+
+ // Test propagating allocator_type.
+ {
+ Container<CopyAssignPropagatingCountingAlloc<int>> c1(allocator1);
+ Container<CopyAssignPropagatingCountingAlloc<int>> c2(allocator2);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_NE(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+
+ c2 = c1;
+
+ EXPECT_EQ(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 200);
+ EXPECT_EQ(instances2, 0);
+ }
+ // Test non-propagating allocator_type with different allocators.
+ {
+ Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_EQ(c2.get_allocator(), allocator2);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+
+ c2 = c1;
+
+ EXPECT_EQ(c2.get_allocator(), allocator2);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 100);
+ }
+ EXPECT_EQ(bytes1, 0);
+ EXPECT_EQ(instances1, 0);
+ EXPECT_EQ(bytes2, 0);
+ EXPECT_EQ(instances2, 0);
+}
+
+template <template <class Alloc> class Container>
+void TestMoveAssignAllocPropagation() {
+ int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
+ MoveAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
+ MoveAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
+
+ // Test propagating allocator_type.
+ {
+ Container<MoveAssignPropagatingCountingAlloc<int>> c1(allocator1);
+ Container<MoveAssignPropagatingCountingAlloc<int>> c2(allocator2);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_NE(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+
+ c2 = std::move(c1);
+
+ EXPECT_EQ(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+ }
+ // Test non-propagating allocator_type with equal allocators.
+ {
+ Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_EQ(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+
+ c2 = std::move(c1);
+
+ EXPECT_EQ(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+ }
+ // Test non-propagating allocator_type with different allocators.
+ {
+ Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_NE(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+
+ c2 = std::move(c1);
+
+ EXPECT_EQ(c2.get_allocator(), allocator2);
+ EXPECT_LE(instances1, 100); // The values in c1 may or may not have been
+ // destroyed at this point.
+ EXPECT_EQ(instances2, 100);
+ }
+ EXPECT_EQ(bytes1, 0);
+ EXPECT_EQ(instances1, 0);
+ EXPECT_EQ(bytes2, 0);
+ EXPECT_EQ(instances2, 0);
+}
+
+template <template <class Alloc> class Container>
+void TestSwapAllocPropagation() {
+ int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
+ SwapPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
+ SwapPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
+
+ // Test propagating allocator_type.
+ {
+ Container<SwapPropagatingCountingAlloc<int>> c1(allocator1), c2(allocator2);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_NE(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+
+ c2.swap(c1);
+
+ EXPECT_EQ(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+ }
+ // Test non-propagating allocator_type with equal allocators.
+ {
+ Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_EQ(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+
+ c2.swap(c1);
+
+ EXPECT_EQ(c2.get_allocator(), allocator1);
+ EXPECT_EQ(instances1, 100);
+ EXPECT_EQ(instances2, 0);
+ }
+ // Test non-propagating allocator_type with different allocators.
+ {
+ Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
+
+ for (int i = 0; i < 100; ++i) c1.insert(i);
+
+ EXPECT_NE(c1.get_allocator(), c2.get_allocator());
+ if (IsAssertEnabled()) {
+ EXPECT_DEATH_IF_SUPPORTED(c2.swap(c1), "");
+ }
+ }
+ EXPECT_EQ(bytes1, 0);
+ EXPECT_EQ(instances1, 0);
+ EXPECT_EQ(bytes2, 0);
+ EXPECT_EQ(instances2, 0);
+}
+
+template <template <class Alloc> class Container>
+void TestAllocPropagation() {
+ TestCopyAssignAllocPropagation<Container>();
+ TestMoveAssignAllocPropagation<Container>();
+ TestSwapAllocPropagation<Container>();
+}
+
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
diff --git a/absl/crc/BUILD.bazel b/absl/crc/BUILD.bazel
index cdbaa9b2..f44c3f6b 100644
--- a/absl/crc/BUILD.bazel
+++ b/absl/crc/BUILD.bazel
@@ -19,7 +19,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:private"])
+package(
+ default_visibility = ["//visibility:private"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -54,10 +61,8 @@ cc_library(
visibility = ["//visibility:private"],
deps = [
":cpu_detect",
- "//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
- "//absl/base:dynamic_annotations",
"//absl/base:endian",
"//absl/base:prefetch",
"//absl/base:raw_logging_internal",
@@ -72,7 +77,7 @@ cc_library(
"crc32c.cc",
"internal/crc32c_inline.h",
"internal/crc_memcpy_fallback.cc",
- "internal/crc_memcpy_x86_64.cc",
+ "internal/crc_memcpy_x86_arm_combined.cc",
"internal/crc_non_temporal_memcpy.cc",
],
hdrs = [
@@ -89,7 +94,6 @@ cc_library(
":non_temporal_memcpy",
"//absl/base:config",
"//absl/base:core_headers",
- "//absl/base:dynamic_annotations",
"//absl/base:endian",
"//absl/base:prefetch",
"//absl/strings",
@@ -107,6 +111,7 @@ cc_test(
":crc32c",
"//absl/strings",
"//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -149,6 +154,7 @@ cc_test(
"//absl/random",
"//absl/random:distributions",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -161,6 +167,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":non_temporal_memcpy",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -189,6 +196,7 @@ cc_test(
deps = [
":crc32c",
":crc_cord_state",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/crc/CMakeLists.txt b/absl/crc/CMakeLists.txt
index 21247160..ec7b4512 100644
--- a/absl/crc/CMakeLists.txt
+++ b/absl/crc/CMakeLists.txt
@@ -42,10 +42,8 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::crc_cpu_detect
- absl::base
absl::config
absl::core_headers
- absl::dynamic_annotations
absl::endian
absl::prefetch
absl::raw_logging_internal
@@ -64,7 +62,7 @@ absl_cc_library(
"crc32c.cc"
"internal/crc32c_inline.h"
"internal/crc_memcpy_fallback.cc"
- "internal/crc_memcpy_x86_64.cc"
+ "internal/crc_memcpy_x86_arm_combined.cc"
"internal/crc_non_temporal_memcpy.cc"
COPTS
${ABSL_DEFAULT_COPTS}
@@ -74,7 +72,6 @@ absl_cc_library(
absl::non_temporal_memcpy
absl::config
absl::core_headers
- absl::dynamic_annotations
absl::endian
absl::prefetch
absl::str_format
diff --git a/absl/crc/internal/cpu_detect.cc b/absl/crc/internal/cpu_detect.cc
index 83838085..d7eedd1c 100644
--- a/absl/crc/internal/cpu_detect.cc
+++ b/absl/crc/internal/cpu_detect.cc
@@ -189,8 +189,14 @@ CpuType GetAmdCpuType() {
break;
case 0x19:
switch (model_num) {
+ case 0x0: // Stepping Ax
case 0x1: // Stepping B0
return CpuType::kAmdMilan;
+ case 0x10: // Stepping A0
+ case 0x11: // Stepping B0
+ return CpuType::kAmdGenoa;
+ case 0x44: // Stepping A0
+ return CpuType::kAmdRyzenV3000;
default:
return CpuType::kUnknown;
}
@@ -237,8 +243,26 @@ CpuType GetCpuType() {
ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr);
uint32_t implementer = (midr >> 24) & 0xff;
uint32_t part_number = (midr >> 4) & 0xfff;
- if (implementer == 0x41 && part_number == 0xd0c) {
- return CpuType::kArmNeoverseN1;
+ switch (implementer) {
+ case 0x41:
+ switch (part_number) {
+ case 0xd0c: return CpuType::kArmNeoverseN1;
+ case 0xd40: return CpuType::kArmNeoverseV1;
+ case 0xd49: return CpuType::kArmNeoverseN2;
+ case 0xd4f: return CpuType::kArmNeoverseV2;
+ default:
+ return CpuType::kUnknown;
+ }
+ break;
+ case 0xc0:
+ switch (part_number) {
+ case 0xac3: return CpuType::kAmpereSiryn;
+ default:
+ return CpuType::kUnknown;
+ }
+ break;
+ default:
+ return CpuType::kUnknown;
}
}
return CpuType::kUnknown;
diff --git a/absl/crc/internal/cpu_detect.h b/absl/crc/internal/cpu_detect.h
index 6054f696..01e19590 100644
--- a/absl/crc/internal/cpu_detect.h
+++ b/absl/crc/internal/cpu_detect.h
@@ -29,6 +29,8 @@ enum class CpuType {
kAmdRome,
kAmdNaples,
kAmdMilan,
+ kAmdGenoa,
+ kAmdRyzenV3000,
kIntelCascadelakeXeon,
kIntelSkylakeXeon,
kIntelBroadwell,
@@ -37,6 +39,10 @@ enum class CpuType {
kIntelSandybridge,
kIntelWestmere,
kArmNeoverseN1,
+ kArmNeoverseV1,
+ kAmpereSiryn,
+ kArmNeoverseN2,
+ kArmNeoverseV2
};
// Returns the type of host CPU this code is running on. Returns kUnknown if
diff --git a/absl/crc/internal/crc32_x86_arm_combined_simd.h b/absl/crc/internal/crc32_x86_arm_combined_simd.h
index 39e53dd0..59995ae3 100644
--- a/absl/crc/internal/crc32_x86_arm_combined_simd.h
+++ b/absl/crc/internal/crc32_x86_arm_combined_simd.h
@@ -59,6 +59,8 @@ namespace crc_internal {
#if defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
using V128 = uint64x2_t;
#else
+// Note: Do not use __m128i_u, it is not portable.
+// Use V128_LoadU() perform an unaligned load from __m128i*.
using V128 = __m128i;
#endif
@@ -78,6 +80,9 @@ V128 V128_Load(const V128* src);
// Load 128 bits of integer data. |src| does not need to be aligned.
V128 V128_LoadU(const V128* src);
+// Store 128 bits of integer data. |src| must be 16-byte aligned.
+void V128_Store(V128* dst, V128 data);
+
// Polynomially multiplies the high 64 bits of |l| and |r|.
V128 V128_PMulHi(const V128 l, const V128 r);
@@ -109,6 +114,10 @@ V128 V128_ShiftRight(const V128 l);
template <int imm>
int V128_Extract32(const V128 l);
+// Extracts a 64-bit integer from |l|, selected with |imm|.
+template <int imm>
+uint64_t V128_Extract64(const V128 l);
+
// Extracts the low 64 bits from V128.
int64_t V128_Low64(const V128 l);
@@ -139,6 +148,8 @@ inline V128 V128_Load(const V128* src) { return _mm_load_si128(src); }
inline V128 V128_LoadU(const V128* src) { return _mm_loadu_si128(src); }
+inline void V128_Store(V128* dst, V128 data) { _mm_store_si128(dst, data); }
+
inline V128 V128_PMulHi(const V128 l, const V128 r) {
return _mm_clmulepi64_si128(l, r, 0x11);
}
@@ -173,6 +184,11 @@ inline int V128_Extract32(const V128 l) {
return _mm_extract_epi32(l, imm);
}
+template <int imm>
+inline uint64_t V128_Extract64(const V128 l) {
+ return static_cast<uint64_t>(_mm_extract_epi64(l, imm));
+}
+
inline int64_t V128_Low64(const V128 l) { return _mm_cvtsi128_si64(l); }
inline V128 V128_ShiftLeft64(const V128 l, const V128 r) {
@@ -203,10 +219,14 @@ inline V128 V128_LoadU(const V128* src) {
return vld1q_u64(reinterpret_cast<const uint64_t*>(src));
}
+inline void V128_Store(V128* dst, V128 data) {
+ vst1q_u64(reinterpret_cast<uint64_t*>(dst), data);
+}
+
// Using inline assembly as clang does not generate the pmull2 instruction and
// performance drops by 15-20%.
-// TODO(b/193678732): Investigate why the compiler decides not to generate
-// such instructions and why it becomes so much worse.
+// TODO(b/193678732): Investigate why there is a slight performance hit when
+// using intrinsics instead of inline assembly.
inline V128 V128_PMulHi(const V128 l, const V128 r) {
uint64x2_t res;
__asm__ __volatile__("pmull2 %0.1q, %1.2d, %2.2d \n\t"
@@ -215,10 +235,14 @@ inline V128 V128_PMulHi(const V128 l, const V128 r) {
return res;
}
+// TODO(b/193678732): Investigate why the compiler decides to move the constant
+// loop multiplicands from GPR to Neon registers every loop iteration.
inline V128 V128_PMulLow(const V128 l, const V128 r) {
- return reinterpret_cast<V128>(vmull_p64(
- reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(l))),
- reinterpret_cast<poly64_t>(vget_low_p64(vreinterpretq_p64_u64(r)))));
+ uint64x2_t res;
+ __asm__ __volatile__("pmull %0.1q, %1.1d, %2.1d \n\t"
+ : "=w"(res)
+ : "w"(l), "w"(r));
+ return res;
}
inline V128 V128_PMul01(const V128 l, const V128 r) {
@@ -252,6 +276,11 @@ inline int V128_Extract32(const V128 l) {
return vgetq_lane_s32(vreinterpretq_s32_u64(l), imm);
}
+template <int imm>
+inline uint64_t V128_Extract64(const V128 l) {
+ return vgetq_lane_u64(l, imm);
+}
+
inline int64_t V128_Low64(const V128 l) {
return vgetq_lane_s64(vreinterpretq_s64_u64(l), 0);
}
diff --git a/absl/crc/internal/crc_memcpy.h b/absl/crc/internal/crc_memcpy.h
index 4909d433..a0fed65a 100644
--- a/absl/crc/internal/crc_memcpy.h
+++ b/absl/crc/internal/crc_memcpy.h
@@ -20,12 +20,15 @@
#include "absl/base/config.h"
#include "absl/crc/crc32c.h"
+#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
// Defined if the class AcceleratedCrcMemcpyEngine exists.
-#if defined(__x86_64__) && defined(__SSE4_2__)
-#define ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1
-#elif defined(_MSC_VER) && defined(__AVX__)
+// TODO(b/299127771): Consider relaxing the pclmul requirement once the other
+// intrinsics are conditionally compiled without it.
+#if defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
#define ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1
+#elif defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
+#define ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE 1
#endif
namespace absl {
diff --git a/absl/crc/internal/crc_memcpy_fallback.cc b/absl/crc/internal/crc_memcpy_fallback.cc
index 15b4b055..07795504 100644
--- a/absl/crc/internal/crc_memcpy_fallback.cc
+++ b/absl/crc/internal/crc_memcpy_fallback.cc
@@ -54,7 +54,8 @@ absl::crc32c_t FallbackCrcMemcpyEngine::Compute(void* __restrict dst,
}
// Compile the following only if we don't have
-#ifndef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#if !defined(ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE) && \
+ !defined(ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE)
CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
CrcMemcpy::ArchSpecificEngines engines;
@@ -68,7 +69,8 @@ std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int /*vector*/,
return std::make_unique<FallbackCrcMemcpyEngine>();
}
-#endif // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#endif // !ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE &&
+ // !ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE
} // namespace crc_internal
ABSL_NAMESPACE_END
diff --git a/absl/crc/internal/crc_memcpy_test.cc b/absl/crc/internal/crc_memcpy_test.cc
index bbdcd205..045a1e5e 100644
--- a/absl/crc/internal/crc_memcpy_test.cc
+++ b/absl/crc/internal/crc_memcpy_test.cc
@@ -33,7 +33,7 @@
namespace {
enum CrcEngine {
- X86 = 0,
+ ACCELERATED = 0,
NONTEMPORAL = 1,
FALLBACK = 2,
};
@@ -66,10 +66,10 @@ typedef CrcMemcpyTest<4500> CrcSmallTest;
typedef CrcMemcpyTest<(1 << 24)> CrcLargeTest;
// Parametrize the small test so that it can be done with all configurations.
template <typename ParamsT>
-class x86ParamTestTemplate : public CrcSmallTest,
- public ::testing::WithParamInterface<ParamsT> {
+class EngineParamTestTemplate : public CrcSmallTest,
+ public ::testing::WithParamInterface<ParamsT> {
protected:
- x86ParamTestTemplate() {
+ EngineParamTestTemplate() {
if (GetParam().crc_engine_selector == FALLBACK) {
engine_ = std::make_unique<absl::crc_internal::FallbackCrcMemcpyEngine>();
} else if (GetParam().crc_engine_selector == NONTEMPORAL) {
@@ -89,14 +89,14 @@ class x86ParamTestTemplate : public CrcSmallTest,
std::unique_ptr<absl::crc_internal::CrcMemcpyEngine> engine_;
};
struct TestParams {
- CrcEngine crc_engine_selector = X86;
+ CrcEngine crc_engine_selector = ACCELERATED;
int vector_lanes = 0;
int integer_lanes = 0;
};
-using x86ParamTest = x86ParamTestTemplate<TestParams>;
+using EngineParamTest = EngineParamTestTemplate<TestParams>;
// SmallCorrectness is designed to exercise every possible set of code paths
// in the memcpy code, not including the loop.
-TEST_P(x86ParamTest, SmallCorrectnessCheckSourceAlignment) {
+TEST_P(EngineParamTest, SmallCorrectnessCheckSourceAlignment) {
constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
for (size_t source_alignment = 0; source_alignment < kAlignment;
@@ -107,6 +107,9 @@ TEST_P(x86ParamTest, SmallCorrectnessCheckSourceAlignment) {
*(base_data + i) =
static_cast<char>(absl::Uniform<unsigned char>(gen_));
}
+ SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",",
+ GetParam().integer_lanes, ">, ", "size=", size,
+ ", source_alignment=", source_alignment));
absl::crc32c_t initial_crc =
absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
absl::crc32c_t experiment_crc =
@@ -127,7 +130,7 @@ TEST_P(x86ParamTest, SmallCorrectnessCheckSourceAlignment) {
}
}
-TEST_P(x86ParamTest, SmallCorrectnessCheckDestAlignment) {
+TEST_P(EngineParamTest, SmallCorrectnessCheckDestAlignment) {
constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
for (size_t dest_alignment = 0; dest_alignment < kAlignment;
@@ -138,6 +141,9 @@ TEST_P(x86ParamTest, SmallCorrectnessCheckDestAlignment) {
*(base_data + i) =
static_cast<char>(absl::Uniform<unsigned char>(gen_));
}
+ SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",",
+ GetParam().integer_lanes, ">, ", "size=", size,
+ ", destination_alignment=", dest_alignment));
absl::crc32c_t initial_crc =
absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
absl::crc32c_t experiment_crc =
@@ -157,10 +163,12 @@ TEST_P(x86ParamTest, SmallCorrectnessCheckDestAlignment) {
}
}
-INSTANTIATE_TEST_SUITE_P(x86ParamTest, x86ParamTest,
+INSTANTIATE_TEST_SUITE_P(EngineParamTest, EngineParamTest,
::testing::Values(
// Tests for configurations that may occur in prod.
- TestParams{X86, 3, 0}, TestParams{X86, 1, 2},
+ TestParams{ACCELERATED, 3, 0},
+ TestParams{ACCELERATED, 1, 2},
+ TestParams{ACCELERATED, 1, 0},
// Fallback test.
TestParams{FALLBACK, 0, 0},
// Non Temporal
diff --git a/absl/crc/internal/crc_memcpy_x86_64.cc b/absl/crc/internal/crc_memcpy_x86_arm_combined.cc
index d42b08dc..968e9ae3 100644
--- a/absl/crc/internal/crc_memcpy_x86_64.cc
+++ b/absl/crc/internal/crc_memcpy_x86_arm_combined.cc
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Simultaneous memcopy and CRC-32C for x86-64. Uses integer registers because
-// XMM registers do not support the CRC instruction (yet). While copying,
-// compute the running CRC of the data being copied.
+// Simultaneous memcopy and CRC-32C for x86-64 and ARM 64. Uses integer
+// registers because XMM registers do not support the CRC instruction (yet).
+// While copying, compute the running CRC of the data being copied.
//
// It is assumed that any CPU running this code has SSE4.2 instructions
// available (for CRC32C). This file will do nothing if that is not true.
@@ -49,17 +49,20 @@
#include <array>
#include <cstddef>
#include <cstdint>
-#include <type_traits>
+#include <cstring>
+#include <memory>
-#include "absl/base/dynamic_annotations.h"
+#include "absl/base/config.h"
#include "absl/base/optimization.h"
#include "absl/base/prefetch.h"
#include "absl/crc/crc32c.h"
#include "absl/crc/internal/cpu_detect.h"
+#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
#include "absl/crc/internal/crc_memcpy.h"
#include "absl/strings/string_view.h"
-#ifdef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#if defined(ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE) || \
+ defined(ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE)
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -74,7 +77,7 @@ inline crc32c_t ShortCrcCopy(char* dst, const char* src, std::size_t length,
uint32_t crc_uint32 = static_cast<uint32_t>(crc);
for (std::size_t i = 0; i < length; i++) {
uint8_t data = *reinterpret_cast<const uint8_t*>(src);
- crc_uint32 = _mm_crc32_u8(crc_uint32, data);
+ crc_uint32 = CRC32_u8(crc_uint32, data);
*reinterpret_cast<uint8_t*>(dst) = data;
++src;
++dst;
@@ -82,36 +85,35 @@ inline crc32c_t ShortCrcCopy(char* dst, const char* src, std::size_t length,
return crc32c_t{crc_uint32};
}
-constexpr size_t kIntLoadsPerVec = sizeof(__m128i) / sizeof(uint64_t);
+constexpr size_t kIntLoadsPerVec = sizeof(V128) / sizeof(uint64_t);
// Common function for copying the tails of multiple large regions.
template <size_t vec_regions, size_t int_regions>
inline void LargeTailCopy(crc32c_t* crcs, char** dst, const char** src,
size_t region_size, size_t copy_rounds) {
- std::array<__m128i, vec_regions> data;
+ std::array<V128, vec_regions> data;
std::array<uint64_t, kIntLoadsPerVec * int_regions> int_data;
while (copy_rounds > 0) {
for (size_t i = 0; i < vec_regions; i++) {
size_t region = i;
- auto* vsrc =
- reinterpret_cast<const __m128i*>(*src + region_size * region);
- auto* vdst = reinterpret_cast<__m128i*>(*dst + region_size * region);
+ auto* vsrc = reinterpret_cast<const V128*>(*src + region_size * region);
+ auto* vdst = reinterpret_cast<V128*>(*dst + region_size * region);
// Load the blocks, unaligned
- data[i] = _mm_loadu_si128(vsrc);
+ data[i] = V128_LoadU(vsrc);
// Store the blocks, aligned
- _mm_store_si128(vdst, data[i]);
+ V128_Store(vdst, data[i]);
// Compute the running CRC
crcs[region] = crc32c_t{static_cast<uint32_t>(
- _mm_crc32_u64(static_cast<uint32_t>(crcs[region]),
- static_cast<uint64_t>(_mm_extract_epi64(data[i], 0))))};
+ CRC32_u64(static_cast<uint32_t>(crcs[region]),
+ static_cast<uint64_t>(V128_Extract64<0>(data[i]))))};
crcs[region] = crc32c_t{static_cast<uint32_t>(
- _mm_crc32_u64(static_cast<uint32_t>(crcs[region]),
- static_cast<uint64_t>(_mm_extract_epi64(data[i], 1))))};
+ CRC32_u64(static_cast<uint32_t>(crcs[region]),
+ static_cast<uint64_t>(V128_Extract64<1>(data[i]))))};
}
for (size_t i = 0; i < int_regions; i++) {
@@ -125,7 +127,7 @@ inline void LargeTailCopy(crc32c_t* crcs, char** dst, const char** src,
size_t data_index = i * kIntLoadsPerVec + j;
int_data[data_index] = *(usrc + j);
- crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+ crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
*(udst + j) = int_data[data_index];
@@ -133,8 +135,8 @@ inline void LargeTailCopy(crc32c_t* crcs, char** dst, const char** src,
}
// Increment pointers
- *src += sizeof(__m128i);
- *dst += sizeof(__m128i);
+ *src += sizeof(V128);
+ *dst += sizeof(V128);
--copy_rounds;
}
}
@@ -158,8 +160,9 @@ crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
void* __restrict dst, const void* __restrict src, std::size_t length,
crc32c_t initial_crc) const {
constexpr std::size_t kRegions = vec_regions + int_regions;
+ static_assert(kRegions > 0, "Must specify at least one region.");
constexpr uint32_t kCrcDataXor = uint32_t{0xffffffff};
- constexpr std::size_t kBlockSize = sizeof(__m128i);
+ constexpr std::size_t kBlockSize = sizeof(V128);
constexpr std::size_t kCopyRoundSize = kRegions * kBlockSize;
// Number of blocks per cacheline.
@@ -235,7 +238,7 @@ crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
const std::size_t tail_size = length - (kRegions * region_size);
// Holding registers for data in each region.
- std::array<__m128i, vec_regions> vec_data;
+ std::array<V128, vec_regions> vec_data;
std::array<uint64_t, int_regions * kIntLoadsPerVec> int_data;
// Main loop.
@@ -243,7 +246,10 @@ crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
// Prefetch kPrefetchAhead bytes ahead of each pointer.
for (size_t i = 0; i < kRegions; i++) {
absl::PrefetchToLocalCache(src_bytes + kPrefetchAhead + region_size * i);
+#ifdef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+ // TODO(b/297082454): investigate dropping prefetch on x86.
absl::PrefetchToLocalCache(dst_bytes + kPrefetchAhead + region_size * i);
+#endif
}
// Load and store data, computing CRC on the way.
@@ -256,21 +262,20 @@ crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
size_t region = (j + i) % kRegions;
auto* vsrc =
- reinterpret_cast<const __m128i*>(src_bytes + region_size * region);
- auto* vdst =
- reinterpret_cast<__m128i*>(dst_bytes + region_size * region);
+ reinterpret_cast<const V128*>(src_bytes + region_size * region);
+ auto* vdst = reinterpret_cast<V128*>(dst_bytes + region_size * region);
// Load and CRC data.
- vec_data[j] = _mm_loadu_si128(vsrc + i);
- crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
- static_cast<uint32_t>(crcs[region]),
- static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 0))))};
- crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
- static_cast<uint32_t>(crcs[region]),
- static_cast<uint64_t>(_mm_extract_epi64(vec_data[j], 1))))};
+ vec_data[j] = V128_LoadU(vsrc + i);
+ crcs[region] = crc32c_t{static_cast<uint32_t>(
+ CRC32_u64(static_cast<uint32_t>(crcs[region]),
+ static_cast<uint64_t>(V128_Extract64<0>(vec_data[j]))))};
+ crcs[region] = crc32c_t{static_cast<uint32_t>(
+ CRC32_u64(static_cast<uint32_t>(crcs[region]),
+ static_cast<uint64_t>(V128_Extract64<1>(vec_data[j]))))};
// Store the data.
- _mm_store_si128(vdst + i, vec_data[j]);
+ V128_Store(vdst + i, vec_data[j]);
}
// Preload the partial CRCs for the CLMUL subregions.
@@ -290,7 +295,7 @@ crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
// Load and CRC the data.
int_data[data_index] = *(usrc + i * kIntLoadsPerVec + k);
- crcs[region] = crc32c_t{static_cast<uint32_t>(_mm_crc32_u64(
+ crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
// Store the data.
@@ -313,6 +318,21 @@ crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
src_bytes += region_size * (kRegions - 1);
dst_bytes += region_size * (kRegions - 1);
+ // Copy and CRC the tail through the XMM registers.
+ std::size_t tail_blocks = tail_size / kBlockSize;
+ LargeTailCopy<0, 1>(&crcs[kRegions - 1], &dst_bytes, &src_bytes, 0,
+ tail_blocks);
+
+ // Final tail copy for under 16 bytes.
+ crcs[kRegions - 1] =
+ ShortCrcCopy(dst_bytes, src_bytes, tail_size - tail_blocks * kBlockSize,
+ crcs[kRegions - 1]);
+
+ if (kRegions == 1) {
+ // If there is only one region, finalize and return its CRC.
+ return crc32c_t{static_cast<uint32_t>(crcs[0]) ^ kCrcDataXor};
+ }
+
// Finalize the first CRCs: XOR the internal CRCs by the XOR mask to undo the
// XOR done before doing block copy + CRCs.
for (size_t i = 0; i + 1 < kRegions; i++) {
@@ -325,16 +345,6 @@ crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
full_crc = ConcatCrc32c(full_crc, crcs[i], region_size);
}
- // Copy and CRC the tail through the XMM registers.
- std::size_t tail_blocks = tail_size / kBlockSize;
- LargeTailCopy<0, 1>(&crcs[kRegions - 1], &dst_bytes, &src_bytes, 0,
- tail_blocks);
-
- // Final tail copy for under 16 bytes.
- crcs[kRegions - 1] =
- ShortCrcCopy(dst_bytes, src_bytes, tail_size - tail_blocks * kBlockSize,
- crcs[kRegions - 1]);
-
// Finalize and concatenate the final CRC, then return.
crcs[kRegions - 1] =
crc32c_t{static_cast<uint32_t>(crcs[kRegions - 1]) ^ kCrcDataXor};
@@ -347,9 +357,11 @@ CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
// Get the underlying architecture.
CpuType cpu_type = GetCpuType();
switch (cpu_type) {
- case CpuType::kUnknown:
case CpuType::kAmdRome:
case CpuType::kAmdNaples:
+ case CpuType::kAmdMilan:
+ case CpuType::kAmdGenoa:
+ case CpuType::kAmdRyzenV3000:
case CpuType::kIntelCascadelakeXeon:
case CpuType::kIntelSkylakeXeon:
case CpuType::kIntelSkylake:
@@ -385,6 +397,9 @@ CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
// strided access to each region, and do the right thing.
case CpuType::kAmdRome:
case CpuType::kAmdNaples:
+ case CpuType::kAmdMilan:
+ case CpuType::kAmdGenoa:
+ case CpuType::kAmdRyzenV3000:
return {
/*.temporal=*/new AcceleratedCrcMemcpyEngine<1, 2>(),
/*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
@@ -421,6 +436,8 @@ std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int vector,
return std::make_unique<AcceleratedCrcMemcpyEngine<3, 0>>();
} else if (vector == 1 && integer == 2) {
return std::make_unique<AcceleratedCrcMemcpyEngine<1, 2>>();
+ } else if (vector == 1 && integer == 0) {
+ return std::make_unique<AcceleratedCrcMemcpyEngine<1, 0>>();
}
return nullptr;
}
@@ -429,4 +446,5 @@ std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int vector,
ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
+#endif // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE ||
+ // ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE
diff --git a/absl/crc/internal/crc_x86_arm_combined.cc b/absl/crc/internal/crc_x86_arm_combined.cc
index ef521d22..51eff4ed 100644
--- a/absl/crc/internal/crc_x86_arm_combined.cc
+++ b/absl/crc/internal/crc_x86_arm_combined.cc
@@ -16,14 +16,14 @@
#include <cstddef>
#include <cstdint>
+#include <memory>
+#include <vector>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
-#include "absl/base/dynamic_annotations.h"
#include "absl/base/internal/endian.h"
#include "absl/base/prefetch.h"
#include "absl/crc/internal/cpu_detect.h"
-#include "absl/crc/internal/crc.h"
#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
#include "absl/crc/internal/crc_internal.h"
#include "absl/memory/memory.h"
@@ -634,8 +634,16 @@ CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() {
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
3, 0, CutoffStrategy::Fold3>();
case CpuType::kArmNeoverseN1:
+ case CpuType::kArmNeoverseN2:
+ case CpuType::kArmNeoverseV1:
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
1, 1, CutoffStrategy::Unroll64CRC>();
+ case CpuType::kAmpereSiryn:
+ return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+ 3, 2, CutoffStrategy::Fold3>();
+ case CpuType::kArmNeoverseV2:
+ return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
+ 1, 2, CutoffStrategy::Unroll64CRC>();
#if defined(__aarch64__)
default:
// Not all ARM processors support the needed instructions, so check here
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index 42124bfb..5baff7a1 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -23,6 +23,11 @@ load(
package(
default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
)
licenses(["notice"])
@@ -62,6 +67,7 @@ cc_test(
deps = [
":stacktrace",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -217,7 +223,10 @@ cc_library(
hdrs = ["internal/demangle.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
- visibility = ["//visibility:private"],
+ visibility = [
+ "//absl/container:__pkg__",
+ "//absl/debugging:__pkg__",
+ ],
deps = [
"//absl/base",
"//absl/base:config",
@@ -237,6 +246,7 @@ cc_test(
"//absl/base:core_headers",
"//absl/log",
"//absl/memory",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -263,6 +273,7 @@ cc_test(
":leak_check",
"//absl/base:config",
"//absl/log",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -280,6 +291,7 @@ cc_binary(
deps = [
":leak_check",
"//absl/log",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -309,6 +321,7 @@ cc_test(
":stack_consumption",
"//absl/base:core_headers",
"//absl/log",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index 992c89c3..570d1e50 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -54,7 +54,7 @@
#include "absl/debugging/internal/examine_stack.h"
#include "absl/debugging/stacktrace.h"
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__wasi__)
#define ABSL_HAVE_SIGACTION
// Apple WatchOS and TVOS don't allow sigaltstack
// Apple macOS has sigaltstack, but using it makes backtrace() unusable.
diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc
index 91eaa76f..be17a105 100644
--- a/absl/debugging/internal/address_is_readable.cc
+++ b/absl/debugging/internal/address_is_readable.cc
@@ -50,8 +50,10 @@ namespace debugging_internal {
// NOTE: any new system calls here may also require sandbox reconfiguration.
//
bool AddressIsReadable(const void *addr) {
- // Align address on 8-byte boundary. On aarch64, checking last
- // byte before inaccessible page returned unexpected EFAULT.
+ // rt_sigprocmask below checks 8 contiguous bytes. If addr resides in the
+ // last 7 bytes of a page (unaligned), rt_sigprocmask would additionally
+ // check the readability of the next page, which is not desired. Align
+ // address on 8-byte boundary to check only the current page.
const uintptr_t u_addr = reinterpret_cast<uintptr_t>(addr) & ~uintptr_t{7};
addr = reinterpret_cast<const void *>(u_addr);
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index f2832915..381a2b50 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -21,7 +21,15 @@
#include <cstdint>
#include <cstdio>
+#include <cstdlib>
#include <limits>
+#include <string>
+
+#include "absl/base/config.h"
+
+#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
+#include <cxxabi.h>
+#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -1983,6 +1991,22 @@ bool Demangle(const char* mangled, char* out, size_t out_size) {
state.parse_state.out_cur_idx > 0;
}
+std::string DemangleString(const char* mangled) {
+ std::string out;
+ int status = 0;
+ char* demangled = nullptr;
+#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
+ demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status);
+#endif
+ if (status == 0 && demangled != nullptr) {
+ out.append(demangled);
+ free(demangled);
+ } else {
+ out.append(mangled);
+ }
+ return out;
+}
+
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h
index e1f15698..146d1150 100644
--- a/absl/debugging/internal/demangle.h
+++ b/absl/debugging/internal/demangle.h
@@ -12,13 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// An async-signal-safe and thread-safe demangler for Itanium C++ ABI
-// (aka G++ V3 ABI).
+#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
+#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
+
+#include <string>
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+
+// Demangle `mangled`. On success, return true and write the
+// demangled symbol name to `out`. Otherwise, return false.
+// `out` is modified even if demangling is unsuccessful.
//
-// The demangler is implemented to be used in async signal handlers to
-// symbolize stack traces. We cannot use libstdc++'s
-// abi::__cxa_demangle() in such signal handlers since it's not async
-// signal safe (it uses malloc() internally).
+// This function provides an alternative to libstdc++'s abi::__cxa_demangle,
+// which is not async signal safe (it uses malloc internally). It's intended to
+// be used in async signal handlers to symbolize stack traces.
//
// Note that this demangler doesn't support full demangling. More
// specifically, it doesn't print types of function parameters and
@@ -30,40 +40,32 @@
//
// Example:
//
-// | Mangled Name | The Demangler | abi::__cxa_demangle()
-// |---------------|---------------|-----------------------
-// | _Z1fv | f() | f()
-// | _Z1fi | f() | f(int)
-// | _Z3foo3bar | foo() | foo(bar)
-// | _Z1fIiEvi | f<>() | void f<int>(int)
-// | _ZN1N1fE | N::f | N::f
-// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar()
-// | _Zrm1XS_" | operator%() | operator%(X, X)
-// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo()
-// | _Z1fSs | f() | f(std::basic_string<char,
-// | | | std::char_traits<char>,
-// | | | std::allocator<char> >)
+// | Mangled Name | Demangle | DemangleString
+// |---------------|-------------|-----------------------
+// | _Z1fv | f() | f()
+// | _Z1fi | f() | f(int)
+// | _Z3foo3bar | foo() | foo(bar)
+// | _Z1fIiEvi | f<>() | void f<int>(int)
+// | _ZN1N1fE | N::f | N::f
+// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar()
+// | _Zrm1XS_" | operator%() | operator%(X, X)
+// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo()
+// | _Z1fSs | f() | f(std::basic_string<char,
+// | | | std::char_traits<char>,
+// | | | std::allocator<char> >)
//
// See the unit test for more examples.
//
// Note: we might want to write demanglers for ABIs other than Itanium
// C++ ABI in the future.
-//
-
-#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
-
-#include "absl/base/config.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace debugging_internal {
-
-// Demangle `mangled`. On success, return true and write the
-// demangled symbol name to `out`. Otherwise, return false.
-// `out` is modified even if demangling is unsuccessful.
bool Demangle(const char* mangled, char* out, size_t out_size);
+// A wrapper around `abi::__cxa_demangle()`. On success, returns the demangled
+// name. On failure, returns the input mangled name.
+//
+// This function is not async-signal-safe.
+std::string DemangleString(const char* mangled);
+
} // namespace debugging_internal
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index faec72b5..a16ab75e 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -17,6 +17,7 @@
#include <cstdlib>
#include <string>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/config.h"
#include "absl/debugging/internal/stack_consumption.h"
@@ -28,15 +29,7 @@ ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
-// A wrapper function for Demangle() to make the unit test simple.
-static const char *DemangleIt(const char * const mangled) {
- static char demangled[4096];
- if (Demangle(mangled, demangled, sizeof(demangled))) {
- return demangled;
- } else {
- return mangled;
- }
-}
+using ::testing::ContainsRegex;
// Test corner cases of boundary conditions.
TEST(Demangle, CornerCases) {
@@ -237,6 +230,25 @@ TEST(DemangleRegression, DeeplyNestedArrayType) {
TestOnInput(data.c_str());
}
+struct Base {
+ virtual ~Base() = default;
+};
+
+struct Derived : public Base {};
+
+TEST(DemangleStringTest, SupportsSymbolNameReturnedByTypeId) {
+ EXPECT_EQ(DemangleString(typeid(int).name()), "int");
+ // We want to test that `DemangleString` can demangle the symbol names
+ // returned by `typeid`, but without hard-coding the actual demangled values
+ // (because they are platform-specific).
+ EXPECT_THAT(
+ DemangleString(typeid(Base).name()),
+ ContainsRegex("absl.*debugging_internal.*anonymous namespace.*::Base"));
+ EXPECT_THAT(DemangleString(typeid(Derived).name()),
+ ContainsRegex(
+ "absl.*debugging_internal.*anonymous namespace.*::Derived"));
+}
+
} // namespace
} // namespace debugging_internal
ABSL_NAMESPACE_END
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index 3f40beac..b54a1b28 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -185,4 +185,22 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
ABSL_NAMESPACE_END
} // namespace absl
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace debugging_internal {
+extern const char kAvoidEmptyStackConsumptionLibraryWarning;
+const char kAvoidEmptyStackConsumptionLibraryWarning = 0;
+} // namespace debugging_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // __APPLE__
+
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc
index 3f087162..1caf7bbe 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -4,6 +4,7 @@
// Generate stack tracer for aarch64
#if defined(__linux__)
+#include <signal.h>
#include <sys/mman.h>
#include <ucontext.h>
#include <unistd.h>
@@ -70,7 +71,7 @@ static const unsigned char* GetKernelRtSigreturnAddress() {
// Compute the size of a stack frame in [low..high). We assume that
// low < high. Return size of kUnknownFrameSize.
template<typename T>
-static inline size_t ComputeStackFrameSize(const T* low,
+static size_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);
@@ -78,6 +79,20 @@ static inline size_t ComputeStackFrameSize(const T* low,
: kUnknownFrameSize;
}
+// Saves stack info that is expensive to calculate to avoid recalculating per frame.
+struct StackInfo {
+ uintptr_t stack_low;
+ uintptr_t stack_high;
+ uintptr_t sig_stack_low;
+ uintptr_t sig_stack_high;
+};
+
+static bool InsideSignalStack(void** ptr, const StackInfo* stack_info) {
+ uintptr_t comparable_ptr = reinterpret_cast<uintptr_t>(ptr);
+ return (comparable_ptr >= stack_info->sig_stack_low &&
+ comparable_ptr < stack_info->sig_stack_high);
+}
+
// 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
@@ -86,9 +101,8 @@ 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,
- size_t stack_low, size_t stack_high) {
+ const StackInfo *stack_info) {
void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer);
- bool check_frame_size = true;
#if defined(__linux__)
if (WITH_CONTEXT && uc != nullptr) {
@@ -114,10 +128,6 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
if (!absl::debugging_internal::AddressIsReadable(
new_frame_pointer))
return nullptr;
-
- // Skip frame size check if we return from a signal. We may be using a
- // an alternate stack for signals.
- check_frame_size = false;
}
}
#endif
@@ -126,9 +136,11 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 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) {
+ // Only check the size if both frames are in the same stack.
+ if (InsideSignalStack(new_frame_pointer, stack_info) ==
+ InsideSignalStack(old_frame_pointer, stack_info)) {
+ // 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.
const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
const size_t frame_size =
ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
@@ -136,15 +148,21 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
return nullptr;
// A very large frame may mean corrupt memory or an erroneous frame
// pointer. But also maybe just a plain-old large frame. Assume that if the
- // frame is within the known stack, then it is valid.
+ // frame is within a known stack, then it is valid.
if (frame_size > max_size) {
- if (stack_high < kUnknownStackEnd &&
+ size_t stack_low = stack_info->stack_low;
+ size_t stack_high = stack_info->stack_high;
+ if (InsideSignalStack(new_frame_pointer, stack_info)) {
+ stack_low = stack_info->sig_stack_low;
+ stack_high = stack_info->sig_stack_high;
+ }
+ if (stack_high < kUnknownStackEnd &&
static_cast<size_t>(getpagesize()) < stack_low) {
const uintptr_t new_fp_u =
reinterpret_cast<uintptr_t>(new_frame_pointer);
// Stack bounds are known.
if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) {
- // new_frame_pointer is not within the known stack.
+ // new_frame_pointer is not within a known stack.
return nullptr;
}
} else {
@@ -158,6 +176,9 @@ static void **NextStackFrame(void **old_frame_pointer, const void *uc,
}
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
+// We count on the bottom frame being this one. See the comment
+// at prev_return_address
+ABSL_ATTRIBUTE_NOINLINE
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,
@@ -171,8 +192,11 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
int n = 0;
// Assume that the first page is not stack.
- size_t stack_low = static_cast<size_t>(getpagesize());
- size_t stack_high = kUnknownStackEnd;
+ StackInfo stack_info;
+ stack_info.stack_low = static_cast<uintptr_t>(getpagesize());
+ stack_info.stack_high = kUnknownStackEnd;
+ stack_info.sig_stack_low = stack_info.stack_low;
+ stack_info.sig_stack_high = kUnknownStackEnd;
// The frame pointer points to low address of a frame. The first 64-bit
// word of a frame points to the next frame up the call chain, which normally
@@ -207,7 +231,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
// that is as complete as possible (even if it contains a few bogus
// entries in some rare cases).
frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
- frame_pointer, ucp, stack_low, stack_high);
+ frame_pointer, ucp, &stack_info);
}
if (min_dropped_frames != nullptr) {
@@ -222,7 +246,7 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
num_dropped_frames++;
}
frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(
- frame_pointer, ucp, stack_low, stack_high);
+ frame_pointer, ucp, &stack_info);
}
*min_dropped_frames = num_dropped_frames;
}
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc
index 30638cb2..ae75cd41 100644
--- a/absl/debugging/symbolize_elf.inc
+++ b/absl/debugging/symbolize_elf.inc
@@ -289,6 +289,30 @@ ObjFile *AddrMap::Add() {
return new (&obj_[size_++]) ObjFile;
}
+class CachingFile {
+ public:
+ // Setup reader for fd that uses buf[0, buf_size-1] as a cache.
+ CachingFile(int fd, char *buf, size_t buf_size)
+ : fd_(fd),
+ cache_(buf),
+ cache_size_(buf_size),
+ cache_start_(0),
+ cache_limit_(0) {}
+
+ int fd() const { return fd_; }
+ ssize_t ReadFromOffset(void *buf, size_t count, off_t offset);
+ bool ReadFromOffsetExact(void *buf, size_t count, off_t offset);
+
+ private:
+ // Bytes [cache_start_, cache_limit_-1] from fd_ are stored in
+ // a prefix of cache_[0, cache_size_-1].
+ int fd_;
+ char *cache_;
+ size_t cache_size_;
+ off_t cache_start_;
+ off_t cache_limit_;
+};
+
// ---------------------------------------------------------------
enum FindSymbolResult { SYMBOL_NOT_FOUND = 1, SYMBOL_TRUNCATED, SYMBOL_FOUND };
@@ -330,6 +354,7 @@ class Symbolizer {
SYMBOL_BUF_SIZE = 3072,
TMP_BUF_SIZE = 1024,
SYMBOL_CACHE_LINES = 128,
+ FILE_CACHE_SIZE = 8192,
};
AddrMap addr_map_;
@@ -338,6 +363,7 @@ class Symbolizer {
bool addr_map_read_;
char symbol_buf_[SYMBOL_BUF_SIZE];
+ char file_cache_[FILE_CACHE_SIZE];
// tmp_buf_ will be used to store arrays of ElfW(Shdr) and ElfW(Sym)
// so we ensure that tmp_buf_ is properly aligned to store either.
@@ -436,34 +462,58 @@ static ssize_t ReadPersistent(int fd, void *buf, size_t count) {
return static_cast<ssize_t>(num_bytes);
}
-// Read up to "count" bytes from "offset" in the file pointed by file
-// descriptor "fd" into the buffer starting at "buf". On success,
-// return the number of bytes read. Otherwise, return -1.
-static ssize_t ReadFromOffset(const int fd, void *buf, const size_t count,
- const off_t offset) {
- off_t off = lseek(fd, offset, SEEK_SET);
- if (off == (off_t)-1) {
- ABSL_RAW_LOG(WARNING, "lseek(%d, %jd, SEEK_SET) failed: errno=%d", fd,
- static_cast<intmax_t>(offset), errno);
- return -1;
+// Read up to "count" bytes from "offset" into the buffer starting at "buf",
+// while handling short reads and EINTR. On success, return the number of bytes
+// read. Otherwise, return -1.
+ssize_t CachingFile::ReadFromOffset(void *buf, size_t count, off_t offset) {
+ char *dst = static_cast<char *>(buf);
+ size_t read = 0;
+ while (read < count) {
+ // Look in cache first.
+ if (offset >= cache_start_ && offset < cache_limit_) {
+ const char *hit_start = &cache_[offset - cache_start_];
+ const size_t n =
+ std::min(count - read, static_cast<size_t>(cache_limit_ - offset));
+ memcpy(dst, hit_start, n);
+ dst += n;
+ read += static_cast<size_t>(n);
+ offset += static_cast<off_t>(n);
+ continue;
+ }
+
+ cache_start_ = 0;
+ cache_limit_ = 0;
+ ssize_t n = pread(fd_, cache_, cache_size_, offset);
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ ABSL_RAW_LOG(WARNING, "read failed: errno=%d", errno);
+ return -1;
+ }
+ if (n == 0) { // Reached EOF.
+ break;
+ }
+
+ cache_start_ = offset;
+ cache_limit_ = offset + static_cast<off_t>(n);
+ // Next iteration will copy from cache into dst.
}
- return ReadPersistent(fd, buf, count);
+ return static_cast<ssize_t>(read);
}
-// Try reading exactly "count" bytes from "offset" bytes in a file
-// pointed by "fd" into the buffer starting at "buf" while handling
-// short reads and EINTR. On success, return true. Otherwise, return
-// false.
-static bool ReadFromOffsetExact(const int fd, void *buf, const size_t count,
- const off_t offset) {
- ssize_t len = ReadFromOffset(fd, buf, count, offset);
+// Try reading exactly "count" bytes from "offset" bytes into the buffer
+// starting at "buf" while handling short reads and EINTR. On success, return
+// true. Otherwise, return false.
+bool CachingFile::ReadFromOffsetExact(void *buf, size_t count, off_t offset) {
+ ssize_t len = ReadFromOffset(buf, count, offset);
return len >= 0 && static_cast<size_t>(len) == count;
}
// Returns elf_header.e_type if the file pointed by fd is an ELF binary.
-static int FileGetElfType(const int fd) {
+static int FileGetElfType(CachingFile *file) {
ElfW(Ehdr) elf_header;
- if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+ if (!file->ReadFromOffsetExact(&elf_header, sizeof(elf_header), 0)) {
return -1;
}
if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
@@ -478,8 +528,8 @@ static int FileGetElfType(const int fd) {
// To keep stack consumption low, we would like this function to not get
// inlined.
static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(
- const int fd, ElfW(Half) sh_num, const off_t sh_offset, ElfW(Word) type,
- ElfW(Shdr) * out, char *tmp_buf, size_t tmp_buf_size) {
+ CachingFile *file, ElfW(Half) sh_num, const off_t sh_offset,
+ ElfW(Word) type, ElfW(Shdr) * out, char *tmp_buf, size_t tmp_buf_size) {
ElfW(Shdr) *buf = reinterpret_cast<ElfW(Shdr) *>(tmp_buf);
const size_t buf_entries = tmp_buf_size / sizeof(buf[0]);
const size_t buf_bytes = buf_entries * sizeof(buf[0]);
@@ -490,7 +540,7 @@ static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(
const size_t num_bytes_to_read =
(buf_bytes > num_bytes_left) ? num_bytes_left : buf_bytes;
const off_t offset = sh_offset + static_cast<off_t>(i * sizeof(buf[0]));
- const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, offset);
+ const ssize_t len = file->ReadFromOffset(buf, num_bytes_to_read, offset);
if (len < 0) {
ABSL_RAW_LOG(
WARNING,
@@ -524,11 +574,17 @@ static ABSL_ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(
// but there has (as yet) been no need for anything longer either.
const int kMaxSectionNameLen = 64;
+// Small cache to use for miscellaneous file reads.
+const int kSmallFileCacheSize = 100;
+
bool ForEachSection(int fd,
const std::function<bool(absl::string_view name,
const ElfW(Shdr) &)> &callback) {
+ char buf[kSmallFileCacheSize];
+ CachingFile file(fd, buf, sizeof(buf));
+
ElfW(Ehdr) elf_header;
- if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+ if (!file.ReadFromOffsetExact(&elf_header, sizeof(elf_header), 0)) {
return false;
}
@@ -540,7 +596,7 @@ bool ForEachSection(int fd,
ElfW(Shdr) shstrtab;
off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
elf_header.e_shentsize * elf_header.e_shstrndx;
- if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
+ if (!file.ReadFromOffsetExact(&shstrtab, sizeof(shstrtab), shstrtab_offset)) {
return false;
}
@@ -548,13 +604,13 @@ bool ForEachSection(int fd,
ElfW(Shdr) out;
off_t section_header_offset =
static_cast<off_t>(elf_header.e_shoff) + elf_header.e_shentsize * i;
- if (!ReadFromOffsetExact(fd, &out, sizeof(out), section_header_offset)) {
+ if (!file.ReadFromOffsetExact(&out, sizeof(out), section_header_offset)) {
return false;
}
off_t name_offset = static_cast<off_t>(shstrtab.sh_offset) + out.sh_name;
char header_name[kMaxSectionNameLen];
ssize_t n_read =
- ReadFromOffset(fd, &header_name, kMaxSectionNameLen, name_offset);
+ file.ReadFromOffset(&header_name, kMaxSectionNameLen, name_offset);
if (n_read < 0) {
return false;
} else if (n_read > kMaxSectionNameLen) {
@@ -584,8 +640,10 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
return false;
}
+ char buf[kSmallFileCacheSize];
+ CachingFile file(fd, buf, sizeof(buf));
ElfW(Ehdr) elf_header;
- if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+ if (!file.ReadFromOffsetExact(&elf_header, sizeof(elf_header), 0)) {
return false;
}
@@ -597,18 +655,18 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
ElfW(Shdr) shstrtab;
off_t shstrtab_offset = static_cast<off_t>(elf_header.e_shoff) +
elf_header.e_shentsize * elf_header.e_shstrndx;
- if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
+ if (!file.ReadFromOffsetExact(&shstrtab, sizeof(shstrtab), shstrtab_offset)) {
return false;
}
for (int i = 0; i < elf_header.e_shnum; ++i) {
off_t section_header_offset =
static_cast<off_t>(elf_header.e_shoff) + elf_header.e_shentsize * i;
- if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) {
+ if (!file.ReadFromOffsetExact(out, sizeof(*out), section_header_offset)) {
return false;
}
off_t name_offset = static_cast<off_t>(shstrtab.sh_offset) + out->sh_name;
- ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset);
+ ssize_t n_read = file.ReadFromOffset(&header_name, name_len, name_offset);
if (n_read < 0) {
return false;
} else if (static_cast<size_t>(n_read) != name_len) {
@@ -683,7 +741,7 @@ static const char *ComputeOffset(const char *base, ptrdiff_t offset) {
// To keep stack consumption low, we would like this function to not get
// inlined.
static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
- const void *const pc, const int fd, char *out, size_t out_size,
+ const void *const pc, CachingFile *file, char *out, size_t out_size,
ptrdiff_t relocation, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab,
const ElfW(Shdr) * opd, char *tmp_buf, size_t tmp_buf_size) {
if (symtab == nullptr) {
@@ -716,7 +774,7 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
const size_t entries_in_chunk =
std::min(num_remaining_symbols, buf_entries);
const size_t bytes_in_chunk = entries_in_chunk * sizeof(buf[0]);
- const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset);
+ const ssize_t len = file->ReadFromOffset(buf, bytes_in_chunk, offset);
SAFE_ASSERT(len >= 0);
SAFE_ASSERT(static_cast<size_t>(len) % sizeof(buf[0]) == 0);
const size_t num_symbols_in_buf = static_cast<size_t>(len) / sizeof(buf[0]);
@@ -772,12 +830,12 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
if (found_match) {
const off_t off =
static_cast<off_t>(strtab->sh_offset) + best_match.st_name;
- const ssize_t n_read = ReadFromOffset(fd, out, out_size, off);
+ const ssize_t n_read = file->ReadFromOffset(out, out_size, off);
if (n_read <= 0) {
// This should never happen.
ABSL_RAW_LOG(WARNING,
- "Unable to read from fd %d at offset %lld: n_read = %zd", fd,
- static_cast<long long>(off), n_read);
+ "Unable to read from fd %d at offset %lld: n_read = %zd",
+ file->fd(), static_cast<long long>(off), n_read);
return SYMBOL_NOT_FOUND;
}
ABSL_RAW_CHECK(static_cast<size_t>(n_read) <= out_size,
@@ -827,22 +885,24 @@ FindSymbolResult Symbolizer::GetSymbolFromObjectFile(
}
}
+ CachingFile file(obj.fd, file_cache_, sizeof(file_cache_));
+
// Consult a regular symbol table, then fall back to the dynamic symbol table.
for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) {
- if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum,
+ if (!GetSectionHeaderByType(&file, obj.elf_header.e_shnum,
static_cast<off_t>(obj.elf_header.e_shoff),
static_cast<ElfW(Word)>(symbol_table_type),
&symtab, tmp_buf, tmp_buf_size)) {
continue;
}
- if (!ReadFromOffsetExact(
- obj.fd, &strtab, sizeof(strtab),
+ if (!file.ReadFromOffsetExact(
+ &strtab, sizeof(strtab),
static_cast<off_t>(obj.elf_header.e_shoff +
symtab.sh_link * sizeof(symtab)))) {
continue;
}
const FindSymbolResult rc =
- FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab,
+ FindSymbol(pc, &file, out, out_size, relocation, &strtab, &symtab,
opd_ptr, tmp_buf, tmp_buf_size);
if (rc != SYMBOL_NOT_FOUND) {
return rc;
@@ -1323,15 +1383,19 @@ static bool MaybeInitializeObjFile(ObjFile *obj) {
ABSL_RAW_LOG(WARNING, "%s: open failed: errno=%d", obj->filename, errno);
return false;
}
- obj->elf_type = FileGetElfType(obj->fd);
+
+ char buf[kSmallFileCacheSize];
+ CachingFile file(obj->fd, buf, sizeof(buf));
+
+ obj->elf_type = FileGetElfType(&file);
if (obj->elf_type < 0) {
ABSL_RAW_LOG(WARNING, "%s: wrong elf type: %d", obj->filename,
obj->elf_type);
return false;
}
- if (!ReadFromOffsetExact(obj->fd, &obj->elf_header, sizeof(obj->elf_header),
- 0)) {
+ if (!file.ReadFromOffsetExact(&obj->elf_header, sizeof(obj->elf_header),
+ 0)) {
ABSL_RAW_LOG(WARNING, "%s: failed to read elf header", obj->filename);
return false;
}
@@ -1341,7 +1405,7 @@ static bool MaybeInitializeObjFile(ObjFile *obj) {
size_t num_interesting_load_segments = 0;
for (int j = 0; j < phnum; j++) {
ElfW(Phdr) phdr;
- if (!ReadFromOffsetExact(obj->fd, &phdr, sizeof(phdr), phoff)) {
+ if (!file.ReadFromOffsetExact(&phdr, sizeof(phdr), phoff)) {
ABSL_RAW_LOG(WARNING, "%s: failed to read program header %d",
obj->filename, j);
return false;
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 50bf387c..d3b06227 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -183,6 +190,7 @@ cc_library(
":private_handle_accessor",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:no_destructor",
"//absl/container:flat_hash_map",
"//absl/strings",
"//absl/synchronization",
@@ -221,10 +229,6 @@ cc_library(
cc_library(
name = "flag",
- srcs = [
- "flag.cc",
- "internal/flag_msvc.inc",
- ],
hdrs = [
"declare.h",
"flag.h",
@@ -266,8 +270,8 @@ cc_library(
":reflection",
"//absl/base:config",
"//absl/base:core_headers",
- "//absl/container:flat_hash_map",
"//absl/strings",
+ "//absl/synchronization",
],
)
@@ -346,6 +350,7 @@ cc_test(
":reflection",
"//absl/memory",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -360,6 +365,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -391,6 +397,7 @@ cc_test(
"//absl/numeric:int128",
"//absl/strings",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -430,6 +437,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":marshalling",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -437,6 +445,7 @@ cc_test(
cc_test(
name = "parse_test",
size = "small",
+ timeout = "moderate",
srcs = [
"parse_test.cc",
],
@@ -458,6 +467,7 @@ cc_test(
"//absl/log",
"//absl/strings",
"//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -472,6 +482,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":path_util",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -488,6 +499,7 @@ cc_test(
deps = [
":program_name",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -515,6 +527,7 @@ cc_test(
":usage_internal",
"//absl/memory",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -535,6 +548,7 @@ cc_test(
"//absl/base",
"//absl/container:fixed_array",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -552,6 +566,7 @@ cc_test(
":path_util",
":program_name",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index b20463f5..44953124 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -169,6 +169,7 @@ absl_cc_library(
absl::strings
absl::synchronization
absl::flat_hash_map
+ absl::no_destructor
)
# Internal-only target, do not depend on directly.
@@ -200,12 +201,9 @@ absl_cc_library(
absl_cc_library(
NAME
flags
- SRCS
- "flag.cc"
HDRS
"declare.h"
"flag.h"
- "internal/flag_msvc.inc"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
@@ -243,7 +241,6 @@ absl_cc_library(
absl::flags_private_handle_accessor
absl::flags_program_name
absl::flags_reflection
- absl::flat_hash_map
absl::strings
absl::synchronization
)
diff --git a/absl/flags/declare.h b/absl/flags/declare.h
index d1437bb9..8d2a856e 100644
--- a/absl/flags/declare.h
+++ b/absl/flags/declare.h
@@ -40,13 +40,8 @@ class Flag;
// Flag
//
// Forward declaration of the `absl::Flag` type for use in defining the macro.
-#if defined(_MSC_VER) && !defined(__clang__)
-template <typename T>
-class Flag;
-#else
template <typename T>
using Flag = flags_internal::Flag<T>;
-#endif
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index b7f94be7..06ea6932 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -71,12 +71,9 @@ ABSL_NAMESPACE_BEGIN
// For type support of Abseil Flags, see the marshalling.h header file, which
// discusses supported standard types, optional flags, and additional Abseil
// type support.
-#if !defined(_MSC_VER) || defined(__clang__)
+
template <typename T>
using Flag = flags_internal::Flag<T>;
-#else
-#include "absl/flags/internal/flag_msvc.inc"
-#endif
// GetFlag()
//
@@ -196,18 +193,12 @@ ABSL_NAMESPACE_END
// -----------------------------------------------------------------------------
// 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) ""
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index f9cda02e..8d14ba8d 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -144,7 +144,6 @@ using String = std::string;
using int128 = absl::int128;
using uint128 = absl::uint128;
-#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}; \
@@ -157,16 +156,6 @@ using uint128 = absl::uint128;
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);
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index b41f9a69..2e6e6b87 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -54,13 +54,8 @@ 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);
diff --git a/absl/flags/internal/flag_msvc.inc b/absl/flags/internal/flag_msvc.inc
deleted file mode 100644
index 614d09fd..00000000
--- a/absl/flags/internal/flag_msvc.inc
+++ /dev/null
@@ -1,116 +0,0 @@
-//
-// 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 recommendation 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/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index 13852e14..8b169bcd 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -27,7 +27,10 @@
#include <utility>
#include <vector>
+#include "absl/base/attributes.h"
#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/flag.h"
#include "absl/flags/internal/flag.h"
@@ -40,6 +43,8 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "absl/synchronization/mutex.h"
// Dummy global variables to prevent anyone else defining these.
bool FLAGS_help = false;
diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc
index dc69754f..ca4a1305 100644
--- a/absl/flags/marshalling.cc
+++ b/absl/flags/marshalling.cc
@@ -247,6 +247,14 @@ bool AbslParseFlag(absl::string_view text, absl::LogSeverity* dst,
*err = "no value provided";
return false;
}
+ if (absl::EqualsIgnoreCase(text, "dfatal")) {
+ *dst = absl::kLogDebugFatal;
+ return true;
+ }
+ if (absl::EqualsIgnoreCase(text, "klogdebugfatal")) {
+ *dst = absl::kLogDebugFatal;
+ return true;
+ }
if (text.front() == 'k' || text.front() == 'K') text.remove_prefix(1);
if (absl::EqualsIgnoreCase(text, "info")) {
*dst = absl::LogSeverity::kInfo;
@@ -269,7 +277,8 @@ bool AbslParseFlag(absl::string_view text, absl::LogSeverity* dst,
*dst = static_cast<absl::LogSeverity>(numeric_value);
return true;
}
- *err = "only integers and absl::LogSeverity enumerators are accepted";
+ *err =
+ "only integers, absl::LogSeverity enumerators, and DFATAL are accepted";
return false;
}
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 4cdd9d0a..526b61d1 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -637,7 +637,7 @@ void ReportUnrecognizedFlags(
// --------------------------------------------------------------------
bool WasPresentOnCommandLine(absl::string_view flag_name) {
- absl::MutexLock l(&specified_flags_guard);
+ absl::ReaderMutexLock l(&specified_flags_guard);
ABSL_INTERNAL_CHECK(specified_flags != nullptr,
"ParseCommandLine is not invoked yet");
diff --git a/absl/flags/reflection.cc b/absl/flags/reflection.cc
index dbce4032..841921a9 100644
--- a/absl/flags/reflection.cc
+++ b/absl/flags/reflection.cc
@@ -21,6 +21,7 @@
#include <string>
#include "absl/base/config.h"
+#include "absl/base/no_destructor.h"
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/flags/commandlineflag.h"
@@ -169,7 +170,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag& flag, const char* filename) {
}
FlagRegistry& FlagRegistry::GlobalRegistry() {
- static FlagRegistry* global_registry = new FlagRegistry;
+ static absl::NoDestructor<FlagRegistry> global_registry;
return *global_registry;
}
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel
index 4ceac539..1a18af25 100644
--- a/absl/functional/BUILD.bazel
+++ b/absl/functional/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -55,6 +62,7 @@ cc_test(
"//absl/base:core_headers",
"//absl/meta:type_traits",
"//absl/utility",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -81,6 +89,7 @@ cc_test(
deps = [
":bind_front",
"//absl/memory",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -109,6 +118,34 @@ cc_test(
":function_ref",
"//absl/container:test_instance_tracker",
"//absl/memory",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "overload",
+ hdrs = ["overload.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ "//absl/meta:type_traits",
+ ],
+)
+
+cc_test(
+ name = "overload_test",
+ size = "small",
+ srcs = ["overload_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":overload",
+ "//absl/base:config",
+ "//absl/strings",
+ "//absl/strings:string_view",
+ "//absl/types:variant",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -126,5 +163,6 @@ cc_test(
":function_ref",
"//absl/base:core_headers",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt
index c704e049..602829cb 100644
--- a/absl/functional/CMakeLists.txt
+++ b/absl/functional/CMakeLists.txt
@@ -108,3 +108,27 @@ absl_cc_test(
absl::test_instance_tracker
GTest::gmock_main
)
+
+absl_cc_library(
+ NAME
+ overload
+ HDRS
+ "overload.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::meta
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ overload_test
+ SRCS
+ "overload_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::strings
+ GTest::gmock_main
+)
diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h
index 2b9139d3..96cece55 100644
--- a/absl/functional/function_ref.h
+++ b/absl/functional/function_ref.h
@@ -137,6 +137,14 @@ class FunctionRef<R(Args...)> {
absl::functional_internal::Invoker<R, Args...> invoker_;
};
+// Allow const qualified function signatures. Since FunctionRef requires
+// constness anyway we can just make this a no-op.
+template <typename R, typename... Args>
+class FunctionRef<R(Args...) const> : public FunctionRef<R(Args...)> {
+ public:
+ using FunctionRef<R(Args...)>::FunctionRef;
+};
+
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc
index c61117eb..c0211135 100644
--- a/absl/functional/function_ref_test.cc
+++ b/absl/functional/function_ref_test.cc
@@ -47,6 +47,11 @@ TEST(FunctionRefTest, Function2) {
EXPECT_EQ(1337, ref());
}
+TEST(FunctionRefTest, ConstFunction) {
+ FunctionRef<int() const> ref(Function);
+ EXPECT_EQ(1337, ref());
+}
+
int NoExceptFunction() noexcept { return 1337; }
// TODO(jdennett): Add a test for noexcept member functions.
diff --git a/absl/functional/internal/any_invocable.h b/absl/functional/internal/any_invocable.h
index f096bb02..b04436d1 100644
--- a/absl/functional/internal/any_invocable.h
+++ b/absl/functional/internal/any_invocable.h
@@ -215,8 +215,8 @@ T& ObjectInLocalStorage(TypeErasedState* const state) {
// behavior, which works as intended on Abseil's officially supported
// platforms as of Q2 2022.
#if !defined(__clang__) && defined(__GNUC__)
-#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
return *reinterpret_cast<T*>(&state->storage);
#if !defined(__clang__) && defined(__GNUC__)
@@ -526,10 +526,10 @@ class CoreImpl {
// Since this is template-heavy code, we prefer to disable these warnings
// locally instead of adding yet another overload of this function.
#if !defined(__clang__) && defined(__GNUC__)
+#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Waddress"
#pragma GCC diagnostic ignored "-Wnonnull-compare"
-#pragma GCC diagnostic push
#endif
if (static_cast<RemoveCVRef<QualDecayedTRef>>(f) == nullptr) {
#if !defined(__clang__) && defined(__GNUC__)
diff --git a/absl/functional/overload.h b/absl/functional/overload.h
new file mode 100644
index 00000000..4651f14b
--- /dev/null
+++ b/absl/functional/overload.h
@@ -0,0 +1,75 @@
+// Copyright 2023 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: overload.h
+// -----------------------------------------------------------------------------
+//
+// `absl::Overload()` returns a functor that provides overloads based on the
+// functors passed to it.
+// Before using this function, consider whether named function overloads would
+// be a better design.
+// One use case for this is locally defining visitors for `std::visit` inside a
+// function using lambdas.
+
+// Example: Using `absl::Overload` to define a visitor for `std::variant`.
+//
+// std::variant<int, std::string, double> v(int{1});
+//
+// assert(std::visit(absl::Overload(
+// [](int) -> absl::string_view { return "int"; },
+// [](const std::string&) -> absl::string_view {
+// return "string";
+// },
+// [](double) -> absl::string_view { return "double"; }),
+// v) == "int");
+//
+// Note: This requires C++17.
+
+#ifndef ABSL_FUNCTIONAL_OVERLOAD_H_
+#define ABSL_FUNCTIONAL_OVERLOAD_H_
+
+#include "absl/base/config.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+ ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+
+template <int&... ExplicitArgumentBarrier, typename... T>
+auto Overload(T&&... ts) {
+ struct OverloadImpl : absl::remove_cvref_t<T>... {
+ using absl::remove_cvref_t<T>::operator()...;
+ };
+ return OverloadImpl{std::forward<T>(ts)...};
+}
+#else
+namespace functional_internal {
+template <typename T>
+constexpr bool kDependentFalse = false;
+}
+
+template <typename Dependent = int, typename... T>
+auto Overload(T&&...) {
+ static_assert(functional_internal::kDependentFalse<Dependent>,
+ "Overload is only usable with C++17 or above.");
+}
+
+#endif
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_FUNCTIONAL_OVERLOAD_H_
diff --git a/absl/functional/overload_test.cc b/absl/functional/overload_test.cc
new file mode 100644
index 00000000..739c4c4c
--- /dev/null
+++ b/absl/functional/overload_test.cc
@@ -0,0 +1,130 @@
+// Copyright 2023 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/functional/overload.h"
+
+#include <cstdint>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/config.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/variant.h"
+
+#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
+ ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+
+#include "gtest/gtest.h"
+
+namespace {
+
+TEST(OverloadTest, DispatchConsidersType) {
+ auto overloaded = absl::Overload(
+ [](int v) -> std::string { return absl::StrCat("int ", v); }, //
+ [](double v) -> std::string { return absl::StrCat("double ", v); }, //
+ [](const char* v) -> std::string { //
+ return absl::StrCat("const char* ", v); //
+ }, //
+ [](auto v) -> std::string { return absl::StrCat("auto ", v); } //
+ );
+ EXPECT_EQ("int 1", overloaded(1));
+ EXPECT_EQ("double 2.5", overloaded(2.5));
+ EXPECT_EQ("const char* hello", overloaded("hello"));
+ EXPECT_EQ("auto 1.5", overloaded(1.5f));
+}
+
+TEST(OverloadTest, DispatchConsidersNumberOfArguments) {
+ auto overloaded = absl::Overload( //
+ [](int a) { return a + 1; }, //
+ [](int a, int b) { return a * b; }, //
+ []() -> absl::string_view { return "none"; } //
+ );
+ EXPECT_EQ(3, overloaded(2));
+ EXPECT_EQ(21, overloaded(3, 7));
+ EXPECT_EQ("none", overloaded());
+}
+
+TEST(OverloadTest, SupportsConstantEvaluation) {
+ auto overloaded = absl::Overload( //
+ [](int a) { return a + 1; }, //
+ [](int a, int b) { return a * b; }, //
+ []() -> absl::string_view { return "none"; } //
+ );
+ static_assert(overloaded() == "none");
+ static_assert(overloaded(2) == 3);
+ static_assert(overloaded(3, 7) == 21);
+}
+
+TEST(OverloadTest, PropogatesDefaults) {
+ auto overloaded = absl::Overload( //
+ [](int a, int b = 5) { return a * b; }, //
+ [](double c) { return c; } //
+ );
+
+ EXPECT_EQ(21, overloaded(3, 7));
+ EXPECT_EQ(35, overloaded(7));
+ EXPECT_EQ(2.5, overloaded(2.5));
+}
+
+TEST(OverloadTest, AmbiguousWithDefaultsNotInvocable) {
+ auto overloaded = absl::Overload( //
+ [](int a, int b = 5) { return a * b; }, //
+ [](int c) { return c; } //
+ );
+ static_assert(!std::is_invocable_v<decltype(overloaded), int>);
+ static_assert(std::is_invocable_v<decltype(overloaded), int, int>);
+}
+
+TEST(OverloadTest, AmbiguousDuplicatesNotInvocable) {
+ auto overloaded = absl::Overload( //
+ [](int a) { return a; }, //
+ [](int c) { return c; } //
+ );
+ static_assert(!std::is_invocable_v<decltype(overloaded), int>);
+}
+
+TEST(OverloadTest, AmbiguousConversionNotInvocable) {
+ auto overloaded = absl::Overload( //
+ [](uint16_t a) { return a; }, //
+ [](uint64_t c) { return c; } //
+ );
+ static_assert(!std::is_invocable_v<decltype(overloaded), int>);
+}
+
+TEST(OverloadTest, DispatchConsidersSfinae) {
+ auto overloaded = absl::Overload( //
+ [](auto a) -> decltype(a + 1) { return a + 1; } //
+ );
+ static_assert(std::is_invocable_v<decltype(overloaded), int>);
+ static_assert(!std::is_invocable_v<decltype(overloaded), std::string>);
+}
+
+TEST(OverloadTest, VariantVisitDispatchesCorrectly) {
+ absl::variant<int, double, std::string> v(1);
+ auto overloaded = absl::Overload(
+ [](int) -> absl::string_view { return "int"; }, //
+ [](double) -> absl::string_view { return "double"; }, //
+ [](const std::string&) -> absl::string_view { return "string"; } //
+ );
+ EXPECT_EQ("int", absl::visit(overloaded, v));
+ v = 1.1;
+ EXPECT_EQ("double", absl::visit(overloaded, v));
+ v = "hello";
+ EXPECT_EQ("string", absl::visit(overloaded, v));
+}
+
+} // namespace
+
+#endif
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
index 4346fc49..1e8ad451 100644
--- a/absl/hash/BUILD.bazel
+++ b/absl/hash/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -85,9 +92,14 @@ cc_test(
"//absl/container:flat_hash_set",
"//absl/container:node_hash_map",
"//absl/container:node_hash_set",
+ "//absl/memory",
"//absl/meta:type_traits",
"//absl/numeric:int128",
"//absl/strings:cord_test_helpers",
+ "//absl/strings:string_view",
+ "//absl/types:optional",
+ "//absl/types:variant",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -109,6 +121,7 @@ cc_test(
"//absl/container:flat_hash_set",
"//absl/container:node_hash_map",
"//absl/container:node_hash_set",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -169,6 +182,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":city",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -197,6 +211,7 @@ cc_test(
deps = [
":low_level_hash",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt
index 65fd2a5f..99d6fa1f 100644
--- a/absl/hash/CMakeLists.txt
+++ b/absl/hash/CMakeLists.txt
@@ -68,18 +68,22 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::btree
absl::cord_test_helpers
- absl::hash
- absl::hash_testing
absl::core_headers
- absl::btree
absl::flat_hash_map
absl::flat_hash_set
+ absl::hash
+ absl::hash_testing
+ absl::int128
+ absl::memory
+ absl::meta
absl::node_hash_map
absl::node_hash_set
+ absl::optional
absl::spy_hash_state
- absl::meta
- absl::int128
+ absl::string_view
+ absl::variant
GTest::gmock_main
)
@@ -87,7 +91,7 @@ absl_cc_test(
NAME
hash_instantiated_test
SRCS
- "hash_test.cc"
+ "hash_instantiated_test.cc"
"internal/hash_test.h"
COPTS
${ABSL_TEST_COPTS}
@@ -100,7 +104,7 @@ absl_cc_test(
absl::flat_hash_set
absl::node_hash_map
absl::node_hash_set
- GTest::gtest_main
+ GTest::gmock_main
)
# Internal-only target, do not depend on directly.
diff --git a/absl/hash/hash_benchmark.cc b/absl/hash/hash_benchmark.cc
index 8712a01c..d18ea694 100644
--- a/absl/hash/hash_benchmark.cc
+++ b/absl/hash/hash_benchmark.cc
@@ -12,7 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
#include <string>
+#include <tuple>
#include <type_traits>
#include <typeindex>
#include <utility>
@@ -79,12 +85,6 @@ struct TypeErasedAbslHash {
}
};
-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();
@@ -160,7 +160,7 @@ absl::flat_hash_set<T> FlatHashSet(size_t count) {
return hash<decltype(__VA_ARGS__)>{}(arg); \
} \
bool absl_hash_test_odr_use##hash##name = \
- ODRUseFunction(&Codegen##hash##name);
+ (benchmark::DoNotOptimize(&Codegen##hash##name), false);
MAKE_BENCHMARK(AbslHash, Int32, int32_t{});
MAKE_BENCHMARK(AbslHash, Int64, int64_t{});
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
index a0e2e4a7..59fe8dea 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -17,42 +17,40 @@
#include <algorithm>
#include <array>
#include <bitset>
+#include <cstddef>
#include <cstdint>
+#include <cstdlib>
#include <cstring>
-#include <deque>
-#include <forward_list>
#include <functional>
#include <initializer_list>
-#include <iterator>
+#include <ios>
#include <limits>
-#include <list>
-#include <map>
#include <memory>
-#include <numeric>
-#include <random>
+#include <ostream>
#include <set>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
-#include <unordered_set>
#include <utility>
#include <vector>
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "absl/container/btree_map.h"
-#include "absl/container/btree_set.h"
-#include "absl/container/flat_hash_map.h"
+#include "absl/base/config.h"
#include "absl/container/flat_hash_set.h"
-#include "absl/container/node_hash_map.h"
-#include "absl/container/node_hash_set.h"
#include "absl/hash/hash_testing.h"
#include "absl/hash/internal/hash_test.h"
#include "absl/hash/internal/spy_hash_state.h"
+#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
-#include "absl/numeric/int128.h"
#include "absl/strings/cord_test_helpers.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "absl/types/variant.h"
+
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+#include <filesystem> // NOLINT
+#endif
#ifdef ABSL_HAVE_STD_STRING_VIEW
#include <string_view>
@@ -486,6 +484,43 @@ TEST(HashValueTest, U32StringView) {
#endif
}
+TEST(HashValueTest, StdFilesystemPath) {
+#ifndef ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
+ GTEST_SKIP() << "std::filesystem::path is unavailable on this platform";
+#else
+ EXPECT_TRUE((is_hashable<std::filesystem::path>::value));
+
+ // clang-format off
+ const auto kTestCases = std::make_tuple(
+ std::filesystem::path(),
+ std::filesystem::path("/"),
+#ifndef __GLIBCXX__
+ // libstdc++ has a known issue normalizing "//".
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106452
+ std::filesystem::path("//"),
+#endif
+ std::filesystem::path("/a/b"),
+ std::filesystem::path("/a//b"),
+ std::filesystem::path("a/b"),
+ std::filesystem::path("a/b/"),
+ std::filesystem::path("a//b"),
+ std::filesystem::path("a//b/"),
+ std::filesystem::path("c:/"),
+ std::filesystem::path("c:\\"),
+ std::filesystem::path("c:\\/"),
+ std::filesystem::path("c:\\//"),
+ std::filesystem::path("c://"),
+ std::filesystem::path("c://\\"),
+ std::filesystem::path("/e/p"),
+ std::filesystem::path("/s/../e/p"),
+ std::filesystem::path("e/p"),
+ std::filesystem::path("s/../e/p"));
+ // clang-format on
+
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(kTestCases));
+#endif
+}
+
TEST(HashValueTest, StdArray) {
EXPECT_TRUE((is_hashable<std::array<int, 3>>::value));
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
index ef3f3664..f4a94f91 100644
--- a/absl/hash/internal/hash.h
+++ b/absl/hash/internal/hash.h
@@ -19,6 +19,11 @@
#ifndef ABSL_HASH_INTERNAL_HASH_H_
#define ABSL_HASH_INTERNAL_HASH_H_
+#ifdef __APPLE__
+#include <Availability.h>
+#include <TargetConditionals.h>
+#endif
+
#include <algorithm>
#include <array>
#include <bitset>
@@ -56,6 +61,11 @@
#include "absl/types/variant.h"
#include "absl/utility/utility.h"
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L && \
+ !defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY)
+#include <filesystem> // NOLINT
+#endif
+
#ifdef ABSL_HAVE_STD_STRING_VIEW
#include <string_view>
#endif
@@ -409,9 +419,23 @@ AbslHashValue(H hash_state, LongDouble value) {
return H::combine(std::move(hash_state), category);
}
+// Without this overload, an array decays to a pointer and we hash that, which
+// is not likely to be what the caller intended.
+template <typename H, typename T, size_t N>
+H AbslHashValue(H hash_state, T (&)[N]) {
+ static_assert(
+ sizeof(T) == -1,
+ "Hashing C arrays is not allowed. For string literals, wrap the literal "
+ "in absl::string_view(). To hash the array contents, use "
+ "absl::MakeSpan() or make the array an std::array. To hash the array "
+ "address, use &array[0].");
+ return hash_state;
+}
+
// AbslHashValue() for hashing pointers
template <typename H, typename T>
-H AbslHashValue(H hash_state, T* ptr) {
+std::enable_if_t<std::is_pointer<T>::value, H> AbslHashValue(H hash_state,
+ T ptr) {
auto v = reinterpret_cast<uintptr_t>(ptr);
// Due to alignment, pointers tend to have low bits as zero, and the next few
// bits follow a pattern since they are also multiples of some base value.
@@ -564,6 +588,29 @@ H AbslHashValue(H hash_state, std::basic_string_view<Char> str) {
#endif // ABSL_HAVE_STD_STRING_VIEW
+#if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \
+ !defined(_LIBCPP_HAS_NO_FILESYSTEM_LIBRARY) && \
+ (!defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || \
+ __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000)
+
+#define ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE 1
+
+// Support std::filesystem::path. The SFINAE is required because some string
+// types are implicitly convertible to std::filesystem::path.
+template <typename Path, typename H,
+ typename = absl::enable_if_t<
+ std::is_same_v<Path, std::filesystem::path>>>
+H AbslHashValue(H hash_state, const Path& path) {
+ // This is implemented by deferring to the standard library to compute the
+ // hash. The standard library requires that for two paths, `p1 == p2`, then
+ // `hash_value(p1) == hash_value(p2)`. `AbslHashValue` has the same
+ // requirement. Since `operator==` does platform specific matching, deferring
+ // to the standard library is the simplest approach.
+ return H::combine(std::move(hash_state), std::filesystem::hash_value(path));
+}
+
+#endif // ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
+
// -----------------------------------------------------------------------------
// AbslHashValue for Sequence Containers
// -----------------------------------------------------------------------------
@@ -818,7 +865,7 @@ AbslHashValue(H hash_state, const absl::variant<T...>& v) {
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++) {
+ for (size_t 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));
}
diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel
index e1410637..40e87cc9 100644
--- a/absl/log/BUILD.bazel
+++ b/absl/log/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -91,6 +98,7 @@ cc_library(
"//absl/flags:marshalling",
"//absl/log/internal:config",
"//absl/log/internal:flags",
+ "//absl/log/internal:vlog_config",
"//absl/strings",
],
# Binaries which do not access these flags from C++ still want this library linked in.
@@ -111,6 +119,7 @@ cc_library(
"//absl/base:log_severity",
"//absl/base:raw_logging_internal",
"//absl/hash",
+ "//absl/log/internal:vlog_config",
"//absl/strings",
],
)
@@ -136,6 +145,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":vlog_is_on",
"//absl/log/internal:log_impl",
],
)
@@ -228,6 +238,58 @@ cc_library(
],
)
+cc_library(
+ name = "absl_vlog_is_on",
+ hdrs = ["absl_vlog_is_on.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = [
+ "//absl/log:__subpackages__",
+ ],
+ deps = [
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/log/internal:vlog_config",
+ "//absl/strings",
+ ],
+)
+
+cc_library(
+ name = "vlog_is_on",
+ hdrs = ["vlog_is_on.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = [
+ "//absl/log:__subpackages__",
+ ],
+ deps = [
+ ":absl_vlog_is_on",
+ ],
+)
+
+# TODO(b/200695798): run this in TAP projects with -DABSL_MAX_VLOG_VERBOSITY={-100,100}
+cc_test(
+ name = "vlog_is_on_test",
+ size = "small",
+ srcs = [
+ "vlog_is_on_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":flags",
+ ":globals",
+ ":log",
+ ":scoped_mock_log",
+ ":vlog_is_on",
+ "//absl/base:log_severity",
+ "//absl/flags:flag",
+ "//absl/types:optional",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
# Test targets
cc_test(
@@ -244,6 +306,7 @@ cc_test(
deps = [
":absl_check",
":check_test_impl",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -257,6 +320,7 @@ cc_test(
deps = [
":absl_log",
":log_basic_test_impl",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -275,6 +339,7 @@ cc_test(
deps = [
":check",
":check_test_impl",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -310,6 +375,7 @@ cc_test(
":die_if_null",
"//absl/base:core_headers",
"//absl/log/internal:test_helpers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -333,6 +399,7 @@ cc_test(
"//absl/log/internal:test_helpers",
"//absl/log/internal:test_matchers",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -351,6 +418,7 @@ cc_test(
"//absl/base:log_severity",
"//absl/log/internal:globals",
"//absl/log/internal:test_helpers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -364,6 +432,7 @@ cc_test(
deps = [
":log",
":log_basic_test_impl",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -405,6 +474,7 @@ cc_test(
"//absl/strings",
"//absl/time",
"//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -423,6 +493,7 @@ cc_test(
"//absl/strings",
"//absl/strings:str_format",
"//absl/types:optional",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -438,6 +509,7 @@ cc_test(
":scoped_mock_log",
"//absl/base:core_headers",
"//absl/base:log_severity",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -463,6 +535,7 @@ cc_test(
"//absl/log/internal:test_helpers",
"//absl/log/internal:test_matchers",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -484,6 +557,7 @@ cc_test(
"//absl/log/internal:test_helpers",
"//absl/log/internal:test_matchers",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -503,6 +577,7 @@ cc_test(
"//absl/log/internal:test_matchers",
"//absl/strings",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -530,6 +605,7 @@ cc_test(
"//absl/memory",
"//absl/strings",
"//absl/synchronization",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -550,8 +626,10 @@ cc_test(
"//absl/base:strerror",
"//absl/flags:program_name",
"//absl/log/internal:test_helpers",
+ "//absl/status",
"//absl/strings",
"//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -569,13 +647,14 @@ cc_test(
"//absl/base:core_headers",
"//absl/log/internal:test_helpers",
"//absl/log/internal:test_matchers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
-cc_binary(
+cc_test(
name = "log_benchmark",
- testonly = 1,
+ size = "small",
srcs = ["log_benchmark.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
index 9320ce59..a7d8b690 100644
--- a/absl/log/CMakeLists.txt
+++ b/absl/log/CMakeLists.txt
@@ -157,6 +157,7 @@ absl_cc_library(
absl::log_internal_conditions
absl::log_internal_message
absl::log_internal_strip
+ absl::absl_vlog_is_on
)
absl_cc_library(
@@ -238,6 +239,7 @@ absl_cc_library(
absl::log_entry
absl::log_severity
absl::log_sink
+ absl::no_destructor
absl::raw_logging_internal
absl::synchronization
absl::span
@@ -459,6 +461,11 @@ absl_cc_library(
PUBLIC
)
+# Warning: Many linkers will strip the contents of this library because its
+# symbols are only used in a global constructor. A workaround is for clients
+# to link this using $<LINK_LIBRARY:WHOLE_ARCHIVE,absl::log_flags> instead of
+# the plain absl::log_flags.
+# TODO(b/320467376): Implement the equivalent of Bazel's alwayslink=True.
absl_cc_library(
NAME
log_flags
@@ -480,6 +487,7 @@ absl_cc_library(
absl::flags
absl::flags_marshalling
absl::strings
+ absl::vlog_config_internal
PUBLIC
)
@@ -502,6 +510,7 @@ absl_cc_library(
absl::log_severity
absl::raw_logging_internal
absl::strings
+ absl::vlog_config_internal
)
absl_cc_library(
@@ -535,6 +544,7 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::log_internal_log_impl
+ absl::vlog_is_on
PUBLIC
)
@@ -671,6 +681,95 @@ absl_cc_library(
PUBLIC
)
+absl_cc_library(
+ NAME
+ vlog_config_internal
+ SRCS
+ "internal/vlog_config.cc"
+ HDRS
+ "internal/vlog_config.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::base
+ absl::config
+ absl::core_headers
+ absl::log_internal_fnmatch
+ absl::memory
+ absl::no_destructor
+ absl::strings
+ absl::synchronization
+ absl::optional
+)
+
+absl_cc_library(
+ NAME
+ absl_vlog_is_on
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ HDRS
+ "absl_vlog_is_on.h"
+ DEPS
+ absl::vlog_config_internal
+ absl::config
+ absl::core_headers
+ absl::strings
+)
+
+absl_cc_library(
+ NAME
+ vlog_is_on
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ HDRS
+ "vlog_is_on.h"
+ DEPS
+ absl::absl_vlog_is_on
+)
+
+absl_cc_test(
+ NAME
+ vlog_is_on_test
+ SRCS
+ "vlog_is_on_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::log
+ absl::log_flags
+ absl::log_globals
+ absl::scoped_mock_log
+ absl::vlog_is_on
+ absl::log_severity
+ absl::flags
+ absl::optional
+ GTest::gmock_main
+)
+
+absl_cc_library(
+ NAME
+ log_internal_fnmatch
+ SRCS
+ "internal/fnmatch.cc"
+ HDRS
+ "internal/fnmatch.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::config
+ absl::strings
+)
+
# Test targets
absl_cc_test(
@@ -689,8 +788,7 @@ absl_cc_test(
absl::core_headers
absl::log_internal_test_helpers
absl::status
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -713,8 +811,7 @@ absl_cc_test(
absl::log_internal_test_helpers
absl::log_internal_test_matchers
absl::scoped_mock_log
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -733,8 +830,7 @@ absl_cc_test(
absl::core_headers
absl::log_internal_test_helpers
absl::status
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -773,8 +869,7 @@ absl_cc_test(
absl::log_internal_test_helpers
absl::log_internal_test_matchers
absl::scoped_mock_log
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -798,8 +893,7 @@ absl_cc_test(
absl::span
absl::strings
absl::time
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -824,8 +918,7 @@ absl_cc_test(
absl::flags_reflection
absl::scoped_mock_log
absl::strings
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -845,7 +938,7 @@ absl_cc_test(
absl::log_internal_test_helpers
absl::log_severity
absl::scoped_mock_log
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -865,8 +958,7 @@ absl_cc_test(
absl::scoped_mock_log
absl::str_format
absl::strings
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -883,8 +975,7 @@ absl_cc_test(
absl::log
absl::log_severity
absl::scoped_mock_log
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -907,7 +998,7 @@ absl_cc_test(
absl::log_severity
absl::scoped_mock_log
absl::strings
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -930,7 +1021,7 @@ absl_cc_test(
absl::log_severity
absl::scoped_mock_log
absl::strings
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -951,8 +1042,7 @@ absl_cc_test(
absl::scoped_mock_log
absl::strings
absl::time
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -995,8 +1085,7 @@ absl_cc_test(
absl::log_globals
absl::log_internal_test_helpers
absl::log_severity
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -1014,11 +1103,11 @@ absl_cc_test(
absl::log
absl::log_internal_test_helpers
absl::log_severity
+ absl::status
absl::strerror
absl::strings
absl::str_format
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
)
absl_cc_test(
@@ -1037,6 +1126,19 @@ absl_cc_test(
absl::log_internal_test_matchers
absl::log_structured
absl::scoped_mock_log
- GTest::gmock
- GTest::gtest_main
+ GTest::gmock_main
+)
+
+absl_cc_test(
+ NAME
+ internal_fnmatch_test
+ SRCS
+ "internal/fnmatch_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::log_internal_fnmatch
+ GTest::gmock_main
)
diff --git a/absl/log/absl_log.h b/absl/log/absl_log.h
index 0517760b..0fd9ae3f 100644
--- a/absl/log/absl_log.h
+++ b/absl/log/absl_log.h
@@ -39,6 +39,9 @@
#define ABSL_PLOG(severity) ABSL_LOG_INTERNAL_PLOG_IMPL(_##severity)
#define ABSL_DLOG(severity) ABSL_LOG_INTERNAL_DLOG_IMPL(_##severity)
+#define ABSL_VLOG(verbose_level) ABSL_LOG_INTERNAL_VLOG_IMPL(verbose_level)
+#define ABSL_DVLOG(verbose_level) ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level)
+
#define ABSL_LOG_IF(severity, condition) \
ABSL_LOG_INTERNAL_LOG_IF_IMPL(_##severity, condition)
#define ABSL_PLOG_IF(severity, condition) \
@@ -73,6 +76,15 @@
#define ABSL_DLOG_EVERY_N_SEC(severity, n_seconds) \
ABSL_LOG_INTERNAL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
+#define ABSL_VLOG_EVERY_N(verbose_level, n) \
+ ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(verbose_level, n)
+#define ABSL_VLOG_FIRST_N(verbose_level, n) \
+ ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(verbose_level, n)
+#define ABSL_VLOG_EVERY_POW_2(verbose_level, n) \
+ ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(verbose_level, n)
+#define ABSL_VLOG_EVERY_N_SEC(verbose_level, n) \
+ ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(verbose_level, n)
+
#define ABSL_LOG_IF_EVERY_N(severity, condition, n) \
ABSL_LOG_INTERNAL_LOG_IF_EVERY_N_IMPL(_##severity, condition, n)
#define ABSL_LOG_IF_FIRST_N(severity, condition, n) \
diff --git a/absl/log/absl_vlog_is_on.h b/absl/log/absl_vlog_is_on.h
new file mode 100644
index 00000000..29096b48
--- /dev/null
+++ b/absl/log/absl_vlog_is_on.h
@@ -0,0 +1,93 @@
+// Copyright 2022 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: log/absl_vlog_is_on.h
+// -----------------------------------------------------------------------------
+//
+// This header defines the `ABSL_VLOG_IS_ON()` macro that controls the
+// variable-verbosity conditional logging.
+//
+// It's used by `VLOG` in log.h, or it can also be used directly like this:
+//
+// if (ABSL_VLOG_IS_ON(2)) {
+// foo_server.RecomputeStatisticsExpensive();
+// LOG(INFO) << foo_server.LastStatisticsAsString();
+// }
+//
+// Each source file has an effective verbosity level that's a non-negative
+// integer computed from the `--vmodule` and `--v` flags.
+// `ABSL_VLOG_IS_ON(n)` is true, and `VLOG(n)` logs, if that effective verbosity
+// level is greater than or equal to `n`.
+//
+// `--vmodule` takes a comma-delimited list of key=value pairs. Each key is a
+// pattern matched against filenames, and the values give the effective severity
+// level applied to matching files. '?' and '*' characters in patterns are
+// interpreted as single-character and zero-or-more-character wildcards.
+// Patterns including a slash character are matched against full pathnames,
+// while those without are matched against basenames only. One suffix (i.e. the
+// last . and everything after it) is stripped from each filename prior to
+// matching, as is the special suffix "-inl".
+//
+// Files are matched against globs in `--vmodule` in order, and the first match
+// determines the verbosity level.
+//
+// Files which do not match any pattern in `--vmodule` use the value of `--v` as
+// their effective verbosity level. The default is 0.
+//
+// SetVLOGLevel helper function is provided to do limited dynamic control over
+// V-logging by appending to `--vmodule`. Because these go at the beginning of
+// the list, they take priority over any globs previously added.
+//
+// Resetting --vmodule will override all previous modifications to `--vmodule`,
+// including via SetVLOGLevel.
+
+#ifndef ABSL_LOG_ABSL_VLOG_IS_ON_H_
+#define ABSL_LOG_ABSL_VLOG_IS_ON_H_
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/log/internal/vlog_config.h" // IWYU pragma: export
+#include "absl/strings/string_view.h"
+
+// IWYU pragma: private, include "absl/log/log.h"
+
+// This is expanded at the callsite to allow the compiler to optimize
+// always-false cases out of the build.
+// An ABSL_MAX_VLOG_VERBOSITY of 2 means that VLOG(3) and above should never
+// log.
+#ifdef ABSL_MAX_VLOG_VERBOSITY
+#define ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(x) \
+ ((x) <= ABSL_MAX_VLOG_VERBOSITY)&&
+#else
+#define ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(x)
+#endif
+
+// Each ABSL_VLOG_IS_ON call site gets its own VLogSite that registers with the
+// global linked list of sites to asynchronously update its verbosity level on
+// changes to --v or --vmodule. The verbosity can also be set by manually
+// calling SetVLOGLevel.
+//
+// ABSL_VLOG_IS_ON is not async signal safe, but it is guaranteed not to
+// allocate new memory.
+#define ABSL_VLOG_IS_ON(verbose_level) \
+ (ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(verbose_level)[]() \
+ ->::absl::log_internal::VLogSite * \
+ { \
+ ABSL_CONST_INIT static ::absl::log_internal::VLogSite site(__FILE__); \
+ return &site; \
+ }() \
+ ->IsEnabled(verbose_level))
+
+#endif // ABSL_LOG_ABSL_VLOG_IS_ON_H_
diff --git a/absl/log/check.h b/absl/log/check.h
index 0a2f2e4e..50f633dd 100644
--- a/absl/log/check.h
+++ b/absl/log/check.h
@@ -169,7 +169,7 @@
#define DCHECK_OK(status) ABSL_LOG_INTERNAL_DCHECK_OK_IMPL((status), #status)
// `CHECK_STREQ` and friends provide `CHECK_EQ` functionality for C strings,
-// i.e., nul-terminated char arrays. The `CASE` versions are case-insensitive.
+// i.e., null-terminated char arrays. The `CASE` versions are case-insensitive.
//
// Example:
//
diff --git a/absl/log/flags.cc b/absl/log/flags.cc
index 215b7bd5..287b3e96 100644
--- a/absl/log/flags.cc
+++ b/absl/log/flags.cc
@@ -28,6 +28,7 @@
#include "absl/flags/marshalling.h"
#include "absl/log/globals.h"
#include "absl/log/internal/config.h"
+#include "absl/log/internal/vlog_config.h"
#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
@@ -118,3 +119,25 @@ ABSL_FLAG(bool, log_prefix, true,
.OnUpdate([] {
absl::log_internal::RawEnableLogPrefix(absl::GetFlag(FLAGS_log_prefix));
});
+
+ABSL_FLAG(int, v, 0,
+ "Show all VLOG(m) messages for m <= this. Overridable by --vmodule.")
+ .OnUpdate([] {
+ absl::log_internal::UpdateGlobalVLogLevel(absl::GetFlag(FLAGS_v));
+ });
+
+ABSL_FLAG(
+ std::string, vmodule, "",
+ "per-module log verbosity level."
+ " Argument is a comma-separated list of <module name>=<log level>."
+ " <module name> is a glob pattern, matched against the filename base"
+ " (that is, name ignoring .cc/.h./-inl.h)."
+ " A pattern without slashes matches just the file name portion, otherwise"
+ " the whole file path below the workspace root"
+ " (still without .cc/.h./-inl.h) is matched."
+ " ? and * in the glob pattern match any single or sequence of characters"
+ " respectively including slashes."
+ " <log level> overrides any value given by --v.")
+ .OnUpdate([] {
+ absl::log_internal::UpdateVModule(absl::GetFlag(FLAGS_vmodule));
+ });
diff --git a/absl/log/globals.h b/absl/log/globals.h
index bc3864c1..b36e47e6 100644
--- a/absl/log/globals.h
+++ b/absl/log/globals.h
@@ -24,6 +24,7 @@
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/log_severity.h"
+#include "absl/log/internal/vlog_config.h"
#include "absl/strings/string_view.h"
namespace absl {
@@ -153,6 +154,28 @@ ABSL_MUST_USE_RESULT bool ShouldPrependLogPrefix();
void EnableLogPrefix(bool on_off);
//------------------------------------------------------------------------------
+// Set Global VLOG Level
+//------------------------------------------------------------------------------
+//
+// Sets the global `(ABSL_)VLOG(_IS_ON)` level to `log_level`. This level is
+// applied to any sites whose filename doesn't match any `module_pattern`.
+// Returns the prior value.
+inline int SetGlobalVLogLevel(int log_level) {
+ return absl::log_internal::UpdateGlobalVLogLevel(log_level);
+}
+
+//------------------------------------------------------------------------------
+// Set VLOG Level
+//------------------------------------------------------------------------------
+//
+// Sets `(ABSL_)VLOG(_IS_ON)` level for `module_pattern` to `log_level`. This
+// allows programmatic control of what is normally set by the --vmodule flag.
+// Returns the level that previously applied to `module_pattern`.
+inline int SetVLogLevel(absl::string_view module_pattern, int log_level) {
+ return absl::log_internal::PrependVModule(module_pattern, log_level);
+}
+
+//------------------------------------------------------------------------------
// Configure Android Native Log Tag
//------------------------------------------------------------------------------
//
diff --git a/absl/log/globals_test.cc b/absl/log/globals_test.cc
index f7af47cd..0dc54d57 100644
--- a/absl/log/globals_test.cc
+++ b/absl/log/globals_test.cc
@@ -88,6 +88,20 @@ TEST(TestGlobals, LogPrefix) {
EXPECT_TRUE(absl::ShouldPrependLogPrefix());
}
+TEST(TestGlobals, SetGlobalVLogLevel) {
+ EXPECT_EQ(absl::SetGlobalVLogLevel(42), 0);
+ EXPECT_EQ(absl::SetGlobalVLogLevel(1337), 42);
+ // Restore the value since it affects the default unset module value for
+ // `SetVLogLevel()`.
+ EXPECT_EQ(absl::SetGlobalVLogLevel(0), 1337);
+}
+
+TEST(TestGlobals, SetVLogLevel) {
+ EXPECT_EQ(absl::SetVLogLevel("setvloglevel", 42), 0);
+ EXPECT_EQ(absl::SetVLogLevel("setvloglevel", 1337), 42);
+ EXPECT_EQ(absl::SetVLogLevel("othersetvloglevel", 50), 0);
+}
+
TEST(TestGlobals, AndroidLogTag) {
// Verify invalid tags result in a check failure.
EXPECT_DEATH_IF_SUPPORTED(absl::SetAndroidNativeTag(nullptr), ".*");
@@ -101,4 +115,33 @@ TEST(TestGlobals, AndroidLogTag) {
EXPECT_DEATH_IF_SUPPORTED(absl::SetAndroidNativeTag("test_tag_fail"), ".*");
}
+TEST(TestExitOnDFatal, OffTest) {
+ // Turn off...
+ absl::log_internal::SetExitOnDFatal(false);
+ EXPECT_FALSE(absl::log_internal::ExitOnDFatal());
+
+ // We don't die.
+ {
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ // LOG(DFATAL) has severity FATAL if debugging, but is
+ // downgraded to ERROR if not debugging.
+ EXPECT_CALL(log, Log(absl::kLogDebugFatal, _, "This should not be fatal"));
+
+ log.StartCapturingLogs();
+ LOG(DFATAL) << "This should not be fatal";
+ }
+}
+
+#if GTEST_HAS_DEATH_TEST
+TEST(TestDeathWhileExitOnDFatal, OnTest) {
+ absl::log_internal::SetExitOnDFatal(true);
+ EXPECT_TRUE(absl::log_internal::ExitOnDFatal());
+
+ // Death comes on little cats' feet.
+ EXPECT_DEBUG_DEATH({ LOG(DFATAL) << "This should be fatal in debug mode"; },
+ "This should be fatal in debug mode");
+}
+#endif
+
} // namespace
diff --git a/absl/log/initialize.cc b/absl/log/initialize.cc
index a3f6d6c1..ef5d3146 100644
--- a/absl/log/initialize.cc
+++ b/absl/log/initialize.cc
@@ -21,14 +21,18 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
-void InitializeLog() {
+namespace {
+void InitializeLogImpl(absl::TimeZone time_zone) {
// This comes first since it is used by RAW_LOG.
- absl::log_internal::SetTimeZone(absl::LocalTimeZone());
+ absl::log_internal::SetTimeZone(time_zone);
// Note that initialization is complete, so logs can now be sent to their
// proper destinations rather than stderr.
log_internal::SetInitialized();
}
+} // namespace
+
+void InitializeLog() { InitializeLogImpl(absl::LocalTimeZone()); }
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel
index 555c5e5c..1be13499 100644
--- a/absl/log/internal/BUILD.bazel
+++ b/absl/log/internal/BUILD.bazel
@@ -21,9 +21,16 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = [
- "//absl/log:__pkg__",
-])
+package(
+ default_visibility = [
+ "//absl/log:__pkg__",
+ ],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -146,6 +153,7 @@ cc_library(
":conditions",
":log_message",
":strip",
+ "//absl/log:absl_vlog_is_on",
],
)
@@ -213,6 +221,7 @@ cc_library(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:log_severity",
+ "//absl/base:no_destructor",
"//absl/base:raw_logging_internal",
"//absl/cleanup",
"//absl/log:globals",
@@ -357,6 +366,60 @@ cc_library(
],
)
+cc_library(
+ name = "fnmatch",
+ srcs = ["fnmatch.cc"],
+ hdrs = ["fnmatch.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ "//absl/strings",
+ ],
+)
+
+cc_library(
+ name = "vlog_config",
+ srcs = ["vlog_config.cc"],
+ hdrs = ["vlog_config.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//absl/log:__subpackages__"],
+ deps = [
+ "//absl/base",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/base:no_destructor",
+ "//absl/log/internal:fnmatch",
+ "//absl/memory",
+ "//absl/strings",
+ "//absl/synchronization",
+ "//absl/types:optional",
+ ],
+)
+
+cc_binary(
+ name = "vlog_config_benchmark",
+ testonly = 1,
+ srcs = ["vlog_config_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = [
+ "benchmark",
+ ],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":vlog_config",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/container:layout",
+ "//absl/memory",
+ "//absl/random:distributions",
+ "//absl/strings",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
# Test targets
cc_test(
name = "stderr_log_sink_test",
@@ -378,6 +441,31 @@ cc_test(
"//absl/base:log_severity",
"//absl/log",
"//absl/log:globals",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "fnmatch_test",
+ srcs = ["fnmatch_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":fnmatch",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "fnmatch_benchmark",
+ srcs = ["fnmatch_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ deps = [
+ ":fnmatch",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h
index 20b01b5e..11f0f407 100644
--- a/absl/log/internal/check_op.h
+++ b/absl/log/internal/check_op.h
@@ -65,6 +65,7 @@
::absl::log_internal::GetReferenceableValue(val2), \
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val1_text \
" " #op " " val2_text))) \
+ ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \
ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream()
#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val1_text, val2, \
val2_text) \
@@ -74,6 +75,7 @@
::absl::log_internal::GetReferenceableValue(val2), \
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL( \
val1_text " " #op " " val2_text))) \
+ ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \
ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream()
#define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s1_text, s2, \
s2_text) \
@@ -82,6 +84,7 @@
(s1), (s2), \
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \
" " s2_text))) \
+ ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \
ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result) \
.InternalStream()
#define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s1_text, s2, \
@@ -91,6 +94,7 @@
(s1), (s2), \
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \
" " s2_text))) \
+ ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \
ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result) \
.InternalStream()
// This one is tricky:
@@ -113,6 +117,10 @@
// * As usual, no braces so we can stream into the expansion with `operator<<`.
// * Also as usual, it must expand to a single (partial) statement with no
// ambiguous-else problems.
+// * When stripped by `ABSL_MIN_LOG_LEVEL`, we must discard the `<expr> is OK`
+// string literal and abort without doing any streaming. We don't need to
+// strip the call to stringify the non-ok `Status` as long as we don't log it;
+// dropping the `Status`'s message text is out of scope.
#define ABSL_LOG_INTERNAL_CHECK_OK(val, val_text) \
for (::std::pair<const ::absl::Status*, ::std::string*> \
absl_log_internal_check_ok_goo; \
@@ -126,22 +134,24 @@
ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \
" is OK")), \
!ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \
+ ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \
ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second) \
.InternalStream()
-#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text) \
- for (::std::pair<const ::absl::Status*, ::std::string*> \
- absl_log_internal_check_ok_goo; \
- absl_log_internal_check_ok_goo.first = \
- ::absl::log_internal::AsStatus(val), \
- absl_log_internal_check_ok_goo.second = \
- ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
- ? nullptr \
- : ::absl::status_internal::MakeCheckFailString( \
- absl_log_internal_check_ok_goo.first, \
- ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \
- " is OK")), \
- !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \
- ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second) \
+#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text) \
+ for (::std::pair<const ::absl::Status*, ::std::string*> \
+ absl_log_internal_qcheck_ok_goo; \
+ absl_log_internal_qcheck_ok_goo.first = \
+ ::absl::log_internal::AsStatus(val), \
+ absl_log_internal_qcheck_ok_goo.second = \
+ ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok()) \
+ ? nullptr \
+ : ::absl::status_internal::MakeCheckFailString( \
+ absl_log_internal_qcheck_ok_goo.first, \
+ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \
+ " is OK")), \
+ !ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok());) \
+ ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \
+ ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_ok_goo.second) \
.InternalStream()
namespace absl {
@@ -152,8 +162,8 @@ template <typename T>
class StatusOr;
namespace status_internal {
-std::string* MakeCheckFailString(const absl::Status* status,
- const char* prefix);
+ABSL_ATTRIBUTE_PURE_FUNCTION std::string* MakeCheckFailString(
+ const absl::Status* status, const char* prefix);
} // namespace status_internal
namespace log_internal {
@@ -314,6 +324,20 @@ ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const unsigned char*);
ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*);
#undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN
+// `ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT` skips formatting the Check_OP result
+// string iff `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`, instead returning an empty
+// string.
+#ifdef ABSL_MIN_LOG_LEVEL
+#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
+ ((::absl::LogSeverity::kFatal >= \
+ static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) \
+ ? MakeCheckOpString<U1, U2>(v1, v2, exprtext) \
+ : new std::string())
+#else
+#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
+ MakeCheckOpString<U1, U2>(v1, v2, exprtext)
+#endif
+
// Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family. The
// `(int, int)` override works around the issue that the compiler will not
// instantiate the template version of the function on values of unnamed enum
@@ -326,7 +350,8 @@ ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*);
using U2 = CheckOpStreamType<T2>; \
return ABSL_PREDICT_TRUE(v1 op v2) \
? nullptr \
- : MakeCheckOpString<U1, U2>(v1, v2, exprtext); \
+ : ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, \
+ exprtext); \
} \
inline constexpr ::std::string* name##Impl(int v1, int v2, \
const char* exprtext) { \
@@ -339,6 +364,7 @@ ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LE, <=)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=)
ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >)
+#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT
#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL
std::string* CheckstrcmptrueImpl(const char* s1, const char* s2,
diff --git a/absl/log/internal/conditions.h b/absl/log/internal/conditions.h
index f576d650..645f3c23 100644
--- a/absl/log/internal/conditions.h
+++ b/absl/log/internal/conditions.h
@@ -57,7 +57,7 @@
// (dangling else, missing switch case) and preserving noreturn semantics (e.g.
// on `LOG(FATAL)`) without requiring braces.
//
-// The `switch` ensures that this expansion is the begnning of a statement (as
+// The `switch` ensures that this expansion is the beginning of a statement (as
// opposed to an expression) and prevents shenanigans like
// `AFunction(LOG(INFO))` and `decltype(LOG(INFO))`. The apparently-redundant
// `default` case makes the condition more amenable to Clang dataflow analysis.
@@ -68,7 +68,7 @@
!(condition) ? (void)0 : ::absl::log_internal::Voidify()&&
// `ABSL_LOG_INTERNAL_STATEFUL_CONDITION` applies a condition like
-// `ABSL_LOG_INTERNAL_CONDITION` but adds to that a series of variable
+// `ABSL_LOG_INTERNAL_STATELESS_CONDITION` but adds to that a series of variable
// declarations, including a local static object which stores the state needed
// to implement the stateful macros like `LOG_EVERY_N`.
//
@@ -137,21 +137,30 @@
? true \
: (::absl::log_internal::ExitQuietly(), false)) \
: false))
-
-#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \
- for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
- log_internal_severity_loop = 0) \
- for (const absl::LogSeverity log_internal_severity = \
- ::absl::NormalizeLogSeverity(severity); \
- log_internal_severity_loop; log_internal_severity_loop = 0) \
+#define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition) \
+ ABSL_LOG_INTERNAL_##type##_CONDITION( \
+ (ABSL_ASSUME(absl::kLogDebugFatal == absl::LogSeverity::kError || \
+ absl::kLogDebugFatal == absl::LogSeverity::kFatal), \
+ (condition) && \
+ (::absl::kLogDebugFatal >= \
+ static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
+ (::absl::kLogDebugFatal == ::absl::LogSeverity::kFatal && \
+ (::absl::log_internal::AbortQuietly(), false)))))
+
+#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \
+ for (int absl_log_internal_severity_loop = 1; \
+ absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \
+ for (const absl::LogSeverity absl_log_internal_severity = \
+ ::absl::NormalizeLogSeverity(severity); \
+ absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \
ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL
-#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \
- ABSL_LOG_INTERNAL_##type##_CONDITION( \
- (condition) && \
- (log_internal_severity >= \
- static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
- (log_internal_severity == ::absl::LogSeverity::kFatal && \
- (::absl::log_internal::AbortQuietly(), false))))
+#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \
+ ABSL_LOG_INTERNAL_##type##_CONDITION(( \
+ (condition) && \
+ (absl_log_internal_severity >= \
+ static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
+ (absl_log_internal_severity == ::absl::LogSeverity::kFatal && \
+ (::absl::log_internal::AbortQuietly(), false)))))
#else // ndef ABSL_MIN_LOG_LEVEL
#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \
ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
@@ -163,12 +172,14 @@
ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition) \
ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
-#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \
- for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
- log_internal_severity_loop = 0) \
- for (const absl::LogSeverity log_internal_severity = \
- ::absl::NormalizeLogSeverity(severity); \
- log_internal_severity_loop; log_internal_severity_loop = 0) \
+#define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition) \
+ ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
+#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \
+ for (int absl_log_internal_severity_loop = 1; \
+ absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \
+ for (const absl::LogSeverity absl_log_internal_severity = \
+ ::absl::NormalizeLogSeverity(severity); \
+ absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \
ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \
ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
diff --git a/absl/log/internal/flags.h b/absl/log/internal/flags.h
index 0c5e81ed..c4539785 100644
--- a/absl/log/internal/flags.h
+++ b/absl/log/internal/flags.h
@@ -33,7 +33,7 @@
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Log messages at this severity or above are sent to stderr in *addition* to
-// logfiles. Defaults to `ERROR`. See log_severity.h for numeric values of
+// `LogSink`s. Defaults to `ERROR`. See log_severity.h for numeric values of
// severity levels.
ABSL_DECLARE_FLAG(int, stderrthreshold);
@@ -50,4 +50,10 @@ ABSL_DECLARE_FLAG(std::string, log_backtrace_at);
// each message logged. Defaults to true.
ABSL_DECLARE_FLAG(bool, log_prefix);
+// Global log verbosity level. Default is 0.
+ABSL_DECLARE_FLAG(int, v);
+
+// Per-module log verbosity level. By default is empty and is unused.
+ABSL_DECLARE_FLAG(std::string, vmodule);
+
#endif // ABSL_LOG_INTERNAL_FLAGS_H_
diff --git a/absl/log/internal/fnmatch.cc b/absl/log/internal/fnmatch.cc
new file mode 100644
index 00000000..26e1e57f
--- /dev/null
+++ b/absl/log/internal/fnmatch.cc
@@ -0,0 +1,73 @@
+// Copyright 2023 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/log/internal/fnmatch.h"
+
+#include <cstddef>
+
+#include "absl/base/config.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+bool FNMatch(absl::string_view pattern, absl::string_view str) {
+ bool in_wildcard_match = false;
+ while (true) {
+ if (pattern.empty()) {
+ // `pattern` is exhausted; succeed if all of `str` was consumed matching
+ // it.
+ return in_wildcard_match || str.empty();
+ }
+ if (str.empty()) {
+ // `str` is exhausted; succeed if `pattern` is empty or all '*'s.
+ return pattern.find_first_not_of('*') == pattern.npos;
+ }
+ switch (pattern.front()) {
+ case '*':
+ pattern.remove_prefix(1);
+ in_wildcard_match = true;
+ break;
+ case '?':
+ pattern.remove_prefix(1);
+ str.remove_prefix(1);
+ break;
+ default:
+ if (in_wildcard_match) {
+ absl::string_view fixed_portion = pattern;
+ const size_t end = fixed_portion.find_first_of("*?");
+ if (end != fixed_portion.npos) {
+ fixed_portion = fixed_portion.substr(0, end);
+ }
+ const size_t match = str.find(fixed_portion);
+ if (match == str.npos) {
+ return false;
+ }
+ pattern.remove_prefix(fixed_portion.size());
+ str.remove_prefix(match + fixed_portion.size());
+ in_wildcard_match = false;
+ } else {
+ if (pattern.front() != str.front()) {
+ return false;
+ }
+ pattern.remove_prefix(1);
+ str.remove_prefix(1);
+ }
+ break;
+ }
+ }
+}
+} // namespace log_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/flags/flag.cc b/absl/log/internal/fnmatch.h
index 531df128..4ea147cc 100644
--- a/absl/flags/flag.cc
+++ b/absl/log/internal/fnmatch.h
@@ -1,11 +1,10 @@
-//
-// Copyright 2019 The Abseil Authors.
+// Copyright 2023 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
+// 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,
@@ -13,26 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "absl/flags/flag.h"
+#ifndef ABSL_LOG_INTERNAL_FNMATCH_H_
+#define ABSL_LOG_INTERNAL_FNMATCH_H_
#include "absl/base/config.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
-
-// This global mutex protects on-demand construction of flag objects in MSVC
-// builds.
-#if defined(_MSC_VER) && !defined(__clang__)
-
-namespace flags_internal {
-
-ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit);
-
-absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; }
-
-} // namespace flags_internal
-
-#endif
-
+namespace log_internal {
+// Like POSIX `fnmatch`, but:
+// * accepts `string_view`
+// * does not allocate any dynamic memory
+// * only supports * and ? wildcards and not bracket expressions [...]
+// * wildcards may match /
+// * no backslash-escaping
+bool FNMatch(absl::string_view pattern, absl::string_view str);
+} // namespace log_internal
ABSL_NAMESPACE_END
} // namespace absl
+
+#endif // ABSL_LOG_INTERNAL_FNMATCH_H_
diff --git a/absl/log/internal/fnmatch_benchmark.cc b/absl/log/internal/fnmatch_benchmark.cc
new file mode 100644
index 00000000..f062ba20
--- /dev/null
+++ b/absl/log/internal/fnmatch_benchmark.cc
@@ -0,0 +1,29 @@
+// Copyright 2023 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/log/internal/fnmatch.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+void BM_FNMatch(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ bool ret =
+ absl::log_internal::FNMatch("*?*asdf*?*we???asdf**asdf*we",
+ "QWERFASVWERASDFWEDFASDasdfQWERGFWASDERREWF"
+ "weHOOasdf@#$%TW#ZSERasdfQW#REGTZSERERwe");
+ benchmark::DoNotOptimize(ret);
+ }
+}
+BENCHMARK(BM_FNMatch);
+} // namespace
diff --git a/absl/log/internal/fnmatch_test.cc b/absl/log/internal/fnmatch_test.cc
new file mode 100644
index 00000000..e16a64ec
--- /dev/null
+++ b/absl/log/internal/fnmatch_test.cc
@@ -0,0 +1,59 @@
+// Copyright 2023 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/log/internal/fnmatch.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+
+TEST(FNMatchTest, Works) {
+ using absl::log_internal::FNMatch;
+ EXPECT_THAT(FNMatch("foo", "foo"), IsTrue());
+ EXPECT_THAT(FNMatch("foo", "bar"), IsFalse());
+ EXPECT_THAT(FNMatch("foo", "fo"), IsFalse());
+ EXPECT_THAT(FNMatch("foo", "foo2"), IsFalse());
+ EXPECT_THAT(FNMatch("bar/foo.ext", "bar/foo.ext"), IsTrue());
+ EXPECT_THAT(FNMatch("*ba*r/fo*o.ext*", "bar/foo.ext"), IsTrue());
+ EXPECT_THAT(FNMatch("bar/foo.ext", "bar/baz.ext"), IsFalse());
+ EXPECT_THAT(FNMatch("bar/foo.ext", "bar/foo"), IsFalse());
+ EXPECT_THAT(FNMatch("bar/foo.ext", "bar/foo.ext.zip"), IsFalse());
+ EXPECT_THAT(FNMatch("ba?/*.ext", "bar/foo.ext"), IsTrue());
+ EXPECT_THAT(FNMatch("ba?/*.ext", "baZ/FOO.ext"), IsTrue());
+ EXPECT_THAT(FNMatch("ba?/*.ext", "barr/foo.ext"), IsFalse());
+ EXPECT_THAT(FNMatch("ba?/*.ext", "bar/foo.ext2"), IsFalse());
+ EXPECT_THAT(FNMatch("ba?/*", "bar/foo.ext2"), IsTrue());
+ EXPECT_THAT(FNMatch("ba?/*", "bar/"), IsTrue());
+ EXPECT_THAT(FNMatch("ba?/?", "bar/"), IsFalse());
+ EXPECT_THAT(FNMatch("ba?/*", "bar"), IsFalse());
+ EXPECT_THAT(FNMatch("?x", "zx"), IsTrue());
+ EXPECT_THAT(FNMatch("*b", "aab"), IsTrue());
+ EXPECT_THAT(FNMatch("a*b", "aXb"), IsTrue());
+ EXPECT_THAT(FNMatch("", ""), IsTrue());
+ EXPECT_THAT(FNMatch("", "a"), IsFalse());
+ EXPECT_THAT(FNMatch("ab*", "ab"), IsTrue());
+ EXPECT_THAT(FNMatch("ab**", "ab"), IsTrue());
+ EXPECT_THAT(FNMatch("ab*?", "ab"), IsFalse());
+ EXPECT_THAT(FNMatch("*", "bbb"), IsTrue());
+ EXPECT_THAT(FNMatch("*", ""), IsTrue());
+ EXPECT_THAT(FNMatch("?", ""), IsFalse());
+ EXPECT_THAT(FNMatch("***", "**p"), IsTrue());
+ EXPECT_THAT(FNMatch("**", "*"), IsTrue());
+ EXPECT_THAT(FNMatch("*?", "*"), IsTrue());
+}
+
+} // namespace
diff --git a/absl/log/internal/log_impl.h b/absl/log/internal/log_impl.h
index 9326780d..99de6dbb 100644
--- a/absl/log/internal/log_impl.h
+++ b/absl/log/internal/log_impl.h
@@ -15,6 +15,7 @@
#ifndef ABSL_LOG_INTERNAL_LOG_IMPL_H_
#define ABSL_LOG_INTERNAL_LOG_IMPL_H_
+#include "absl/log/absl_vlog_is_on.h"
#include "absl/log/internal/conditions.h"
#include "absl/log/internal/log_message.h"
#include "absl/log/internal/strip.h"
@@ -41,6 +42,35 @@
ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
#endif
+// The `switch` ensures that this expansion is the begnning of a statement (as
+// opposed to an expression). The use of both `case 0` and `default` is to
+// suppress a compiler warning.
+#define ABSL_LOG_INTERNAL_VLOG_IMPL(verbose_level) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ case 0: \
+ default: \
+ ABSL_LOG_INTERNAL_LOG_IF_IMPL( \
+ _INFO, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ .WithVerbosity(absl_logging_internal_verbose_level)
+
+#ifndef NDEBUG
+#define ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ case 0: \
+ default: \
+ ABSL_LOG_INTERNAL_LOG_IF_IMPL( \
+ _INFO, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ .WithVerbosity(absl_logging_internal_verbose_level)
+#else
+#define ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ case 0: \
+ default: \
+ ABSL_LOG_INTERNAL_LOG_IF_IMPL( \
+ _INFO, false && ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ .WithVerbosity(absl_logging_internal_verbose_level)
+#endif
+
#define ABSL_LOG_INTERNAL_LOG_IF_IMPL(severity, condition) \
ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, condition) \
ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
@@ -134,6 +164,42 @@
(EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
#endif // def NDEBUG
+#define ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(verbose_level, n) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ case 0: \
+ default: \
+ ABSL_LOG_INTERNAL_CONDITION_INFO( \
+ STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ (EveryN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
+ absl_logging_internal_verbose_level)
+
+#define ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(verbose_level, n) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ case 0: \
+ default: \
+ ABSL_LOG_INTERNAL_CONDITION_INFO( \
+ STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ (FirstN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
+ absl_logging_internal_verbose_level)
+
+#define ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(verbose_level) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ case 0: \
+ default: \
+ ABSL_LOG_INTERNAL_CONDITION_INFO( \
+ STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ (EveryPow2) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \
+ absl_logging_internal_verbose_level)
+
+#define ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(verbose_level, n_seconds) \
+ switch (const int absl_logging_internal_verbose_level = (verbose_level)) \
+ case 0: \
+ default: \
+ ABSL_LOG_INTERNAL_CONDITION_INFO( \
+ STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \
+ (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream() \
+ .WithVerbosity(absl_logging_internal_verbose_level)
+
#define ABSL_LOG_INTERNAL_LOG_IF_EVERY_N_IMPL(severity, condition, n) \
ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryN, n) \
ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h
index 46937728..4ecb8a14 100644
--- a/absl/log/internal/log_message.h
+++ b/absl/log/internal/log_message.h
@@ -40,7 +40,7 @@
#include "absl/log/internal/nullguard.h"
#include "absl/log/log_entry.h"
#include "absl/log/log_sink.h"
-#include "absl/strings/internal/has_absl_stringify.h"
+#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
@@ -170,15 +170,15 @@ class LogMessage {
// Types that support `AbslStringify()` are serialized that way.
template <typename T,
- typename std::enable_if<
- strings_internal::HasAbslStringify<T>::value, int>::type = 0>
+ typename std::enable_if<absl::HasAbslStringify<T>::value,
+ int>::type = 0>
LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
// Types that don't support `AbslStringify()` but do support streaming into a
// `std::ostream&` are serialized that way.
template <typename T,
- typename std::enable_if<
- !strings_internal::HasAbslStringify<T>::value, int>::type = 0>
+ typename std::enable_if<!absl::HasAbslStringify<T>::value,
+ int>::type = 0>
LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;
// Note: We explicitly do not support `operator<<` for non-const references
@@ -283,8 +283,7 @@ class StringifySink final {
// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
template <typename T,
- typename std::enable_if<strings_internal::HasAbslStringify<T>::value,
- int>::type>
+ typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type>
LogMessage& LogMessage::operator<<(const T& v) {
StringifySink sink(*this);
// Replace with public API.
@@ -294,8 +293,7 @@ LogMessage& LogMessage::operator<<(const T& v) {
// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
template <typename T,
- typename std::enable_if<!strings_internal::HasAbslStringify<T>::value,
- int>::type>
+ typename std::enable_if<!absl::HasAbslStringify<T>::value, int>::type>
LogMessage& LogMessage::operator<<(const T& v) {
OstreamView view(*data_);
view.stream() << log_internal::NullGuard<T>().Guard(v);
diff --git a/absl/log/internal/log_sink_set.cc b/absl/log/internal/log_sink_set.cc
index b7cbe364..3d5c6995 100644
--- a/absl/log/internal/log_sink_set.cc
+++ b/absl/log/internal/log_sink_set.cc
@@ -35,6 +35,7 @@
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/log_severity.h"
+#include "absl/base/no_destructor.h"
#include "absl/base/thread_annotations.h"
#include "absl/cleanup/cleanup.h"
#include "absl/log/globals.h"
@@ -168,17 +169,16 @@ class GlobalLogSinkSet final {
#if defined(__myriad2__) || defined(__Fuchsia__)
// myriad2 and Fuchsia do not log to stderr by default.
#else
- static StderrLogSink* stderr_log_sink = new StderrLogSink;
- AddLogSink(stderr_log_sink);
+ static absl::NoDestructor<StderrLogSink> stderr_log_sink;
+ AddLogSink(stderr_log_sink.get());
#endif
#ifdef __ANDROID__
- static AndroidLogSink* android_log_sink = new AndroidLogSink;
- AddLogSink(android_log_sink);
+ static absl::NoDestructor<AndroidLogSink> android_log_sink;
+ AddLogSink(android_log_sink.get());
#endif
#if defined(_WIN32)
- static WindowsDebuggerLogSink* debugger_log_sink =
- new WindowsDebuggerLogSink;
- AddLogSink(debugger_log_sink);
+ static absl::NoDestructor<WindowsDebuggerLogSink> debugger_log_sink;
+ AddLogSink(debugger_log_sink.get());
#endif // !defined(_WIN32)
}
@@ -268,7 +268,7 @@ class GlobalLogSinkSet final {
// Returns reference to the global LogSinks set.
GlobalLogSinkSet& GlobalSinks() {
- static GlobalLogSinkSet* global_sinks = new GlobalLogSinkSet;
+ static absl::NoDestructor<GlobalLogSinkSet> global_sinks;
return *global_sinks;
}
diff --git a/absl/log/internal/strip.h b/absl/log/internal/strip.h
index adc86ffd..f8d27869 100644
--- a/absl/log/internal/strip.h
+++ b/absl/log/internal/strip.h
@@ -37,7 +37,7 @@
#define ABSL_LOGGING_INTERNAL_LOG_DFATAL \
::absl::log_internal::NullStreamMaybeFatal(::absl::kLogDebugFatal)
#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \
- ::absl::log_internal::NullStreamMaybeFatal(log_internal_severity)
+ ::absl::log_internal::NullStreamMaybeFatal(absl_log_internal_severity)
#define ABSL_LOG_INTERNAL_CHECK(failure_message) ABSL_LOGGING_INTERNAL_LOG_FATAL
#define ABSL_LOG_INTERNAL_QCHECK(failure_message) \
ABSL_LOGGING_INTERNAL_LOG_QFATAL
@@ -57,8 +57,9 @@
::absl::log_internal::LogMessageQuietlyFatal(__FILE__, __LINE__)
#define ABSL_LOGGING_INTERNAL_LOG_DFATAL \
::absl::log_internal::LogMessage(__FILE__, __LINE__, ::absl::kLogDebugFatal)
-#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \
- ::absl::log_internal::LogMessage(__FILE__, __LINE__, log_internal_severity)
+#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \
+ ::absl::log_internal::LogMessage(__FILE__, __LINE__, \
+ absl_log_internal_severity)
// These special cases dispatch to special-case constructors that allow us to
// avoid an extra function call and shrink non-LTO binaries by a percent or so.
#define ABSL_LOG_INTERNAL_CHECK(failure_message) \
diff --git a/absl/log/internal/vlog_config.cc b/absl/log/internal/vlog_config.cc
new file mode 100644
index 00000000..b5788500
--- /dev/null
+++ b/absl/log/internal/vlog_config.cc
@@ -0,0 +1,340 @@
+// Copyright 2022 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/log/internal/vlog_config.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/internal/spinlock.h"
+#include "absl/base/no_destructor.h"
+#include "absl/base/optimization.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/log/internal/fnmatch.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+namespace {
+bool ModuleIsPath(absl::string_view module_pattern) {
+#ifdef _WIN32
+ return module_pattern.find_first_of("/\\") != module_pattern.npos;
+#else
+ return module_pattern.find('/') != module_pattern.npos;
+#endif
+}
+} // namespace
+
+bool VLogSite::SlowIsEnabled(int stale_v, int level) {
+ if (ABSL_PREDICT_TRUE(stale_v != kUninitialized)) {
+ // Because of the prerequisites to this function, we know that stale_v is
+ // either uninitialized or >= level. If it's not uninitialized, that means
+ // it must be >= level, thus we should log.
+ return true;
+ }
+ stale_v = log_internal::RegisterAndInitialize(this);
+ return ABSL_PREDICT_FALSE(stale_v >= level);
+}
+
+bool VLogSite::SlowIsEnabled0(int stale_v) { return SlowIsEnabled(stale_v, 0); }
+bool VLogSite::SlowIsEnabled1(int stale_v) { return SlowIsEnabled(stale_v, 1); }
+bool VLogSite::SlowIsEnabled2(int stale_v) { return SlowIsEnabled(stale_v, 2); }
+bool VLogSite::SlowIsEnabled3(int stale_v) { return SlowIsEnabled(stale_v, 3); }
+bool VLogSite::SlowIsEnabled4(int stale_v) { return SlowIsEnabled(stale_v, 4); }
+bool VLogSite::SlowIsEnabled5(int stale_v) { return SlowIsEnabled(stale_v, 5); }
+
+namespace {
+struct VModuleInfo final {
+ std::string module_pattern;
+ bool module_is_path; // i.e. it contains a path separator.
+ int vlog_level;
+
+ // Allocates memory.
+ VModuleInfo(absl::string_view module_pattern_arg, bool module_is_path_arg,
+ int vlog_level_arg)
+ : module_pattern(std::string(module_pattern_arg)),
+ module_is_path(module_is_path_arg),
+ vlog_level(vlog_level_arg) {}
+};
+
+// `mutex` guards all of the data structures that aren't lock-free.
+// To avoid problems with the heap checker which calls into `VLOG`, `mutex` must
+// be a `SpinLock` that prevents fiber scheduling instead of a `Mutex`.
+ABSL_CONST_INIT absl::base_internal::SpinLock mutex(
+ absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY);
+
+// `GetUpdateSitesMutex()` serializes updates to all of the sites (i.e. those in
+// `site_list_head`) themselves.
+absl::Mutex* GetUpdateSitesMutex() {
+ // Chromium requires no global destructors, so we can't use the
+ // absl::kConstInit idiom since absl::Mutex as a non-trivial destructor.
+ static absl::NoDestructor<absl::Mutex> update_sites_mutex ABSL_ACQUIRED_AFTER(
+ mutex);
+ return update_sites_mutex.get();
+}
+
+ABSL_CONST_INIT int global_v ABSL_GUARDED_BY(mutex) = 0;
+// `site_list_head` is the head of a singly-linked list. Traversal, insertion,
+// and reads are atomic, so no locks are required, but updates to existing
+// elements are guarded by `GetUpdateSitesMutex()`.
+ABSL_CONST_INIT std::atomic<VLogSite*> site_list_head{nullptr};
+ABSL_CONST_INIT std::vector<VModuleInfo>* vmodule_info ABSL_GUARDED_BY(mutex)
+ ABSL_PT_GUARDED_BY(mutex){nullptr};
+
+// Only used for lisp.
+ABSL_CONST_INIT std::vector<std::function<void()>>* update_callbacks
+ ABSL_GUARDED_BY(GetUpdateSitesMutex())
+ ABSL_PT_GUARDED_BY(GetUpdateSitesMutex()){nullptr};
+
+// Allocates memory.
+std::vector<VModuleInfo>& get_vmodule_info()
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
+ if (!vmodule_info) vmodule_info = new std::vector<VModuleInfo>;
+ return *vmodule_info;
+}
+
+// Does not allocate or take locks.
+int VLogLevel(absl::string_view file, const std::vector<VModuleInfo>* infos,
+ int current_global_v) {
+ // `infos` is null during a call to `VLOG` prior to setting `vmodule` (e.g. by
+ // parsing flags). We can't allocate in `VLOG`, so we treat null as empty
+ // here and press on.
+ if (!infos || infos->empty()) return current_global_v;
+ // Get basename for file
+ absl::string_view basename = file;
+ {
+ const size_t sep = basename.rfind('/');
+ if (sep != basename.npos) {
+ basename.remove_prefix(sep + 1);
+#ifdef _WIN32
+ } else {
+ const size_t sep = basename.rfind('\\');
+ if (sep != basename.npos) basename.remove_prefix(sep + 1);
+#endif
+ }
+ }
+
+ absl::string_view stem = file, stem_basename = basename;
+ {
+ const size_t sep = stem_basename.find('.');
+ if (sep != stem_basename.npos) {
+ stem.remove_suffix(stem_basename.size() - sep);
+ stem_basename.remove_suffix(stem_basename.size() - sep);
+ }
+ if (absl::ConsumeSuffix(&stem_basename, "-inl")) {
+ stem.remove_suffix(absl::string_view("-inl").size());
+ }
+ }
+ for (const auto& info : *infos) {
+ if (info.module_is_path) {
+ // If there are any slashes in the pattern, try to match the full
+ // name.
+ if (FNMatch(info.module_pattern, stem)) {
+ return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level;
+ }
+ } else if (FNMatch(info.module_pattern, stem_basename)) {
+ return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level;
+ }
+ }
+
+ return current_global_v;
+}
+
+// Allocates memory.
+int AppendVModuleLocked(absl::string_view module_pattern, int log_level)
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
+ for (const auto& info : get_vmodule_info()) {
+ if (FNMatch(info.module_pattern, module_pattern)) {
+ // This is a memory optimization to avoid storing patterns that will never
+ // match due to exit early semantics. Primarily optimized for our own unit
+ // tests.
+ return info.vlog_level;
+ }
+ }
+ bool module_is_path = ModuleIsPath(module_pattern);
+ get_vmodule_info().emplace_back(std::string(module_pattern), module_is_path,
+ log_level);
+ return global_v;
+}
+
+// Allocates memory.
+int PrependVModuleLocked(absl::string_view module_pattern, int log_level)
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
+ absl::optional<int> old_log_level;
+ for (const auto& info : get_vmodule_info()) {
+ if (FNMatch(info.module_pattern, module_pattern)) {
+ old_log_level = info.vlog_level;
+ break;
+ }
+ }
+ bool module_is_path = ModuleIsPath(module_pattern);
+ auto iter = get_vmodule_info().emplace(get_vmodule_info().cbegin(),
+ std::string(module_pattern),
+ module_is_path, log_level);
+
+ // This is a memory optimization to avoid storing patterns that will never
+ // match due to exit early semantics. Primarily optimized for our own unit
+ // tests.
+ get_vmodule_info().erase(
+ std::remove_if(++iter, get_vmodule_info().end(),
+ [module_pattern](const VModuleInfo& info) {
+ return FNMatch(info.module_pattern, module_pattern);
+ }),
+ get_vmodule_info().cend());
+ return old_log_level.value_or(global_v);
+}
+} // namespace
+
+int VLogLevel(absl::string_view file) ABSL_LOCKS_EXCLUDED(mutex) {
+ absl::base_internal::SpinLockHolder l(&mutex);
+ return VLogLevel(file, vmodule_info, global_v);
+}
+
+int RegisterAndInitialize(VLogSite* v) ABSL_LOCKS_EXCLUDED(mutex) {
+ // std::memory_order_seq_cst is overkill in this function, but given that this
+ // path is intended to be slow, it's not worth the brain power to relax that.
+ VLogSite* h = site_list_head.load(std::memory_order_seq_cst);
+
+ VLogSite* old = nullptr;
+ if (v->next_.compare_exchange_strong(old, h, std::memory_order_seq_cst,
+ std::memory_order_seq_cst)) {
+ // Multiple threads may attempt to register this site concurrently.
+ // By successfully setting `v->next` this thread commits to being *the*
+ // thread that installs `v` in the list.
+ while (!site_list_head.compare_exchange_weak(
+ h, v, std::memory_order_seq_cst, std::memory_order_seq_cst)) {
+ v->next_.store(h, std::memory_order_seq_cst);
+ }
+ }
+
+ int old_v = VLogSite::kUninitialized;
+ int new_v = VLogLevel(v->file_);
+ // No loop, if someone else set this, we should respect their evaluation of
+ // `VLogLevel`. This may mean we return a stale `v`, but `v` itself will
+ // always arrive at the freshest value. Otherwise, we could be writing a
+ // stale value and clobbering the fresher one.
+ if (v->v_.compare_exchange_strong(old_v, new_v, std::memory_order_seq_cst,
+ std::memory_order_seq_cst)) {
+ return new_v;
+ }
+ return old_v;
+}
+
+void UpdateVLogSites() ABSL_UNLOCK_FUNCTION(mutex)
+ ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
+ std::vector<VModuleInfo> infos = get_vmodule_info();
+ int current_global_v = global_v;
+ // We need to grab `GetUpdateSitesMutex()` before we release `mutex` to ensure
+ // that updates are not interleaved (resulting in an inconsistent final state)
+ // and to ensure that the final state in the sites matches the final state of
+ // `vmodule_info`. We unlock `mutex` to ensure that uninitialized sites don't
+ // have to wait on all updates in order to acquire `mutex` and initialize
+ // themselves.
+ absl::MutexLock ul(GetUpdateSitesMutex());
+ mutex.Unlock();
+ VLogSite* n = site_list_head.load(std::memory_order_seq_cst);
+ // Because sites are added to the list in the order they are executed, there
+ // tend to be clusters of entries with the same file.
+ const char* last_file = nullptr;
+ int last_file_level = 0;
+ while (n != nullptr) {
+ if (n->file_ != last_file) {
+ last_file = n->file_;
+ last_file_level = VLogLevel(n->file_, &infos, current_global_v);
+ }
+ n->v_.store(last_file_level, std::memory_order_seq_cst);
+ n = n->next_.load(std::memory_order_seq_cst);
+ }
+ if (update_callbacks) {
+ for (auto& cb : *update_callbacks) {
+ cb();
+ }
+ }
+}
+
+void UpdateVModule(absl::string_view vmodule)
+ ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
+ std::vector<std::pair<absl::string_view, int>> glob_levels;
+ for (absl::string_view glob_level : absl::StrSplit(vmodule, ',')) {
+ const size_t eq = glob_level.rfind('=');
+ if (eq == glob_level.npos) continue;
+ const absl::string_view glob = glob_level.substr(0, eq);
+ int level;
+ if (!absl::SimpleAtoi(glob_level.substr(eq + 1), &level)) continue;
+ glob_levels.emplace_back(glob, level);
+ }
+ mutex.Lock(); // Unlocked by UpdateVLogSites().
+ get_vmodule_info().clear();
+ for (const auto& it : glob_levels) {
+ const absl::string_view glob = it.first;
+ const int level = it.second;
+ AppendVModuleLocked(glob, level);
+ }
+ UpdateVLogSites();
+}
+
+int UpdateGlobalVLogLevel(int v)
+ ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
+ mutex.Lock(); // Unlocked by UpdateVLogSites().
+ const int old_global_v = global_v;
+ if (v == global_v) {
+ mutex.Unlock();
+ return old_global_v;
+ }
+ global_v = v;
+ UpdateVLogSites();
+ return old_global_v;
+}
+
+int PrependVModule(absl::string_view module_pattern, int log_level)
+ ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
+ mutex.Lock(); // Unlocked by UpdateVLogSites().
+ int old_v = PrependVModuleLocked(module_pattern, log_level);
+ UpdateVLogSites();
+ return old_v;
+}
+
+void OnVLogVerbosityUpdate(std::function<void()> cb)
+ ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
+ absl::MutexLock ul(GetUpdateSitesMutex());
+ if (!update_callbacks)
+ update_callbacks = new std::vector<std::function<void()>>;
+ update_callbacks->push_back(std::move(cb));
+}
+
+VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v) {
+ return site_list_head.exchange(v, std::memory_order_seq_cst);
+}
+
+} // namespace log_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/log/internal/vlog_config.h b/absl/log/internal/vlog_config.h
new file mode 100644
index 00000000..b6e322c4
--- /dev/null
+++ b/absl/log/internal/vlog_config.h
@@ -0,0 +1,163 @@
+// Copyright 2022 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.
+//
+// -----------------------------------------------------------------------------
+// vlog_config.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines `VLogSite`, a public primitive that represents
+// a callsite for the `VLOG` family of macros and related libraries.
+// It also declares and defines multiple internal utilities used to implement
+// `VLOG`, such as `VLogSiteManager`.
+
+#ifndef ABSL_LOG_INTERNAL_VLOG_CONFIG_H_
+#define ABSL_LOG_INTERNAL_VLOG_CONFIG_H_
+
+// IWYU pragma: private, include "absl/log/log.h"
+
+#include <atomic>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <type_traits>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+
+class SyntheticBinary;
+class VLogSite;
+
+int RegisterAndInitialize(VLogSite* v);
+void UpdateVLogSites();
+constexpr int kUseFlag = (std::numeric_limits<int16_t>::min)();
+
+// Represents a unique callsite for a `VLOG()` or `VLOG_IS_ON()` call.
+//
+// Libraries that provide `VLOG`-like functionality should use this to
+// efficiently handle --vmodule.
+//
+// VLogSite objects must not be destroyed until the program exits. Doing so will
+// probably yield nasty segfaults in VLogSiteManager::UpdateLogSites(). The
+// recommendation is to make all such objects function-local statics.
+class VLogSite final {
+ public:
+ // `f` must not be destroyed until the program exits.
+ explicit constexpr VLogSite(const char* f)
+ : file_(f), v_(kUninitialized), next_(nullptr) {}
+ VLogSite(const VLogSite&) = delete;
+ VLogSite& operator=(const VLogSite&) = delete;
+
+ // Inlining the function yields a ~3x performance improvement at the cost of a
+ // 1.5x code size increase at the call site.
+ // Takes locks but does not allocate memory.
+ ABSL_ATTRIBUTE_ALWAYS_INLINE
+ bool IsEnabled(int level) {
+ int stale_v = v_.load(std::memory_order_relaxed);
+ if (ABSL_PREDICT_TRUE(level > stale_v)) {
+ return false;
+ }
+
+ // We put everything other than the fast path, i.e. vlogging is initialized
+ // but not on, behind an out-of-line function to reduce code size.
+ // "level" is almost always a call-site constant, so we can save a bit
+ // of code space by special-casing for a few common levels.
+#if ABSL_HAVE_BUILTIN(__builtin_constant_p) || defined(__GNUC__)
+ if (__builtin_constant_p(level)) {
+ if (level == 0) return SlowIsEnabled0(stale_v);
+ if (level == 1) return SlowIsEnabled1(stale_v);
+ if (level == 2) return SlowIsEnabled2(stale_v);
+ if (level == 3) return SlowIsEnabled3(stale_v);
+ if (level == 4) return SlowIsEnabled4(stale_v);
+ if (level == 5) return SlowIsEnabled5(stale_v);
+ }
+#endif
+ return SlowIsEnabled(stale_v, level);
+ }
+
+ private:
+ friend int log_internal::RegisterAndInitialize(VLogSite* v);
+ friend void log_internal::UpdateVLogSites();
+ friend class log_internal::SyntheticBinary;
+ static constexpr int kUninitialized = (std::numeric_limits<int>::max)();
+
+ // SlowIsEnabled performs slower checks to determine whether a log site is
+ // enabled. Because it is expected to be called somewhat rarely
+ // (comparatively), it is not inlined to save on code size.
+ //
+ // Prerequisites to calling SlowIsEnabled:
+ // 1) stale_v is uninitialized OR
+ // 2) stale_v is initialized and >= level (meaning we must log).
+ // Takes locks but does not allocate memory.
+ ABSL_ATTRIBUTE_NOINLINE
+ bool SlowIsEnabled(int stale_v, int level);
+ ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled0(int stale_v);
+ ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled1(int stale_v);
+ ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled2(int stale_v);
+ ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled3(int stale_v);
+ ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled4(int stale_v);
+ ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled5(int stale_v);
+
+ // This object is too size-sensitive to use absl::string_view.
+ const char* const file_;
+ std::atomic<int> v_;
+ std::atomic<VLogSite*> next_;
+};
+static_assert(std::is_trivially_destructible<VLogSite>::value,
+ "VLogSite must be trivially destructible");
+
+// Returns the current verbose log level of `file`.
+// Does not allocate memory.
+int VLogLevel(absl::string_view file);
+
+// Registers a site `v` to get updated as `vmodule` and `v` change. Also
+// initializes the site based on their current values, and returns that result.
+// Does not allocate memory.
+int RegisterAndInitialize(VLogSite* v);
+
+// Allocates memory.
+void UpdateVLogSites();
+
+// Completely overwrites the saved value of `vmodule`.
+// Allocates memory.
+void UpdateVModule(absl::string_view vmodule);
+
+// Updates the global verbosity level to `v` and returns the prior value.
+// Allocates memory.
+int UpdateGlobalVLogLevel(int v);
+
+// Atomically prepends `module_pattern=log_level` to the start of vmodule.
+// Returns the prior value for `module_pattern` if there was an exact match and
+// `global_v` otherwise.
+// Allocates memory.
+int PrependVModule(absl::string_view module_pattern, int log_level);
+
+// Registers `on_update` to be called whenever `v` or `vmodule` change.
+// Allocates memory.
+void OnVLogVerbosityUpdate(std::function<void()> cb);
+
+// Does not allocate memory.
+VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v);
+
+} // namespace log_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_LOG_INTERNAL_VLOG_CONFIG_H_
diff --git a/absl/log/internal/vlog_config_benchmark.cc b/absl/log/internal/vlog_config_benchmark.cc
new file mode 100644
index 00000000..9004e2ee
--- /dev/null
+++ b/absl/log/internal/vlog_config_benchmark.cc
@@ -0,0 +1,187 @@
+// Copyright 2022 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 <algorithm>
+#include <atomic>
+#include <cstddef>
+#include <cstring>
+#include <memory>
+#include <new>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "absl/base/config.h"
+#include "absl/container/internal/layout.h"
+#include "absl/log/internal/vlog_config.h"
+#include "absl/memory/memory.h"
+#include "absl/random/distributions.h"
+#include "absl/strings/str_cat.h"
+#include "benchmark/benchmark.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace log_internal {
+// Performance of `UpdateLogSites` depends upon the number and organization of
+// `VLogSite`s in the program. We can synthesize some on the heap to mimic
+// their layout and linkage in static data.
+class SyntheticBinary {
+ public:
+ explicit SyntheticBinary(const size_t num_tus,
+ const size_t max_extra_static_data_bytes_per_tu,
+ const size_t max_sites_per_tu,
+ const int num_shuffles) {
+ per_tu_data_.reserve(num_tus);
+ auto sites = absl::make_unique<VLogSite *[]>(num_tus * max_sites_per_tu);
+ for (size_t i = 0; i < num_tus; i++) {
+ const std::string filename =
+ absl::StrCat("directory-", i / 100, "/subdirectory-", i % 100 / 10,
+ "/file-", i % 10, ".cc");
+ container_internal::Layout<char, VLogSite, char> layout(
+ filename.size() + 1,
+ absl::LogUniform<size_t>(bitgen_, 1, max_sites_per_tu),
+ absl::LogUniform<size_t>(bitgen_, 0,
+ max_extra_static_data_bytes_per_tu));
+ auto buf = absl::make_unique<char[]>(layout.AllocSize());
+ layout.PoisonPadding(buf.get());
+ memcpy(layout.Pointer<0>(buf.get()), filename.c_str(),
+ filename.size() + 1);
+ for (VLogSite &site : layout.Slice<1>(buf.get())) {
+ sites[num_sites_++] =
+ new (&site) VLogSite(layout.Pointer<0>(buf.get()));
+ // The last one is a dangling pointer but will be fixed below.
+ site.next_.store(&site + 1, std::memory_order_seq_cst);
+ }
+ num_extra_static_data_bytes_ += layout.Size<2>();
+ per_tu_data_.push_back(PerTU{layout, std::move(buf)});
+ }
+ // Now link the files together back-to-front into a circular list.
+ for (size_t i = 0; i < num_tus; i++) {
+ auto &tu = per_tu_data_[i];
+ auto &next_tu = per_tu_data_[(i + 1) % num_tus];
+ tu.layout.Slice<1>(tu.buf.get())
+ .back()
+ .next_.store(next_tu.layout.Pointer<1>(next_tu.buf.get()),
+ std::memory_order_seq_cst);
+ }
+ // Now do some shufflin'.
+ auto new_sites = absl::make_unique<VLogSite *[]>(num_sites_);
+ for (int shuffle_num = 0; shuffle_num < num_shuffles; shuffle_num++) {
+ // Each shuffle cuts the ring into three pieces and rearranges them.
+ const size_t cut_a = absl::Uniform(bitgen_, size_t{0}, num_sites_);
+ const size_t cut_b = absl::Uniform(bitgen_, size_t{0}, num_sites_);
+ const size_t cut_c = absl::Uniform(bitgen_, size_t{0}, num_sites_);
+ if (cut_a == cut_b || cut_b == cut_c || cut_a == cut_c) continue;
+ // The same cuts, sorted:
+ const size_t cut_1 = std::min({cut_a, cut_b, cut_c});
+ const size_t cut_3 = std::max({cut_a, cut_b, cut_c});
+ const size_t cut_2 = cut_a ^ cut_b ^ cut_c ^ cut_1 ^ cut_3;
+ VLogSite *const tmp = sites[cut_1]->next_.load(std::memory_order_seq_cst);
+ sites[cut_1]->next_.store(
+ sites[cut_2]->next_.load(std::memory_order_seq_cst),
+ std::memory_order_seq_cst);
+ sites[cut_2]->next_.store(
+ sites[cut_3]->next_.load(std::memory_order_seq_cst),
+ std::memory_order_seq_cst);
+ sites[cut_3]->next_.store(tmp, std::memory_order_seq_cst);
+ memcpy(&new_sites[0], &sites[0], sizeof(VLogSite *) * (cut_1 + 1));
+ memcpy(&new_sites[cut_1 + 1], &sites[cut_2 + 1],
+ sizeof(VLogSite *) * (cut_3 - cut_2));
+ memcpy(&new_sites[cut_1 + 1 + cut_3 - cut_2], &sites[cut_1 + 1],
+ sizeof(VLogSite *) * (cut_2 - cut_1));
+ memcpy(&new_sites[cut_3 + 1], &sites[cut_3 + 1],
+ sizeof(VLogSite *) * (num_sites_ - cut_3 - 1));
+ sites.swap(new_sites);
+ }
+ const char *last_file = nullptr;
+ for (size_t i = 0; i < num_sites_; i++) {
+ if (sites[i]->file_ == last_file) continue;
+ last_file = sites[i]->file_;
+ num_new_files_++;
+ }
+ absl::log_internal::SetVModuleListHeadForTestOnly(sites[0]);
+ sites[num_tus - 1]->next_.store(nullptr, std::memory_order_seq_cst);
+ }
+ ~SyntheticBinary() {
+ static_assert(std::is_trivially_destructible<VLogSite>::value, "");
+ absl::log_internal::SetVModuleListHeadForTestOnly(nullptr);
+ }
+
+ size_t num_sites() const { return num_sites_; }
+ size_t num_new_files() const { return num_new_files_; }
+ size_t num_extra_static_data_bytes() const {
+ return num_extra_static_data_bytes_;
+ }
+
+ private:
+ struct PerTU {
+ container_internal::Layout<char, VLogSite, char> layout;
+ std::unique_ptr<char[]> buf;
+ };
+
+ std::mt19937 bitgen_;
+ std::vector<PerTU> per_tu_data_;
+ size_t num_sites_ = 0;
+ size_t num_new_files_ = 0;
+ size_t num_extra_static_data_bytes_ = 0;
+};
+
+namespace {
+void BM_UpdateVModuleEmpty(benchmark::State& state) {
+ SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
+ 256, state.range(1));
+ for (auto s : state) {
+ absl::log_internal::UpdateVModule("");
+ }
+ state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
+}
+BENCHMARK(BM_UpdateVModuleEmpty)
+ ->ArgPair(100, 200)
+ ->ArgPair(1000, 2000)
+ ->ArgPair(10000, 20000);
+
+void BM_UpdateVModuleShort(benchmark::State& state) {
+ SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
+ 256, state.range(1));
+ for (auto s : state) {
+ absl::log_internal::UpdateVModule("directory2/*=4");
+ }
+ state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
+}
+BENCHMARK(BM_UpdateVModuleShort)
+ ->ArgPair(100, 200)
+ ->ArgPair(1000, 2000)
+ ->ArgPair(10000, 20000);
+
+void BM_UpdateVModuleLong(benchmark::State& state) {
+ SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024,
+ 256, state.range(1));
+ for (auto s : state) {
+ absl::log_internal::UpdateVModule(
+ "d?r?c?o?y2/*=4,d?*r?*c?**o?*y1/*=2,d?*rc?**o?*y3/*=2,,"
+ "d?*r?*c?**o?*1/*=1,d?*r?**o?*y1/*=2,d?*r???***y1/*=7,"
+ "d?*r?**o?*y1/aaa=6");
+ }
+ state.SetItemsProcessed(static_cast<int>(bin.num_new_files()));
+}
+BENCHMARK(BM_UpdateVModuleLong)
+ ->ArgPair(100, 200)
+ ->ArgPair(1000, 2000)
+ ->ArgPair(10000, 20000);
+} // namespace
+} // namespace log_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/log/log.h b/absl/log/log.h
index 602b5acf..b721b087 100644
--- a/absl/log/log.h
+++ b/absl/log/log.h
@@ -32,6 +32,8 @@
// * The `QFATAL` pseudo-severity level is equivalent to `FATAL` but triggers
// quieter termination messages, e.g. without a full stack trace, and skips
// running registered error handlers.
+// * The `DFATAL` pseudo-severity level is defined as `FATAL` in debug mode and
+// as `ERROR` otherwise.
// Some preprocessor shenanigans are used to ensure that e.g. `LOG(INFO)` has
// the same meaning even if a local symbol or preprocessor macro named `INFO` is
// defined. To specify a severity level using an expression instead of a
@@ -51,6 +53,14 @@
// * .NoPrefix()
// Omits the prefix from this line. The prefix includes metadata about the
// logged data such as source code location and timestamp.
+// * .WithVerbosity(int verbose_level)
+// Sets the verbosity field of the logged message as if it was logged by
+// `VLOG(verbose_level)`. Unlike `VLOG`, this method does not affect
+// evaluation of the statement when the specified `verbose_level` has been
+// disabled. The only effect is on `LogSink` implementations which make use
+// of the `absl::LogSink::verbosity()` value. The value
+// `absl::LogEntry::kNoVerbosityLevel` can be specified to mark the message
+// not verbose.
// * .WithTimestamp(absl::Time timestamp)
// Uses the specified timestamp instead of one collected at the time of
// execution.
@@ -188,6 +198,7 @@
#define ABSL_LOG_LOG_H_
#include "absl/log/internal/log_impl.h"
+#include "absl/log/vlog_is_on.h" // IWYU pragma: export
// LOG()
//
@@ -211,11 +222,32 @@
// terminate the program if `NDEBUG` is defined.
#define DLOG(severity) ABSL_LOG_INTERNAL_DLOG_IMPL(_##severity)
+// `VLOG` uses numeric levels to provide verbose logging that can configured at
+// runtime, including at a per-module level. `VLOG` statements are logged at
+// `INFO` severity if they are logged at all; the numeric levels are on a
+// different scale than the proper severity levels. Positive levels are
+// disabled by default. Negative levels should not be used.
+// Example:
+//
+// VLOG(1) << "I print when you run the program with --v=1 or higher";
+// VLOG(2) << "I print when you run the program with --v=2 or higher";
+//
+// See vlog_is_on.h for further documentation, including the usage of the
+// --vmodule flag to log at different levels in different source files.
+#define VLOG(severity) ABSL_LOG_INTERNAL_VLOG_IMPL(severity)
+
+// `DVLOG` behaves like `VLOG` in debug mode (i.e. `#ifndef NDEBUG`).
+// Otherwise, it compiles away and does nothing.
+#define DVLOG(severity) ABSL_LOG_INTERNAL_DVLOG_IMPL(severity)
+
// `LOG_IF` and friends add a second argument which specifies a condition. If
// the condition is false, nothing is logged.
// Example:
//
// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// There is no `VLOG_IF` because the order of evaluation of the arguments is
+// ambiguous and the alternate spelling with an `if`-statement is trivial.
#define LOG_IF(severity, condition) \
ABSL_LOG_INTERNAL_LOG_IF_IMPL(_##severity, condition)
#define PLOG_IF(severity, condition) \
@@ -283,6 +315,15 @@
#define DLOG_EVERY_N_SEC(severity, n_seconds) \
ABSL_LOG_INTERNAL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds)
+#define VLOG_EVERY_N(severity, n) \
+ ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(severity, n)
+#define VLOG_FIRST_N(severity, n) \
+ ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(severity, n)
+#define VLOG_EVERY_POW_2(severity) \
+ ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(severity)
+#define VLOG_EVERY_N_SEC(severity, n_seconds) \
+ ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(severity, n_seconds)
+
// `LOG_IF_EVERY_N` and friends behave as the corresponding `LOG_EVERY_N`
// but neither increment a counter nor log a message if condition is false (as
// `LOG_IF`).
diff --git a/absl/log/log_basic_test_impl.inc b/absl/log/log_basic_test_impl.inc
index f3400095..e2f33566 100644
--- a/absl/log/log_basic_test_impl.inc
+++ b/absl/log/log_basic_test_impl.inc
@@ -277,6 +277,96 @@ TEST_P(BasicLogDeathTest, QFatal) {
}
#endif
+#ifdef NDEBUG
+TEST_P(BasicLogTest, DFatal) {
+ absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
+
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ const int log_line = __LINE__ + 1;
+ auto do_log = [] { ABSL_TEST_LOG(DFATAL) << "hello world"; };
+
+ if (LoggingEnabledAt(absl::LogSeverity::kError)) {
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(SourceFilename(Eq(__FILE__)),
+ SourceBasename(Eq("log_basic_test_impl.inc")),
+ SourceLine(Eq(log_line)), Prefix(IsTrue()),
+ LogSeverity(Eq(absl::LogSeverity::kError)),
+ TimestampInMatchWindow(),
+ ThreadID(Eq(absl::base_internal::GetTID())),
+ TextMessage(Eq("hello world")),
+ Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value {
+ literal: "hello world"
+ })pb")),
+ Stacktrace(IsEmpty()))));
+ }
+
+ test_sink.StartCapturingLogs();
+ do_log();
+}
+
+#elif GTEST_HAS_DEATH_TEST
+TEST_P(BasicLogDeathTest, DFatal) {
+ // TODO(b/242568884): re-enable once bug is fixed.
+ // absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
+
+ const int log_line = __LINE__ + 1;
+ auto do_log = [] { ABSL_TEST_LOG(DFATAL) << "hello world"; };
+
+ EXPECT_EXIT(
+ {
+ absl::ScopedMockLog test_sink(
+ absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(test_sink, Send)
+ .Times(AnyNumber())
+ .WillRepeatedly(DeathTestUnexpectedLogging());
+
+ ::testing::InSequence s;
+
+ if (LoggingEnabledAt(absl::LogSeverity::kFatal)) {
+ // The first call without the stack trace.
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(SourceFilename(Eq(__FILE__)),
+ SourceBasename(Eq("log_basic_test_impl.inc")),
+ SourceLine(Eq(log_line)), Prefix(IsTrue()),
+ LogSeverity(Eq(absl::LogSeverity::kFatal)),
+ TimestampInMatchWindow(),
+ ThreadID(Eq(absl::base_internal::GetTID())),
+ TextMessage(Eq("hello world")),
+ Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)),
+ ENCODED_MESSAGE(EqualsProto(
+ R"pb(value { literal: "hello world" })pb")),
+ Stacktrace(IsEmpty()))))
+ .WillOnce(DeathTestExpectedLogging());
+
+ // The second call with the stack trace.
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(SourceFilename(Eq(__FILE__)),
+ SourceBasename(Eq("log_basic_test_impl.inc")),
+ SourceLine(Eq(log_line)), Prefix(IsTrue()),
+ LogSeverity(Eq(absl::LogSeverity::kFatal)),
+ TimestampInMatchWindow(),
+ ThreadID(Eq(absl::base_internal::GetTID())),
+ TextMessage(Eq("hello world")),
+ Verbosity(Eq(absl::LogEntry::kNoVerbosityLevel)),
+ ENCODED_MESSAGE(EqualsProto(
+ R"pb(value { literal: "hello world" })pb")),
+ Stacktrace(Not(IsEmpty())))))
+ .WillOnce(DeathTestExpectedLogging());
+ }
+
+ test_sink.StartCapturingLogs();
+ do_log();
+ },
+ DiedOfFatal, DeathTestValidateExpectations());
+}
+#endif
+
TEST_P(BasicLogTest, Level) {
absl::log_internal::ScopedMinLogLevel scoped_min_log_level(GetParam());
diff --git a/absl/log/log_entry.cc b/absl/log/log_entry.cc
index 19c3b3f1..fe58a576 100644
--- a/absl/log/log_entry.cc
+++ b/absl/log/log_entry.cc
@@ -25,5 +25,17 @@ constexpr int LogEntry::kNoVerbosityLevel;
constexpr int LogEntry::kNoVerboseLevel;
#endif
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace log_internal {
+extern const char kAvoidEmptyLogEntryLibraryWarning;
+const char kAvoidEmptyLogEntryLibraryWarning = 0;
+} // namespace log_internal
+#endif // __APPLE__
+
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/log/log_entry.h b/absl/log/log_entry.h
index 9e4ae8eb..7a55dfe2 100644
--- a/absl/log/log_entry.h
+++ b/absl/log/log_entry.h
@@ -96,7 +96,8 @@ class LogEntry final {
// LogEntry::verbosity()
//
// Returns this entry's verbosity, or `kNoVerbosityLevel` for a non-verbose
- // entry. Verbosity control is not available outside of Google yet.
+ // entry. Taken from the argument to `VLOG` or from
+ // `LOG(...).WithVerbosity(...)`.
int verbosity() const { return verbose_level_; }
// LogEntry::timestamp()
diff --git a/absl/log/log_streamer.h b/absl/log/log_streamer.h
index 2d41a07f..4ed2435d 100644
--- a/absl/log/log_streamer.h
+++ b/absl/log/log_streamer.h
@@ -165,6 +165,16 @@ inline LogStreamer LogFatalStreamer(absl::string_view file, int line) {
return absl::LogStreamer(absl::LogSeverity::kFatal, file, line);
}
+// LogDebugFatalStreamer()
+//
+// Returns a LogStreamer that writes at level LogSeverity::kLogDebugFatal.
+//
+// In debug mode, the program will be terminated when this `LogStreamer` is
+// destroyed, regardless of whether any data were streamed in.
+inline LogStreamer LogDebugFatalStreamer(absl::string_view file, int line) {
+ return absl::LogStreamer(absl::kLogDebugFatal, file, line);
+}
+
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/log/log_streamer_test.cc b/absl/log/log_streamer_test.cc
index 328d70d0..40c7d488 100644
--- a/absl/log/log_streamer_test.cc
+++ b/absl/log/log_streamer_test.cc
@@ -151,6 +151,57 @@ TEST(LogStreamerDeathTest, LogFatalStreamer) {
}
#endif
+#ifdef NDEBUG
+TEST(LogStreamerTest, LogDebugFatalStreamer) {
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)),
+ Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kError)),
+ TimestampInMatchWindow(),
+ ThreadID(Eq(absl::base_internal::GetTID())),
+ TextMessage(Eq("WriteToStream: foo")),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value {
+ str: "WriteToStream: foo"
+ })pb")),
+ Stacktrace(IsEmpty()))));
+
+ test_sink.StartCapturingLogs();
+ WriteToStream("foo",
+ &absl::LogDebugFatalStreamer("path/file.cc", 1234).stream());
+}
+#elif GTEST_HAS_DEATH_TEST
+TEST(LogStreamerDeathTest, LogDebugFatalStreamer) {
+ EXPECT_EXIT(
+ {
+ absl::ScopedMockLog test_sink;
+
+ EXPECT_CALL(test_sink, Send)
+ .Times(AnyNumber())
+ .WillRepeatedly(DeathTestUnexpectedLogging());
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(
+ SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)),
+ Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kFatal)),
+ TimestampInMatchWindow(),
+ ThreadID(Eq(absl::base_internal::GetTID())),
+ TextMessage(Eq("WriteToStream: foo")),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value {
+ str: "WriteToStream: foo"
+ })pb")))))
+ .WillOnce(DeathTestExpectedLogging());
+
+ test_sink.StartCapturingLogs();
+ WriteToStream(
+ "foo", &absl::LogDebugFatalStreamer("path/file.cc", 1234).stream());
+ },
+ DiedOfFatal, DeathTestValidateExpectations());
+}
+#endif
+
TEST(LogStreamerTest, LogStreamer) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
diff --git a/absl/log/stripping_test.cc b/absl/log/stripping_test.cc
index aff91495..271fae1d 100644
--- a/absl/log/stripping_test.cc
+++ b/absl/log/stripping_test.cc
@@ -54,6 +54,7 @@
#include "absl/log/check.h"
#include "absl/log/internal/test_helpers.h"
#include "absl/log/log.h"
+#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
@@ -322,6 +323,49 @@ TEST_F(StrippingTest, Fatal) {
}
}
+TEST_F(StrippingTest, DFatal) {
+ // We need to load a copy of the needle string into memory (so we can search
+ // for it) without leaving it lying around in plaintext in the executable file
+ // as would happen if we used a literal. We might (or might not) leave it
+ // lying around later; that's what the tests are for!
+ const std::string needle = absl::Base64Escape("StrippingTest.DFatal");
+ // We don't care if the LOG statement is actually executed, we're just
+ // checking that it's stripped.
+ if (kReallyDie) LOG(DFATAL) << "U3RyaXBwaW5nVGVzdC5ERmF0YWw=";
+
+ std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+ ASSERT_THAT(exe, NotNull());
+ // `DFATAL` can be `ERROR` or `FATAL`, and a compile-time optimizer doesn't
+ // know which, because `absl::kLogDebugFatal` is declared `extern` and defined
+ // in another TU. Link-time optimization might do better. We have six cases:
+ // | `AMLL` is-> | `<=ERROR` | `FATAL` | `>FATAL` |
+ // | ------------------- | --------- | ------- | -------- |
+ // | `DFATAL` is `ERROR` | present | ? | stripped |
+ // | `DFATAL` is `FATAL` | present | present | stripped |
+
+ // These constexpr variables are used to suppress unreachable code warnings
+ // in the if-else statements below.
+
+ // "present" in the table above: `DFATAL` exceeds `ABSL_MIN_LOG_LEVEL`, so
+ // `DFATAL` statements should not be stripped (and they should be logged
+ // when executed, but that's a different testsuite).
+ constexpr bool kExpectPresent = absl::kLogDebugFatal >= kAbslMinLogLevel;
+
+ // "stripped" in the table above: even though the compiler may not know
+ // which value `DFATAL` has, it should be able to strip it since both
+ // possible values ought to be stripped.
+ constexpr bool kExpectStripped = kAbslMinLogLevel > absl::LogSeverity::kFatal;
+
+ if (kExpectPresent) {
+ EXPECT_THAT(exe.get(), FileHasSubstr(needle));
+ } else if (kExpectStripped) {
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
+ } else {
+ // "?" in the table above; may or may not be stripped depending on whether
+ // any link-time optimization is done. Either outcome is ok.
+ }
+}
+
TEST_F(StrippingTest, Level) {
const std::string needle = absl::Base64Escape("StrippingTest.Level");
volatile auto severity = absl::LogSeverity::kWarning;
@@ -371,4 +415,88 @@ TEST_F(StrippingTest, Check) {
}
}
+TEST_F(StrippingTest, CheckOp) {
+ // See `StrippingTest.Check` for some hairy implementation notes.
+ const std::string var_needle1 =
+ absl::Base64Escape("StrippingTestCheckOpVar1");
+ const std::string var_needle2 =
+ absl::Base64Escape("StrippingTestCheckOpVar2");
+ const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOp");
+ volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx = 0xFEED;
+ volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy = 0xCAFE;
+ if (kReallyDie) {
+ CHECK_EQ(U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx, U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy)
+ << "U3RyaXBwaW5nVGVzdC5DaGVja09w";
+ }
+
+ std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+ ASSERT_THAT(exe, NotNull());
+
+ if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
+ EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1));
+ EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2));
+ EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
+ } else {
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1)));
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2)));
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
+ }
+}
+
+TEST_F(StrippingTest, CheckStrOp) {
+ // See `StrippingTest.Check` for some hairy implementation notes.
+ const std::string var_needle1 =
+ absl::Base64Escape("StrippingTestCheckStrOpVar1");
+ const std::string var_needle2 =
+ absl::Base64Escape("StrippingTestCheckStrOpVar2");
+ const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckStrOp");
+ const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx = "FEED";
+ const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy = "CAFE";
+ if (kReallyDie) {
+ CHECK_STREQ(U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx,
+ U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy)
+ << "U3RyaXBwaW5nVGVzdC5DaGVja1N0ck9w";
+ }
+
+ std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+ ASSERT_THAT(exe, NotNull());
+
+ if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
+ EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1));
+ EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2));
+ EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
+ } else {
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1)));
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2)));
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
+ }
+}
+
+TEST_F(StrippingTest, CheckOk) {
+ // See `StrippingTest.Check` for some hairy implementation notes.
+ const std::string var_needle = absl::Base64Escape("StrippingTestCheckOkVar1");
+ const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOk");
+ volatile bool x = false;
+ auto U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx = absl::OkStatus();
+ if (x) {
+ U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx =
+ absl::InvalidArgumentError("Stripping this is not my job!");
+ }
+ if (kReallyDie) {
+ CHECK_OK(U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx)
+ << "U3RyaXBwaW5nVGVzdC5DaGVja09r";
+ }
+
+ std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+ ASSERT_THAT(exe, NotNull());
+
+ if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
+ EXPECT_THAT(exe.get(), FileHasSubstr(var_needle));
+ EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
+ } else {
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle)));
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
+ }
+}
+
} // namespace
diff --git a/absl/log/vlog_is_on.h b/absl/log/vlog_is_on.h
new file mode 100644
index 00000000..78986513
--- /dev/null
+++ b/absl/log/vlog_is_on.h
@@ -0,0 +1,72 @@
+// Copyright 2022 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: log/vlog_is_on.h
+// -----------------------------------------------------------------------------
+//
+// This header defines the `VLOG_IS_ON()` macro that controls the
+// variable-verbosity conditional logging.
+//
+// It's used by `VLOG` in log.h, or it can also be used directly like this:
+//
+// if (VLOG_IS_ON(2)) {
+// foo_server.RecomputeStatisticsExpensive();
+// LOG(INFO) << foo_server.LastStatisticsAsString();
+// }
+//
+// Each source file has an effective verbosity level that's a non-negative
+// integer computed from the `--vmodule` and `--v` flags.
+// `VLOG_IS_ON(n)` is true, and `VLOG(n)` logs, if that effective verbosity
+// level is greater than or equal to `n`.
+//
+// `--vmodule` takes a comma-delimited list of key=value pairs. Each key is a
+// pattern matched against filenames, and the values give the effective severity
+// level applied to matching files. '?' and '*' characters in patterns are
+// interpreted as single-character and zero-or-more-character wildcards.
+// Patterns including a slash character are matched against full pathnames,
+// while those without are matched against basenames only. One suffix (i.e. the
+// last . and everything after it) is stripped from each filename prior to
+// matching, as is the special suffix "-inl".
+//
+// Files are matched against globs in `--vmodule` in order, and the first match
+// determines the verbosity level.
+//
+// Files which do not match any pattern in `--vmodule` use the value of `--v` as
+// their effective verbosity level. The default is 0.
+//
+// SetVLOGLevel helper function is provided to do limited dynamic control over
+// V-logging by appending to `--vmodule`. Because these go at the beginning of
+// the list, they take priority over any globs previously added.
+//
+// Resetting --vmodule will override all previous modifications to `--vmodule`,
+// including via SetVLOGLevel.
+
+#ifndef ABSL_LOG_VLOG_IS_ON_H_
+#define ABSL_LOG_VLOG_IS_ON_H_
+
+#include "absl/log/absl_vlog_is_on.h" // IWYU pragma: export
+
+// IWYU pragma: private, include "absl/log/log.h"
+
+// Each VLOG_IS_ON call site gets its own VLogSite that registers with the
+// global linked list of sites to asynchronously update its verbosity level on
+// changes to --v or --vmodule. The verbosity can also be set by manually
+// calling SetVLOGLevel.
+//
+// VLOG_IS_ON is not async signal safe, but it is guaranteed not to allocate
+// new memory.
+#define VLOG_IS_ON(verbose_level) ABSL_VLOG_IS_ON(verbose_level)
+
+#endif // ABSL_LOG_VLOG_IS_ON_H_
diff --git a/absl/log/vlog_is_on_test.cc b/absl/log/vlog_is_on_test.cc
new file mode 100644
index 00000000..883d798e
--- /dev/null
+++ b/absl/log/vlog_is_on_test.cc
@@ -0,0 +1,176 @@
+// Copyright 2023 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/log/vlog_is_on.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/log_severity.h"
+#include "absl/flags/flag.h"
+#include "absl/log/flags.h"
+#include "absl/log/globals.h"
+#include "absl/log/log.h"
+#include "absl/log/scoped_mock_log.h"
+#include "absl/types/optional.h"
+
+namespace {
+
+using ::testing::_;
+
+absl::optional<int> MaxLogVerbosity() {
+#ifdef ABSL_MAX_VLOG_VERBOSITY
+ return ABSL_MAX_VLOG_VERBOSITY;
+#else
+ return absl::nullopt;
+#endif
+}
+
+absl::optional<int> MinLogLevel() {
+#ifdef ABSL_MIN_LOG_LEVEL
+ return static_cast<int>(ABSL_MIN_LOG_LEVEL);
+#else
+ return absl::nullopt;
+#endif
+}
+
+TEST(VLogIsOn, GlobalWorksWithoutMaxVerbosityAndMinLogLevel) {
+ if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) {
+ GTEST_SKIP();
+ }
+
+ absl::SetGlobalVLogLevel(3);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important"));
+
+ log.StartCapturingLogs();
+ VLOG(3) << "important";
+ VLOG(4) << "spam";
+}
+
+TEST(VLogIsOn, FileWorksWithoutMaxVerbosityAndMinLogLevel) {
+ if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) {
+ GTEST_SKIP();
+ }
+
+ absl::SetVLogLevel("vlog_is_on_test", 3);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important"));
+
+ log.StartCapturingLogs();
+ VLOG(3) << "important";
+ VLOG(4) << "spam";
+}
+
+TEST(VLogIsOn, PatternWorksWithoutMaxVerbosityAndMinLogLevel) {
+ if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) {
+ GTEST_SKIP();
+ }
+
+ absl::SetVLogLevel("vlog_is_on*", 3);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important"));
+
+ log.StartCapturingLogs();
+ VLOG(3) << "important";
+ VLOG(4) << "spam";
+}
+
+TEST(VLogIsOn, GlobalDoesNotFilterBelowMaxVerbosity) {
+ if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) {
+ GTEST_SKIP();
+ }
+
+ // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+ absl::SetGlobalVLogLevel(1000);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf"));
+
+ log.StartCapturingLogs();
+ VLOG(2) << "asdf";
+}
+
+TEST(VLogIsOn, FileDoesNotFilterBelowMaxVerbosity) {
+ if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) {
+ GTEST_SKIP();
+ }
+
+ // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+ absl::SetVLogLevel("vlog_is_on_test", 1000);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf"));
+
+ log.StartCapturingLogs();
+ VLOG(2) << "asdf";
+}
+
+TEST(VLogIsOn, PatternDoesNotFilterBelowMaxVerbosity) {
+ if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) {
+ GTEST_SKIP();
+ }
+
+ // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+ absl::SetVLogLevel("vlog_is_on*", 1000);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf"));
+
+ log.StartCapturingLogs();
+ VLOG(2) << "asdf";
+}
+
+TEST(VLogIsOn, GlobalFiltersAboveMaxVerbosity) {
+ if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) {
+ GTEST_SKIP();
+ }
+
+ // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+ absl::SetGlobalVLogLevel(1000);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ log.StartCapturingLogs();
+ VLOG(4) << "dfgh";
+}
+
+TEST(VLogIsOn, FileFiltersAboveMaxVerbosity) {
+ if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) {
+ GTEST_SKIP();
+ }
+
+ // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+ absl::SetVLogLevel("vlog_is_on_test", 1000);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ log.StartCapturingLogs();
+ VLOG(4) << "dfgh";
+}
+
+TEST(VLogIsOn, PatternFiltersAboveMaxVerbosity) {
+ if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) {
+ GTEST_SKIP();
+ }
+
+ // Set an arbitrary high value to avoid filtering VLOGs in tests by default.
+ absl::SetVLogLevel("vlog_is_on*", 1000);
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ log.StartCapturingLogs();
+ VLOG(4) << "dfgh";
+}
+
+} // namespace
diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel
index a93f54a6..4573f17d 100644
--- a/absl/memory/BUILD.bazel
+++ b/absl/memory/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -47,6 +54,7 @@ cc_test(
deps = [
":memory",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel
index 13051d83..cf5df9b8 100644
--- a/absl/meta/BUILD.bazel
+++ b/absl/meta/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -46,6 +53,7 @@ cc_test(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel
index c5aaf72b..db02b9c0 100644
--- a/absl/numeric/BUILD.bazel
+++ b/absl/numeric/BUILD.bazel
@@ -19,7 +19,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -48,6 +55,7 @@ cc_binary(
"//absl/base:core_headers",
"//absl/random",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -62,6 +70,7 @@ cc_test(
deps = [
":bits",
"//absl/random",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -98,6 +107,7 @@ cc_test(
"//absl/hash:hash_testing",
"//absl/meta:type_traits",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -112,6 +122,7 @@ cc_test(
":int128",
"//absl/base:config",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
diff --git a/absl/numeric/bits.h b/absl/numeric/bits.h
index 5ed36f52..c76454c8 100644
--- a/absl/numeric/bits.h
+++ b/absl/numeric/bits.h
@@ -49,9 +49,19 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
-#if !(defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L)
-// rotating
+// https://github.com/llvm/llvm-project/issues/64544
+// libc++ had the wrong signature for std::rotl and std::rotr
+// prior to libc++ 18.0.
+//
+#if (defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L) && \
+ (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 180000)
+using std::rotl;
+using std::rotr;
+
+#else
+
+// Rotating functions
template <class T>
ABSL_MUST_USE_RESULT constexpr
typename std::enable_if<std::is_unsigned<T>::value, T>::type
@@ -66,6 +76,22 @@ ABSL_MUST_USE_RESULT constexpr
return numeric_internal::RotateRight(x, s);
}
+#endif
+
+// https://github.com/llvm/llvm-project/issues/64544
+// libc++ had the wrong signature for std::rotl and std::rotr
+// prior to libc++ 18.0.
+//
+#if (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;
+
+#else
+
// Counting functions
//
// While these functions are typically constexpr, on some platforms, they may
@@ -107,19 +133,18 @@ ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline
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)
+#if (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;
+
+#else
+
// 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
@@ -162,12 +187,6 @@ ABSL_INTERNAL_CONSTEXPR_CLZ inline
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
diff --git a/absl/profiling/BUILD.bazel b/absl/profiling/BUILD.bazel
index 3392c96c..86f205f9 100644
--- a/absl/profiling/BUILD.bazel
+++ b/absl/profiling/BUILD.bazel
@@ -19,7 +19,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:private"])
+package(
+ default_visibility = ["//visibility:private"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -52,6 +59,7 @@ cc_test(
"//absl/synchronization",
"//absl/synchronization:thread_pool",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -80,6 +88,7 @@ cc_test(
deps = [
":exponential_biased",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -91,6 +100,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
+ # TODO(b/304670045): remove after periodic_sampler moves to //spanner/common.
"//absl:__subpackages__",
],
deps = [
@@ -109,6 +119,7 @@ cc_test(
deps = [
":periodic_sampler",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel
index 19130ff7..80c4f055 100644
--- a/absl/random/BUILD.bazel
+++ b/absl/random/BUILD.bazel
@@ -23,7 +23,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -171,6 +178,7 @@ cc_test(
":random",
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -196,6 +204,7 @@ cc_test(
"//absl/random/internal:sequence_urbg",
"//absl/strings",
"//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -213,6 +222,7 @@ cc_test(
":distributions",
":random",
"//absl/random/internal:distribution_test_util",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -226,6 +236,7 @@ cc_test(
deps = [
":distributions",
":random",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -250,6 +261,7 @@ cc_test(
"//absl/random/internal:sequence_urbg",
"//absl/strings",
"//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -270,6 +282,7 @@ cc_test(
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -299,6 +312,7 @@ cc_test(
"//absl/random/internal:sequence_urbg",
"//absl/strings",
"//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -321,6 +335,7 @@ cc_test(
"//absl/random/internal:sequence_urbg",
"//absl/strings",
"//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -344,6 +359,7 @@ cc_test(
"//absl/random/internal:sequence_urbg",
"//absl/strings",
"//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -365,6 +381,7 @@ cc_test(
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -391,6 +408,7 @@ cc_test(
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -411,6 +429,7 @@ cc_test(
"//absl/random/internal:pcg_engine",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -426,6 +445,7 @@ cc_test(
":random",
"//absl/base:fast_type_id",
"//absl/random/internal:sequence_urbg",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -444,6 +464,7 @@ cc_test(
":mock_distributions",
":mocking_bit_gen",
":random",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -461,6 +482,7 @@ cc_test(
":mock_distributions",
":mocking_bit_gen",
":random",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -476,6 +498,7 @@ cc_test(
],
deps = [
":random",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -490,6 +513,7 @@ cc_test(
":random",
":seed_sequences",
"//absl/random/internal:nonsecure_base",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/random/distributions.h b/absl/random/distributions.h
index 37fc3aa7..4e3b332e 100644
--- a/absl/random/distributions.h
+++ b/absl/random/distributions.h
@@ -362,7 +362,7 @@ RealType Gaussian(URBG&& urbg, // NOLINT(runtime/references)
// If `lo` is nonzero then this distribution is shifted to the desired interval,
// so LogUniform(lo, hi, b) is equivalent to LogUniform(0, hi-lo, b)+lo.
//
-// See http://ecolego.facilia.se/ecolego/show/Log-Uniform%20Distribution
+// See https://en.wikipedia.org/wiki/Log-normal_distribution
//
// Example:
//
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index 37f4d6e2..71a742ee 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -28,7 +28,14 @@ default_package_visibility = [
"//absl/random:__pkg__",
]
-package(default_visibility = default_package_visibility)
+package(
+ default_visibility = default_package_visibility,
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -409,6 +416,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":traits",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -425,6 +433,7 @@ cc_test(
":generate_real",
"//absl/flags:flag",
"//absl/numeric:bits",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -437,6 +446,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":distribution_test_util",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -449,6 +459,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":fastmath",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -462,6 +473,7 @@ cc_test(
deps = [
":explicit_seed_seq",
"//absl/random:seed_sequences",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -474,6 +486,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":salted_seed_seq",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -489,6 +502,7 @@ cc_test(
deps = [
":distribution_test_util",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -503,6 +517,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":fast_uniform_bits",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -543,6 +558,7 @@ cc_test(
"//absl/random:distributions",
"//absl/random:seed_sequences",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -555,6 +571,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":seed_material",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -571,6 +588,7 @@ cc_test(
":pool_urbg",
"//absl/meta:type_traits",
"//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -586,6 +604,7 @@ cc_test(
":explicit_seed_seq",
":pcg_engine",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -604,6 +623,7 @@ cc_test(
"//absl/log",
"//absl/strings",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -617,6 +637,7 @@ cc_test(
deps = [
":randen",
"//absl/meta:type_traits",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -631,6 +652,7 @@ cc_test(
":platform",
":randen_slow",
"//absl/base:endian",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -662,6 +684,7 @@ cc_test(
":wide_multiply",
"//absl/numeric:bits",
"//absl/numeric:int128",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -742,6 +765,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":iostream_state_saver",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -754,6 +778,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":uniform_helper",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index 1f58b307..981b37fd 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -24,13 +24,21 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
cc_library(
name = "status",
srcs = [
+ "internal/status_internal.cc",
"internal/status_internal.h",
"status.cc",
"status_payload_printer.cc",
@@ -43,17 +51,22 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
"//absl/base:atomic_hook",
+ "//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:no_destructor",
+ "//absl/base:nullability",
"//absl/base:raw_logging_internal",
"//absl/base:strerror",
"//absl/container:inlined_vector",
"//absl/debugging:stacktrace",
"//absl/debugging:symbolize",
"//absl/functional:function_ref",
+ "//absl/memory",
"//absl/strings",
"//absl/strings:cord",
"//absl/strings:str_format",
"//absl/types:optional",
+ "//absl/types:span",
],
)
@@ -65,6 +78,9 @@ cc_test(
deps = [
":status",
"//absl/strings",
+ "//absl/strings:cord",
+ "//absl/strings:str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -83,10 +99,14 @@ cc_library(
deps = [
":status",
"//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:nullability",
"//absl/base:raw_logging_internal",
"//absl/meta:type_traits",
"//absl/strings",
+ "//absl/strings:has_ostream_operator",
+ "//absl/strings:str_format",
"//absl/types:variant",
"//absl/utility",
],
@@ -103,7 +123,9 @@ cc_test(
"//absl/memory",
"//absl/strings",
"//absl/types:any",
+ "//absl/types:variant",
"//absl/utility",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
index 4a3c5d68..00415ab9 100644
--- a/absl/status/CMakeLists.txt
+++ b/absl/status/CMakeLists.txt
@@ -20,6 +20,7 @@ absl_cc_library(
"status.h"
SRCS
"internal/status_internal.h"
+ "internal/status_internal.cc"
"status.cc"
"status_payload_printer.h"
"status_payload_printer.cc"
@@ -34,11 +35,15 @@ absl_cc_library(
absl::core_headers
absl::function_ref
absl::inlined_vector
+ absl::memory
+ absl::no_destructor
+ absl::nullability
absl::optional
absl::raw_logging_internal
+ absl::span
absl::stacktrace
- absl::str_format
absl::strerror
+ absl::str_format
absl::strings
absl::symbolize
PUBLIC
@@ -53,6 +58,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::status
+ absl::str_format
absl::strings
GTest::gmock_main
)
@@ -69,11 +75,15 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::base
- absl::status
+ absl::config
absl::core_headers
+ absl::has_ostream_operator
+ absl::nullability
absl::raw_logging_internal
- absl::type_traits
+ absl::status
+ absl::str_format
absl::strings
+ absl::type_traits
absl::utility
absl::variant
PUBLIC
@@ -89,5 +99,6 @@ absl_cc_test(
DEPS
absl::status
absl::statusor
+ absl::strings
GTest::gmock_main
)
diff --git a/absl/status/internal/status_internal.cc b/absl/status/internal/status_internal.cc
new file mode 100644
index 00000000..a9156754
--- /dev/null
+++ b/absl/status/internal/status_internal.cc
@@ -0,0 +1,248 @@
+// Copyright 2023 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/internal/status_internal.h"
+
+#include <atomic>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
+#include "absl/debugging/stacktrace.h"
+#include "absl/debugging/symbolize.h"
+#include "absl/memory/memory.h"
+#include "absl/status/status.h"
+#include "absl/status/status_payload_printer.h"
+#include "absl/strings/cord.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace status_internal {
+
+void StatusRep::Unref() const {
+ // Fast path: if ref==1, there is no need for a RefCountDec (since
+ // this is the only reference and therefore no other thread is
+ // allowed to be mucking with r).
+ if (ref_.load(std::memory_order_acquire) == 1 ||
+ ref_.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
+ delete this;
+ }
+}
+
+static absl::optional<size_t> FindPayloadIndexByUrl(
+ const Payloads* payloads, absl::string_view type_url) {
+ if (payloads == nullptr) return absl::nullopt;
+
+ for (size_t i = 0; i < payloads->size(); ++i) {
+ if ((*payloads)[i].type_url == type_url) return i;
+ }
+
+ return absl::nullopt;
+}
+
+absl::optional<absl::Cord> StatusRep::GetPayload(
+ absl::string_view type_url) const {
+ absl::optional<size_t> index =
+ status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
+ if (index.has_value()) return (*payloads_)[index.value()].payload;
+
+ return absl::nullopt;
+}
+
+void StatusRep::SetPayload(absl::string_view type_url, absl::Cord payload) {
+ if (payloads_ == nullptr) {
+ payloads_ = absl::make_unique<status_internal::Payloads>();
+ }
+
+ absl::optional<size_t> index =
+ status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
+ if (index.has_value()) {
+ (*payloads_)[index.value()].payload = std::move(payload);
+ return;
+ }
+
+ payloads_->push_back({std::string(type_url), std::move(payload)});
+}
+
+StatusRep::EraseResult StatusRep::ErasePayload(absl::string_view type_url) {
+ absl::optional<size_t> index =
+ status_internal::FindPayloadIndexByUrl(payloads_.get(), type_url);
+ if (!index.has_value()) return {false, Status::PointerToRep(this)};
+ payloads_->erase(payloads_->begin() + index.value());
+ if (payloads_->empty() && message_.empty()) {
+ // Special case: If this can be represented inlined, it MUST be inlined
+ // (== depends on this behavior).
+ EraseResult result = {true, Status::CodeToInlinedRep(code_)};
+ Unref();
+ return result;
+ }
+ return {true, Status::PointerToRep(this)};
+}
+
+void StatusRep::ForEachPayload(
+ absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
+ const {
+ if (auto* payloads = payloads_.get()) {
+ bool in_reverse =
+ payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
+
+ 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 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);
+#endif // NDEBUG
+ }
+ }
+}
+
+std::string StatusRep::ToString(StatusToStringMode mode) const {
+ std::string text;
+ absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
+
+ 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;
+}
+
+bool StatusRep::operator==(const StatusRep& other) const {
+ assert(this != &other);
+ if (code_ != other.code_) return false;
+ if (message_ != other.message_) return false;
+ const status_internal::Payloads* this_payloads = payloads_.get();
+ const status_internal::Payloads* other_payloads = other.payloads_.get();
+
+ const status_internal::Payloads no_payloads;
+ const status_internal::Payloads* larger_payloads =
+ this_payloads ? this_payloads : &no_payloads;
+ const status_internal::Payloads* smaller_payloads =
+ other_payloads ? other_payloads : &no_payloads;
+ if (larger_payloads->size() < smaller_payloads->size()) {
+ std::swap(larger_payloads, smaller_payloads);
+ }
+ if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
+ // Payloads can be ordered differently, so we can't just compare payload
+ // vectors.
+ for (const auto& payload : *larger_payloads) {
+
+ bool found = false;
+ for (const auto& other_payload : *smaller_payloads) {
+ if (payload.type_url == other_payload.type_url) {
+ if (payload.payload != other_payload.payload) {
+ return false;
+ }
+ found = true;
+ break;
+ }
+ }
+ if (!found) return false;
+ }
+ return true;
+}
+
+absl::Nonnull<StatusRep*> StatusRep::CloneAndUnref() const {
+ // Optimization: no need to create a clone if we already have a refcount of 1.
+ if (ref_.load(std::memory_order_acquire) == 1) {
+ // All StatusRep instances are heap allocated and mutable, therefore this
+ // const_cast will never cast away const from a stack instance.
+ //
+ // CloneAndUnref is the only method that doesn't involve an external cast to
+ // get a mutable StatusRep* from the uintptr_t rep stored in Status.
+ return const_cast<StatusRep*>(this);
+ }
+ std::unique_ptr<status_internal::Payloads> payloads;
+ if (payloads_) {
+ payloads = absl::make_unique<status_internal::Payloads>(*payloads_);
+ }
+ auto* new_rep = new StatusRep(code_, message_, std::move(payloads));
+ Unref();
+ return new_rep;
+}
+
+// Convert canonical code to a value known to this binary.
+absl::StatusCode MapToLocalCode(int value) {
+ absl::StatusCode code = static_cast<absl::StatusCode>(value);
+ switch (code) {
+ case absl::StatusCode::kOk:
+ case absl::StatusCode::kCancelled:
+ case absl::StatusCode::kUnknown:
+ case absl::StatusCode::kInvalidArgument:
+ case absl::StatusCode::kDeadlineExceeded:
+ case absl::StatusCode::kNotFound:
+ case absl::StatusCode::kAlreadyExists:
+ case absl::StatusCode::kPermissionDenied:
+ case absl::StatusCode::kResourceExhausted:
+ case absl::StatusCode::kFailedPrecondition:
+ case absl::StatusCode::kAborted:
+ case absl::StatusCode::kOutOfRange:
+ case absl::StatusCode::kUnimplemented:
+ case absl::StatusCode::kInternal:
+ case absl::StatusCode::kUnavailable:
+ case absl::StatusCode::kDataLoss:
+ case absl::StatusCode::kUnauthenticated:
+ return code;
+ default:
+ return absl::StatusCode::kUnknown;
+ }
+}
+
+absl::Nonnull<std::string*> MakeCheckFailString(
+ absl::Nonnull<const absl::Status*> status,
+ absl::Nonnull<const char*> prefix) {
+ return new std::string(
+ absl::StrCat(prefix, " (",
+ status->ToString(StatusToStringMode::kWithEverything), ")"));
+}
+
+} // namespace status_internal
+
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h
index 6198e726..c9f43832 100644
--- a/absl/status/internal/status_internal.h
+++ b/absl/status/internal/status_internal.h
@@ -14,13 +14,19 @@
#ifndef ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUS_INTERNAL_H_
+#include <atomic>
+#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
#include "absl/container/inlined_vector.h"
#include "absl/strings/cord.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
#ifndef SWIG
// Disabled for SWIG as it doesn't parse attributes correctly.
@@ -32,9 +38,9 @@ ABSL_NAMESPACE_BEGIN
// TODO(b/176172494): ABSL_MUST_USE_RESULT should expand to the more strict
// [[nodiscard]]. For now, just use [[nodiscard]] directly when it is available.
#if ABSL_HAVE_CPP_ATTRIBUTE(nodiscard)
-class [[nodiscard]] Status;
+class [[nodiscard]] ABSL_ATTRIBUTE_TRIVIAL_ABI Status;
#else
-class ABSL_MUST_USE_RESULT Status;
+class ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_TRIVIAL_ABI Status;
#endif
ABSL_NAMESPACE_END
} // namespace absl
@@ -44,6 +50,7 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
enum class StatusCode : int;
+enum class StatusToStringMode : int;
namespace status_internal {
@@ -56,22 +63,54 @@ struct Payload {
using Payloads = absl::InlinedVector<Payload, 1>;
// Reference-counted representation of Status data.
-struct StatusRep {
+class StatusRep {
+ public:
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;
+ : ref_(int32_t{1}),
+ code_(code_arg),
+ message_(message_arg),
+ payloads_(std::move(payloads_arg)) {}
+
+ absl::StatusCode code() const { return code_; }
+ const std::string& message() const { return message_; }
+
+ // Ref and unref are const to allow access through a const pointer, and are
+ // used during copying operations.
+ void Ref() const { ref_.fetch_add(1, std::memory_order_relaxed); }
+ void Unref() const;
+
+ // Payload methods correspond to the same methods in absl::Status.
+ absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
+ void SetPayload(absl::string_view type_url, absl::Cord payload);
+ struct EraseResult {
+ bool erased;
+ uintptr_t new_rep;
+ };
+ EraseResult ErasePayload(absl::string_view type_url);
+ void ForEachPayload(
+ absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
+ const;
+
+ std::string ToString(StatusToStringMode mode) const;
+
+ bool operator==(const StatusRep& other) const;
+ bool operator!=(const StatusRep& other) const { return !(*this == other); }
+
+ // Returns an equivalent heap allocated StatusRep with refcount 1.
+ //
+ // `this` is not safe to be used after calling as it may have been deleted.
+ absl::Nonnull<StatusRep*> CloneAndUnref() const;
+
+ private:
+ mutable std::atomic<int32_t> ref_;
+ absl::StatusCode code_;
// As an internal implementation detail, we guarantee that if status.message()
// is non-empty, then the resulting string_view is null terminated.
// This is required to implement 'StatusMessageAsCStr(...)'
- std::string message;
- std::unique_ptr<status_internal::Payloads> payloads;
+ std::string message_;
+ std::unique_ptr<status_internal::Payloads> payloads_;
};
absl::StatusCode MapToLocalCode(int value);
@@ -80,8 +119,10 @@ absl::StatusCode MapToLocalCode(int value);
// suitable for output as an error message in assertion/`CHECK()` failures.
//
// This is an internal implementation detail for Abseil logging.
-std::string* MakeCheckFailString(const absl::Status* status,
- const char* prefix);
+ABSL_ATTRIBUTE_PURE_FUNCTION
+absl::Nonnull<std::string*> MakeCheckFailString(
+ absl::Nonnull<const absl::Status*> status,
+ absl::Nonnull<const char*> prefix);
} // namespace status_internal
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h
index 49cead7a..5be94903 100644
--- a/absl/status/internal/statusor_internal.h
+++ b/absl/status/internal/statusor_internal.h
@@ -14,12 +14,15 @@
#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
+#include <cstdint>
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
#include "absl/meta/type_traits.h"
#include "absl/status/status.h"
+#include "absl/strings/string_view.h"
#include "absl/utility/utility.h"
namespace absl {
@@ -123,7 +126,7 @@ using IsForwardingAssignmentValid = absl::disjunction<
class Helper {
public:
// Move type-agnostic error handling to the .cc.
- static void HandleInvalidStatusCtorArg(Status*);
+ static void HandleInvalidStatusCtorArg(absl::Nonnull<Status*>);
ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status);
};
@@ -131,7 +134,8 @@ class Helper {
// 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) {
+ABSL_ATTRIBUTE_NONNULL(1)
+void PlacementNew(absl::Nonnull<void*> p, Args&&... args) {
new (p) T(std::forward<Args>(args)...);
}
@@ -377,6 +381,53 @@ struct MoveAssignBase<T, false> {
ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
+// Used to introduce jitter into the output of printing functions for
+// `StatusOr` (i.e. `AbslStringify` and `operator<<`).
+class StringifyRandom {
+ enum BracesType {
+ kBareParens = 0,
+ kSpaceParens,
+ kBareBrackets,
+ kSpaceBrackets,
+ };
+
+ // Returns a random `BracesType` determined once per binary load.
+ static BracesType RandomBraces() {
+ static const BracesType kRandomBraces = static_cast<BracesType>(
+ (reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
+ return kRandomBraces;
+ }
+
+ public:
+ static inline absl::string_view OpenBrackets() {
+ switch (RandomBraces()) {
+ case kBareParens:
+ return "(";
+ case kSpaceParens:
+ return "( ";
+ case kBareBrackets:
+ return "[";
+ case kSpaceBrackets:
+ return "[ ";
+ }
+ return "(";
+ }
+
+ static inline absl::string_view CloseBrackets() {
+ switch (RandomBraces()) {
+ case kBareParens:
+ return ")";
+ case kSpaceParens:
+ return " )";
+ case kBareBrackets:
+ return "]";
+ case kSpaceBrackets:
+ return " ]";
+ }
+ return ")";
+ }
+};
+
} // namespace internal_statusor
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/status/status.cc b/absl/status/status.cc
index 26e68294..4dd5ae06 100644
--- a/absl/status/status.cc
+++ b/absl/status/status.cc
@@ -15,23 +15,37 @@
#include <errno.h>
-#include <cassert>
-#include <utility>
-
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/strerror.h"
#include "absl/base/macros.h"
+#include "absl/base/no_destructor.h"
+#include "absl/base/nullability.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
-#include "absl/status/status_payload_printer.h"
-#include "absl/strings/escaping.h"
+#include "absl/status/internal/status_internal.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
+static_assert(
+ alignof(status_internal::StatusRep) >= 4,
+ "absl::Status assumes it can use the bottom 2 bits of a StatusRep*.");
+
std::string StatusCodeToString(StatusCode code) {
switch (code) {
case StatusCode::kOk:
@@ -77,146 +91,18 @@ std::ostream& operator<<(std::ostream& os, StatusCode code) {
return os << StatusCodeToString(code);
}
-namespace status_internal {
-
-static absl::optional<size_t> FindPayloadIndexByUrl(
- const Payloads* payloads, absl::string_view type_url) {
- if (payloads == nullptr) return absl::nullopt;
-
- for (size_t i = 0; i < payloads->size(); ++i) {
- if ((*payloads)[i].type_url == type_url) return i;
- }
-
- return absl::nullopt;
-}
-
-// Convert canonical code to a value known to this binary.
-absl::StatusCode MapToLocalCode(int value) {
- absl::StatusCode code = static_cast<absl::StatusCode>(value);
- switch (code) {
- case absl::StatusCode::kOk:
- case absl::StatusCode::kCancelled:
- case absl::StatusCode::kUnknown:
- case absl::StatusCode::kInvalidArgument:
- case absl::StatusCode::kDeadlineExceeded:
- case absl::StatusCode::kNotFound:
- case absl::StatusCode::kAlreadyExists:
- case absl::StatusCode::kPermissionDenied:
- case absl::StatusCode::kResourceExhausted:
- case absl::StatusCode::kFailedPrecondition:
- case absl::StatusCode::kAborted:
- case absl::StatusCode::kOutOfRange:
- case absl::StatusCode::kUnimplemented:
- case absl::StatusCode::kInternal:
- case absl::StatusCode::kUnavailable:
- case absl::StatusCode::kDataLoss:
- case absl::StatusCode::kUnauthenticated:
- return code;
- default:
- return absl::StatusCode::kUnknown;
- }
-}
-} // namespace status_internal
-
-absl::optional<absl::Cord> Status::GetPayload(
- absl::string_view type_url) const {
- const auto* payloads = GetPayloads();
- absl::optional<size_t> index =
- status_internal::FindPayloadIndexByUrl(payloads, type_url);
- if (index.has_value()) return (*payloads)[index.value()].payload;
-
- return absl::nullopt;
-}
-
-void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
- if (ok()) return;
-
- PrepareToModify();
-
- status_internal::StatusRep* rep = RepToPointer(rep_);
- if (!rep->payloads) {
- rep->payloads = absl::make_unique<status_internal::Payloads>();
- }
-
- absl::optional<size_t> index =
- status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url);
- if (index.has_value()) {
- (*rep->payloads)[index.value()].payload = std::move(payload);
- return;
- }
-
- rep->payloads->push_back({std::string(type_url), std::move(payload)});
-}
-
-bool Status::ErasePayload(absl::string_view type_url) {
- absl::optional<size_t> index =
- status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url);
- if (index.has_value()) {
- PrepareToModify();
- GetPayloads()->erase(GetPayloads()->begin() + index.value());
- if (GetPayloads()->empty() && message().empty()) {
- // Special case: If this can be represented inlined, it MUST be
- // inlined (EqualsSlow depends on this behavior).
- StatusCode c = static_cast<StatusCode>(raw_code());
- Unref(rep_);
- rep_ = CodeToInlinedRep(c);
- }
- return true;
- }
-
- return false;
-}
-
-void Status::ForEachPayload(
- 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 (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 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);
-#endif // NDEBUG
- }
- }
-}
-
-const std::string* Status::EmptyString() {
- static union EmptyString {
- std::string str;
- ~EmptyString() {}
- } empty = {{}};
- return &empty.str;
+absl::Nonnull<const std::string*> Status::EmptyString() {
+ static const absl::NoDestructor<std::string> kEmpty;
+ return kEmpty.get();
}
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr const char Status::kMovedFromString[];
#endif
-const std::string* Status::MovedFromString() {
- static std::string* moved_from_string = new std::string(kMovedFromString);
- return moved_from_string;
-}
-
-void Status::UnrefNonInlined(uintptr_t rep) {
- status_internal::StatusRep* r = RepToPointer(rep);
- // Fast path: if ref==1, there is no need for a RefCountDec (since
- // this is the only reference and therefore no other thread is
- // allowed to be mucking with r).
- if (r->ref.load(std::memory_order_acquire) == 1 ||
- r->ref.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) {
- delete r;
- }
+absl::Nonnull<const std::string*> Status::MovedFromString() {
+ static const absl::NoDestructor<std::string> kMovedFrom(kMovedFromString);
+ return kMovedFrom.get();
}
Status::Status(absl::StatusCode code, absl::string_view msg)
@@ -226,97 +112,20 @@ Status::Status(absl::StatusCode code, absl::string_view msg)
}
}
-int Status::raw_code() const {
- if (IsInlined(rep_)) {
- return static_cast<int>(InlinedRepToCode(rep_));
+absl::Nonnull<status_internal::StatusRep*> Status::PrepareToModify(
+ uintptr_t rep) {
+ if (IsInlined(rep)) {
+ return new status_internal::StatusRep(InlinedRepToCode(rep),
+ absl::string_view(), nullptr);
}
- status_internal::StatusRep* rep = RepToPointer(rep_);
- return static_cast<int>(rep->code);
+ return RepToPointer(rep)->CloneAndUnref();
}
-absl::StatusCode Status::code() const {
- return status_internal::MapToLocalCode(raw_code());
-}
-
-void Status::PrepareToModify() {
- ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status.");
- if (IsInlined(rep_)) {
- rep_ = PointerToRep(new status_internal::StatusRep(
- static_cast<absl::StatusCode>(raw_code()), absl::string_view(),
- nullptr));
- return;
+std::string Status::ToStringSlow(uintptr_t rep, StatusToStringMode mode) {
+ if (IsInlined(rep)) {
+ return absl::StrCat(absl::StatusCodeToString(InlinedRepToCode(rep)), ": ");
}
-
- uintptr_t rep_i = rep_;
- status_internal::StatusRep* rep = RepToPointer(rep_);
- if (rep->ref.load(std::memory_order_acquire) != 1) {
- std::unique_ptr<status_internal::Payloads> payloads;
- if (rep->payloads) {
- payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads);
- }
- status_internal::StatusRep* const new_rep = new status_internal::StatusRep(
- rep->code, message(), std::move(payloads));
- rep_ = PointerToRep(new_rep);
- UnrefNonInlined(rep_i);
- }
-}
-
-bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) {
- if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false;
- if (a.message() != b.message()) return false;
- if (a.raw_code() != b.raw_code()) return false;
- if (a.GetPayloads() == b.GetPayloads()) return true;
-
- const status_internal::Payloads no_payloads;
- const status_internal::Payloads* larger_payloads =
- a.GetPayloads() ? a.GetPayloads() : &no_payloads;
- const status_internal::Payloads* smaller_payloads =
- b.GetPayloads() ? b.GetPayloads() : &no_payloads;
- if (larger_payloads->size() < smaller_payloads->size()) {
- std::swap(larger_payloads, smaller_payloads);
- }
- if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false;
- // Payloads can be ordered differently, so we can't just compare payload
- // vectors.
- for (const auto& payload : *larger_payloads) {
-
- bool found = false;
- for (const auto& other_payload : *smaller_payloads) {
- if (payload.type_url == other_payload.type_url) {
- if (payload.payload != other_payload.payload) {
- return false;
- }
- found = true;
- break;
- }
- }
- if (!found) return false;
- }
- return true;
-}
-
-std::string Status::ToStringSlow(StatusToStringMode mode) const {
- std::string text;
- absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
-
- 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;
+ return RepToPointer(rep)->ToString(mode);
}
std::ostream& operator<<(std::ostream& os, const Status& x) {
@@ -605,18 +414,7 @@ Status ErrnoToStatus(int error_number, absl::string_view message) {
MessageForErrnoToStatus(error_number, message));
}
-namespace status_internal {
-
-std::string* MakeCheckFailString(const absl::Status* status,
- const char* prefix) {
- return new std::string(
- absl::StrCat(prefix, " (",
- status->ToString(StatusToStringMode::kWithEverything), ")"));
-}
-
-} // namespace status_internal
-
-const char* StatusMessageAsCStr(const Status& status) {
+absl::Nonnull<const char*> StatusMessageAsCStr(const Status& status) {
// As an internal implementation detail, we guarantee that if status.message()
// is non-empty, then the resulting string_view is null terminated.
auto sv_message = status.message();
diff --git a/absl/status/status.h b/absl/status/status.h
index 595064c0..9ce16db9 100644
--- a/absl/status/status.h
+++ b/absl/status/status.h
@@ -51,10 +51,17 @@
#ifndef ABSL_STATUS_STATUS_H_
#define ABSL_STATUS_STATUS_H_
+#include <cassert>
+#include <cstdint>
#include <ostream>
#include <string>
#include <utility>
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
+#include "absl/base/optimization.h"
#include "absl/functional/function_ref.h"
#include "absl/status/internal/status_internal.h"
#include "absl/strings/cord.h"
@@ -421,7 +428,7 @@ inline StatusToStringMode& operator^=(StatusToStringMode& lhs,
// 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 {
+class ABSL_ATTRIBUTE_TRIVIAL_ABI Status final {
public:
// Constructors
@@ -516,6 +523,12 @@ class Status final {
std::string ToString(
StatusToStringMode mode = StatusToStringMode::kDefault) const;
+ // Support `absl::StrCat`, `absl::StrFormat`, etc.
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const Status& status) {
+ sink.Append(status.ToString(StatusToStringMode::kWithEverything));
+ }
+
// Status::IgnoreError()
//
// Ignores any errors. This method does nothing except potentially suppress
@@ -602,56 +615,57 @@ class Status final {
// code, and an empty error message.
explicit Status(absl::StatusCode code);
- static void UnrefNonInlined(uintptr_t rep);
+ // Underlying constructor for status from a rep_.
+ explicit Status(uintptr_t rep) : rep_(rep) {}
+
static void Ref(uintptr_t rep);
static void Unref(uintptr_t rep);
// REQUIRES: !ok()
- // Ensures rep_ is not shared with any other Status.
- void PrepareToModify();
-
- const status_internal::Payloads* GetPayloads() const;
- status_internal::Payloads* GetPayloads();
-
- static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
+ // Ensures rep is not inlined or shared with any other Status.
+ static absl::Nonnull<status_internal::StatusRep*> PrepareToModify(
+ uintptr_t rep);
// MSVC 14.0 limitation requires the const.
static constexpr const char kMovedFromString[] =
"Status accessed after move.";
- static const std::string* EmptyString();
- static const std::string* MovedFromString();
+ static absl::Nonnull<const std::string*> EmptyString();
+ static absl::Nonnull<const std::string*> MovedFromString();
// Returns whether rep contains an inlined representation.
// See rep_ for details.
- static bool IsInlined(uintptr_t rep);
+ static constexpr bool IsInlined(uintptr_t rep);
// Indicates whether this Status was the rhs of a move operation. See rep_
// for details.
- static bool IsMovedFrom(uintptr_t rep);
- static uintptr_t MovedFromRep();
+ static constexpr bool IsMovedFrom(uintptr_t rep);
+ static constexpr uintptr_t MovedFromRep();
// Convert between error::Code and the inlined uintptr_t representation used
// by rep_. See rep_ for details.
- static uintptr_t CodeToInlinedRep(absl::StatusCode code);
- static absl::StatusCode InlinedRepToCode(uintptr_t rep);
+ static constexpr uintptr_t CodeToInlinedRep(absl::StatusCode code);
+ static constexpr absl::StatusCode InlinedRepToCode(uintptr_t rep);
// Converts between StatusRep* and the external uintptr_t representation used
// by rep_. See rep_ for details.
static uintptr_t PointerToRep(status_internal::StatusRep* r);
- static status_internal::StatusRep* RepToPointer(uintptr_t r);
+ static absl::Nonnull<const status_internal::StatusRep*> RepToPointer(
+ uintptr_t r);
- std::string ToStringSlow(StatusToStringMode mode) const;
+ static std::string ToStringSlow(uintptr_t rep, StatusToStringMode mode);
// Status supports two different representations.
- // - When the low bit is off it is an inlined representation.
+ // - When the low bit is set it is an inlined representation.
// It uses the canonical error space, no message or payload.
// The error code is (rep_ >> 2).
// The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom().
- // - When the low bit is on it is an external representation.
+ // - When the low bit is off it is an external representation.
// In this case all the data comes from a heap allocated Rep object.
- // (rep_ - 1) is a status_internal::StatusRep* pointer to that structure.
+ // rep_ is a status_internal::StatusRep* pointer to that structure.
uintptr_t rep_;
+
+ friend class status_internal::StatusRep;
};
// OkStatus()
@@ -755,11 +769,11 @@ Status ErrnoToStatus(int error_number, absl::string_view message);
// Implementation details follow
//------------------------------------------------------------------------------
-inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {}
+inline Status::Status() : Status(absl::StatusCode::kOk) {}
-inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {}
+inline Status::Status(absl::StatusCode code) : Status(CodeToInlinedRep(code)) {}
-inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); }
+inline Status::Status(const Status& x) : Status(x.rep_) { Ref(rep_); }
inline Status& Status::operator=(const Status& x) {
uintptr_t old_rep = rep_;
@@ -771,7 +785,7 @@ inline Status& Status::operator=(const Status& x) {
return *this;
}
-inline Status::Status(Status&& x) noexcept : rep_(x.rep_) {
+inline Status::Status(Status&& x) noexcept : Status(x.rep_) {
x.rep_ = MovedFromRep();
}
@@ -803,15 +817,27 @@ inline bool Status::ok() const {
return rep_ == CodeToInlinedRep(absl::StatusCode::kOk);
}
+inline absl::StatusCode Status::code() const {
+ return status_internal::MapToLocalCode(raw_code());
+}
+
+inline int Status::raw_code() const {
+ if (IsInlined(rep_)) return static_cast<int>(InlinedRepToCode(rep_));
+ return static_cast<int>(RepToPointer(rep_)->code());
+}
+
inline absl::string_view Status::message() const {
return !IsInlined(rep_)
- ? RepToPointer(rep_)->message
+ ? RepToPointer(rep_)->message()
: (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString)
: absl::string_view());
}
inline bool operator==(const Status& lhs, const Status& rhs) {
- return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs);
+ if (lhs.rep_ == rhs.rep_) return true;
+ if (Status::IsInlined(lhs.rep_)) return false;
+ if (Status::IsInlined(rhs.rep_)) return false;
+ return *Status::RepToPointer(lhs.rep_) == *Status::RepToPointer(rhs.rep_);
}
inline bool operator!=(const Status& lhs, const Status& rhs) {
@@ -819,7 +845,7 @@ inline bool operator!=(const Status& lhs, const Status& rhs) {
}
inline std::string Status::ToString(StatusToStringMode mode) const {
- return ok() ? "OK" : ToStringSlow(mode);
+ return ok() ? "OK" : ToStringSlow(rep_, mode);
}
inline void Status::IgnoreError() const {
@@ -831,52 +857,68 @@ inline void swap(absl::Status& a, absl::Status& b) {
swap(a.rep_, b.rep_);
}
-inline const status_internal::Payloads* Status::GetPayloads() const {
- return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
+inline absl::optional<absl::Cord> Status::GetPayload(
+ absl::string_view type_url) const {
+ if (IsInlined(rep_)) return absl::nullopt;
+ return RepToPointer(rep_)->GetPayload(type_url);
}
-inline status_internal::Payloads* Status::GetPayloads() {
- return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
+inline void Status::SetPayload(absl::string_view type_url, absl::Cord payload) {
+ if (ok()) return;
+ status_internal::StatusRep* rep = PrepareToModify(rep_);
+ rep->SetPayload(type_url, std::move(payload));
+ rep_ = PointerToRep(rep);
}
-inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) == 0; }
-
-inline bool Status::IsMovedFrom(uintptr_t rep) {
- return IsInlined(rep) && (rep & 2) != 0;
+inline bool Status::ErasePayload(absl::string_view type_url) {
+ if (IsInlined(rep_)) return false;
+ status_internal::StatusRep* rep = PrepareToModify(rep_);
+ auto res = rep->ErasePayload(type_url);
+ rep_ = res.new_rep;
+ return res.erased;
}
-inline uintptr_t Status::MovedFromRep() {
- return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
+inline void Status::ForEachPayload(
+ absl::FunctionRef<void(absl::string_view, const absl::Cord&)> visitor)
+ const {
+ if (IsInlined(rep_)) return;
+ RepToPointer(rep_)->ForEachPayload(visitor);
}
-inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
- return static_cast<uintptr_t>(code) << 2;
+constexpr bool Status::IsInlined(uintptr_t rep) { return (rep & 1) != 0; }
+
+constexpr bool Status::IsMovedFrom(uintptr_t rep) { return (rep & 2) != 0; }
+
+constexpr uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
+ return (static_cast<uintptr_t>(code) << 2) + 1;
}
-inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
- assert(IsInlined(rep));
+constexpr absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
+ ABSL_ASSERT(IsInlined(rep));
return static_cast<absl::StatusCode>(rep >> 2);
}
-inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) {
+constexpr uintptr_t Status::MovedFromRep() {
+ return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
+}
+
+inline absl::Nonnull<const status_internal::StatusRep*> Status::RepToPointer(
+ uintptr_t rep) {
assert(!IsInlined(rep));
- return reinterpret_cast<status_internal::StatusRep*>(rep - 1);
+ return reinterpret_cast<const status_internal::StatusRep*>(rep);
}
-inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
- return reinterpret_cast<uintptr_t>(rep) + 1;
+inline uintptr_t Status::PointerToRep(
+ absl::Nonnull<status_internal::StatusRep*> rep) {
+ return reinterpret_cast<uintptr_t>(rep);
}
inline void Status::Ref(uintptr_t rep) {
- if (!IsInlined(rep)) {
- RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed);
- }
+ if (!IsInlined(rep)) RepToPointer(rep)->Ref();
}
inline void Status::Unref(uintptr_t rep) {
- if (!IsInlined(rep)) {
- UnrefNonInlined(rep);
- }
+ if (!IsInlined(rep)) RepToPointer(rep)->Unref();
}
inline Status OkStatus() { return Status(); }
@@ -892,7 +934,7 @@ inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); }
// If the status's message is empty, the empty string is returned.
//
// StatusMessageAsCStr exists for C support. Use `status.message()` in C++.
-const char* StatusMessageAsCStr(
+absl::Nonnull<const char*> StatusMessageAsCStr(
const Status& status ABSL_ATTRIBUTE_LIFETIME_BOUND);
ABSL_NAMESPACE_END
diff --git a/absl/status/status_payload_printer.cc b/absl/status/status_payload_printer.cc
index a47aea11..98401e90 100644
--- a/absl/status/status_payload_printer.cc
+++ b/absl/status/status_payload_printer.cc
@@ -13,9 +13,7 @@
// limitations under the License.
#include "absl/status/status_payload_printer.h"
-#include <atomic>
-
-#include "absl/base/attributes.h"
+#include "absl/base/config.h"
#include "absl/base/internal/atomic_hook.h"
namespace absl {
diff --git a/absl/status/status_payload_printer.h b/absl/status/status_payload_printer.h
index 5e0937f6..f22255e1 100644
--- a/absl/status/status_payload_printer.h
+++ b/absl/status/status_payload_printer.h
@@ -16,6 +16,7 @@
#include <string>
+#include "absl/base/nullability.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
@@ -34,8 +35,8 @@ namespace status_internal {
// NOTE: This is an internal API and the design is subject to change in the
// future in a non-backward-compatible way. Since it's only meant for debugging
// purpose, you should not rely on it in any critical logic.
-using StatusPayloadPrinter = absl::optional<std::string> (*)(absl::string_view,
- const absl::Cord&);
+using StatusPayloadPrinter = absl::Nullable<absl::optional<std::string> (*)(
+ absl::string_view, const absl::Cord&)>;
// Sets the global payload printer. Only one printer should be set per process.
// If multiple printers are set, it's undefined which one will be used.
diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc
index 898a9cb2..585e7807 100644
--- a/absl/status/status_test.cc
+++ b/absl/status/status_test.cc
@@ -16,9 +16,16 @@
#include <errno.h>
+#include <array>
+#include <cstddef>
+#include <sstream>
+#include <utility>
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
namespace {
@@ -299,37 +306,77 @@ TEST(Status, TestForEachPayload) {
}
TEST(Status, ToString) {
- absl::Status s(absl::StatusCode::kInternal, "fail");
- EXPECT_EQ("INTERNAL: fail", s.ToString());
- s.SetPayload("foo", absl::Cord("bar"));
- EXPECT_EQ("INTERNAL: fail [foo='bar']", s.ToString());
- s.SetPayload("bar", absl::Cord("\377"));
- EXPECT_THAT(s.ToString(),
+ absl::Status status(absl::StatusCode::kInternal, "fail");
+ EXPECT_EQ("INTERNAL: fail", status.ToString());
+ status.SetPayload("foo", absl::Cord("bar"));
+ EXPECT_EQ("INTERNAL: fail [foo='bar']", status.ToString());
+ status.SetPayload("bar", absl::Cord("\377"));
+ EXPECT_THAT(status.ToString(),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
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"));
+ absl::Status status(absl::StatusCode::kInternal, "fail");
+ status.SetPayload("foo", absl::Cord("bar"));
+ status.SetPayload("bar", absl::Cord("\377"));
EXPECT_EQ("INTERNAL: fail",
- s.ToString(absl::StatusToStringMode::kWithNoExtraData));
+ status.ToString(absl::StatusToStringMode::kWithNoExtraData));
- EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithPayload),
+ EXPECT_THAT(status.ToString(absl::StatusToStringMode::kWithPayload),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
HasSubstr("[bar='\\xff']")));
- EXPECT_THAT(s.ToString(absl::StatusToStringMode::kWithEverything),
+ EXPECT_THAT(status.ToString(absl::StatusToStringMode::kWithEverything),
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
HasSubstr("[bar='\\xff']")));
- EXPECT_THAT(s.ToString(~absl::StatusToStringMode::kWithPayload),
+ EXPECT_THAT(status.ToString(~absl::StatusToStringMode::kWithPayload),
AllOf(HasSubstr("INTERNAL: fail"), Not(HasSubstr("[foo='bar']")),
Not(HasSubstr("[bar='\\xff']"))));
}
+TEST(Status, OstreamOperator) {
+ absl::Status status(absl::StatusCode::kInternal, "fail");
+ { std::stringstream stream;
+ stream << status;
+ EXPECT_EQ("INTERNAL: fail", stream.str());
+ }
+ status.SetPayload("foo", absl::Cord("bar"));
+ { std::stringstream stream;
+ stream << status;
+ EXPECT_EQ("INTERNAL: fail [foo='bar']", stream.str());
+ }
+ status.SetPayload("bar", absl::Cord("\377"));
+ { std::stringstream stream;
+ stream << status;
+ EXPECT_THAT(stream.str(),
+ AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
+ HasSubstr("[bar='\\xff']")));
+ }
+}
+
+TEST(Status, AbslStringify) {
+ absl::Status status(absl::StatusCode::kInternal, "fail");
+ EXPECT_EQ("INTERNAL: fail", absl::StrCat(status));
+ EXPECT_EQ("INTERNAL: fail", absl::StrFormat("%v", status));
+ status.SetPayload("foo", absl::Cord("bar"));
+ EXPECT_EQ("INTERNAL: fail [foo='bar']", absl::StrCat(status));
+ status.SetPayload("bar", absl::Cord("\377"));
+ EXPECT_THAT(absl::StrCat(status),
+ AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
+ HasSubstr("[bar='\\xff']")));
+}
+
+TEST(Status, OstreamEqStringify) {
+ absl::Status status(absl::StatusCode::kUnknown, "fail");
+ status.SetPayload("foo", absl::Cord("bar"));
+ std::stringstream stream;
+ stream << status;
+ EXPECT_EQ(stream.str(), absl::StrCat(status));
+}
+
absl::Status EraseAndReturn(const absl::Status& base) {
absl::Status copy = base;
EXPECT_TRUE(copy.ErasePayload(kUrl1));
diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc
index 96642b34..7e6b334c 100644
--- a/absl/status/statusor.cc
+++ b/absl/status/statusor.cc
@@ -17,7 +17,10 @@
#include <utility>
#include "absl/base/call_once.h"
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
+#include "absl/base/nullability.h"
+#include "absl/status/internal/statusor_internal.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
@@ -52,7 +55,7 @@ BadStatusOrAccess& BadStatusOrAccess::operator=(BadStatusOrAccess&& other) {
BadStatusOrAccess::BadStatusOrAccess(BadStatusOrAccess&& other)
: status_(std::move(other.status_)) {}
-const char* BadStatusOrAccess::what() const noexcept {
+absl::Nonnull<const char*> BadStatusOrAccess::what() const noexcept {
InitWhat();
return what_.c_str();
}
@@ -67,7 +70,7 @@ void BadStatusOrAccess::InitWhat() const {
namespace internal_statusor {
-void Helper::HandleInvalidStatusCtorArg(absl::Status* status) {
+void Helper::HandleInvalidStatusCtorArg(absl::Nonnull<absl::Status*> status) {
const char* kMessage =
"An OK status is not a valid constructor argument to StatusOr<T>";
#ifdef NDEBUG
diff --git a/absl/status/statusor.h b/absl/status/statusor.h
index 54c7ce02..cd35e5b4 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -39,15 +39,20 @@
#include <exception>
#include <initializer_list>
#include <new>
+#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
+#include "absl/base/nullability.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/strings/has_absl_stringify.h"
+#include "absl/strings/has_ostream_operator.h"
+#include "absl/strings/str_format.h"
#include "absl/types/variant.h"
#include "absl/utility/utility.h"
@@ -88,7 +93,7 @@ class BadStatusOrAccess : public std::exception {
//
// 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;
+ absl::Nonnull<const char*> what() const noexcept override;
// BadStatusOrAccess::status()
//
@@ -650,6 +655,41 @@ bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
return !(lhs == rhs);
}
+// Prints the `value` or the status in brackets to `os`.
+//
+// Requires `T` supports `operator<<`. Do not rely on the output format which
+// may change without notice.
+template <typename T, typename std::enable_if<
+ absl::HasOstreamOperator<T>::value, int>::type = 0>
+std::ostream& operator<<(std::ostream& os, const StatusOr<T>& status_or) {
+ if (status_or.ok()) {
+ os << status_or.value();
+ } else {
+ os << internal_statusor::StringifyRandom::OpenBrackets()
+ << status_or.status()
+ << internal_statusor::StringifyRandom::CloseBrackets();
+ }
+ return os;
+}
+
+// As above, but supports `StrCat`, `StrFormat`, etc.
+//
+// Requires `T` has `AbslStringify`. Do not rely on the output format which
+// may change without notice.
+template <
+ typename Sink, typename T,
+ typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type = 0>
+void AbslStringify(Sink& sink, const StatusOr<T>& status_or) {
+ if (status_or.ok()) {
+ absl::Format(&sink, "%v", status_or.value());
+ } else {
+ absl::Format(&sink, "%s%v%s",
+ internal_statusor::StringifyRandom::OpenBrackets(),
+ status_or.status(),
+ internal_statusor::StringifyRandom::CloseBrackets());
+ }
+}
+
//------------------------------------------------------------------------------
// Implementation details for StatusOr<T>
//------------------------------------------------------------------------------
@@ -750,13 +790,13 @@ T&& StatusOr<T>::operator*() && {
}
template <typename T>
-const T* StatusOr<T>::operator->() const {
+absl::Nonnull<const T*> StatusOr<T>::operator->() const {
this->EnsureOk();
return &this->data_;
}
template <typename T>
-T* StatusOr<T>::operator->() {
+absl::Nonnull<T*> StatusOr<T>::operator->() {
this->EnsureOk();
return &this->data_;
}
diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc
index e65f5d27..09ffc658 100644
--- a/absl/status/statusor_test.cc
+++ b/absl/status/statusor_test.cc
@@ -15,31 +15,41 @@
#include "absl/status/statusor.h"
#include <array>
+#include <cstddef>
#include <initializer_list>
+#include <map>
#include <memory>
+#include <ostream>
+#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
+#include <vector>
#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/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/any.h"
+#include "absl/types/variant.h"
#include "absl/utility/utility.h"
namespace {
using ::testing::AllOf;
+using ::testing::AnyOf;
using ::testing::AnyWith;
using ::testing::ElementsAre;
+using ::testing::EndsWith;
using ::testing::Field;
using ::testing::HasSubstr;
using ::testing::Ne;
using ::testing::Not;
using ::testing::Pointee;
+using ::testing::StartsWith;
using ::testing::VariantWith;
#ifdef GTEST_HAS_STATUS_MATCHERS
@@ -1877,4 +1887,35 @@ TEST(StatusOr, StatusAssignmentFromTypeConvertibleToStatus) {
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
+struct PrintTestStruct {
+ friend std::ostream& operator<<(std::ostream& os, const PrintTestStruct&) {
+ return os << "ostream";
+ }
+
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const PrintTestStruct&) {
+ sink.Append("stringify");
+ }
+};
+
+TEST(StatusOr, OkPrinting) {
+ absl::StatusOr<PrintTestStruct> print_me = PrintTestStruct{};
+ std::stringstream stream;
+ stream << print_me;
+ EXPECT_EQ(stream.str(), "ostream");
+ EXPECT_EQ(absl::StrCat(print_me), "stringify");
+}
+
+TEST(StatusOr, ErrorPrinting) {
+ absl::StatusOr<PrintTestStruct> print_me = absl::UnknownError("error");
+ std::stringstream stream;
+ stream << print_me;
+ const auto error_matcher =
+ AllOf(HasSubstr("UNKNOWN"), HasSubstr("error"),
+ AnyOf(AllOf(StartsWith("("), EndsWith(")")),
+ AllOf(StartsWith("["), EndsWith("]"))));
+ EXPECT_THAT(stream.str(), error_matcher);
+ EXPECT_THAT(absl::StrCat(print_me), error_matcher);
+}
+
} // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 819bbe69..8e8371b3 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -22,7 +22,11 @@ load(
package(
default_visibility = ["//visibility:public"],
- features = ["parse_headers"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
)
licenses(["notice"])
@@ -37,6 +41,7 @@ cc_library(
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:nullability",
"//absl/base:throw_delegate",
],
)
@@ -70,6 +75,7 @@ cc_library(
"ascii.h",
"charconv.h",
"escaping.h",
+ "has_absl_stringify.h",
"internal/damerau_levenshtein_distance.h",
"internal/has_absl_stringify.h",
"internal/string_constant.h",
@@ -92,12 +98,14 @@ cc_library(
"string_view.h",
],
deps = [
+ ":charset",
":internal",
":string_view",
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
+ "//absl/base:nullability",
"//absl/base:raw_logging_internal",
"//absl/base:throw_delegate",
"//absl/memory",
@@ -115,7 +123,6 @@ cc_library(
"internal/utf8.cc",
],
hdrs = [
- "internal/char_map.h",
"internal/escaping.h",
"internal/ostringstream.h",
"internal/resize_uninitialized.h",
@@ -140,6 +147,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -158,6 +166,8 @@ cc_test(
":strings",
"//absl/base:core_headers",
"//absl/container:fixed_array",
+ "//absl/log:check",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -175,6 +185,45 @@ cc_test(
":strings",
"//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_test(
+ name = "has_absl_stringify_test",
+ size = "small",
+ srcs = ["has_absl_stringify_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":strings",
+ "//absl/types:optional",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "has_ostream_operator",
+ hdrs = ["has_ostream_operator.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ ],
+)
+
+cc_test(
+ name = "has_ostream_operator_test",
+ size = "small",
+ srcs = ["has_ostream_operator_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":has_ostream_operator",
+ "//absl/types:optional",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
],
)
@@ -187,6 +236,7 @@ cc_test(
deps = [
":strings",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -200,6 +250,7 @@ cc_test(
deps = [
":strings",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -212,6 +263,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
deps = [
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -229,6 +281,7 @@ cc_test(
":strings",
"//absl/base:core_headers",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -244,6 +297,7 @@ cc_test(
deps = [
":strings",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -259,6 +313,7 @@ cc_test(
deps = [
":internal",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -272,6 +327,7 @@ cc_test(
deps = [
":strings",
"//absl/meta:type_traits",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -288,6 +344,7 @@ cc_test(
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -302,6 +359,53 @@ cc_test(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "charset_benchmark",
+ size = "small",
+ srcs = [
+ "charset_benchmark.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ tags = [
+ "benchmark",
+ ],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":charset",
+ "//absl/log:check",
+ "@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "charset",
+ hdrs = [
+ "charset.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":string_view",
+ "//absl/base:core_headers",
+ ],
+)
+
+cc_test(
+ name = "charset_test",
+ size = "small",
+ srcs = ["charset_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":charset",
+ ":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -315,7 +419,6 @@ cc_library(
"internal/cord_rep_btree_reader.cc",
"internal/cord_rep_consume.cc",
"internal/cord_rep_crc.cc",
- "internal/cord_rep_ring.cc",
],
hdrs = [
"internal/cord_data_edge.h",
@@ -326,8 +429,6 @@ cc_library(
"internal/cord_rep_consume.h",
"internal/cord_rep_crc.h",
"internal/cord_rep_flat.h",
- "internal/cord_rep_ring.h",
- "internal/cord_rep_ring_reader.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
@@ -364,6 +465,7 @@ cc_test(
":cord_rep_test_util",
":strings",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -382,6 +484,7 @@ cc_test(
"//absl/base:config",
"//absl/base:raw_logging_internal",
"//absl/cleanup",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -398,6 +501,7 @@ cc_test(
":strings",
"//absl/base:config",
"//absl/base:raw_logging_internal",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -415,6 +519,7 @@ cc_test(
":strings",
"//absl/base:config",
"//absl/base:raw_logging_internal",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -430,6 +535,7 @@ cc_test(
":cord_rep_test_util",
"//absl/base:config",
"//absl/crc:crc_cord_state",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -453,6 +559,7 @@ cc_test(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/synchronization",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -484,9 +591,10 @@ cc_library(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
+ "//absl/base:nullability",
"//absl/base:raw_logging_internal",
- "//absl/container:fixed_array",
"//absl/container:inlined_vector",
+ "//absl/crc:crc32c",
"//absl/crc:crc_cord_state",
"//absl/functional:function_ref",
"//absl/meta:type_traits",
@@ -568,6 +676,7 @@ cc_test(
":cordz_update_scope",
":cordz_update_tracker",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -628,6 +737,7 @@ cc_test(
":cordz_functions",
":cordz_test_helpers",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -646,6 +756,7 @@ cc_test(
"//absl/synchronization",
"//absl/synchronization:thread_pool",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -667,6 +778,7 @@ cc_test(
"//absl/debugging:stacktrace",
"//absl/debugging:symbolize",
"//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -688,6 +800,7 @@ cc_test(
"//absl/crc:crc_cord_state",
"//absl/synchronization",
"//absl/synchronization:thread_pool",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -709,6 +822,7 @@ cc_test(
"//absl/synchronization",
"//absl/synchronization:thread_pool",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -759,6 +873,7 @@ cc_library(
":strings",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:nullability",
"@com_google_googletest//:gtest",
],
)
@@ -773,8 +888,10 @@ cc_test(
":cord",
":cord_internal",
":cord_rep_test_util",
+ ":string_view",
"//absl/base:config",
"//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -787,19 +904,25 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":cord",
+ ":cord_internal",
":cord_test_helpers",
":cordz_functions",
+ ":cordz_statistics",
":cordz_test_helpers",
+ ":cordz_update_tracker",
":str_format",
":strings",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
"//absl/container:fixed_array",
+ "//absl/functional:function_ref",
"//absl/hash",
"//absl/log",
"//absl/log:check",
"//absl/random",
+ "//absl/types:optional",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -821,6 +944,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":cord",
+ ":cord_internal",
":cord_test_helpers",
":cordz_functions",
":cordz_info",
@@ -832,38 +956,7 @@ cc_test(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
- "@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",
"@com_google_googletest//:gtest_main",
],
)
@@ -877,6 +970,7 @@ cc_test(
deps = [
":strings",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -891,6 +985,7 @@ cc_test(
":strings",
"//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -902,6 +997,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -918,6 +1014,7 @@ cc_test(
"//absl/container:btree",
"//absl/container:flat_hash_map",
"//absl/container:node_hash_map",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -932,6 +1029,7 @@ cc_test(
":strings",
"//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -943,6 +1041,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":internal",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -956,6 +1055,7 @@ cc_test(
deps = [
":internal",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -971,6 +1071,7 @@ cc_test(
deps = [
"//absl/base:core_headers",
"//absl/meta:type_traits",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -985,6 +1086,7 @@ cc_test(
":strings",
"//absl/base:core_headers",
"//absl/memory",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -998,6 +1100,7 @@ cc_test(
deps = [
":strings",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -1010,6 +1113,7 @@ cc_test(
deps = [
":str_format",
":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1022,7 +1126,10 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
+ "//absl/random",
+ "//absl/random:distributions",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -1041,8 +1148,10 @@ cc_test(
":strings",
"//absl/base:config",
"//absl/log",
+ "//absl/numeric:int128",
"//absl/random",
"//absl/random:distributions",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1059,6 +1168,7 @@ cc_test(
"//absl/random",
"//absl/random:distributions",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -1070,32 +1180,12 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
- name = "char_map_test",
- srcs = ["internal/char_map_test.cc"],
- copts = ABSL_TEST_COPTS,
- deps = [
- ":internal",
- "@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_test(
- name = "char_map_benchmark",
- srcs = ["internal/char_map_benchmark.cc"],
- copts = ABSL_TEST_COPTS,
- tags = ["benchmark"],
- deps = [
- ":internal",
- "@com_github_google_benchmark//:benchmark_main",
- ],
-)
-
-cc_test(
name = "charconv_test",
srcs = ["charconv_test.cc"],
copts = ABSL_TEST_COPTS,
@@ -1103,6 +1193,7 @@ cc_test(
":pow10_helper",
":str_format",
":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1118,6 +1209,7 @@ cc_test(
":strings",
"//absl/base:config",
"//absl/log:check",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1133,6 +1225,7 @@ cc_test(
deps = [
":strings",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1148,6 +1241,7 @@ cc_test(
deps = [
":strings",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -1160,6 +1254,11 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":str_format_internal",
+ ":string_view",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/base:nullability",
+ "//absl/types:span",
],
)
@@ -1190,6 +1289,7 @@ cc_library(
":strings",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/container:fixed_array",
"//absl/container:inlined_vector",
"//absl/functional:function_ref",
"//absl/meta:type_traits",
@@ -1211,6 +1311,10 @@ cc_test(
":cord",
":str_format",
":strings",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1226,6 +1330,7 @@ cc_test(
":str_format",
":str_format_internal",
":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1238,6 +1343,8 @@ cc_test(
deps = [
":str_format",
":str_format_internal",
+ "//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1249,6 +1356,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":str_format_internal",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1260,6 +1368,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1271,12 +1380,17 @@ cc_test(
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
+ ":str_format",
":str_format_internal",
":strings",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/base:raw_logging_internal",
"//absl/log",
+ "//absl/numeric:int128",
"//absl/types:optional",
+ "//absl/types:span",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1289,6 +1403,7 @@ cc_test(
deps = [
":cord",
":str_format_internal",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1300,7 +1415,10 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":str_format_internal",
+ ":string_view",
+ "//absl/base:config",
"//absl/base:core_headers",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1323,6 +1441,7 @@ cc_test(
deps = [
":pow10_helper",
":str_format",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -1350,6 +1469,7 @@ cc_test(
deps = [
":str_format",
":strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index 1959dc91..1b245362 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -27,6 +27,7 @@ absl_cc_library(
absl::base
absl::config
absl::core_headers
+ absl::nullability
absl::throw_delegate
PUBLIC
)
@@ -38,6 +39,7 @@ absl_cc_library(
"ascii.h"
"charconv.h"
"escaping.h"
+ "has_absl_stringify.h"
"internal/damerau_levenshtein_distance.h"
"internal/string_constant.h"
"internal/has_absl_stringify.h"
@@ -78,23 +80,49 @@ absl_cc_library(
absl::strings_internal
absl::base
absl::bits
+ absl::charset
absl::config
absl::core_headers
absl::endian
absl::int128
absl::memory
+ absl::nullability
absl::raw_logging_internal
absl::throw_delegate
absl::type_traits
PUBLIC
)
+absl_cc_library(
+ NAME
+ charset
+ HDRS
+ charset.h
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ absl::string_view
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ has_ostream_operator
+ HDRS
+ "has_ostream_operator.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ PUBLIC
+)
+
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
strings_internal
HDRS
- "internal/char_map.h"
"internal/escaping.cc"
"internal/escaping.h"
"internal/ostringstream.h"
@@ -138,6 +166,33 @@ absl_cc_test(
absl::core_headers
absl::fixed_array
GTest::gmock_main
+ absl::check
+)
+
+absl_cc_test(
+ NAME
+ has_absl_stringify_test
+ SRCS
+ "has_absl_stringify_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::optional
+ absl::strings
+ GTest::gmock_main
+)
+
+absl_cc_test(
+ NAME
+ has_ostream_operator_test
+ SRCS
+ "has_ostream_operator_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::has_ostream_operator
+ absl::optional
+ GTest::gmock_main
)
absl_cc_test(
@@ -329,13 +384,14 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::strings
- absl::core_headers
- absl::pow10_helper
absl::config
+ absl::core_headers
+ absl::int128
absl::log
- absl::random_random
+ absl::pow10_helper
absl::random_distributions
+ absl::random_random
+ absl::strings
absl::strings_internal
GTest::gmock_main
)
@@ -355,13 +411,13 @@ absl_cc_test(
absl_cc_test(
NAME
- char_map_test
+ charset_test
SRCS
- "internal/char_map_test.cc"
+ "charset_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::strings_internal
+ absl::strings
GTest::gmock_main
)
@@ -417,7 +473,12 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::config
+ absl::core_headers
+ absl::nullability
+ absl::span
absl::str_format_internal
+ absl::string_view
PUBLIC
)
@@ -448,6 +509,7 @@ absl_cc_library(
absl::strings
absl::config
absl::core_headers
+ absl::fixed_array
absl::inlined_vector
absl::numeric_representation
absl::type_traits
@@ -464,10 +526,12 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::str_format
+ absl::config
absl::cord
- absl::strings
absl::core_headers
+ absl::span
+ absl::str_format
+ absl::strings
GTest::gmock_main
)
@@ -493,6 +557,7 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::config
absl::str_format
absl::str_format_internal
GTest::gmock_main
@@ -530,12 +595,15 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::strings
- absl::str_format_internal
+ absl::config
absl::core_headers
+ absl::int128
absl::log
absl::raw_logging_internal
- absl::int128
+ absl::span
+ absl::str_format
+ absl::str_format_internal
+ absl::strings
GTest::gmock_main
)
@@ -561,6 +629,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::str_format_internal
+ absl::string_view
+ absl::config
absl::core_headers
GTest::gmock_main
)
@@ -620,8 +690,6 @@ absl_cc_library(
"internal/cord_rep_crc.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"
@@ -629,7 +697,6 @@ absl_cc_library(
"internal/cord_rep_btree_reader.cc"
"internal/cord_rep_crc.cc"
"internal/cord_rep_consume.cc"
- "internal/cord_rep_ring.cc"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
@@ -918,11 +985,12 @@ absl_cc_library(
absl::cordz_update_scope
absl::cordz_update_tracker
absl::core_headers
+ absl::crc32c
absl::crc_cord_state
absl::endian
- absl::fixed_array
absl::function_ref
absl::inlined_vector
+ absl::nullability
absl::optional
absl::raw_logging_internal
absl::span
@@ -980,6 +1048,7 @@ absl_cc_library(
absl::cordz_statistics
absl::cordz_update_tracker
absl::core_headers
+ absl::nullability
absl::strings
TESTONLY
)
@@ -1001,8 +1070,10 @@ absl_cc_test(
absl::core_headers
absl::endian
absl::fixed_array
+ absl::function_ref
absl::hash
absl::log
+ absl::optional
absl::random_random
absl::str_format
absl::strings
@@ -1098,38 +1169,6 @@ absl_cc_test(
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"
@@ -1137,6 +1176,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::cord
+ absl::cord_internal
absl::cord_test_helpers
absl::cordz_test_helpers
absl::cordz_functions
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index 16c96899..5460b2c6 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -15,8 +15,13 @@
#include "absl/strings/ascii.h"
#include <climits>
+#include <cstdint>
#include <cstring>
#include <string>
+#include <type_traits>
+
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -157,8 +162,74 @@ ABSL_DLL const char kToUpper[256] = {
};
// clang-format on
+template <class T>
+static constexpr T BroadcastByte(unsigned char value) {
+ static_assert(std::is_integral<T>::value && sizeof(T) <= sizeof(uint64_t) &&
+ std::is_unsigned<T>::value,
+ "only unsigned integers up to 64-bit allowed");
+ T result = value;
+ constexpr size_t result_bit_width = sizeof(result) * CHAR_BIT;
+ result |= result << ((CHAR_BIT << 0) & (result_bit_width - 1));
+ result |= result << ((CHAR_BIT << 1) & (result_bit_width - 1));
+ result |= result << ((CHAR_BIT << 2) & (result_bit_width - 1));
+ return result;
+}
+
+// Returns whether `c` is in the a-z/A-Z range (w.r.t. `ToUpper`).
+// Implemented by:
+// 1. Pushing the a-z/A-Z range to [SCHAR_MIN, SCHAR_MIN + 26).
+// 2. Comparing to SCHAR_MIN + 26.
+template <bool ToUpper>
+constexpr bool AsciiInAZRange(unsigned char c) {
+ constexpr unsigned char sub = (ToUpper ? 'a' : 'A') - SCHAR_MIN;
+ constexpr signed char threshold = SCHAR_MIN + 26; // 26 = alphabet size.
+ // Using unsigned arithmetic as overflows/underflows are well defined.
+ unsigned char u = c - sub;
+ // Using signed cmp, as SIMD unsigned cmp isn't available in many platforms.
+ return static_cast<signed char>(u) < threshold;
+}
+
template <bool ToUpper>
-constexpr void AsciiStrCaseFold(char* p, char* end) {
+static constexpr char* PartialAsciiStrCaseFold(absl::Nonnull<char*> p,
+ absl::Nonnull<char*> end) {
+ using vec_t = size_t;
+ const size_t n = static_cast<size_t>(end - p);
+
+ // SWAR algorithm: http://0x80.pl/notesen/2016-01-06-swar-swap-case.html
+ constexpr char ch_a = ToUpper ? 'a' : 'A', ch_z = ToUpper ? 'z' : 'Z';
+ char* const swar_end = p + (n / sizeof(vec_t)) * sizeof(vec_t);
+ while (p < swar_end) {
+ vec_t v = vec_t();
+
+ // memcpy the vector, but constexpr
+ for (size_t i = 0; i < sizeof(vec_t); ++i) {
+ v |= static_cast<vec_t>(static_cast<unsigned char>(p[i]))
+ << (i * CHAR_BIT);
+ }
+
+ constexpr unsigned int msb = 1u << (CHAR_BIT - 1);
+ const vec_t v_msb = v & BroadcastByte<vec_t>(msb);
+ const vec_t v_nonascii_mask = (v_msb << 1) - (v_msb >> (CHAR_BIT - 1));
+ const vec_t v_nonascii = v & v_nonascii_mask;
+ const vec_t v_ascii = v & ~v_nonascii_mask;
+ const vec_t a = v_ascii + BroadcastByte<vec_t>(msb - ch_a - 0),
+ z = v_ascii + BroadcastByte<vec_t>(msb - ch_z - 1);
+ v = v_nonascii | (v_ascii ^ ((a ^ z) & BroadcastByte<vec_t>(msb)) >> 2);
+
+ // memcpy the vector, but constexpr
+ for (size_t i = 0; i < sizeof(vec_t); ++i) {
+ p[i] = static_cast<char>(v >> (i * CHAR_BIT));
+ }
+
+ p += sizeof(v);
+ }
+
+ return p;
+}
+
+template <bool ToUpper>
+static constexpr void AsciiStrCaseFold(absl::Nonnull<char*> p,
+ absl::Nonnull<char*> end) {
// The upper- and lowercase versions of ASCII characters differ by only 1 bit.
// When we need to flip the case, we can xor with this bit to achieve the
// desired result. Note that the choice of 'a' and 'A' here is arbitrary. We
@@ -166,16 +237,17 @@ constexpr void AsciiStrCaseFold(char* p, char* end) {
// have the same single bit difference.
constexpr unsigned char kAsciiCaseBitFlip = 'a' ^ 'A';
- constexpr char ch_a = ToUpper ? 'a' : 'A';
- constexpr char ch_z = ToUpper ? 'z' : 'Z';
- for (; p < end; ++p) {
+ using vec_t = size_t;
+ // TODO(b/316380338): When FDO becomes able to vectorize these,
+ // revert this manual optimization and just leave the naive loop.
+ if (static_cast<size_t>(end - p) >= sizeof(vec_t)) {
+ p = ascii_internal::PartialAsciiStrCaseFold<ToUpper>(p, end);
+ }
+ while (p < end) {
unsigned char v = static_cast<unsigned char>(*p);
- // We use & instead of && to ensure this always stays branchless
- // We use static_cast<int> to suppress -Wbitwise-instead-of-logical
- bool is_in_range = static_cast<bool>(static_cast<int>(ch_a <= v) &
- static_cast<int>(v <= ch_z));
- v ^= is_in_range ? kAsciiCaseBitFlip : 0;
+ v ^= AsciiInAZRange<ToUpper>(v) ? kAsciiCaseBitFlip : 0;
*p = static_cast<char>(v);
+ ++p;
}
}
@@ -205,17 +277,17 @@ static_assert(ValidateAsciiCasefold() == 0, "error in case conversion");
} // namespace ascii_internal
-void AsciiStrToLower(std::string* s) {
+void AsciiStrToLower(absl::Nonnull<std::string*> s) {
char* p = &(*s)[0]; // Guaranteed to be valid for empty strings
return ascii_internal::AsciiStrCaseFold<false>(p, p + s->size());
}
-void AsciiStrToUpper(std::string* s) {
+void AsciiStrToUpper(absl::Nonnull<std::string*> s) {
char* p = &(*s)[0]; // Guaranteed to be valid for empty strings
return ascii_internal::AsciiStrCaseFold<true>(p, p + s->size());
}
-void RemoveExtraAsciiWhitespace(std::string* str) {
+void RemoveExtraAsciiWhitespace(absl::Nonnull<std::string*> str) {
auto stripped = StripAsciiWhitespace(*str);
if (stripped.empty()) {
diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h
index 42eadaea..c238f4de 100644
--- a/absl/strings/ascii.h
+++ b/absl/strings/ascii.h
@@ -53,10 +53,12 @@
#define ABSL_STRINGS_ASCII_H_
#include <algorithm>
+#include <cstddef>
#include <string>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
+#include "absl/base/nullability.h"
#include "absl/strings/string_view.h"
namespace absl {
@@ -165,7 +167,7 @@ inline char ascii_tolower(unsigned char c) {
}
// Converts the characters in `s` to lowercase, changing the contents of `s`.
-void AsciiStrToLower(std::string* s);
+void AsciiStrToLower(absl::Nonnull<std::string*> s);
// Creates a lowercase string from a given absl::string_view.
ABSL_MUST_USE_RESULT inline std::string AsciiStrToLower(absl::string_view s) {
@@ -183,7 +185,7 @@ inline char ascii_toupper(unsigned char c) {
}
// Converts the characters in `s` to uppercase, changing the contents of `s`.
-void AsciiStrToUpper(std::string* s);
+void AsciiStrToUpper(absl::Nonnull<std::string*> s);
// Creates an uppercase string from a given absl::string_view.
ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) {
@@ -201,7 +203,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripLeadingAsciiWhitespace(
}
// Strips in place whitespace from the beginning of the given string.
-inline void StripLeadingAsciiWhitespace(std::string* str) {
+inline void StripLeadingAsciiWhitespace(absl::Nonnull<std::string*> str) {
auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace);
str->erase(str->begin(), it);
}
@@ -215,7 +217,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripTrailingAsciiWhitespace(
}
// Strips in place whitespace from the end of the given string
-inline void StripTrailingAsciiWhitespace(std::string* str) {
+inline void StripTrailingAsciiWhitespace(absl::Nonnull<std::string*> str) {
auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace);
str->erase(static_cast<size_t>(str->rend() - it));
}
@@ -228,13 +230,13 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripAsciiWhitespace(
}
// Strips in place whitespace from both ends of the given string
-inline void StripAsciiWhitespace(std::string* str) {
+inline void StripAsciiWhitespace(absl::Nonnull<std::string*> str) {
StripTrailingAsciiWhitespace(str);
StripLeadingAsciiWhitespace(str);
}
// Removes leading, trailing, and consecutive internal whitespace.
-void RemoveExtraAsciiWhitespace(std::string*);
+void RemoveExtraAsciiWhitespace(absl::Nonnull<std::string*> str);
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/ascii_benchmark.cc b/absl/strings/ascii_benchmark.cc
index aca458c8..4ae73174 100644
--- a/absl/strings/ascii_benchmark.cc
+++ b/absl/strings/ascii_benchmark.cc
@@ -14,7 +14,9 @@
#include "absl/strings/ascii.h"
+#include <algorithm>
#include <cctype>
+#include <cstddef>
#include <string>
#include <array>
#include <random>
@@ -103,18 +105,28 @@ static void BM_StrToLower(benchmark::State& state) {
const int size = state.range(0);
std::string s(size, 'X');
for (auto _ : state) {
- benchmark::DoNotOptimize(absl::AsciiStrToLower(s));
+ benchmark::DoNotOptimize(s);
+ std::string res = absl::AsciiStrToLower(s);
+ benchmark::DoNotOptimize(res);
}
}
-BENCHMARK(BM_StrToLower)->Range(1, 1 << 20);
+BENCHMARK(BM_StrToLower)
+ ->DenseRange(0, 32)
+ ->RangeMultiplier(2)
+ ->Range(64, 1 << 26);
static void BM_StrToUpper(benchmark::State& state) {
const int size = state.range(0);
std::string s(size, 'x');
for (auto _ : state) {
- benchmark::DoNotOptimize(absl::AsciiStrToUpper(s));
+ benchmark::DoNotOptimize(s);
+ std::string res = absl::AsciiStrToUpper(s);
+ benchmark::DoNotOptimize(res);
}
}
-BENCHMARK(BM_StrToUpper)->Range(1, 1 << 20);
+BENCHMARK(BM_StrToUpper)
+ ->DenseRange(0, 32)
+ ->RangeMultiplier(2)
+ ->Range(64, 1 << 26);
} // namespace
diff --git a/absl/strings/ascii_test.cc b/absl/strings/ascii_test.cc
index 4ea262f1..117140c1 100644
--- a/absl/strings/ascii_test.cc
+++ b/absl/strings/ascii_test.cc
@@ -22,7 +22,7 @@
#include "gtest/gtest.h"
#include "absl/base/macros.h"
-#include "absl/base/port.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index 778a1c75..0c9227f8 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -16,12 +16,14 @@
#include <algorithm>
#include <cassert>
-#include <cmath>
-#include <cstring>
+#include <cstddef>
+#include <cstdint>
#include <limits>
+#include <system_error> // NOLINT(build/c++11)
#include "absl/base/casts.h"
#include "absl/base/config.h"
+#include "absl/base/nullability.h"
#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
#include "absl/strings/internal/charconv_bigint.h"
@@ -118,7 +120,7 @@ struct FloatTraits<double> {
// Parsing a smaller N will produce something finite.
static constexpr int kEiselLemireMaxExclusiveExp10 = 309;
- static double MakeNan(const char* tagp) {
+ static double MakeNan(absl::Nonnull<const char*> tagp) {
#if ABSL_HAVE_BUILTIN(__builtin_nan)
// Use __builtin_nan() if available since it has a fix for
// https://bugs.llvm.org/show_bug.cgi?id=37778
@@ -191,7 +193,7 @@ struct FloatTraits<float> {
static constexpr int kEiselLemireMinInclusiveExp10 = -46 - 18;
static constexpr int kEiselLemireMaxExclusiveExp10 = 39;
- static float MakeNan(const char* tagp) {
+ static float MakeNan(absl::Nonnull<const char*> tagp) {
#if ABSL_HAVE_BUILTIN(__builtin_nanf)
// Use __builtin_nanf() if available since it has a fix for
// https://bugs.llvm.org/show_bug.cgi?id=37778
@@ -343,7 +345,7 @@ int NormalizedShiftSize(int mantissa_width, int binary_exponent) {
// `value` must be wider than the requested bit width.
//
// Returns the number of bits shifted.
-int TruncateToBitWidth(int bit_width, uint128* value) {
+int TruncateToBitWidth(int bit_width, absl::Nonnull<uint128*> value) {
const int current_bit_width = BitWidth(*value);
const int shift = current_bit_width - bit_width;
*value >>= shift;
@@ -355,7 +357,7 @@ int TruncateToBitWidth(int bit_width, uint128* value) {
// the appropriate double, and returns true.
template <typename FloatType>
bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative,
- FloatType* value) {
+ absl::Nonnull<FloatType*> value) {
if (input.type == strings_internal::FloatType::kNan) {
// A bug in both clang < 7 and gcc would cause the compiler to optimize
// away the buffer we are building below. Declaring the buffer volatile
@@ -404,7 +406,8 @@ bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative,
// number is stored in *value.
template <typename FloatType>
void EncodeResult(const CalculatedFloat& calculated, bool negative,
- absl::from_chars_result* result, FloatType* value) {
+ absl::Nonnull<absl::from_chars_result*> result,
+ absl::Nonnull<FloatType*> value) {
if (calculated.exponent == kOverflow) {
result->ec = std::errc::result_out_of_range;
*value = negative ? -std::numeric_limits<FloatType>::max()
@@ -450,7 +453,7 @@ void EncodeResult(const CalculatedFloat& calculated, bool negative,
// Zero and negative values of `shift` are accepted, in which case the word is
// shifted left, as necessary.
uint64_t ShiftRightAndRound(uint128 value, int shift, bool input_exact,
- bool* output_exact) {
+ absl::Nonnull<bool*> output_exact) {
if (shift <= 0) {
*output_exact = input_exact;
return static_cast<uint64_t>(value << -shift);
@@ -684,7 +687,8 @@ CalculatedFloat CalculateFromParsedDecimal(
// this function returns false) is both fast and correct.
template <typename FloatType>
bool EiselLemire(const strings_internal::ParsedFloat& input, bool negative,
- FloatType* value, std::errc* ec) {
+ absl::Nonnull<FloatType*> value,
+ absl::Nonnull<std::errc*> ec) {
uint64_t man = input.mantissa;
int exp10 = input.exponent;
if (exp10 < FloatTraits<FloatType>::kEiselLemireMinInclusiveExp10) {
@@ -857,7 +861,8 @@ bool EiselLemire(const strings_internal::ParsedFloat& input, bool negative,
}
template <typename FloatType>
-from_chars_result FromCharsImpl(const char* first, const char* last,
+from_chars_result FromCharsImpl(absl::Nonnull<const char*> first,
+ absl::Nonnull<const char*> last,
FloatType& value, chars_format fmt_flags) {
from_chars_result result;
result.ptr = first; // overwritten on successful parse
@@ -943,12 +948,14 @@ from_chars_result FromCharsImpl(const char* first, const char* last,
}
} // namespace
-from_chars_result from_chars(const char* first, const char* last, double& value,
+from_chars_result from_chars(absl::Nonnull<const char*> first,
+ absl::Nonnull<const char*> last, double& value,
chars_format fmt) {
return FromCharsImpl(first, last, value, fmt);
}
-from_chars_result from_chars(const char* first, const char* last, float& value,
+from_chars_result from_chars(absl::Nonnull<const char*> first,
+ absl::Nonnull<const char*> last, float& value,
chars_format fmt) {
return FromCharsImpl(first, last, value, fmt);
}
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
index 111c7120..be250902 100644
--- a/absl/strings/charconv.h
+++ b/absl/strings/charconv.h
@@ -18,6 +18,7 @@
#include <system_error> // NOLINT(build/c++11)
#include "absl/base/config.h"
+#include "absl/base/nullability.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -44,7 +45,7 @@ enum class chars_format {
// characters that were successfully parsed. If none was found, `ptr` is set
// to the `first` argument to from_chars.
struct from_chars_result {
- const char* ptr;
+ absl::Nonnull<const char*> ptr;
std::errc ec;
};
@@ -76,11 +77,13 @@ struct from_chars_result {
// format that strtod() accepts, except that a "0x" prefix is NOT matched.
// (In particular, in `hex` mode, the input "0xff" results in the largest
// matching pattern "0".)
-absl::from_chars_result from_chars(const char* first, const char* last,
+absl::from_chars_result from_chars(absl::Nonnull<const char*> first,
+ absl::Nonnull<const char*> last,
double& value, // NOLINT
chars_format fmt = chars_format::general);
-absl::from_chars_result from_chars(const char* first, const char* last,
+absl::from_chars_result from_chars(absl::Nonnull<const char*> first,
+ absl::Nonnull<const char*> last,
float& value, // NOLINT
chars_format fmt = chars_format::general);
diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc
index b83de5a0..c16c735c 100644
--- a/absl/strings/charconv_test.cc
+++ b/absl/strings/charconv_test.cc
@@ -14,14 +14,19 @@
#include "absl/strings/charconv.h"
+#include <cfloat>
+#include <cmath>
#include <cstdlib>
+#include <functional>
+#include <limits>
#include <string>
+#include <system_error> // NOLINT(build/c++11)
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/internal/pow10_helper.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
#ifdef _MSC_FULL_VER
#define ABSL_COMPILER_DOES_EXACT_ROUNDING 0
diff --git a/absl/strings/charset.h b/absl/strings/charset.h
new file mode 100644
index 00000000..ff4e81a4
--- /dev/null
+++ b/absl/strings/charset.h
@@ -0,0 +1,164 @@
+// Copyright 2022 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: charset.h
+// -----------------------------------------------------------------------------
+//
+// This file contains absl::CharSet, a fast, bit-vector set of 8-bit unsigned
+// characters.
+//
+// Instances can be initialized as constexpr constants. For example:
+//
+// constexpr absl::CharSet kJustX = absl::CharSet::Char('x');
+// constexpr absl::CharSet kMySymbols = absl::CharSet("$@!");
+// constexpr absl::CharSet kLetters = absl::CharSet::Range('a', 'z');
+//
+// Multiple instances can be combined that still forms a constexpr expression.
+// For example:
+//
+// constexpr absl::CharSet kLettersAndNumbers =
+// absl::CharSet::Range('a', 'z') | absl::CharSet::Range('0', '9');
+//
+// Several pre-defined character classes are available that mirror the methods
+// from <cctype>. For example:
+//
+// constexpr absl::CharSet kLettersAndWhitespace =
+// absl::CharSet::AsciiAlphabet() | absl::CharSet::AsciiWhitespace();
+//
+// To check membership, use the .contains method, e.g.
+//
+// absl::CharSet hex_letters("abcdef");
+// hex_letters.contains('a'); // true
+// hex_letters.contains('g'); // false
+
+#ifndef ABSL_STRINGS_CHARSET_H_
+#define ABSL_STRINGS_CHARSET_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+#include "absl/base/macros.h"
+#include "absl/base/port.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+
+class CharSet {
+ public:
+ constexpr CharSet() : m_() {}
+
+ // Initializes with a given string_view.
+ constexpr explicit CharSet(absl::string_view str) : m_() {
+ for (char c : str) {
+ SetChar(static_cast<unsigned char>(c));
+ }
+ }
+
+ constexpr bool contains(char c) const {
+ return ((m_[static_cast<unsigned char>(c) / 64] >>
+ (static_cast<unsigned char>(c) % 64)) &
+ 0x1) == 0x1;
+ }
+
+ constexpr bool empty() const {
+ for (uint64_t c : m_) {
+ if (c != 0) return false;
+ }
+ return true;
+ }
+
+ // Containing only a single specified char.
+ static constexpr CharSet Char(char x) {
+ return CharSet(CharMaskForWord(x, 0), CharMaskForWord(x, 1),
+ CharMaskForWord(x, 2), CharMaskForWord(x, 3));
+ }
+
+ // Containing all the chars in the closed interval [lo,hi].
+ static constexpr CharSet Range(char lo, char hi) {
+ return CharSet(RangeForWord(lo, hi, 0), RangeForWord(lo, hi, 1),
+ RangeForWord(lo, hi, 2), RangeForWord(lo, hi, 3));
+ }
+
+ friend constexpr CharSet operator&(const CharSet& a, const CharSet& b) {
+ return CharSet(a.m_[0] & b.m_[0], a.m_[1] & b.m_[1], a.m_[2] & b.m_[2],
+ a.m_[3] & b.m_[3]);
+ }
+
+ friend constexpr CharSet operator|(const CharSet& a, const CharSet& b) {
+ return CharSet(a.m_[0] | b.m_[0], a.m_[1] | b.m_[1], a.m_[2] | b.m_[2],
+ a.m_[3] | b.m_[3]);
+ }
+
+ friend constexpr CharSet operator~(const CharSet& a) {
+ return CharSet(~a.m_[0], ~a.m_[1], ~a.m_[2], ~a.m_[3]);
+ }
+
+ // Mirrors the char-classifying predicates in <cctype>.
+ static constexpr CharSet AsciiUppercase() { return CharSet::Range('A', 'Z'); }
+ static constexpr CharSet AsciiLowercase() { return CharSet::Range('a', 'z'); }
+ static constexpr CharSet AsciiDigits() { return CharSet::Range('0', '9'); }
+ static constexpr CharSet AsciiAlphabet() {
+ return AsciiLowercase() | AsciiUppercase();
+ }
+ static constexpr CharSet AsciiAlphanumerics() {
+ return AsciiDigits() | AsciiAlphabet();
+ }
+ static constexpr CharSet AsciiHexDigits() {
+ return AsciiDigits() | CharSet::Range('A', 'F') | CharSet::Range('a', 'f');
+ }
+ static constexpr CharSet AsciiPrintable() {
+ return CharSet::Range(0x20, 0x7e);
+ }
+ static constexpr CharSet AsciiWhitespace() { return CharSet("\t\n\v\f\r "); }
+ static constexpr CharSet AsciiPunctuation() {
+ return AsciiPrintable() & ~AsciiWhitespace() & ~AsciiAlphanumerics();
+ }
+
+ private:
+ constexpr CharSet(uint64_t b0, uint64_t b1, uint64_t b2, uint64_t b3)
+ : m_{b0, b1, b2, b3} {}
+
+ static constexpr uint64_t RangeForWord(char lo, char hi, uint64_t word) {
+ return OpenRangeFromZeroForWord(static_cast<unsigned char>(hi) + 1, word) &
+ ~OpenRangeFromZeroForWord(static_cast<unsigned char>(lo), word);
+ }
+
+ // All the chars in the specified word of the range [0, upper).
+ static constexpr uint64_t OpenRangeFromZeroForWord(uint64_t upper,
+ uint64_t word) {
+ return (upper <= 64 * word) ? 0
+ : (upper >= 64 * (word + 1))
+ ? ~static_cast<uint64_t>(0)
+ : (~static_cast<uint64_t>(0) >> (64 - upper % 64));
+ }
+
+ static constexpr uint64_t CharMaskForWord(char x, uint64_t word) {
+ return (static_cast<unsigned char>(x) / 64 == word)
+ ? (static_cast<uint64_t>(1)
+ << (static_cast<unsigned char>(x) % 64))
+ : 0;
+ }
+
+ constexpr void SetChar(unsigned char c) {
+ m_[c / 64] |= static_cast<uint64_t>(1) << (c % 64);
+ }
+
+ uint64_t m_[4];
+};
+
+} // namespace absl
+
+#endif // ABSL_STRINGS_CHARSET_H_
diff --git a/absl/strings/internal/char_map_benchmark.cc b/absl/strings/charset_benchmark.cc
index 5cef967b..bf7ae560 100644
--- a/absl/strings/internal/char_map_benchmark.cc
+++ b/absl/strings/charset_benchmark.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 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,30 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "absl/strings/internal/char_map.h"
-
#include <cstdint>
#include "benchmark/benchmark.h"
+#include "absl/log/check.h"
+#include "absl/strings/charset.h"
namespace {
-absl::strings_internal::Charmap MakeBenchmarkMap() {
- absl::strings_internal::Charmap m;
+absl::CharSet MakeBenchmarkMap() {
+ absl::CharSet m;
uint32_t x[] = {0x0, 0x1, 0x2, 0x3, 0xf, 0xe, 0xd, 0xc};
for (uint32_t& t : x) t *= static_cast<uint32_t>(0x11111111UL);
for (uint32_t i = 0; i < 256; ++i) {
- if ((x[i / 32] >> (i % 32)) & 1)
- m = m | absl::strings_internal::Charmap::Char(i);
+ if ((x[i / 32] >> (i % 32)) & 1) m = m | absl::CharSet::Char(i);
}
return m;
}
// Micro-benchmark for Charmap::contains.
-void BM_Contains(benchmark::State& state) {
+static void BM_Contains(benchmark::State& state) {
// Loop-body replicated 10 times to increase time per iteration.
// Argument continuously changed to avoid generating common subexpressions.
- const absl::strings_internal::Charmap benchmark_map = MakeBenchmarkMap();
+ // Final CHECK used to discourage unwanted optimization.
+ const absl::CharSet benchmark_map = MakeBenchmarkMap();
unsigned char c = 0;
int ops = 0;
for (auto _ : state) {
@@ -50,12 +50,8 @@ void BM_Contains(benchmark::State& state) {
ops += benchmark_map.contains(c++);
ops += benchmark_map.contains(c++);
}
- benchmark::DoNotOptimize(ops);
+ CHECK_NE(ops, -1);
}
BENCHMARK(BM_Contains);
-// We don't bother benchmarking Charmap::IsZero or Charmap::IntersectsWith;
-// their running time is data-dependent and it is not worth characterizing
-// "typical" data.
-
} // namespace
diff --git a/absl/strings/charset_test.cc b/absl/strings/charset_test.cc
new file mode 100644
index 00000000..fff943ae
--- /dev/null
+++ b/absl/strings/charset_test.cc
@@ -0,0 +1,181 @@
+// 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/charset.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/string_view.h"
+
+namespace {
+
+constexpr absl::CharSet everything_map = ~absl::CharSet();
+constexpr absl::CharSet nothing_map = absl::CharSet();
+
+TEST(Charmap, AllTests) {
+ const absl::CharSet also_nothing_map("");
+ EXPECT_TRUE(everything_map.contains('\0'));
+ EXPECT_FALSE(nothing_map.contains('\0'));
+ EXPECT_FALSE(also_nothing_map.contains('\0'));
+ for (unsigned char ch = 1; ch != 0; ++ch) {
+ SCOPED_TRACE(ch);
+ EXPECT_TRUE(everything_map.contains(ch));
+ EXPECT_FALSE(nothing_map.contains(ch));
+ EXPECT_FALSE(also_nothing_map.contains(ch));
+ }
+
+ const absl::CharSet symbols(absl::string_view("&@#@^!@?", 5));
+ EXPECT_TRUE(symbols.contains('&'));
+ EXPECT_TRUE(symbols.contains('@'));
+ EXPECT_TRUE(symbols.contains('#'));
+ EXPECT_TRUE(symbols.contains('^'));
+ EXPECT_FALSE(symbols.contains('!'));
+ EXPECT_FALSE(symbols.contains('?'));
+ int cnt = 0;
+ for (unsigned char ch = 1; ch != 0; ++ch) cnt += symbols.contains(ch);
+ EXPECT_EQ(cnt, 4);
+
+ const absl::CharSet lets(absl::string_view("^abcde", 3));
+ const absl::CharSet lets2(absl::string_view("fghij\0klmnop", 10));
+ const absl::CharSet lets3("fghij\0klmnop");
+ EXPECT_TRUE(lets2.contains('k'));
+ EXPECT_FALSE(lets3.contains('k'));
+
+ EXPECT_FALSE((symbols & lets).empty());
+ EXPECT_TRUE((lets2 & lets).empty());
+ EXPECT_FALSE((lets & symbols).empty());
+ EXPECT_TRUE((lets & lets2).empty());
+
+ EXPECT_TRUE(nothing_map.empty());
+ EXPECT_FALSE(lets.empty());
+}
+
+std::string Members(const absl::CharSet& m) {
+ std::string r;
+ for (size_t i = 0; i < 256; ++i)
+ if (m.contains(i)) r.push_back(i);
+ return r;
+}
+
+std::string ClosedRangeString(unsigned char lo, unsigned char hi) {
+ // Don't depend on lo<hi. Just increment until lo==hi.
+ std::string s;
+ while (true) {
+ s.push_back(lo);
+ if (lo == hi) break;
+ ++lo;
+ }
+ return s;
+}
+
+TEST(Charmap, Constexpr) {
+ constexpr absl::CharSet kEmpty = absl::CharSet();
+ EXPECT_EQ(Members(kEmpty), "");
+ constexpr absl::CharSet kA = absl::CharSet::Char('A');
+ EXPECT_EQ(Members(kA), "A");
+ constexpr absl::CharSet kAZ = absl::CharSet::Range('A', 'Z');
+ EXPECT_EQ(Members(kAZ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ constexpr absl::CharSet kIdentifier =
+ absl::CharSet::Range('0', '9') | absl::CharSet::Range('A', 'Z') |
+ absl::CharSet::Range('a', 'z') | absl::CharSet::Char('_');
+ EXPECT_EQ(Members(kIdentifier),
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "_"
+ "abcdefghijklmnopqrstuvwxyz");
+ constexpr absl::CharSet kAll = ~absl::CharSet();
+ for (size_t i = 0; i < 256; ++i) {
+ SCOPED_TRACE(i);
+ EXPECT_TRUE(kAll.contains(i));
+ }
+ constexpr absl::CharSet kHello = absl::CharSet("Hello, world!");
+ EXPECT_EQ(Members(kHello), " !,Hdelorw");
+
+ // test negation and intersection
+ constexpr absl::CharSet kABC =
+ absl::CharSet::Range('A', 'Z') & ~absl::CharSet::Range('D', 'Z');
+ EXPECT_EQ(Members(kABC), "ABC");
+
+ // contains
+ constexpr bool kContainsA = absl::CharSet("abc").contains('a');
+ EXPECT_TRUE(kContainsA);
+ constexpr bool kContainsD = absl::CharSet("abc").contains('d');
+ EXPECT_FALSE(kContainsD);
+
+ // empty
+ constexpr bool kEmptyIsEmpty = absl::CharSet().empty();
+ EXPECT_TRUE(kEmptyIsEmpty);
+ constexpr bool kNotEmptyIsEmpty = absl::CharSet("abc").empty();
+ EXPECT_FALSE(kNotEmptyIsEmpty);
+}
+
+TEST(Charmap, Range) {
+ // Exhaustive testing takes too long, so test some of the boundaries that
+ // are perhaps going to cause trouble.
+ std::vector<size_t> poi = {0, 1, 2, 3, 4, 7, 8, 9, 15,
+ 16, 17, 30, 31, 32, 33, 63, 64, 65,
+ 127, 128, 129, 223, 224, 225, 254, 255};
+ for (auto lo = poi.begin(); lo != poi.end(); ++lo) {
+ SCOPED_TRACE(*lo);
+ for (auto hi = lo; hi != poi.end(); ++hi) {
+ SCOPED_TRACE(*hi);
+ EXPECT_EQ(Members(absl::CharSet::Range(*lo, *hi)),
+ ClosedRangeString(*lo, *hi));
+ }
+ }
+}
+
+TEST(Charmap, NullByteWithStringView) {
+ char characters[5] = {'a', 'b', '\0', 'd', 'x'};
+ absl::string_view view(characters, 5);
+ absl::CharSet tester(view);
+ EXPECT_TRUE(tester.contains('a'));
+ EXPECT_TRUE(tester.contains('b'));
+ EXPECT_TRUE(tester.contains('\0'));
+ EXPECT_TRUE(tester.contains('d'));
+ EXPECT_TRUE(tester.contains('x'));
+ EXPECT_FALSE(tester.contains('c'));
+}
+
+TEST(CharmapCtype, Match) {
+ for (int c = 0; c < 256; ++c) {
+ SCOPED_TRACE(c);
+ SCOPED_TRACE(static_cast<char>(c));
+ EXPECT_EQ(absl::ascii_isupper(c),
+ absl::CharSet::AsciiUppercase().contains(c));
+ EXPECT_EQ(absl::ascii_islower(c),
+ absl::CharSet::AsciiLowercase().contains(c));
+ EXPECT_EQ(absl::ascii_isdigit(c), absl::CharSet::AsciiDigits().contains(c));
+ EXPECT_EQ(absl::ascii_isalpha(c),
+ absl::CharSet::AsciiAlphabet().contains(c));
+ EXPECT_EQ(absl::ascii_isalnum(c),
+ absl::CharSet::AsciiAlphanumerics().contains(c));
+ EXPECT_EQ(absl::ascii_isxdigit(c),
+ absl::CharSet::AsciiHexDigits().contains(c));
+ EXPECT_EQ(absl::ascii_isprint(c),
+ absl::CharSet::AsciiPrintable().contains(c));
+ EXPECT_EQ(absl::ascii_isspace(c),
+ absl::CharSet::AsciiWhitespace().contains(c));
+ EXPECT_EQ(absl::ascii_ispunct(c),
+ absl::CharSet::AsciiPunctuation().contains(c));
+ }
+}
+
+} // namespace
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
index 14976aef..f67326fd 100644
--- a/absl/strings/cord.cc
+++ b/absl/strings/cord.cc
@@ -15,27 +15,33 @@
#include "absl/strings/cord.h"
#include <algorithm>
-#include <atomic>
+#include <cassert>
#include <cstddef>
+#include <cstdint>
#include <cstdio>
#include <cstdlib>
+#include <cstring>
#include <iomanip>
#include <ios>
#include <iostream>
#include <limits>
+#include <memory>
#include <ostream>
#include <sstream>
-#include <type_traits>
-#include <unordered_set>
-#include <vector>
+#include <string>
+#include <utility>
-#include "absl/base/casts.h"
+#include "absl/base/attributes.h"
+#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/base/port.h"
-#include "absl/container/fixed_array.h"
+#include "absl/base/optimization.h"
+#include "absl/base/nullability.h"
#include "absl/container/inlined_vector.h"
+#include "absl/crc/crc32c.h"
#include "absl/crc/internal/crc_cord_state.h"
+#include "absl/functional/function_ref.h"
#include "absl/strings/cord_buffer.h"
#include "absl/strings/escaping.h"
#include "absl/strings/internal/cord_data_edge.h"
@@ -43,13 +49,14 @@
#include "absl/strings/internal/cord_rep_btree.h"
#include "absl/strings/internal/cord_rep_crc.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/match.h"
#include "absl/strings/str_cat.h"
-#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
+#include "absl/strings/strip.h"
+#include "absl/types/optional.h"
+#include "absl/types/span.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -68,29 +75,21 @@ using ::absl::cord_internal::kMinFlatLength;
using ::absl::cord_internal::kInlinedVectorSize;
using ::absl::cord_internal::kMaxBytesToCopy;
-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);
-
-static inline CordRep* VerifyTree(CordRep* node) {
- // Verification is expensive, so only do it in debug mode.
- // Even in debug mode we normally do only light validation.
- // If you are debugging Cord itself, you should define the
- // macro EXTRA_CORD_VALIDATION, e.g. by adding
- // --copt=-DEXTRA_CORD_VALIDATION to the blaze line.
-#ifdef EXTRA_CORD_VALIDATION
- assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/true));
-#else // EXTRA_CORD_VALIDATION
- assert(node == nullptr || VerifyNode(node, node, /*full_validation=*/false));
-#endif // EXTRA_CORD_VALIDATION
- static_cast<void>(&VerifyNode);
+static void DumpNode(absl::Nonnull<CordRep*> rep, bool include_data,
+ absl::Nonnull<std::ostream*> os, int indent = 0);
+static bool VerifyNode(absl::Nonnull<CordRep*> root,
+ absl::Nonnull<CordRep*> start_node);
+static inline absl::Nullable<CordRep*> VerifyTree(
+ absl::Nullable<CordRep*> node) {
+ assert(node == nullptr || VerifyNode(node, node));
+ static_cast<void>(&VerifyNode);
return node;
}
-static CordRepFlat* CreateFlat(const char* data, size_t length,
- size_t alloc_hint) {
+static absl::Nonnull<CordRepFlat*> CreateFlat(absl::Nonnull<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);
@@ -99,7 +98,8 @@ static CordRepFlat* CreateFlat(const char* data, size_t length,
// 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) {
+static absl::Nonnull<CordRep*> NewBtree(absl::Nonnull<const char*> data,
+ size_t length, size_t alloc_hint) {
if (length <= kMaxFlatLength) {
return CreateFlat(data, length, alloc_hint);
}
@@ -112,14 +112,16 @@ static CordRep* NewBtree(const char* data, size_t length, size_t 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 absl::Nullable<CordRep*> NewTree(absl::Nullable<const char*> data,
+ size_t length, size_t alloc_hint) {
if (length == 0) return nullptr;
return NewBtree(data, length, alloc_hint);
}
namespace cord_internal {
-void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) {
+void InitializeCordRepExternal(absl::string_view data,
+ absl::Nonnull<CordRepExternal*> rep) {
assert(!data.empty());
rep->length = data.size();
rep->tag = EXTERNAL;
@@ -133,7 +135,7 @@ void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) {
// 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) {
+static absl::Nonnull<CordRep*> CordRepFromString(std::string&& src) {
assert(src.length() > cord_internal::kMaxInline);
if (
// String is short: copy data to avoid external block overhead.
@@ -165,12 +167,13 @@ static CordRep* CordRepFromString(std::string&& src) {
constexpr unsigned char Cord::InlineRep::kMaxInline;
#endif
-inline void Cord::InlineRep::set_data(const char* data, size_t n) {
+inline void Cord::InlineRep::set_data(absl::Nonnull<const char*> data,
+ size_t n) {
static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
data_.set_inline_data(data, n);
}
-inline char* Cord::InlineRep::set_data(size_t n) {
+inline absl::Nonnull<char*> Cord::InlineRep::set_data(size_t n) {
assert(n <= kMaxInline);
ResetToEmpty();
set_inline_size(n);
@@ -194,13 +197,13 @@ inline void Cord::InlineRep::remove_prefix(size_t n) {
// Returns `rep` converted into a CordRepBtree.
// Directly returns `rep` if `rep` is already a CordRepBtree.
-static CordRepBtree* ForceBtree(CordRep* rep) {
+static absl::Nonnull<CordRepBtree*> ForceBtree(CordRep* rep) {
return rep->IsBtree()
? rep->btree()
: CordRepBtree::Create(cord_internal::RemoveCrcNode(rep));
}
-void Cord::InlineRep::AppendTreeToInlined(CordRep* tree,
+void Cord::InlineRep::AppendTreeToInlined(absl::Nonnull<CordRep*> tree,
MethodIdentifier method) {
assert(!is_tree());
if (!data_.is_empty()) {
@@ -210,14 +213,16 @@ void Cord::InlineRep::AppendTreeToInlined(CordRep* tree,
EmplaceTree(tree, method);
}
-void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) {
+void Cord::InlineRep::AppendTreeToTree(absl::Nonnull<CordRep*> tree,
+ MethodIdentifier method) {
assert(is_tree());
const CordzUpdateScope scope(data_.cordz_info(), method);
tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree);
SetTree(tree, scope);
}
-void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) {
+void Cord::InlineRep::AppendTree(absl::Nonnull<CordRep*> tree,
+ MethodIdentifier method) {
assert(tree != nullptr);
assert(tree->length != 0);
assert(!tree->IsCrc());
@@ -228,7 +233,7 @@ void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) {
}
}
-void Cord::InlineRep::PrependTreeToInlined(CordRep* tree,
+void Cord::InlineRep::PrependTreeToInlined(absl::Nonnull<CordRep*> tree,
MethodIdentifier method) {
assert(!is_tree());
if (!data_.is_empty()) {
@@ -238,7 +243,7 @@ void Cord::InlineRep::PrependTreeToInlined(CordRep* tree,
EmplaceTree(tree, method);
}
-void Cord::InlineRep::PrependTreeToTree(CordRep* tree,
+void Cord::InlineRep::PrependTreeToTree(absl::Nonnull<CordRep*> tree,
MethodIdentifier method) {
assert(is_tree());
const CordzUpdateScope scope(data_.cordz_info(), method);
@@ -246,7 +251,8 @@ void Cord::InlineRep::PrependTreeToTree(CordRep* tree,
SetTree(tree, scope);
}
-void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) {
+void Cord::InlineRep::PrependTree(absl::Nonnull<CordRep*> tree,
+ MethodIdentifier method) {
assert(tree != nullptr);
assert(tree->length != 0);
assert(!tree->IsCrc());
@@ -261,8 +267,9 @@ void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) {
// suitable leaf is found, the function will update the length field for all
// nodes to account for the size increase. The append region address will be
// 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) {
+static inline bool PrepareAppendRegion(
+ absl::Nonnull<CordRep*> root, absl::Nonnull<absl::Nullable<char*>*> region,
+ absl::Nonnull<size_t*> size, size_t max_length) {
if (root->IsBtree() && root->refcount.IsOne()) {
Span<char> span = root->btree()->GetAppendBuffer(max_length);
if (!span.empty()) {
@@ -465,11 +472,11 @@ void Cord::InlineRep::AppendArray(absl::string_view src,
CommitTree(root, rep, scope, method);
}
-inline CordRep* Cord::TakeRep() const& {
+inline absl::Nonnull<CordRep*> Cord::TakeRep() const& {
return CordRep::Ref(contents_.tree());
}
-inline CordRep* Cord::TakeRep() && {
+inline absl::Nonnull<CordRep*> Cord::TakeRep() && {
CordRep* rep = contents_.tree();
contents_.clear();
return rep;
@@ -527,7 +534,7 @@ inline void Cord::AppendImpl(C&& src) {
contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord);
}
-static CordRep::ExtractResult ExtractAppendBuffer(CordRep* rep,
+static CordRep::ExtractResult ExtractAppendBuffer(absl::Nonnull<CordRep*> rep,
size_t min_capacity) {
switch (rep->tag) {
case cord_internal::BTREE:
@@ -573,13 +580,9 @@ CordBuffer Cord::GetAppendBufferSlowPath(size_t block_size, size_t capacity,
return CreateAppendBuffer(contents_.data_, block_size, capacity);
}
-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) {
@@ -778,8 +781,9 @@ int ClampResult(int memcmp_res) {
return static_cast<int>(memcmp_res > 0) - static_cast<int>(memcmp_res < 0);
}
-int CompareChunks(absl::string_view* lhs, absl::string_view* rhs,
- size_t* size_to_compare) {
+int CompareChunks(absl::Nonnull<absl::string_view*> lhs,
+ absl::Nonnull<absl::string_view*> rhs,
+ absl::Nonnull<size_t*> size_to_compare) {
size_t compared_size = std::min(lhs->size(), rhs->size());
assert(*size_to_compare >= compared_size);
*size_to_compare -= compared_size;
@@ -877,7 +881,8 @@ void Cord::SetExpectedChecksum(uint32_t crc) {
SetCrcCordState(std::move(state));
}
-const crc_internal::CrcCordState* Cord::MaybeGetCrcCordState() const {
+absl::Nullable<const crc_internal::CrcCordState*> Cord::MaybeGetCrcCordState()
+ const {
if (!contents_.is_tree() || !contents_.tree()->IsCrc()) {
return nullptr;
}
@@ -894,7 +899,8 @@ absl::optional<uint32_t> Cord::ExpectedChecksum() const {
inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size,
size_t size_to_compare) const {
- auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
+ auto advance = [](absl::Nonnull<Cord::ChunkIterator*> it,
+ absl::Nonnull<absl::string_view*> chunk) {
if (!chunk->empty()) return true;
++*it;
if (it->bytes_remaining_ == 0) return false;
@@ -924,7 +930,8 @@ inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size,
inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size,
size_t size_to_compare) const {
- auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) {
+ auto advance = [](absl::Nonnull<Cord::ChunkIterator*> it,
+ absl::Nonnull<absl::string_view*> chunk) {
if (!chunk->empty()) return true;
++*it;
if (it->bytes_remaining_ == 0) return false;
@@ -1046,7 +1053,7 @@ Cord::operator std::string() const {
return s;
}
-void CopyCordToString(const Cord& src, std::string* dst) {
+void CopyCordToString(const Cord& src, absl::Nonnull<std::string*> dst) {
if (!src.contents_.is_tree()) {
src.contents_.CopyTo(dst);
} else {
@@ -1055,7 +1062,7 @@ void CopyCordToString(const Cord& src, std::string* dst) {
}
}
-void Cord::CopyToArraySlowPath(char* dst) const {
+void Cord::CopyToArraySlowPath(absl::Nonnull<char*> dst) const {
assert(contents_.is_tree());
absl::string_view fragment;
if (GetFlatAux(contents_.tree(), &fragment)) {
@@ -1165,6 +1172,194 @@ char Cord::operator[](size_t i) const {
}
}
+namespace {
+
+// Tests whether the sequence of chunks beginning at `position` starts with
+// `needle`.
+//
+// REQUIRES: remaining `absl::Cord` starting at `position` is greater than or
+// equal to `needle.size()`.
+bool IsSubstringInCordAt(absl::Cord::CharIterator position,
+ absl::string_view needle) {
+ auto haystack_chunk = absl::Cord::ChunkRemaining(position);
+ while (true) {
+ // Precondition is that `absl::Cord::ChunkRemaining(position)` is not
+ // empty. This assert will trigger if that is not true.
+ assert(!haystack_chunk.empty());
+ auto min_length = std::min(haystack_chunk.size(), needle.size());
+ if (!absl::ConsumePrefix(&needle, haystack_chunk.substr(0, min_length))) {
+ return false;
+ }
+ if (needle.empty()) {
+ return true;
+ }
+ absl::Cord::Advance(&position, min_length);
+ haystack_chunk = absl::Cord::ChunkRemaining(position);
+ }
+}
+
+} // namespace
+
+// A few options how this could be implemented:
+// (a) Flatten the Cord and find, i.e.
+// haystack.Flatten().find(needle)
+// For large 'haystack' (where Cord makes sense to be used), this copies
+// the whole 'haystack' and can be slow.
+// (b) Use std::search, i.e.
+// std::search(haystack.char_begin(), haystack.char_end(),
+// needle.begin(), needle.end())
+// This avoids the copy, but compares one byte at a time, and branches a
+// lot every time it has to advance. It is also not possible to use
+// std::search as is, because CharIterator is only an input iterator, not a
+// forward iterator.
+// (c) Use string_view::find in each fragment, and specifically handle fragment
+// boundaries.
+//
+// This currently implements option (b).
+absl::Cord::CharIterator absl::Cord::FindImpl(CharIterator it,
+ absl::string_view needle) const {
+ // Ensure preconditions are met by callers first.
+
+ // Needle must not be empty.
+ assert(!needle.empty());
+ // Haystack must be at least as large as needle.
+ assert(it.chunk_iterator_.bytes_remaining_ >= needle.size());
+
+ // Cord is a sequence of chunks. To find `needle` we go chunk by chunk looking
+ // for the first char of needle, up until we have advanced `N` defined as
+ // `haystack.size() - needle.size()`. If we find the first char of needle at
+ // `P` and `P` is less than `N`, we then call `IsSubstringInCordAt` to
+ // see if this is the needle. If not, we advance to `P + 1` and try again.
+ while (it.chunk_iterator_.bytes_remaining_ >= needle.size()) {
+ auto haystack_chunk = Cord::ChunkRemaining(it);
+ assert(!haystack_chunk.empty());
+ // Look for the first char of `needle` in the current chunk.
+ auto idx = haystack_chunk.find(needle.front());
+ if (idx == absl::string_view::npos) {
+ // No potential match in this chunk, advance past it.
+ Cord::Advance(&it, haystack_chunk.size());
+ continue;
+ }
+ // We found the start of a potential match in the chunk. Advance the
+ // iterator and haystack chunk to the match the position.
+ Cord::Advance(&it, idx);
+ // Check if there is enough haystack remaining to actually have a match.
+ if (it.chunk_iterator_.bytes_remaining_ < needle.size()) {
+ break;
+ }
+ // Check if this is `needle`.
+ if (IsSubstringInCordAt(it, needle)) {
+ return it;
+ }
+ // No match, increment the iterator for the next attempt.
+ Cord::Advance(&it, 1);
+ }
+ // If we got here, we did not find `needle`.
+ return char_end();
+}
+
+absl::Cord::CharIterator absl::Cord::Find(absl::string_view needle) const {
+ if (needle.empty()) {
+ return char_begin();
+ }
+ if (needle.size() > size()) {
+ return char_end();
+ }
+ if (needle.size() == size()) {
+ return *this == needle ? char_begin() : char_end();
+ }
+ return FindImpl(char_begin(), needle);
+}
+
+namespace {
+
+// Tests whether the sequence of chunks beginning at `haystack` starts with the
+// sequence of chunks beginning at `needle_begin` and extending to `needle_end`.
+//
+// REQUIRES: remaining `absl::Cord` starting at `position` is greater than or
+// equal to `needle_end - needle_begin` and `advance`.
+bool IsSubcordInCordAt(absl::Cord::CharIterator haystack,
+ absl::Cord::CharIterator needle_begin,
+ absl::Cord::CharIterator needle_end) {
+ while (needle_begin != needle_end) {
+ auto haystack_chunk = absl::Cord::ChunkRemaining(haystack);
+ assert(!haystack_chunk.empty());
+ auto needle_chunk = absl::Cord::ChunkRemaining(needle_begin);
+ auto min_length = std::min(haystack_chunk.size(), needle_chunk.size());
+ if (haystack_chunk.substr(0, min_length) !=
+ needle_chunk.substr(0, min_length)) {
+ return false;
+ }
+ absl::Cord::Advance(&haystack, min_length);
+ absl::Cord::Advance(&needle_begin, min_length);
+ }
+ return true;
+}
+
+// Tests whether the sequence of chunks beginning at `position` starts with the
+// cord `needle`.
+//
+// REQUIRES: remaining `absl::Cord` starting at `position` is greater than or
+// equal to `needle.size()`.
+bool IsSubcordInCordAt(absl::Cord::CharIterator position,
+ const absl::Cord& needle) {
+ return IsSubcordInCordAt(position, needle.char_begin(), needle.char_end());
+}
+
+} // namespace
+
+absl::Cord::CharIterator absl::Cord::Find(const absl::Cord& needle) const {
+ if (needle.empty()) {
+ return char_begin();
+ }
+ const auto needle_size = needle.size();
+ if (needle_size > size()) {
+ return char_end();
+ }
+ if (needle_size == size()) {
+ return *this == needle ? char_begin() : char_end();
+ }
+ const auto needle_chunk = Cord::ChunkRemaining(needle.char_begin());
+ auto haystack_it = char_begin();
+ while (true) {
+ haystack_it = FindImpl(haystack_it, needle_chunk);
+ if (haystack_it == char_end() ||
+ haystack_it.chunk_iterator_.bytes_remaining_ < needle_size) {
+ break;
+ }
+ // We found the first chunk of `needle` at `haystack_it` but not the entire
+ // subcord. Advance past the first chunk and check for the remainder.
+ auto haystack_advanced_it = haystack_it;
+ auto needle_it = needle.char_begin();
+ Cord::Advance(&haystack_advanced_it, needle_chunk.size());
+ Cord::Advance(&needle_it, needle_chunk.size());
+ if (IsSubcordInCordAt(haystack_advanced_it, needle_it, needle.char_end())) {
+ return haystack_it;
+ }
+ Cord::Advance(&haystack_it, 1);
+ if (haystack_it.chunk_iterator_.bytes_remaining_ < needle_size) {
+ break;
+ }
+ if (haystack_it.chunk_iterator_.bytes_remaining_ == needle_size) {
+ // Special case, if there is exactly `needle_size` bytes remaining, the
+ // subcord is either at `haystack_it` or not at all.
+ if (IsSubcordInCordAt(haystack_it, needle)) {
+ return haystack_it;
+ }
+ break;
+ }
+ }
+ return char_end();
+}
+
+bool Cord::Contains(absl::string_view rhs) const {
+ return rhs.empty() || Find(rhs) != char_end();
+}
+
+bool Cord::Contains(const absl::Cord& rhs) const {
+ return rhs.empty() || Find(rhs) != char_end();
+}
+
absl::string_view Cord::FlattenSlowPath() {
assert(contents_.is_tree());
size_t total_size = size();
@@ -1193,7 +1388,8 @@ absl::string_view Cord::FlattenSlowPath() {
return absl::string_view(new_buffer, total_size);
}
-/* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
+/* static */ bool Cord::GetFlatAux(absl::Nonnull<CordRep*> rep,
+ absl::Nonnull<absl::string_view*> fragment) {
assert(rep != nullptr);
if (rep->length == 0) {
*fragment = absl::string_view();
@@ -1227,7 +1423,7 @@ absl::string_view Cord::FlattenSlowPath() {
}
/* static */ void Cord::ForEachChunkAux(
- absl::cord_internal::CordRep* rep,
+ absl::Nonnull<absl::cord_internal::CordRep*> rep,
absl::FunctionRef<void(absl::string_view)> callback) {
assert(rep != nullptr);
if (rep->length == 0) return;
@@ -1252,8 +1448,8 @@ absl::string_view Cord::FlattenSlowPath() {
}
}
-static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
- int indent) {
+static void DumpNode(absl::Nonnull<CordRep*> rep, bool include_data,
+ absl::Nonnull<std::ostream*> os, int indent) {
const int kIndentStep = 1;
absl::InlinedVector<CordRep*, kInlinedVectorSize> stack;
absl::InlinedVector<int, kInlinedVectorSize> indents;
@@ -1289,7 +1485,7 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
*os << absl::CEscape(std::string(rep->flat()->Data(), rep->length));
*os << "]\n";
} else {
- CordRepBtree::Dump(rep, /*label=*/ "", include_data, *os);
+ CordRepBtree::Dump(rep, /*label=*/"", include_data, *os);
}
}
if (leaf) {
@@ -1303,16 +1499,17 @@ static void DumpNode(CordRep* rep, bool include_data, std::ostream* os,
ABSL_INTERNAL_CHECK(indents.empty(), "");
}
-static std::string ReportError(CordRep* root, CordRep* node) {
+static std::string ReportError(absl::Nonnull<CordRep*> root,
+ absl::Nonnull<CordRep*> node) {
std::ostringstream buf;
buf << "Error at node " << node << " in:";
DumpNode(root, true, &buf);
return buf.str();
}
-static bool VerifyNode(CordRep* root, CordRep* start_node,
- bool /* full_validation */) {
- absl::InlinedVector<CordRep*, 2> worklist;
+static bool VerifyNode(absl::Nonnull<CordRep*> root,
+ absl::Nonnull<CordRep*> start_node) {
+ absl::InlinedVector<absl::Nonnull<CordRep*>, 2> worklist;
worklist.push_back(start_node);
do {
CordRep* node = worklist.back();
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
index 457ccf06..b3e556b6 100644
--- a/absl/strings/cord.h
+++ b/absl/strings/cord.h
@@ -74,6 +74,7 @@
#include "absl/base/internal/endian.h"
#include "absl/base/internal/per_thread_tls.h"
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
#include "absl/base/port.h"
#include "absl/container/inlined_vector.h"
#include "absl/crc/internal/crc_cord_state.h"
@@ -86,7 +87,6 @@
#include "absl/strings/internal/cord_rep_btree.h"
#include "absl/strings/internal/cord_rep_btree_reader.h"
#include "absl/strings/internal/cord_rep_crc.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"
@@ -103,7 +103,7 @@ class Cord;
class CordTestPeer;
template <typename Releaser>
Cord MakeCordFromExternal(absl::string_view, Releaser&&);
-void CopyCordToString(const Cord& src, std::string* dst);
+void CopyCordToString(const Cord& src, absl::Nonnull<std::string*> dst);
// Cord memory accounting modes
enum class CordMemoryAccounting {
@@ -120,8 +120,8 @@ enum class CordMemoryAccounting {
//
// For example:
// absl::Cord cord;
- // cord.append(some_other_cord);
- // cord.append(some_other_cord);
+ // cord.Append(some_other_cord);
+ // cord.Append(some_other_cord);
// // Counts `some_other_cord` twice:
// cord.EstimatedMemoryUsage(kTotal);
// // Counts `some_other_cord` once:
@@ -362,7 +362,7 @@ class Cord {
// Cord::empty()
//
- // Determines whether the given Cord is empty, returning `true` is so.
+ // Determines whether the given Cord is empty, returning `true` if so.
bool empty() const;
// Cord::EstimatedMemoryUsage()
@@ -396,6 +396,12 @@ class Cord {
bool EndsWith(absl::string_view rhs) const;
bool EndsWith(const Cord& rhs) const;
+ // Cord::Contains()
+ //
+ // Determines whether the Cord contains the passed string data `rhs`.
+ bool Contains(absl::string_view rhs) const;
+ bool Contains(const Cord& rhs) const;
+
// Cord::operator std::string()
//
// Converts a Cord into a `std::string()`. This operator is marked explicit to
@@ -411,7 +417,8 @@ class Cord {
// 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);
+ friend void CopyCordToString(const Cord& src,
+ absl::Nonnull<std::string*> dst);
class CharIterator;
@@ -448,7 +455,7 @@ class Cord {
using iterator_category = std::input_iterator_tag;
using value_type = absl::string_view;
using difference_type = ptrdiff_t;
- using pointer = const value_type*;
+ using pointer = absl::Nonnull<const value_type*>;
using reference = value_type;
ChunkIterator() = default;
@@ -468,14 +475,14 @@ class Cord {
using CordRepBtree = absl::cord_internal::CordRepBtree;
using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader;
- // Constructs a `begin()` iterator from `tree`. `tree` must not be null.
- explicit ChunkIterator(cord_internal::CordRep* tree);
+ // Constructs a `begin()` iterator from `tree`.
+ explicit ChunkIterator(absl::Nonnull<cord_internal::CordRep*> tree);
// Constructs a `begin()` iterator from `cord`.
- explicit ChunkIterator(const Cord* cord);
+ explicit ChunkIterator(absl::Nonnull<const Cord*> cord);
// Initializes this instance from a tree. Invoked by constructors.
- void InitTree(cord_internal::CordRep* tree);
+ void InitTree(absl::Nonnull<cord_internal::CordRep*> tree);
// Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than
// `current_chunk_.size()`.
@@ -493,7 +500,7 @@ class Cord {
// The current leaf, or `nullptr` if the iterator points to short data.
// If the current chunk is a substring node, current_leaf_ points to the
// underlying flat or external node.
- absl::cord_internal::CordRep* current_leaf_ = nullptr;
+ absl::Nullable<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;
@@ -515,7 +522,7 @@ class Cord {
// absl::string_view s) {
// return std::find(c.chunk_begin(), c.chunk_end(), s);
// }
- ChunkIterator chunk_begin() const;
+ ChunkIterator chunk_begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
// Cord::chunk_end()
//
@@ -524,7 +531,7 @@ class 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;
+ ChunkIterator chunk_end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
//----------------------------------------------------------------------------
// Cord::ChunkRange
@@ -550,13 +557,13 @@ class Cord {
using iterator = ChunkIterator;
using const_iterator = ChunkIterator;
- explicit ChunkRange(const Cord* cord) : cord_(cord) {}
+ explicit ChunkRange(absl::Nonnull<const Cord*> cord) : cord_(cord) {}
ChunkIterator begin() const;
ChunkIterator end() const;
private:
- const Cord* cord_;
+ absl::Nonnull<const Cord*> cord_;
};
// Cord::Chunks()
@@ -578,7 +585,7 @@ class Cord {
// // The temporary Cord returned by CordFactory has been destroyed!
// }
// }
- ChunkRange Chunks() const;
+ ChunkRange Chunks() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
//----------------------------------------------------------------------------
// Cord::CharIterator
@@ -609,7 +616,7 @@ class Cord {
using iterator_category = std::input_iterator_tag;
using value_type = char;
using difference_type = ptrdiff_t;
- using pointer = const char*;
+ using pointer = absl::Nonnull<const char*>;
using reference = const char&;
CharIterator() = default;
@@ -624,7 +631,8 @@ class Cord {
friend Cord;
private:
- explicit CharIterator(const Cord* cord) : chunk_iterator_(cord) {}
+ explicit CharIterator(absl::Nonnull<const Cord*> cord)
+ : chunk_iterator_(cord) {}
ChunkIterator chunk_iterator_;
};
@@ -635,14 +643,14 @@ class Cord {
// 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);
+ static Cord AdvanceAndRead(absl::Nonnull<CharIterator*> it, size_t n_bytes);
// Cord::Advance()
//
// 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);
+ static void Advance(absl::Nonnull<CharIterator*> it, size_t n_bytes);
// Cord::ChunkRemaining()
//
@@ -658,7 +666,7 @@ class 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;
+ CharIterator char_begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
// Cord::char_end()
//
@@ -667,7 +675,7 @@ class 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;
+ CharIterator char_end() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
// Cord::CharRange
//
@@ -691,13 +699,13 @@ class Cord {
using iterator = CharIterator;
using const_iterator = CharIterator;
- explicit CharRange(const Cord* cord) : cord_(cord) {}
+ explicit CharRange(absl::Nonnull<const Cord*> cord) : cord_(cord) {}
CharIterator begin() const;
CharIterator end() const;
private:
- const Cord* cord_;
+ absl::Nonnull<const Cord*> cord_;
};
// Cord::Chars()
@@ -719,7 +727,7 @@ class Cord {
// // The temporary Cord returned by CordFactory has been destroyed!
// }
// }
- CharRange Chars() const;
+ CharRange Chars() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
// Cord::operator[]
//
@@ -737,20 +745,38 @@ class Cord {
//
// 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;
+ absl::optional<absl::string_view> TryFlat() const
+ ABSL_ATTRIBUTE_LIFETIME_BOUND;
// 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();
+ absl::string_view Flatten() ABSL_ATTRIBUTE_LIFETIME_BOUND;
+
+ // Cord::Find()
+ //
+ // Returns an iterator to the first occurrance of the substring `needle`.
+ //
+ // If the substring `needle` does not occur, `Cord::char_end()` is returned.
+ CharIterator Find(absl::string_view needle) const;
+ CharIterator Find(const absl::Cord& needle) const;
// Supports absl::Cord as a sink object for absl::Format().
- friend void AbslFormatFlush(absl::Cord* cord, absl::string_view part) {
+ friend void AbslFormatFlush(absl::Nonnull<absl::Cord*> cord,
+ absl::string_view part) {
cord->Append(part);
}
+ // Support automatic stringification with absl::StrCat and absl::StrFormat.
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const absl::Cord& cord) {
+ for (absl::string_view chunk : cord.Chunks()) {
+ sink.Append(chunk);
+ }
+ }
+
// Cord::SetExpectedChecksum()
//
// Stores a checksum value with this non-empty cord instance, for later
@@ -809,7 +835,8 @@ class Cord {
friend bool operator==(const Cord& lhs, const Cord& rhs);
friend bool operator==(const Cord& lhs, absl::string_view rhs);
- friend const CordzInfo* GetCordzInfoForTesting(const Cord& cord);
+ friend absl::Nullable<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.
@@ -836,20 +863,22 @@ class Cord {
InlineRep& operator=(const InlineRep& src);
InlineRep& operator=(InlineRep&& src) noexcept;
- explicit constexpr InlineRep(absl::string_view sv, CordRep* rep);
+ explicit constexpr InlineRep(absl::string_view sv,
+ absl::Nullable<CordRep*> rep);
- void Swap(InlineRep* rhs);
- bool empty() const;
+ void Swap(absl::Nonnull<InlineRep*> rhs);
size_t size() const;
- const char* data() const; // Returns nullptr if holding pointer
- void set_data(const char* data, size_t n); // Discards pointer, if any
- char* set_data(size_t n); // Write data to the result
+ // Returns nullptr if holding pointer
+ absl::Nullable<const char*> data() const;
+ // Discards pointer, if any
+ void set_data(absl::Nonnull<const char*> data, size_t n);
+ absl::Nonnull<char*> set_data(size_t n); // Write data to the result
// Returns nullptr if holding bytes
- absl::cord_internal::CordRep* tree() const;
- absl::cord_internal::CordRep* as_tree() const;
- const char* as_chars() const;
+ absl::Nullable<absl::cord_internal::CordRep*> tree() const;
+ absl::Nonnull<absl::cord_internal::CordRep*> as_tree() const;
+ absl::Nonnull<const char*> as_chars() const;
// Returns non-null iff was holding a pointer
- absl::cord_internal::CordRep* clear();
+ absl::Nullable<absl::cord_internal::CordRep*> clear();
// Converts to pointer if necessary.
void reduce_size(size_t n); // REQUIRES: holding data
void remove_prefix(size_t n); // REQUIRES: holding data
@@ -858,46 +887,52 @@ class Cord {
// Creates a CordRepFlat instance from the current inlined data with `extra'
// bytes of desired additional capacity.
- CordRepFlat* MakeFlatWithExtraCapacity(size_t extra);
+ absl::Nonnull<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);
+ void SetTree(absl::Nonnull<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);
+ void SetTreeOrEmpty(absl::Nullable<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);
+ void EmplaceTree(absl::Nonnull<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,
+ void EmplaceTree(absl::Nonnull<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);
+ void CommitTree(absl::Nullable<const CordRep*> old_rep,
+ absl::Nonnull<CordRep*> rep, const CordzUpdateScope& scope,
+ MethodIdentifier method);
+
+ void AppendTreeToInlined(absl::Nonnull<CordRep*> tree,
+ MethodIdentifier method);
+ void AppendTreeToTree(absl::Nonnull<CordRep*> tree,
+ MethodIdentifier method);
+ void AppendTree(absl::Nonnull<CordRep*> tree, MethodIdentifier method);
+ void PrependTreeToInlined(absl::Nonnull<CordRep*> tree,
+ MethodIdentifier method);
+ void PrependTreeToTree(absl::Nonnull<CordRep*> tree,
+ MethodIdentifier method);
+ void PrependTree(absl::Nonnull<CordRep*> tree, MethodIdentifier method);
bool IsSame(const InlineRep& other) const { return data_ == other.data_; }
- void CopyTo(std::string* dst) const {
+ void CopyTo(absl::Nonnull<std::string*> dst) const {
// memcpy is much faster when operating on a known size. On most supported
// platforms, the small string optimization is large enough that resizing
// to 15 bytes does not cause a memory allocation.
@@ -909,7 +944,7 @@ class Cord {
}
// Copies the inline contents into `dst`. Assumes the cord is not empty.
- void CopyToArray(char* dst) const;
+ void CopyToArray(absl::Nonnull<char*> dst) const;
bool is_tree() const { return data_.is_tree(); }
@@ -922,12 +957,12 @@ class Cord {
}
// Returns the profiled CordzInfo, or nullptr if not sampled.
- absl::cord_internal::CordzInfo* cordz_info() const {
+ absl::Nullable<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) {
+ // Sets the profiled CordzInfo.
+ void set_cordz_info(absl::Nonnull<cord_internal::CordzInfo*> cordz_info) {
assert(cordz_info != nullptr);
data_.set_cordz_info(cordz_info);
}
@@ -959,19 +994,19 @@ class Cord {
InlineRep contents_;
// Helper for GetFlat() and TryFlat().
- static bool GetFlatAux(absl::cord_internal::CordRep* rep,
- absl::string_view* fragment);
+ static bool GetFlatAux(absl::Nonnull<absl::cord_internal::CordRep*> rep,
+ absl::Nonnull<absl::string_view*> fragment);
// Helper for ForEachChunk().
static void ForEachChunkAux(
- absl::cord_internal::CordRep* rep,
+ absl::Nonnull<absl::cord_internal::CordRep*> rep,
absl::FunctionRef<void(absl::string_view)> callback);
// The destructor for non-empty Cords.
void DestroyCordSlow();
// Out-of-line implementation of slower parts of logic.
- void CopyToArraySlowPath(char* dst) const;
+ void CopyToArraySlowPath(absl::Nonnull<char*> dst) const;
int CompareSlowPath(absl::string_view rhs, size_t compared_size,
size_t size_to_compare) const;
int CompareSlowPath(const Cord& rhs, size_t compared_size,
@@ -988,8 +1023,8 @@ class Cord {
// Returns a new reference to contents_.tree(), or steals an existing
// reference if called on an rvalue.
- absl::cord_internal::CordRep* TakeRep() const&;
- absl::cord_internal::CordRep* TakeRep() &&;
+ absl::Nonnull<absl::cord_internal::CordRep*> TakeRep() const&;
+ absl::Nonnull<absl::cord_internal::CordRep*> TakeRep() &&;
// Helper for Append().
template <typename C>
@@ -1026,7 +1061,10 @@ class Cord {
friend class CrcCord;
void SetCrcCordState(crc_internal::CrcCordState state);
- const crc_internal::CrcCordState* MaybeGetCrcCordState() const;
+ absl::Nullable<const crc_internal::CrcCordState*> MaybeGetCrcCordState()
+ const;
+
+ CharIterator FindImpl(CharIterator it, absl::string_view needle) const;
};
ABSL_NAMESPACE_END
@@ -1045,13 +1083,15 @@ namespace cord_internal {
// Does non-template-specific `CordRepExternal` initialization.
// Requires `data` to be non-empty.
-void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep);
+void InitializeCordRepExternal(absl::string_view data,
+ absl::Nonnull<CordRepExternal*> rep);
// Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer
// to it. Requires `data` to be non-empty.
template <typename Releaser>
// NOLINTNEXTLINE - suppress clang-tidy raw pointer return.
-CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) {
+absl::Nonnull<CordRep*> NewExternalRep(absl::string_view data,
+ Releaser&& releaser) {
assert(!data.empty());
using ReleaserType = absl::decay_t<Releaser>;
CordRepExternal* rep = new CordRepExternalImpl<ReleaserType>(
@@ -1063,7 +1103,7 @@ CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) {
// Overload for function reference types that dispatches using a function
// pointer because there are no `alignof()` or `sizeof()` a function reference.
// NOLINTNEXTLINE - suppress clang-tidy raw pointer return.
-inline CordRep* NewExternalRep(absl::string_view data,
+inline absl::Nonnull<CordRep*> NewExternalRep(absl::string_view data,
void (&releaser)(absl::string_view)) {
return NewExternalRep(data, &releaser);
}
@@ -1086,7 +1126,8 @@ Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) {
return cord;
}
-constexpr Cord::InlineRep::InlineRep(absl::string_view sv, CordRep* rep)
+constexpr Cord::InlineRep::InlineRep(absl::string_view sv,
+ absl::Nullable<CordRep*> rep)
: data_(sv, rep) {}
inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src)
@@ -1125,28 +1166,30 @@ inline Cord::InlineRep& Cord::InlineRep::operator=(
return *this;
}
-inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) {
+inline void Cord::InlineRep::Swap(absl::Nonnull<Cord::InlineRep*> rhs) {
if (rhs == this) {
return;
}
std::swap(data_, rhs->data_);
}
-inline const char* Cord::InlineRep::data() const {
+inline absl::Nullable<const char*> Cord::InlineRep::data() const {
return is_tree() ? nullptr : data_.as_chars();
}
-inline const char* Cord::InlineRep::as_chars() const {
+inline absl::Nonnull<const char*> Cord::InlineRep::as_chars() const {
assert(!data_.is_tree());
return data_.as_chars();
}
-inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const {
+inline absl::Nonnull<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 {
+inline absl::Nullable<absl::cord_internal::CordRep*> Cord::InlineRep::tree()
+ const {
if (is_tree()) {
return as_tree();
} else {
@@ -1154,14 +1197,12 @@ inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const {
}
}
-inline bool Cord::InlineRep::empty() const { return data_.is_empty(); }
-
inline size_t Cord::InlineRep::size() const {
return is_tree() ? as_tree()->length : inline_size();
}
-inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity(
- size_t extra) {
+inline absl::Nonnull<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);
@@ -1170,20 +1211,21 @@ inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity(
return result;
}
-inline void Cord::InlineRep::EmplaceTree(CordRep* rep,
+inline void Cord::InlineRep::EmplaceTree(absl::Nonnull<CordRep*> rep,
MethodIdentifier method) {
assert(rep);
data_.make_tree(rep);
CordzInfo::MaybeTrackCord(data_, method);
}
-inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent,
+inline void Cord::InlineRep::EmplaceTree(absl::Nonnull<CordRep*> rep,
+ const InlineData& parent,
MethodIdentifier method) {
data_.make_tree(rep);
CordzInfo::MaybeTrackCord(data_, parent, method);
}
-inline void Cord::InlineRep::SetTree(CordRep* rep,
+inline void Cord::InlineRep::SetTree(absl::Nonnull<CordRep*> rep,
const CordzUpdateScope& scope) {
assert(rep);
assert(data_.is_tree());
@@ -1191,7 +1233,7 @@ inline void Cord::InlineRep::SetTree(CordRep* rep,
scope.SetCordRep(rep);
}
-inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep,
+inline void Cord::InlineRep::SetTreeOrEmpty(absl::Nullable<CordRep*> rep,
const CordzUpdateScope& scope) {
assert(data_.is_tree());
if (rep) {
@@ -1202,7 +1244,8 @@ inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep,
scope.SetCordRep(rep);
}
-inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep,
+inline void Cord::InlineRep::CommitTree(absl::Nullable<const CordRep*> old_rep,
+ absl::Nonnull<CordRep*> rep,
const CordzUpdateScope& scope,
MethodIdentifier method) {
if (old_rep) {
@@ -1212,7 +1255,7 @@ inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep,
}
}
-inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
+inline absl::Nullable<absl::cord_internal::CordRep*> Cord::InlineRep::clear() {
if (is_tree()) {
CordzInfo::MaybeUntrackCord(cordz_info());
}
@@ -1221,7 +1264,7 @@ inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
return result;
}
-inline void Cord::InlineRep::CopyToArray(char* dst) const {
+inline void Cord::InlineRep::CopyToArray(absl::Nonnull<char*> dst) const {
assert(!is_tree());
size_t n = inline_size();
assert(n != 0);
@@ -1402,7 +1445,8 @@ inline bool Cord::StartsWith(absl::string_view rhs) const {
return EqualsImpl(rhs, rhs_size);
}
-inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) {
+inline void Cord::ChunkIterator::InitTree(
+ absl::Nonnull<cord_internal::CordRep*> tree) {
tree = cord_internal::SkipCrcNode(tree);
if (tree->tag == cord_internal::BTREE) {
current_chunk_ = btree_reader_.Init(tree->btree());
@@ -1412,12 +1456,13 @@ inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) {
}
}
-inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) {
+inline Cord::ChunkIterator::ChunkIterator(
+ absl::Nonnull<cord_internal::CordRep*> tree) {
bytes_remaining_ = tree->length;
InitTree(tree);
}
-inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) {
+inline Cord::ChunkIterator::ChunkIterator(absl::Nonnull<const Cord*> cord) {
if (CordRep* tree = cord->contents_.tree()) {
bytes_remaining_ = tree->length;
if (ABSL_PREDICT_TRUE(bytes_remaining_ != 0)) {
@@ -1557,12 +1602,13 @@ inline Cord::CharIterator::pointer Cord::CharIterator::operator->() const {
return chunk_iterator_->data();
}
-inline Cord Cord::AdvanceAndRead(CharIterator* it, size_t n_bytes) {
+inline Cord Cord::AdvanceAndRead(absl::Nonnull<CharIterator*> it,
+ size_t n_bytes) {
assert(it != nullptr);
return it->chunk_iterator_.AdvanceAndReadBytes(n_bytes);
}
-inline void Cord::Advance(CharIterator* it, size_t n_bytes) {
+inline void Cord::Advance(absl::Nonnull<CharIterator*> it, size_t n_bytes) {
assert(it != nullptr);
it->chunk_iterator_.AdvanceBytes(n_bytes);
}
@@ -1618,7 +1664,7 @@ inline bool operator>=(const Cord& x, const Cord& y) {
// Nonmember Cord-to-absl::string_view relational operators.
//
// Due to implicit conversions, these also enable comparisons of Cord with
-// with std::string, ::string, and const char*.
+// std::string and const char*.
inline bool operator==(const Cord& lhs, absl::string_view rhs) {
size_t lhs_size = lhs.size();
size_t rhs_size = rhs.size();
diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc
index e859b0db..19b0fa44 100644
--- a/absl/strings/cord_analysis.cc
+++ b/absl/strings/cord_analysis.cc
@@ -14,23 +14,17 @@
#include "absl/strings/cord_analysis.h"
+#include <cassert>
#include <cstddef>
#include <cstdint>
#include <unordered_set>
-#include "absl/base/attributes.h"
#include "absl/base/config.h"
-#include "absl/container/inlined_vector.h"
+#include "absl/base/nullability.h"
#include "absl/strings/internal/cord_data_edge.h"
#include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cord_rep_btree.h"
#include "absl/strings/internal/cord_rep_crc.h"
-#include "absl/strings/internal/cord_rep_flat.h"
-#include "absl/strings/internal/cord_rep_ring.h"
-//
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-#include "absl/functional/function_ref.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -45,13 +39,15 @@ enum class Mode { kFairShare, kTotal, kTotalMorePrecise };
template <Mode mode>
struct CordRepRef {
// Instantiates a CordRepRef instance.
- explicit CordRepRef(const CordRep* r) : rep(r) {}
+ explicit CordRepRef(absl::Nonnull<const CordRep*> r) : rep(r) {}
// Creates a child reference holding the provided child.
// Overloaded to add cumulative reference count for kFairShare.
- CordRepRef Child(const CordRep* child) const { return CordRepRef(child); }
+ CordRepRef Child(absl::Nonnull<const CordRep*> child) const {
+ return CordRepRef(child);
+ }
- const CordRep* rep;
+ absl::Nonnull<const CordRep*> rep;
};
// RawUsage holds the computed total number of bytes.
@@ -70,11 +66,10 @@ template <>
struct RawUsage<Mode::kTotalMorePrecise> {
size_t total = 0;
// TODO(b/289250880): Replace this with a flat_hash_set.
- std::unordered_set<const CordRep*> counted;
+ std::unordered_set<absl::Nonnull<const CordRep*>> counted;
void Add(size_t size, CordRepRef<Mode::kTotalMorePrecise> repref) {
- if (counted.find(repref.rep) == counted.end()) {
- counted.insert(repref.rep);
+ if (counted.insert(repref.rep).second) {
total += size;
}
}
@@ -95,15 +90,15 @@ double MaybeDiv(double d, refcount_t refcount) {
template <>
struct CordRepRef<Mode::kFairShare> {
// Creates a CordRepRef with the provided rep and top (parent) fraction.
- explicit CordRepRef(const CordRep* r, double frac = 1.0)
+ explicit CordRepRef(absl::Nonnull<const CordRep*> r, double frac = 1.0)
: rep(r), fraction(MaybeDiv(frac, r->refcount.Get())) {}
// Returns a CordRepRef with a fraction of `this->fraction / child.refcount`
- CordRepRef Child(const CordRep* child) const {
+ CordRepRef Child(absl::Nonnull<const CordRep*> child) const {
return CordRepRef(child, fraction);
}
- const CordRep* rep;
+ absl::Nonnull<const CordRep*> rep;
double fraction;
};
@@ -138,16 +133,6 @@ void AnalyzeDataEdge(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
raw_usage.Add(size, rep);
}
-// Computes the memory size of the provided Ring tree.
-template <Mode mode>
-void AnalyzeRing(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
- const CordRepRing* ring = rep.rep->ring();
- raw_usage.Add(CordRepRing::AllocSize(ring->capacity()), rep);
- ring->ForEach([&](CordRepRing::index_type pos) {
- AnalyzeDataEdge(rep.Child(ring->entry_child(pos)), raw_usage);
- });
-}
-
// Computes the memory size of the provided Btree tree.
template <Mode mode>
void AnalyzeBtree(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
@@ -165,7 +150,7 @@ void AnalyzeBtree(CordRepRef<mode> rep, RawUsage<mode>& raw_usage) {
}
template <Mode mode>
-size_t GetEstimatedUsage(const CordRep* rep) {
+size_t GetEstimatedUsage(absl::Nonnull<const CordRep*> rep) {
// Zero initialized memory usage totals.
RawUsage<mode> raw_usage;
@@ -175,6 +160,9 @@ size_t GetEstimatedUsage(const CordRep* rep) {
// Consume the top level CRC node if present.
if (repref.rep->tag == CRC) {
raw_usage.Add(sizeof(CordRepCrc), repref);
+ if (repref.rep->crc()->child == nullptr) {
+ return static_cast<size_t>(raw_usage.total);
+ }
repref = repref.Child(repref.rep->crc()->child);
}
@@ -182,8 +170,6 @@ size_t GetEstimatedUsage(const CordRep* rep) {
AnalyzeDataEdge(repref, raw_usage);
} else if (repref.rep->tag == BTREE) {
AnalyzeBtree(repref, raw_usage);
- } else if (repref.rep->tag == RING) {
- AnalyzeRing(repref, raw_usage);
} else {
assert(false);
}
@@ -193,15 +179,15 @@ size_t GetEstimatedUsage(const CordRep* rep) {
} // namespace
-size_t GetEstimatedMemoryUsage(const CordRep* rep) {
+size_t GetEstimatedMemoryUsage(absl::Nonnull<const CordRep*> rep) {
return GetEstimatedUsage<Mode::kTotal>(rep);
}
-size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep) {
+size_t GetEstimatedFairShareMemoryUsage(absl::Nonnull<const CordRep*> rep) {
return GetEstimatedUsage<Mode::kFairShare>(rep);
}
-size_t GetMorePreciseMemoryUsage(const CordRep* rep) {
+size_t GetMorePreciseMemoryUsage(absl::Nonnull<const CordRep*> rep) {
return GetEstimatedUsage<Mode::kTotalMorePrecise>(rep);
}
diff --git a/absl/strings/cord_analysis.h b/absl/strings/cord_analysis.h
index 9b9527a5..f8ce3489 100644
--- a/absl/strings/cord_analysis.h
+++ b/absl/strings/cord_analysis.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include "absl/base/config.h"
+#include "absl/base/nullability.h"
#include "absl/strings/internal/cord_internal.h"
namespace absl {
@@ -28,7 +29,7 @@ namespace cord_internal {
// Returns the *approximate* number of bytes held in full or in part by this
// Cord (which may not remain the same between invocations). Cords that share
// memory could each be "charged" independently for the same shared memory.
-size_t GetEstimatedMemoryUsage(const CordRep* rep);
+size_t GetEstimatedMemoryUsage(absl::Nonnull<const CordRep*> rep);
// Returns the *approximate* number of bytes held in full or in part by this
// Cord for the distinct memory held by this cord. This is similar to
@@ -46,13 +47,13 @@ size_t GetEstimatedMemoryUsage(const CordRep* rep);
//
// This is more expensive than `GetEstimatedMemoryUsage()` as it requires
// deduplicating all memory references.
-size_t GetMorePreciseMemoryUsage(const CordRep* rep);
+size_t GetMorePreciseMemoryUsage(absl::Nonnull<const CordRep*> rep);
// Returns the *approximate* number of bytes held in full or in part by this
// CordRep weighted by the sharing ratio of that data. For example, if some data
// edge is shared by 4 different Cords, then each cord is attribute 1/4th of
// the total memory usage as a 'fair share' of the total memory usage.
-size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep);
+size_t GetEstimatedFairShareMemoryUsage(absl::Nonnull<const CordRep*> rep);
} // namespace cord_internal
ABSL_NAMESPACE_END
diff --git a/absl/strings/cord_buffer_test.cc b/absl/strings/cord_buffer_test.cc
index 5c7437ae..ab628081 100644
--- a/absl/strings/cord_buffer_test.cc
+++ b/absl/strings/cord_buffer_test.cc
@@ -16,16 +16,18 @@
#include <algorithm>
-#include <climits>
#include <cstring>
+#include <limits>
#include <string>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/config.h"
+#include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cord_rep_flat.h"
#include "absl/strings/internal/cord_rep_test_util.h"
+#include "absl/strings/string_view.h"
#include "absl/types/span.h"
using testing::Eq;
diff --git a/absl/strings/cord_ring_reader_test.cc b/absl/strings/cord_ring_reader_test.cc
deleted file mode 100644
index 8e7183bf..00000000
--- a/absl/strings/cord_ring_reader_test.cc
+++ /dev/null
@@ -1,180 +0,0 @@
-// 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/absl/strings/cord_ring_test.cc b/absl/strings/cord_ring_test.cc
deleted file mode 100644
index f39a0a4f..00000000
--- a/absl/strings/cord_ring_test.cc
+++ /dev/null
@@ -1,1454 +0,0 @@
-// 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::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);
-}
-
-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"};
-
-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(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/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
index 36e397ed..f1a5f39c 100644
--- a/absl/strings/cord_test.cc
+++ b/absl/strings/cord_test.cc
@@ -15,33 +15,50 @@
#include "absl/strings/cord.h"
#include <algorithm>
-#include <climits>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
#include <cstdio>
+#include <cstring>
+#include <iostream>
#include <iterator>
-#include <map>
-#include <numeric>
+#include <limits>
#include <random>
+#include <set>
#include <sstream>
+#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/endian.h"
#include "absl/base/macros.h"
+#include "absl/base/options.h"
#include "absl/container/fixed_array.h"
+#include "absl/functional/function_ref.h"
#include "absl/hash/hash.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/random/random.h"
+#include "absl/strings/cord_buffer.h"
#include "absl/strings/cord_test_helpers.h"
#include "absl/strings/cordz_test_helpers.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_crc.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+#include "absl/strings/internal/cordz_statistics.h"
+#include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/strings/internal/string_constant.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
// convenience local constants
static constexpr auto FLAT = absl::cord_internal::FLAT;
@@ -484,6 +501,93 @@ TEST_P(CordTest, StartsEndsWith) {
ASSERT_TRUE(!empty.EndsWith("xyz"));
}
+TEST_P(CordTest, Contains) {
+ auto flat_haystack = absl::Cord("this is a flat cord");
+ auto fragmented_haystack = absl::MakeFragmentedCord(
+ {"this", " ", "is", " ", "a", " ", "fragmented", " ", "cord"});
+
+ EXPECT_TRUE(flat_haystack.Contains(""));
+ EXPECT_TRUE(fragmented_haystack.Contains(""));
+ EXPECT_TRUE(flat_haystack.Contains(absl::Cord("")));
+ EXPECT_TRUE(fragmented_haystack.Contains(absl::Cord("")));
+ EXPECT_TRUE(absl::Cord("").Contains(""));
+ EXPECT_TRUE(absl::Cord("").Contains(absl::Cord("")));
+ EXPECT_FALSE(absl::Cord("").Contains(flat_haystack));
+ EXPECT_FALSE(absl::Cord("").Contains(fragmented_haystack));
+
+ EXPECT_FALSE(flat_haystack.Contains("z"));
+ EXPECT_FALSE(fragmented_haystack.Contains("z"));
+ EXPECT_FALSE(flat_haystack.Contains(absl::Cord("z")));
+ EXPECT_FALSE(fragmented_haystack.Contains(absl::Cord("z")));
+
+ EXPECT_FALSE(flat_haystack.Contains("is an"));
+ EXPECT_FALSE(fragmented_haystack.Contains("is an"));
+ EXPECT_FALSE(flat_haystack.Contains(absl::Cord("is an")));
+ EXPECT_FALSE(fragmented_haystack.Contains(absl::Cord("is an")));
+ EXPECT_FALSE(
+ flat_haystack.Contains(absl::MakeFragmentedCord({"is", " ", "an"})));
+ EXPECT_FALSE(fragmented_haystack.Contains(
+ absl::MakeFragmentedCord({"is", " ", "an"})));
+
+ EXPECT_TRUE(flat_haystack.Contains("is a"));
+ EXPECT_TRUE(fragmented_haystack.Contains("is a"));
+ EXPECT_TRUE(flat_haystack.Contains(absl::Cord("is a")));
+ EXPECT_TRUE(fragmented_haystack.Contains(absl::Cord("is a")));
+ EXPECT_TRUE(
+ flat_haystack.Contains(absl::MakeFragmentedCord({"is", " ", "a"})));
+ EXPECT_TRUE(
+ fragmented_haystack.Contains(absl::MakeFragmentedCord({"is", " ", "a"})));
+}
+
+TEST_P(CordTest, Find) {
+ auto flat_haystack = absl::Cord("this is a flat cord");
+ auto fragmented_haystack = absl::MakeFragmentedCord(
+ {"this", " ", "is", " ", "a", " ", "fragmented", " ", "cord"});
+ auto empty_haystack = absl::Cord("");
+
+ EXPECT_EQ(flat_haystack.Find(""), flat_haystack.char_begin());
+ EXPECT_EQ(fragmented_haystack.Find(""), fragmented_haystack.char_begin());
+ EXPECT_EQ(flat_haystack.Find(absl::Cord("")), flat_haystack.char_begin());
+ EXPECT_EQ(fragmented_haystack.Find(absl::Cord("")),
+ fragmented_haystack.char_begin());
+ EXPECT_EQ(empty_haystack.Find(""), empty_haystack.char_begin());
+ EXPECT_EQ(empty_haystack.Find(absl::Cord("")), empty_haystack.char_begin());
+ EXPECT_EQ(empty_haystack.Find(flat_haystack), empty_haystack.char_end());
+ EXPECT_EQ(empty_haystack.Find(fragmented_haystack),
+ empty_haystack.char_end());
+
+ EXPECT_EQ(flat_haystack.Find("z"), flat_haystack.char_end());
+ EXPECT_EQ(fragmented_haystack.Find("z"), fragmented_haystack.char_end());
+ EXPECT_EQ(flat_haystack.Find(absl::Cord("z")), flat_haystack.char_end());
+ EXPECT_EQ(fragmented_haystack.Find(absl::Cord("z")),
+ fragmented_haystack.char_end());
+
+ EXPECT_EQ(flat_haystack.Find("is an"), flat_haystack.char_end());
+ EXPECT_EQ(fragmented_haystack.Find("is an"), fragmented_haystack.char_end());
+ EXPECT_EQ(flat_haystack.Find(absl::Cord("is an")), flat_haystack.char_end());
+ EXPECT_EQ(fragmented_haystack.Find(absl::Cord("is an")),
+ fragmented_haystack.char_end());
+ EXPECT_EQ(flat_haystack.Find(absl::MakeFragmentedCord({"is", " ", "an"})),
+ flat_haystack.char_end());
+ EXPECT_EQ(
+ fragmented_haystack.Find(absl::MakeFragmentedCord({"is", " ", "an"})),
+ fragmented_haystack.char_end());
+
+ EXPECT_EQ(flat_haystack.Find("is a"),
+ std::next(flat_haystack.char_begin(), 5));
+ EXPECT_EQ(fragmented_haystack.Find("is a"),
+ std::next(fragmented_haystack.char_begin(), 5));
+ EXPECT_EQ(flat_haystack.Find(absl::Cord("is a")),
+ std::next(flat_haystack.char_begin(), 5));
+ EXPECT_EQ(fragmented_haystack.Find(absl::Cord("is a")),
+ std::next(fragmented_haystack.char_begin(), 5));
+ EXPECT_EQ(flat_haystack.Find(absl::MakeFragmentedCord({"is", " ", "a"})),
+ std::next(flat_haystack.char_begin(), 5));
+ EXPECT_EQ(
+ fragmented_haystack.Find(absl::MakeFragmentedCord({"is", " ", "a"})),
+ std::next(fragmented_haystack.char_begin(), 5));
+}
+
TEST_P(CordTest, Subcord) {
RandomEngine rng(GTEST_FLAG_GET(random_seed));
const std::string s = RandomLowercaseString(&rng, 1024);
@@ -2006,8 +2110,6 @@ 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;
@@ -2554,6 +2656,13 @@ TEST_P(CordTest, Format) {
EXPECT_EQ(c, "There were 0003 little pigs.And 1 bad wolf!");
}
+TEST_P(CordTest, Stringify) {
+ absl::Cord c =
+ absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
+ MaybeHarden(c);
+ EXPECT_EQ(absl::StrCat(c), "A small fragmented Cord.");
+}
+
TEST_P(CordTest, Hardening) {
absl::Cord cord("hello");
MaybeHarden(cord);
@@ -3138,6 +3247,12 @@ TEST_P(CordTest, ChecksummedEmptyCord) {
EXPECT_EQ(absl::HashOf(c3), absl::HashOf(absl::string_view()));
}
+TEST(CrcCordTest, ChecksummedEmptyCordEstimateMemoryUsage) {
+ absl::Cord cord;
+ cord.SetExpectedChecksum(0);
+ EXPECT_NE(cord.EstimatedMemoryUsage(), 0);
+}
+
#if defined(GTEST_HAS_DEATH_TEST) && defined(ABSL_INTERNAL_CORD_HAVE_SANITIZER)
// Returns an expected poison / uninitialized death message expression.
diff --git a/absl/strings/cordz_test.cc b/absl/strings/cordz_test.cc
index 2b7d30b0..a35493e3 100644
--- a/absl/strings/cordz_test.cc
+++ b/absl/strings/cordz_test.cc
@@ -12,18 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <cstdint>
+#include <cstddef>
+#include <cstring>
+#include <ostream>
#include <string>
+#include <utility>
#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_buffer.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/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"
diff --git a/absl/strings/cordz_test_helpers.h b/absl/strings/cordz_test_helpers.h
index e410eecf..619f13c2 100644
--- a/absl/strings/cordz_test_helpers.h
+++ b/absl/strings/cordz_test_helpers.h
@@ -21,6 +21,7 @@
#include "gtest/gtest.h"
#include "absl/base/config.h"
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
#include "absl/strings/cord.h"
#include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cordz_info.h"
@@ -33,15 +34,16 @@ 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(
+inline absl::Nullable<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 = {}) {
+inline bool CordzInfoIsListed(
+ absl::Nonnull<const cord_internal::CordzInfo*> cordz_info,
+ cord_internal::CordzSampleToken token = {}) {
for (const cord_internal::CordzInfo& info : token) {
if (cordz_info == &info) return true;
}
@@ -119,7 +121,7 @@ class CordzSamplingIntervalHelper {
// Wrapper struct managing a small CordRep `rep`
struct TestCordRep {
- cord_internal::CordRepFlat* rep;
+ absl::Nonnull<cord_internal::CordRepFlat*> rep;
TestCordRep() {
rep = cord_internal::CordRepFlat::New(100);
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index 2827fbaa..1c0eac42 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -16,21 +16,22 @@
#include <algorithm>
#include <cassert>
+#include <cstddef>
#include <cstdint>
#include <cstring>
-#include <iterator>
#include <limits>
#include <string>
-#include "absl/base/internal/endian.h"
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/unaligned_access.h"
-#include "absl/strings/internal/char_map.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/charset.h"
#include "absl/strings/internal/escaping.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/utf8.h"
+#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
-#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
namespace absl {
diff --git a/absl/strings/escaping_benchmark.cc b/absl/strings/escaping_benchmark.cc
index 10d5b033..f792226a 100644
--- a/absl/strings/escaping_benchmark.cc
+++ b/absl/strings/escaping_benchmark.cc
@@ -14,13 +14,17 @@
#include "absl/strings/escaping.h"
+#include <cstdint>
#include <cstdio>
#include <cstring>
+#include <memory>
#include <random>
+#include <string>
#include "benchmark/benchmark.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/internal/escaping_test_common.h"
+#include "absl/strings/str_cat.h"
namespace {
diff --git a/absl/strings/escaping_test.cc b/absl/strings/escaping_test.cc
index 9f62c1ee..ca1ee45c 100644
--- a/absl/strings/escaping_test.cc
+++ b/absl/strings/escaping_test.cc
@@ -15,17 +15,20 @@
#include "absl/strings/escaping.h"
#include <array>
+#include <cstddef>
#include <cstdio>
#include <cstring>
+#include <initializer_list>
#include <memory>
+#include <string>
#include <vector>
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "absl/container/fixed_array.h"
+#include "absl/log/check.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/internal/escaping_test_common.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/strings/has_absl_stringify.h b/absl/strings/has_absl_stringify.h
new file mode 100644
index 00000000..274a7865
--- /dev/null
+++ b/absl/strings/has_absl_stringify.h
@@ -0,0 +1,63 @@
+// Copyright 2022 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_HAS_ABSL_STRINGIFY_H_
+#define ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_
+
+#include <type_traits>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace strings_internal {
+
+// This is an empty class not intended to be used. It exists so that
+// `HasAbslStringify` can reference a universal class rather than needing to be
+// copied for each new sink.
+class UnimplementedSink {
+ public:
+ void Append(size_t count, char ch);
+
+ void Append(string_view v);
+
+ // Support `absl::Format(&sink, format, args...)`.
+ friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
+};
+
+} // namespace strings_internal
+
+// `HasAbslStringify<T>` detects if type `T` supports the `AbslStringify()`
+// customization point (see
+// https://abseil.io/docs/cpp/guides/format#abslstringify for the
+// documentation).
+//
+// Note that there are types that can be `StrCat`-ed that do not use the
+// `AbslStringify` customization point (for example, `int`).
+
+template <typename T, typename = void>
+struct HasAbslStringify : std::false_type {};
+
+template <typename T>
+struct HasAbslStringify<
+ T, std::enable_if_t<std::is_void<decltype(AbslStringify(
+ std::declval<strings_internal::UnimplementedSink&>(),
+ std::declval<const T&>()))>::value>> : std::true_type {};
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_
diff --git a/absl/strings/has_absl_stringify_test.cc b/absl/strings/has_absl_stringify_test.cc
new file mode 100644
index 00000000..59e7e1d3
--- /dev/null
+++ b/absl/strings/has_absl_stringify_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2023 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/has_absl_stringify.h"
+
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/types/optional.h"
+
+namespace {
+
+struct TypeWithoutAbslStringify {};
+
+struct TypeWithAbslStringify {
+ template <typename Sink>
+ friend void AbslStringify(Sink&, const TypeWithAbslStringify&) {}
+};
+
+TEST(HasAbslStringifyTest, Works) {
+ EXPECT_FALSE(absl::HasAbslStringify<int>::value);
+ EXPECT_FALSE(absl::HasAbslStringify<std::string>::value);
+ EXPECT_FALSE(absl::HasAbslStringify<TypeWithoutAbslStringify>::value);
+ EXPECT_TRUE(absl::HasAbslStringify<TypeWithAbslStringify>::value);
+ EXPECT_FALSE(
+ absl::HasAbslStringify<absl::optional<TypeWithAbslStringify>>::value);
+}
+
+} // namespace
diff --git a/absl/strings/has_ostream_operator.h b/absl/strings/has_ostream_operator.h
new file mode 100644
index 00000000..156ffc74
--- /dev/null
+++ b/absl/strings/has_ostream_operator.h
@@ -0,0 +1,42 @@
+// Copyright 2023 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_HAS_OSTREAM_OPERATOR_H_
+#define ABSL_STRINGS_HAS_OSTREAM_OPERATOR_H_
+
+#include <ostream>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// Detects if type `T` supports streaming to `std::ostream`s with `operator<<`.
+
+template <typename T, typename = void>
+struct HasOstreamOperator : std::false_type {};
+
+template <typename T>
+struct HasOstreamOperator<
+ T, std::enable_if_t<std::is_same<
+ std::ostream&, decltype(std::declval<std::ostream&>()
+ << std::declval<const T&>())>::value>>
+ : std::true_type {};
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_HAS_OSTREAM_OPERATOR_H_
diff --git a/absl/strings/has_ostream_operator_test.cc b/absl/strings/has_ostream_operator_test.cc
new file mode 100644
index 00000000..9ef41366
--- /dev/null
+++ b/absl/strings/has_ostream_operator_test.cc
@@ -0,0 +1,41 @@
+// Copyright 2023 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/has_ostream_operator.h"
+
+#include <ostream>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "absl/types/optional.h"
+
+namespace {
+
+struct TypeWithoutOstreamOp {};
+
+struct TypeWithOstreamOp {
+ friend std::ostream& operator<<(std::ostream& os, const TypeWithOstreamOp&) {
+ return os;
+ }
+};
+
+TEST(HasOstreamOperatorTest, Works) {
+ EXPECT_TRUE(absl::HasOstreamOperator<int>::value);
+ EXPECT_TRUE(absl::HasOstreamOperator<std::string>::value);
+ EXPECT_FALSE(absl::HasOstreamOperator<absl::optional<int>>::value);
+ EXPECT_FALSE(absl::HasOstreamOperator<TypeWithoutOstreamOp>::value);
+ EXPECT_TRUE(absl::HasOstreamOperator<TypeWithOstreamOp>::value);
+}
+
+} // namespace
diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h
deleted file mode 100644
index 70a90343..00000000
--- a/absl/strings/internal/char_map.h
+++ /dev/null
@@ -1,158 +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.
-//
-// Character Map Class
-//
-// A fast, bit-vector map for 8-bit unsigned characters.
-// This class is useful for non-character purposes as well.
-
-#ifndef ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
-#define ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-
-#include "absl/base/macros.h"
-#include "absl/base/port.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace strings_internal {
-
-class Charmap {
- public:
- constexpr Charmap() : m_() {}
-
- // Initializes with a given char*. Note that NUL is not treated as
- // a terminator, but rather a char to be flicked.
- Charmap(const char* str, int len) : m_() {
- while (len--) SetChar(*str++);
- }
-
- // Initializes with a given char*. NUL is treated as a terminator
- // and will not be in the charmap.
- explicit Charmap(const char* str) : m_() {
- while (*str) SetChar(*str++);
- }
-
- constexpr bool contains(unsigned char c) const {
- return (m_[c / 64] >> (c % 64)) & 0x1;
- }
-
- // Returns true if and only if a character exists in both maps.
- bool IntersectsWith(const Charmap& c) const {
- for (size_t i = 0; i < ABSL_ARRAYSIZE(m_); ++i) {
- if ((m_[i] & c.m_[i]) != 0) return true;
- }
- return false;
- }
-
- bool IsZero() const {
- for (uint64_t c : m_) {
- if (c != 0) return false;
- }
- return true;
- }
-
- // Containing only a single specified char.
- static constexpr Charmap Char(char x) {
- return Charmap(CharMaskForWord(x, 0), CharMaskForWord(x, 1),
- CharMaskForWord(x, 2), CharMaskForWord(x, 3));
- }
-
- // Containing all the chars in the C-string 's'.
- static constexpr Charmap FromString(const char* s) {
- Charmap ret;
- while (*s) ret = ret | Char(*s++);
- return ret;
- }
-
- // Containing all the chars in the closed interval [lo,hi].
- static constexpr Charmap Range(char lo, char hi) {
- return Charmap(RangeForWord(lo, hi, 0), RangeForWord(lo, hi, 1),
- RangeForWord(lo, hi, 2), RangeForWord(lo, hi, 3));
- }
-
- friend constexpr Charmap operator&(const Charmap& a, const Charmap& b) {
- return Charmap(a.m_[0] & b.m_[0], a.m_[1] & b.m_[1], a.m_[2] & b.m_[2],
- a.m_[3] & b.m_[3]);
- }
-
- friend constexpr Charmap operator|(const Charmap& a, const Charmap& b) {
- return Charmap(a.m_[0] | b.m_[0], a.m_[1] | b.m_[1], a.m_[2] | b.m_[2],
- a.m_[3] | b.m_[3]);
- }
-
- friend constexpr Charmap operator~(const Charmap& a) {
- return Charmap(~a.m_[0], ~a.m_[1], ~a.m_[2], ~a.m_[3]);
- }
-
- private:
- constexpr Charmap(uint64_t b0, uint64_t b1, uint64_t b2, uint64_t b3)
- : m_{b0, b1, b2, b3} {}
-
- static constexpr uint64_t RangeForWord(char lo, char hi, uint64_t word) {
- return OpenRangeFromZeroForWord(static_cast<unsigned char>(hi) + 1, word) &
- ~OpenRangeFromZeroForWord(static_cast<unsigned char>(lo), word);
- }
-
- // All the chars in the specified word of the range [0, upper).
- static constexpr uint64_t OpenRangeFromZeroForWord(uint64_t upper,
- uint64_t word) {
- return (upper <= 64 * word)
- ? 0
- : (upper >= 64 * (word + 1))
- ? ~static_cast<uint64_t>(0)
- : (~static_cast<uint64_t>(0) >> (64 - upper % 64));
- }
-
- static constexpr uint64_t CharMaskForWord(char x, uint64_t word) {
- const auto unsigned_x = static_cast<unsigned char>(x);
- return (unsigned_x / 64 == word)
- ? (static_cast<uint64_t>(1) << (unsigned_x % 64))
- : 0;
- }
-
- void SetChar(char c) {
- const auto unsigned_c = static_cast<unsigned char>(c);
- m_[unsigned_c / 64] |= static_cast<uint64_t>(1) << (unsigned_c % 64);
- }
-
- uint64_t m_[4];
-};
-
-// Mirror the char-classifying predicates in <cctype>
-constexpr Charmap UpperCharmap() { return Charmap::Range('A', 'Z'); }
-constexpr Charmap LowerCharmap() { return Charmap::Range('a', 'z'); }
-constexpr Charmap DigitCharmap() { return Charmap::Range('0', '9'); }
-constexpr Charmap AlphaCharmap() { return LowerCharmap() | UpperCharmap(); }
-constexpr Charmap AlnumCharmap() { return DigitCharmap() | AlphaCharmap(); }
-constexpr Charmap XDigitCharmap() {
- return DigitCharmap() | Charmap::Range('A', 'F') | Charmap::Range('a', 'f');
-}
-constexpr Charmap PrintCharmap() { return Charmap::Range(0x20, 0x7e); }
-constexpr Charmap SpaceCharmap() { return Charmap::FromString("\t\n\v\f\r "); }
-constexpr Charmap CntrlCharmap() {
- return Charmap::Range(0, 0x7f) & ~PrintCharmap();
-}
-constexpr Charmap BlankCharmap() { return Charmap::FromString("\t "); }
-constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); }
-constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); }
-
-} // namespace strings_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-#endif // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
diff --git a/absl/strings/internal/char_map_test.cc b/absl/strings/internal/char_map_test.cc
deleted file mode 100644
index d3306241..00000000
--- a/absl/strings/internal/char_map_test.cc
+++ /dev/null
@@ -1,172 +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 "absl/strings/internal/char_map.h"
-
-#include <cctype>
-#include <string>
-#include <vector>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace {
-
-constexpr absl::strings_internal::Charmap everything_map =
- ~absl::strings_internal::Charmap();
-constexpr absl::strings_internal::Charmap nothing_map{};
-
-TEST(Charmap, AllTests) {
- const absl::strings_internal::Charmap also_nothing_map("", 0);
- ASSERT_TRUE(everything_map.contains('\0'));
- ASSERT_TRUE(!nothing_map.contains('\0'));
- ASSERT_TRUE(!also_nothing_map.contains('\0'));
- for (unsigned char ch = 1; ch != 0; ++ch) {
- ASSERT_TRUE(everything_map.contains(ch));
- ASSERT_TRUE(!nothing_map.contains(ch));
- ASSERT_TRUE(!also_nothing_map.contains(ch));
- }
-
- const absl::strings_internal::Charmap symbols("&@#@^!@?", 5);
- ASSERT_TRUE(symbols.contains('&'));
- ASSERT_TRUE(symbols.contains('@'));
- ASSERT_TRUE(symbols.contains('#'));
- ASSERT_TRUE(symbols.contains('^'));
- ASSERT_TRUE(!symbols.contains('!'));
- ASSERT_TRUE(!symbols.contains('?'));
- int cnt = 0;
- for (unsigned char ch = 1; ch != 0; ++ch)
- cnt += symbols.contains(ch);
- ASSERT_EQ(cnt, 4);
-
- const absl::strings_internal::Charmap lets("^abcde", 3);
- const absl::strings_internal::Charmap lets2("fghij\0klmnop", 10);
- const absl::strings_internal::Charmap lets3("fghij\0klmnop");
- ASSERT_TRUE(lets2.contains('k'));
- ASSERT_TRUE(!lets3.contains('k'));
-
- ASSERT_TRUE(symbols.IntersectsWith(lets));
- ASSERT_TRUE(!lets2.IntersectsWith(lets));
- ASSERT_TRUE(lets.IntersectsWith(symbols));
- ASSERT_TRUE(!lets.IntersectsWith(lets2));
-
- ASSERT_TRUE(nothing_map.IsZero());
- ASSERT_TRUE(!lets.IsZero());
-}
-
-namespace {
-std::string Members(const absl::strings_internal::Charmap& m) {
- std::string r;
- for (size_t i = 0; i < 256; ++i)
- if (m.contains(i)) r.push_back(i);
- return r;
-}
-
-std::string ClosedRangeString(unsigned char lo, unsigned char hi) {
- // Don't depend on lo<hi. Just increment until lo==hi.
- std::string s;
- while (true) {
- s.push_back(lo);
- if (lo == hi) break;
- ++lo;
- }
- return s;
-}
-
-} // namespace
-
-TEST(Charmap, Constexpr) {
- constexpr absl::strings_internal::Charmap kEmpty = nothing_map;
- EXPECT_THAT(Members(kEmpty), "");
- constexpr absl::strings_internal::Charmap kA =
- absl::strings_internal::Charmap::Char('A');
- EXPECT_THAT(Members(kA), "A");
- constexpr absl::strings_internal::Charmap kAZ =
- absl::strings_internal::Charmap::Range('A', 'Z');
- EXPECT_THAT(Members(kAZ), "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
- constexpr absl::strings_internal::Charmap kIdentifier =
- absl::strings_internal::Charmap::Range('0', '9') |
- absl::strings_internal::Charmap::Range('A', 'Z') |
- absl::strings_internal::Charmap::Range('a', 'z') |
- absl::strings_internal::Charmap::Char('_');
- EXPECT_THAT(Members(kIdentifier),
- "0123456789"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "_"
- "abcdefghijklmnopqrstuvwxyz");
- constexpr absl::strings_internal::Charmap kAll = everything_map;
- for (size_t i = 0; i < 256; ++i) {
- EXPECT_TRUE(kAll.contains(i)) << i;
- }
- constexpr absl::strings_internal::Charmap kHello =
- absl::strings_internal::Charmap::FromString("Hello, world!");
- EXPECT_THAT(Members(kHello), " !,Hdelorw");
-
- // test negation and intersection
- constexpr absl::strings_internal::Charmap kABC =
- absl::strings_internal::Charmap::Range('A', 'Z') &
- ~absl::strings_internal::Charmap::Range('D', 'Z');
- EXPECT_THAT(Members(kABC), "ABC");
-}
-
-TEST(Charmap, Range) {
- // Exhaustive testing takes too long, so test some of the boundaries that
- // are perhaps going to cause trouble.
- std::vector<size_t> poi = {0, 1, 2, 3, 4, 7, 8, 9, 15,
- 16, 17, 30, 31, 32, 33, 63, 64, 65,
- 127, 128, 129, 223, 224, 225, 254, 255};
- for (auto lo = poi.begin(); lo != poi.end(); ++lo) {
- SCOPED_TRACE(*lo);
- for (auto hi = lo; hi != poi.end(); ++hi) {
- SCOPED_TRACE(*hi);
- EXPECT_THAT(Members(absl::strings_internal::Charmap::Range(*lo, *hi)),
- ClosedRangeString(*lo, *hi));
- }
- }
-}
-
-bool AsBool(int x) { return static_cast<bool>(x); }
-
-TEST(CharmapCtype, Match) {
- for (int c = 0; c < 256; ++c) {
- SCOPED_TRACE(c);
- SCOPED_TRACE(static_cast<char>(c));
- EXPECT_EQ(AsBool(std::isupper(c)),
- absl::strings_internal::UpperCharmap().contains(c));
- EXPECT_EQ(AsBool(std::islower(c)),
- absl::strings_internal::LowerCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isdigit(c)),
- absl::strings_internal::DigitCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isalpha(c)),
- absl::strings_internal::AlphaCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isalnum(c)),
- absl::strings_internal::AlnumCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isxdigit(c)),
- absl::strings_internal::XDigitCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isprint(c)),
- absl::strings_internal::PrintCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isspace(c)),
- absl::strings_internal::SpaceCharmap().contains(c));
- EXPECT_EQ(AsBool(std::iscntrl(c)),
- absl::strings_internal::CntrlCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isblank(c)),
- absl::strings_internal::BlankCharmap().contains(c));
- EXPECT_EQ(AsBool(std::isgraph(c)),
- absl::strings_internal::GraphCharmap().contains(c));
- EXPECT_EQ(AsBool(std::ispunct(c)),
- absl::strings_internal::PunctCharmap().contains(c));
- }
-}
-
-} // namespace
diff --git a/absl/strings/internal/cord_internal.cc b/absl/strings/internal/cord_internal.cc
index b7874385..57d9d385 100644
--- a/absl/strings/internal/cord_internal.cc
+++ b/absl/strings/internal/cord_internal.cc
@@ -22,15 +22,12 @@
#include "absl/strings/internal/cord_rep_btree.h"
#include "absl/strings/internal/cord_rep_crc.h"
#include "absl/strings/internal/cord_rep_flat.h"
-#include "absl/strings/internal/cord_rep_ring.h"
#include "absl/strings/str_cat.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace cord_internal {
-ABSL_CONST_INIT std::atomic<bool> cord_ring_buffer_enabled(
- kCordEnableRingBufferDefault);
ABSL_CONST_INIT std::atomic<bool> shallow_subcords_enabled(
kCordShallowSubcordsDefault);
@@ -47,9 +44,6 @@ void CordRep::Destroy(CordRep* rep) {
if (rep->tag == BTREE) {
CordRepBtree::Destroy(rep->btree());
return;
- } else if (rep->tag == RING) {
- CordRepRing::Destroy(rep->ring());
- return;
} else if (rep->tag == EXTERNAL) {
CordRepExternal::Delete(rep);
return;
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index 20dd008c..8744540e 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -55,24 +55,15 @@ struct CordRepExternal;
struct CordRepFlat;
struct CordRepSubstring;
struct CordRepCrc;
-class CordRepRing;
class CordRepBtree;
class CordzInfo;
// Default feature enable states for cord ring buffers
-enum CordFeatureDefaults {
- kCordEnableRingBufferDefault = false,
- kCordShallowSubcordsDefault = false
-};
+enum CordFeatureDefaults { kCordShallowSubcordsDefault = false };
-extern std::atomic<bool> cord_ring_buffer_enabled;
extern std::atomic<bool> shallow_subcords_enabled;
-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);
}
@@ -110,8 +101,16 @@ inline void SmallMemmove(char* dst, const char* src, size_t n) {
if (nullify_tail) {
memset(dst + 7, 0, 8);
}
+ // GCC 12 has a false-positive -Wstringop-overflow warning here.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
memcpy(dst, &buf1, 8);
memcpy(dst + n - 8, &buf2, 8);
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic pop
+#endif
} else if (n >= 4) {
uint32_t buf1;
uint32_t buf2;
@@ -158,18 +157,18 @@ class RefcountAndFlags {
// false. Always returns false when the immortal bit is set.
inline bool Decrement() {
int32_t refcount = count_.load(std::memory_order_acquire);
- assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag);
+ assert(refcount > 0 || refcount & kImmortalFlag);
return refcount != kRefIncrement &&
- (count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) &
- kHighRefcountMask) != 0;
+ count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel) !=
+ kRefIncrement;
}
// Same as Decrement but expect that refcount is greater than 1.
inline bool DecrementExpectHighRefcount() {
int32_t refcount =
count_.fetch_sub(kRefIncrement, std::memory_order_acq_rel);
- assert((refcount & kRefcountMask) > 0 || refcount & kImmortalFlag);
- return (refcount & kHighRefcountMask) != 0;
+ assert(refcount > 0 || refcount & kImmortalFlag);
+ return refcount != kRefIncrement;
}
// Returns the current reference count using acquire semantics.
@@ -185,10 +184,9 @@ class RefcountAndFlags {
// 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. Always returns false when the immortal bit is set.
+ // object. Always returns false when the immortal bit is set.
inline bool IsOne() {
- return (count_.load(std::memory_order_acquire) & kRefcountMask) ==
- kRefIncrement;
+ return count_.load(std::memory_order_acquire) == kRefIncrement;
}
bool IsImmortal() const {
@@ -196,32 +194,15 @@ class RefcountAndFlags {
}
private:
- // We reserve the bottom bits for flags.
+ // We reserve the bottom bit for flag.
// 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 Flags {
- kNumFlags = 2,
+ kNumFlags = 1,
kImmortalFlag = 0x1,
- kReservedFlag = 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 = ~kReservedFlag,
-
- // Bitmask to use when checking if refcount is equal to 1 and not
- // immortal when decrementing the refcount. This masks out kRefIncrement and
- // all flags except kImmortalFlag. If the masked RefcountAndFlags is 0, we
- // assume the refcount is equal to 1, since we know it's not immortal and
- // not greater than 1. If the masked RefcountAndFlags is not 0, we can
- // assume the refcount is not equal to 1 since either a higher bit in the
- // refcount is set, or kImmortal is set.
- kHighRefcountMask = kRefcountMask & ~kRefIncrement,
};
std::atomic<int32_t> count_;
@@ -233,7 +214,7 @@ enum CordRepKind {
SUBSTRING = 1,
CRC = 2,
BTREE = 3,
- RING = 4,
+ UNUSED_4 = 4,
EXTERNAL = 5,
// We have different tags for different sized flat arrays,
@@ -252,12 +233,8 @@ enum CordRepKind {
// 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 {
@@ -301,15 +278,12 @@ struct CordRep {
// # LINT.ThenChange(cord_rep_btree.h:copy_raw)
// Returns true if this instance's tag matches the requested type.
- constexpr bool IsRing() const { return tag == RING; }
constexpr bool IsSubstring() const { return tag == SUBSTRING; }
constexpr bool IsCrc() const { return tag == CRC; }
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 CordRepSubstring* substring();
inline const CordRepSubstring* substring() const;
inline CordRepCrc* crc();
diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc
deleted file mode 100644
index af2fc768..00000000
--- a/absl/strings/internal/cord_rep_ring.cc
+++ /dev/null
@@ -1,773 +0,0 @@
-// 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_;
-};
-
-#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
-constexpr size_t CordRepRing::kMaxCapacity;
-#endif
-
-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.IsOne()) {
- 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.IsOne());
- index_type back = retreat(tail_);
- CordRep* child = entry_child(back);
- if (child->tag >= FLAT && child->refcount.IsOne()) {
- 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.IsOne());
- CordRep* child = entry_child(head_);
- size_t data_offset = entry_data_offset(head_);
- if (data_offset && child->refcount.IsOne() && 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.IsOne()) {
- 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.IsOne()) {
- 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.IsOne() && 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.IsOne()) {
- 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.IsOne()) {
- // 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/absl/strings/internal/cord_rep_ring.h b/absl/strings/internal/cord_rep_ring.h
deleted file mode 100644
index 79a2fdb1..00000000
--- a/absl/strings/internal/cord_rep_ring.h
+++ /dev/null
@@ -1,607 +0,0 @@
-// 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 maximum 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);
-
- // Decreases 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/absl/strings/internal/cord_rep_ring_reader.h b/absl/strings/internal/cord_rep_ring_reader.h
deleted file mode 100644
index 7ceeaa00..00000000
--- a/absl/strings/internal/cord_rep_ring_reader.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// 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/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc
index 515dfafb..b24c3da7 100644
--- a/absl/strings/internal/cordz_info.cc
+++ b/absl/strings/internal/cordz_info.cc
@@ -21,7 +21,6 @@
#include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cord_rep_btree.h"
#include "absl/strings/internal/cord_rep_crc.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"
@@ -33,8 +32,6 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace cord_internal {
-using ::absl::base_internal::SpinLockHolder;
-
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr size_t CordzInfo::kMaxStackDepth;
#endif
@@ -79,6 +76,8 @@ class CordRepAnalyzer {
// 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) {
+ ABSL_ASSERT(rep != nullptr);
+
// 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.
@@ -86,7 +85,7 @@ class CordRepAnalyzer {
RepRef repref{rep, (refcount > 1) ? refcount - 1 : 1};
// Process the top level CRC node, if present.
- if (repref.rep->tag == CRC) {
+ if (repref.tag() == CRC) {
statistics_.node_count++;
statistics_.node_counts.crc++;
memory_usage_.Add(sizeof(CordRepCrc), repref.refcount);
@@ -96,15 +95,14 @@ class CordRepAnalyzer {
// 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) {
+ switch (repref.tag()) {
+ case CordRepKind::BTREE:
AnalyzeBtree(repref);
- } else {
- // We should have either a concat, btree, or ring node if not null.
- assert(false);
- }
+ break;
+ default:
+ // We should have a btree node if not null.
+ ABSL_ASSERT(repref.tag() == CordRepKind::UNUSED_0);
+ break;
}
// Adds values to output
@@ -122,11 +120,19 @@ class CordRepAnalyzer {
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.
+ // Returns a 'child' RepRef which contains the cumulative reference count
+ // of this instance multiplied by the child's reference count. Returns a
+ // nullptr RepRef value with a refcount of 0 if `child` is nullptr.
RepRef Child(const CordRep* child) const {
+ if (child == nullptr) return RepRef{nullptr, 0};
return RepRef{child, refcount * child->refcount.Get()};
}
+
+ // Returns the tag of this rep, or UNUSED_0 if this instance is null
+ constexpr CordRepKind tag() const {
+ ABSL_ASSERT(rep == nullptr || rep->tag != CordRepKind::UNUSED_0);
+ return rep ? static_cast<CordRepKind>(rep->tag) : CordRepKind::UNUSED_0;
+ }
};
// Memory usage values
@@ -167,7 +173,7 @@ class CordRepAnalyzer {
// buffers where we count children unrounded.
RepRef CountLinearReps(RepRef rep, MemoryUsage& memory_usage) {
// Consume all substrings
- while (rep.rep->tag == SUBSTRING) {
+ while (rep.tag() == SUBSTRING) {
statistics_.node_count++;
statistics_.node_counts.substring++;
memory_usage.Add(sizeof(CordRepSubstring), rep.refcount);
@@ -175,7 +181,7 @@ class CordRepAnalyzer {
}
// Consume possible FLAT
- if (rep.rep->tag >= FLAT) {
+ if (rep.tag() >= FLAT) {
size_t size = rep.rep->flat()->AllocatedSize();
CountFlat(size);
memory_usage.Add(size, rep.refcount);
@@ -183,7 +189,7 @@ class CordRepAnalyzer {
}
// Consume possible external
- if (rep.rep->tag == EXTERNAL) {
+ if (rep.tag() == EXTERNAL) {
statistics_.node_count++;
statistics_.node_counts.external++;
size_t size = rep.rep->length + sizeof(CordRepExternalImpl<intptr_t>);
@@ -194,17 +200,6 @@ class CordRepAnalyzer {
return rep;
}
- // 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++;
diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc
index 53d2f2ea..d55773f2 100644
--- a/absl/strings/internal/cordz_info_statistics_test.cc
+++ b/absl/strings/internal/cordz_info_statistics_test.cc
@@ -25,7 +25,6 @@
#include "absl/strings/internal/cord_rep_btree.h"
#include "absl/strings/internal/cord_rep_crc.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"
@@ -123,11 +122,6 @@ size_t SizeOf(const CordRepExternal* rep) {
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;
@@ -144,11 +138,6 @@ double FairShareImpl(CordRep* rep, size_t ref) {
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 {
assert(false);
}
@@ -294,64 +283,6 @@ TEST(CordzInfoStatisticsTest, SharedSubstring) {
EXPECT_THAT(SampleCord(substring), 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(3u));
RefHelper ref;
@@ -452,8 +383,7 @@ TEST(CordzInfoStatisticsTest, BtreeNodeShared) {
TEST(CordzInfoStatisticsTest, Crc) {
RefHelper ref;
auto* left = Flat(1000);
- auto* crc =
- ref.NeedsUnref(CordRepCrc::New(left, crc_internal::CrcCordState()));
+ auto* crc = ref.NeedsUnref(CordRepCrc::New(left, {}));
CordzStatistics expected;
expected.size = left->length;
@@ -467,6 +397,20 @@ TEST(CordzInfoStatisticsTest, Crc) {
EXPECT_THAT(SampleCord(crc), EqStatistics(expected));
}
+TEST(CordzInfoStatisticsTest, EmptyCrc) {
+ RefHelper ref;
+ auto* crc = ref.NeedsUnref(CordRepCrc::New(nullptr, {}));
+
+ CordzStatistics expected;
+ expected.size = 0;
+ expected.estimated_memory_usage = SizeOf(crc);
+ expected.estimated_fair_share_memory_usage = expected.estimated_memory_usage;
+ expected.node_count = 1;
+ expected.node_counts.crc = 1;
+
+ EXPECT_THAT(SampleCord(crc), EqStatistics(expected));
+}
+
TEST(CordzInfoStatisticsTest, ThreadSafety) {
Notification stop;
static constexpr int kNumThreads = 8;
@@ -497,6 +441,7 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) {
InlineData cords[2];
std::minstd_rand gen;
std::uniform_int_distribution<int> coin_toss(0, 1);
+ std::uniform_int_distribution<int> dice_roll(1, 6);
while (!stop.HasBeenNotified()) {
for (InlineData& cord : cords) {
@@ -514,13 +459,21 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) {
CordRep::Unref(cord.as_tree());
cord.set_inline_size(0);
} else {
- // Coin toss to 25% ring, 25% btree, and 50% flat.
+ // Coin toss to 50% btree, and 50% flat.
CordRep* rep = Flat(256);
if (coin_toss(gen) != 0) {
- if (coin_toss(gen) != 0) {
- rep = CordRepRing::Create(rep);
+ rep = CordRepBtree::Create(rep);
+ }
+
+ // Maybe CRC this cord
+ if (dice_roll(gen) == 6) {
+ if (dice_roll(gen) == 6) {
+ // Empty CRC rep
+ CordRep::Unref(rep);
+ rep = CordRepCrc::New(nullptr, {});
} else {
- rep = CordRepBtree::Create(rep);
+ // Regular CRC rep
+ rep = CordRepCrc::New(rep, {});
}
}
cord.make_tree(rep);
diff --git a/absl/strings/internal/has_absl_stringify.h b/absl/strings/internal/has_absl_stringify.h
index 55a08508..f82cfe26 100644
--- a/absl/strings/internal/has_absl_stringify.h
+++ b/absl/strings/internal/has_absl_stringify.h
@@ -1,4 +1,4 @@
-// Copyright 2022 The Abseil Authors
+// Copyright 2024 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.
@@ -14,38 +14,27 @@
#ifndef ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
#define ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
-#include <string>
-#include <type_traits>
-#include <utility>
-#include "absl/strings/string_view.h"
+#include "absl/strings/has_absl_stringify.h"
+
+#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace strings_internal {
-// This is an empty class not intended to be used. It exists so that
-// `HasAbslStringify` can reference a universal class rather than needing to be
-// copied for each new sink.
-class UnimplementedSink {
- public:
- void Append(size_t count, char ch);
-
- void Append(string_view v);
-
- // Support `absl::Format(&sink, format, args...)`.
- friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
-};
-
-template <typename T, typename = void>
-struct HasAbslStringify : std::false_type {};
-
-template <typename T>
-struct HasAbslStringify<
- T, std::enable_if_t<std::is_void<decltype(AbslStringify(
- std::declval<strings_internal::UnimplementedSink&>(),
- std::declval<const T&>()))>::value>> : std::true_type {};
+// This exists to fix a circular dependency problem with the GoogleTest release.
+// GoogleTest referenced this internal file and this internal trait. Since
+// simultaneous releases are not possible since once release must reference
+// another, we will temporarily add this back.
+// https://github.com/google/googletest/blob/v1.14.x/googletest/include/gtest/gtest-printers.h#L119
+//
+// This file can be deleted after the next Abseil and GoogleTest release.
+//
+// https://github.com/google/googletest/pull/4368#issuecomment-1717699895
+// https://github.com/google/googletest/pull/4368#issuecomment-1717699895
+using ::absl::HasAbslStringify;
} // namespace strings_internal
diff --git a/absl/strings/internal/memutil.cc b/absl/strings/internal/memutil.cc
index e2e7347c..0bbd8aa1 100644
--- a/absl/strings/internal/memutil.cc
+++ b/absl/strings/internal/memutil.cc
@@ -27,10 +27,18 @@ int memcasecmp(const char* s1, const char* s2, size_t len) {
const unsigned char* us2 = reinterpret_cast<const unsigned char*>(s2);
for (size_t i = 0; i < len; i++) {
- const int diff =
- int{static_cast<unsigned char>(absl::ascii_tolower(us1[i]))} -
- int{static_cast<unsigned char>(absl::ascii_tolower(us2[i]))};
- if (diff != 0) return diff;
+ unsigned char c1 = us1[i];
+ unsigned char c2 = us2[i];
+ // If bytes are the same, they will be the same when converted to lower.
+ // So we only need to convert if bytes are not equal.
+ // NOTE(b/308193381): We do not use `absl::ascii_tolower` here in order
+ // to avoid its lookup table and improve performance.
+ if (c1 != c2) {
+ c1 = c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1;
+ c2 = c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2;
+ const int diff = int{c1} - int{c2};
+ if (diff != 0) return diff;
+ }
}
return 0;
}
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index c0a9a28e..eeb21081 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -18,15 +18,28 @@
//
#include "absl/strings/internal/str_format/arg.h"
+#include <algorithm>
#include <cassert>
-#include <cerrno>
+#include <cstddef>
+#include <cstdint>
#include <cstdlib>
+#include <cstring>
+#include <cwchar>
#include <string>
#include <type_traits>
-#include "absl/base/port.h"
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/container/fixed_array.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/internal/str_format/extension.h"
#include "absl/strings/internal/str_format/float_conversion.h"
#include "absl/strings/numbers.h"
+#include "absl/strings/string_view.h"
+
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#include <string_view>
+#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -298,6 +311,83 @@ inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv,
conv.has_left_flag());
}
+struct ShiftState {
+ bool saw_high_surrogate = false;
+ uint8_t bits = 0;
+};
+
+// Converts `v` from UTF-16 or UTF-32 to UTF-8 and writes to `buf`. `buf` is
+// assumed to have enough space for the output. `s` is used to carry state
+// between successive calls with a UTF-16 surrogate pair. Returns the number of
+// chars written, or `static_cast<size_t>(-1)` on failure.
+//
+// This is basically std::wcrtomb(), but always outputting UTF-8 instead of
+// respecting the current locale.
+inline size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s) {
+ const auto v = static_cast<uint32_t>(wc);
+ if (v < 0x80) {
+ *buf = static_cast<char>(v);
+ return 1;
+ } else if (v < 0x800) {
+ *buf++ = static_cast<char>(0xc0 | (v >> 6));
+ *buf = static_cast<char>(0x80 | (v & 0x3f));
+ return 2;
+ } else if (v < 0xd800 || (v - 0xe000) < 0x2000) {
+ *buf++ = static_cast<char>(0xe0 | (v >> 12));
+ *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
+ *buf = static_cast<char>(0x80 | (v & 0x3f));
+ return 3;
+ } else if ((v - 0x10000) < 0x100000) {
+ *buf++ = static_cast<char>(0xf0 | (v >> 18));
+ *buf++ = static_cast<char>(0x80 | ((v >> 12) & 0x3f));
+ *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f));
+ *buf = static_cast<char>(0x80 | (v & 0x3f));
+ return 4;
+ } else if (v < 0xdc00) {
+ s.saw_high_surrogate = true;
+ s.bits = static_cast<uint8_t>(v & 0x3);
+ const uint8_t high_bits = ((v >> 6) & 0xf) + 1;
+ *buf++ = static_cast<char>(0xf0 | (high_bits >> 2));
+ *buf =
+ static_cast<char>(0x80 | static_cast<uint8_t>((high_bits & 0x3) << 4) |
+ static_cast<uint8_t>((v >> 2) & 0xf));
+ return 2;
+ } else if (v < 0xe000 && s.saw_high_surrogate) {
+ *buf++ = static_cast<char>(0x80 | static_cast<uint8_t>(s.bits << 4) |
+ static_cast<uint8_t>((v >> 6) & 0xf));
+ *buf = static_cast<char>(0x80 | (v & 0x3f));
+ s.saw_high_surrogate = false;
+ s.bits = 0;
+ return 2;
+ } else {
+ return static_cast<size_t>(-1);
+ }
+}
+
+inline bool ConvertStringArg(const wchar_t *v,
+ size_t len,
+ const FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink) {
+ FixedArray<char> mb(len * 4);
+ ShiftState s;
+ size_t chars_written = 0;
+ for (size_t i = 0; i < len; ++i) {
+ const size_t chars = WideToUtf8(v[i], &mb[chars_written], s);
+ if (chars == static_cast<size_t>(-1)) { return false; }
+ chars_written += chars;
+ }
+ return ConvertStringArg(string_view(mb.data(), chars_written), conv, sink);
+}
+
+bool ConvertWCharTImpl(wchar_t v, const FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink) {
+ char mb[4];
+ ShiftState s;
+ const size_t chars_written = WideToUtf8(v, mb, s);
+ return chars_written != static_cast<size_t>(-1) && !s.saw_high_surrogate &&
+ ConvertStringArg(string_view(mb, chars_written), conv, sink);
+}
+
} // namespace
bool ConvertBoolArg(bool v, FormatSinkImpl *sink) {
@@ -316,11 +406,14 @@ bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
// 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. 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<char>(v), conv, sink);
+ return (std::is_same<T, wchar_t>::value ||
+ (conv.length_mod() == LengthMod::l))
+ ? ConvertWCharTImpl(static_cast<wchar_t>(v), conv, sink)
+ : ConvertCharImpl(static_cast<char>(v), conv, sink);
case static_cast<uint8_t>(FormatConversionCharInternal::o):
as_digits.PrintAsOct(static_cast<U>(v));
@@ -372,6 +465,8 @@ template bool ConvertIntArg<signed char>(signed char v,
template bool ConvertIntArg<unsigned char>(unsigned char v,
FormatConversionSpecImpl conv,
FormatSinkImpl *sink);
+template bool ConvertIntArg<wchar_t>(wchar_t v, FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink);
template bool ConvertIntArg<short>(short v, // NOLINT
FormatConversionSpecImpl conv,
FormatSinkImpl *sink);
@@ -403,16 +498,29 @@ StringConvertResult FormatConvertImpl(const std::string &v,
return {ConvertStringArg(v, conv, sink)};
}
+StringConvertResult FormatConvertImpl(const std::wstring &v,
+ const FormatConversionSpecImpl conv,
+ FormatSinkImpl *sink) {
+ return {ConvertStringArg(v.data(), v.size(), conv, sink)};
+}
+
StringConvertResult FormatConvertImpl(string_view v,
const FormatConversionSpecImpl conv,
FormatSinkImpl *sink) {
return {ConvertStringArg(v, conv, sink)};
}
-ArgConvertResult<FormatConversionCharSetUnion(
- FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
-FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv,
- FormatSinkImpl *sink) {
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+StringConvertResult FormatConvertImpl(std::wstring_view v,
+ const FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink) {
+ return {ConvertStringArg(v.data(), v.size(), conv, sink)};
+}
+#endif
+
+StringPtrConvertResult 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;
@@ -427,6 +535,30 @@ FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv,
return {ConvertStringArg(string_view(v, len), conv, sink)};
}
+StringPtrConvertResult FormatConvertImpl(const wchar_t* 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) {
+ len = 0;
+ } else if (conv.precision() < 0) {
+ len = std::wcslen(v);
+ } else {
+ // If precision is set, we look for the NUL-terminator on the valid range.
+ len = static_cast<size_t>(std::find(v, v + conv.precision(), L'\0') - v);
+ }
+ return {ConvertStringArg(v, len, conv, sink)};
+}
+
+StringPtrConvertResult FormatConvertImpl(std::nullptr_t,
+ const FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink) {
+ return FormatConvertImpl(static_cast<const char*>(nullptr), conv, sink);
+}
+
// ==================== Raw pointers ====================
ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
@@ -461,6 +593,11 @@ CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv,
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
+CharConvertResult FormatConvertImpl(wchar_t v,
+ const FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
// ==================== Ints ====================
IntegralConvertResult FormatConvertImpl(signed char v,
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index 3ce30feb..309161d5 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -19,8 +19,9 @@
#include <wchar.h>
#include <algorithm>
+#include <cstddef>
+#include <cstdint>
#include <cstdio>
-#include <iomanip>
#include <limits>
#include <memory>
#include <sstream>
@@ -28,13 +29,18 @@
#include <type_traits>
#include <utility>
-#include "absl/base/port.h"
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
#include "absl/meta/type_traits.h"
#include "absl/numeric/int128.h"
-#include "absl/strings/internal/has_absl_stringify.h"
+#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/internal/str_format/extension.h"
#include "absl/strings/string_view.h"
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#include <string_view>
+#endif
+
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -97,6 +103,9 @@ extern template bool ConvertIntArg<signed char>(signed char v,
extern template bool ConvertIntArg<unsigned char>(unsigned char v,
FormatConversionSpecImpl conv,
FormatSinkImpl* sink);
+extern template bool ConvertIntArg<wchar_t>(wchar_t v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
extern template bool ConvertIntArg<short>(short v, // NOLINT
FormatConversionSpecImpl conv,
FormatSinkImpl* sink);
@@ -158,6 +167,7 @@ template <typename T>
auto FormatConvertImpl(const T& v, FormatConversionSpecImpl,
FormatSinkImpl* sink)
-> std::enable_if_t<!std::is_enum<T>::value &&
+ !std::is_same<T, absl::Cord>::value &&
std::is_void<decltype(AbslStringify(
std::declval<FormatSink&>(), v))>::value,
ArgConvertResult<FormatConversionCharSetInternal::v>> {
@@ -202,30 +212,49 @@ constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) {
return C;
}
-using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
- FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::v)>;
ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl(
VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
// Strings.
+using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+ FormatConversionCharSetInternal::s,
+ FormatConversionCharSetInternal::v)>;
StringConvertResult FormatConvertImpl(const std::string& v,
FormatConversionSpecImpl conv,
FormatSinkImpl* sink);
+StringConvertResult FormatConvertImpl(const std::wstring& 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)
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+StringConvertResult FormatConvertImpl(std::wstring_view v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+#if !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);
+#endif // !ABSL_USES_STD_STRING_VIEW
+#endif // ABSL_HAVE_STD_STRING_VIEW
+
+using StringPtrConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+ FormatConversionCharSetInternal::s,
+ FormatConversionCharSetInternal::p)>;
+StringPtrConvertResult FormatConvertImpl(const char* v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+StringPtrConvertResult FormatConvertImpl(const wchar_t* v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
+// This overload is needed to disambiguate, since `nullptr` could match either
+// of the other overloads equally well.
+StringPtrConvertResult FormatConvertImpl(std::nullptr_t,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
template <class AbslCord, typename std::enable_if<std::is_same<
AbslCord, absl::Cord>::value>::type* = nullptr>
@@ -279,6 +308,9 @@ FloatingConvertResult FormatConvertImpl(long double v,
// Chars.
CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
FormatSinkImpl* sink);
+CharConvertResult FormatConvertImpl(wchar_t v,
+ FormatConversionSpecImpl conv,
+ FormatSinkImpl* sink);
// Ints.
IntegralConvertResult FormatConvertImpl(signed char v,
@@ -333,7 +365,7 @@ IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv,
template <typename T>
typename std::enable_if<std::is_enum<T>::value &&
!HasUserDefinedConvert<T>::value &&
- !strings_internal::HasAbslStringify<T>::value,
+ !HasAbslStringify<T>::value,
IntegralConvertResult>::type
FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);
@@ -440,6 +472,7 @@ class FormatArgImpl {
// Anything with a user-defined Convert will get its own vtable.
// For everything else:
// - Decay char* and char arrays into `const char*`
+ // - Decay wchar_t* and wchar_t arrays into `const wchar_t*`
// - Decay any other pointer to `const void*`
// - Decay all enums to the integral promotion of their underlying type.
// - Decay function pointers to void*.
@@ -447,20 +480,23 @@ class FormatArgImpl {
struct DecayType {
static constexpr bool kHasUserDefined =
str_format_internal::HasUserDefinedConvert<T>::value ||
- strings_internal::HasAbslStringify<T>::value;
+ HasAbslStringify<T>::value;
using type = typename std::conditional<
!kHasUserDefined && std::is_convertible<T, const char*>::value,
const char*,
- typename std::conditional<!kHasUserDefined &&
- std::is_convertible<T, VoidPtr>::value,
- VoidPtr, const T&>::type>::type;
+ typename std::conditional<
+ !kHasUserDefined && std::is_convertible<T, const wchar_t*>::value,
+ const wchar_t*,
+ typename std::conditional<
+ !kHasUserDefined && std::is_convertible<T, VoidPtr>::value,
+ VoidPtr,
+ const T&>::type>::type>::type;
};
template <typename T>
- struct DecayType<T,
- typename std::enable_if<
- !str_format_internal::HasUserDefinedConvert<T>::value &&
- !strings_internal::HasAbslStringify<T>::value &&
- std::is_enum<T>::value>::type> {
+ struct DecayType<
+ T, typename std::enable_if<
+ !str_format_internal::HasUserDefinedConvert<T>::value &&
+ !HasAbslStringify<T>::value && std::is_enum<T>::value>::type> {
using type = decltype(+typename std::underlying_type<T>::type());
};
@@ -585,7 +621,7 @@ class FormatArgImpl {
E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \
void*)
-#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(...) \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \
__VA_ARGS__); \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__); \
@@ -611,7 +647,19 @@ class FormatArgImpl {
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \
- ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__)
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const wchar_t*, __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring, __VA_ARGS__)
+
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
+ ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_( \
+ __VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring_view, __VA_ARGS__)
+#else
+#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
+ ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(__VA_ARGS__)
+#endif
ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc
index 1261937c..f663d7c5 100644
--- a/absl/strings/internal/str_format/arg_test.cc
+++ b/absl/strings/internal/str_format/arg_test.cc
@@ -14,9 +14,10 @@
#include "absl/strings/internal/str_format/arg.h"
-#include <ostream>
+#include <limits>
#include <string>
#include "gtest/gtest.h"
+#include "absl/base/config.h"
#include "absl/strings/str_format.h"
namespace absl {
@@ -93,6 +94,21 @@ TEST_F(FormatArgImplTest, CharArraysDecayToCharPtr) {
FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray)));
}
+extern const wchar_t kMyWCharTArray[];
+
+TEST_F(FormatArgImplTest, WCharTArraysDecayToWCharTPtr) {
+ const wchar_t* a = L"";
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"")));
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"A")));
+ EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"ABC")));
+ EXPECT_EQ(
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)),
+ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyWCharTArray)));
+}
+
TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) {
auto expected = FormatArgImplFriend::GetVTablePtrForTest(
FormatArgImpl(static_cast<void *>(nullptr)));
@@ -124,6 +140,22 @@ TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) {
}
const char kMyArray[] = "ABCDE";
+TEST_F(FormatArgImplTest, WorksWithWCharTArraysOfUnknownSize) {
+ std::string s;
+ FormatSinkImpl sink(&s);
+ FormatConversionSpecImpl conv;
+ FormatConversionSpecImplFriend::SetConversionChar(
+ FormatConversionCharInternal::s, &conv);
+ FormatConversionSpecImplFriend::SetFlags(Flags(), &conv);
+ FormatConversionSpecImplFriend::SetWidth(-1, &conv);
+ FormatConversionSpecImplFriend::SetPrecision(-1, &conv);
+ EXPECT_TRUE(
+ FormatArgImplFriend::Convert(FormatArgImpl(kMyWCharTArray), conv, &sink));
+ sink.Flush();
+ EXPECT_EQ("ABCDE", s);
+}
+const wchar_t kMyWCharTArray[] = L"ABCDE";
+
} // namespace
} // namespace str_format_internal
ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc
index 77a42223..87e23b56 100644
--- a/absl/strings/internal/str_format/bind.cc
+++ b/absl/strings/internal/str_format/bind.cc
@@ -14,10 +14,24 @@
#include "absl/strings/internal/str_format/bind.h"
+#include <algorithm>
+#include <cassert>
#include <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <ios>
#include <limits>
+#include <ostream>
#include <sstream>
#include <string>
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
+#include "absl/strings/internal/str_format/arg.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
+#include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/internal/str_format/output.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -90,6 +104,8 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound,
} else {
FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
}
+
+ FormatConversionSpecImplFriend::SetLengthMod(unbound->length_mod, bound);
} else {
FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
FormatConversionSpecImplFriend::SetWidth(-1, bound);
@@ -215,7 +231,7 @@ std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format,
return *out;
}
-std::string FormatPack(const UntypedFormatSpecImpl format,
+std::string FormatPack(UntypedFormatSpecImpl format,
absl::Span<const FormatArgImpl> args) {
std::string out;
if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) {
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index 5e2a43d5..120bc355 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -15,16 +15,19 @@
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
-#include <array>
+#include <cassert>
#include <cstdio>
-#include <sstream>
+#include <ostream>
#include <string>
-#include "absl/base/port.h"
+#include "absl/base/config.h"
#include "absl/container/inlined_vector.h"
#include "absl/strings/internal/str_format/arg.h"
#include "absl/strings/internal/str_format/checker.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
+#include "absl/strings/internal/str_format/extension.h"
#include "absl/strings/internal/str_format/parser.h"
+#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "absl/utility/utility.h"
@@ -203,7 +206,7 @@ bool FormatUntyped(FormatRawSinkImpl raw_sink, UntypedFormatSpecImpl format,
std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format,
absl::Span<const FormatArgImpl> args);
-std::string FormatPack(const UntypedFormatSpecImpl format,
+std::string FormatPack(UntypedFormatSpecImpl format,
absl::Span<const FormatArgImpl> args);
int FprintF(std::FILE* output, UntypedFormatSpecImpl format,
diff --git a/absl/strings/internal/str_format/constexpr_parser.h b/absl/strings/internal/str_format/constexpr_parser.h
index b70a16e4..8f593870 100644
--- a/absl/strings/internal/str_format/constexpr_parser.h
+++ b/absl/strings/internal/str_format/constexpr_parser.h
@@ -17,17 +17,18 @@
#include <cassert>
#include <cstdint>
+#include <cstdio>
#include <limits>
+#include "absl/base/config.h"
#include "absl/base/const_init.h"
+#include "absl/base/optimization.h"
#include "absl/strings/internal/str_format/extension.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
-enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
-
// The analyzed properties of a single specified conversion.
struct UnboundConversion {
// This is a user defined default constructor on purpose to skip the
@@ -306,7 +307,6 @@ constexpr const char* ConsumeConversion(const char* pos, const char* const end,
if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr;
// It is a length modifier.
- using str_format_internal::LengthMod;
LengthMod length_mod = tag.as_length();
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
if (c == 'h' && length_mod == LengthMod::h) {
@@ -322,6 +322,11 @@ constexpr const char* ConsumeConversion(const char* pos, const char* const end,
if (ABSL_PREDICT_FALSE(c == 'v')) return nullptr;
if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr;
+
+ // `wchar_t` args are marked non-basic so `Bind()` will copy the length mod.
+ if (conv->length_mod == LengthMod::l && c == 'c') {
+ conv->flags = conv->flags | Flags::kNonBasic;
+ }
}
#undef ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 16ff9879..7f222778 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -12,24 +12,43 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <errno.h>
+#include <assert.h>
+#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
-#include <cctype>
+#include <algorithm>
+#include <climits>
#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <cwctype>
#include <limits>
+#include <set>
+#include <sstream>
#include <string>
#include <thread> // NOLINT
+#include <type_traits>
+#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/log/log.h"
+#include "absl/numeric/int128.h"
+#include "absl/strings/ascii.h"
+#include "absl/strings/internal/str_format/arg.h"
#include "absl/strings/internal/str_format/bind.h"
#include "absl/strings/match.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
+#include "absl/types/span.h"
+
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#include <string_view>
+#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -48,36 +67,103 @@ size_t ArraySize(T (&)[N]) {
return N;
}
-std::string LengthModFor(float) { return ""; }
-std::string LengthModFor(double) { return ""; }
-std::string LengthModFor(long double) { return "L"; }
-std::string LengthModFor(char) { return "hh"; }
-std::string LengthModFor(signed char) { return "hh"; }
-std::string LengthModFor(unsigned char) { return "hh"; }
-std::string LengthModFor(short) { return "h"; } // NOLINT
-std::string LengthModFor(unsigned short) { return "h"; } // NOLINT
-std::string LengthModFor(int) { return ""; }
-std::string LengthModFor(unsigned) { return ""; }
-std::string LengthModFor(long) { return "l"; } // NOLINT
-std::string LengthModFor(unsigned long) { return "l"; } // NOLINT
-std::string LengthModFor(long long) { return "ll"; } // NOLINT
-std::string LengthModFor(unsigned long long) { return "ll"; } // NOLINT
+template <typename T>
+struct AlwaysFalse : std::false_type {};
+
+template <typename T>
+std::string LengthModFor() {
+ static_assert(AlwaysFalse<T>::value, "Unsupported type");
+ return "";
+}
+template <>
+std::string LengthModFor<char>() {
+ return "hh";
+}
+template <>
+std::string LengthModFor<signed char>() {
+ return "hh";
+}
+template <>
+std::string LengthModFor<unsigned char>() {
+ return "hh";
+}
+template <>
+std::string LengthModFor<short>() { // NOLINT
+ return "h";
+}
+template <>
+std::string LengthModFor<unsigned short>() { // NOLINT
+ return "h";
+}
+template <>
+std::string LengthModFor<int>() {
+ return "";
+}
+template <>
+std::string LengthModFor<unsigned>() {
+ return "";
+}
+template <>
+std::string LengthModFor<long>() { // NOLINT
+ return "l";
+}
+template <>
+std::string LengthModFor<unsigned long>() { // NOLINT
+ return "l";
+}
+template <>
+std::string LengthModFor<long long>() { // NOLINT
+ return "ll";
+}
+template <>
+std::string LengthModFor<unsigned long long>() { // NOLINT
+ return "ll";
+}
+
+// An integral type of the same rank and signedness as `wchar_t`, that isn't
+// `wchar_t`.
+using IntegralTypeForWCharT =
+ std::conditional_t<std::is_signed<wchar_t>::value,
+ // Some STLs are broken and return `wchar_t` from
+ // `std::make_[un]signed_t<wchar_t>` when the signedness
+ // matches. Work around by round-tripping through the
+ // opposite signedness.
+ std::make_signed_t<std::make_unsigned_t<wchar_t>>,
+ std::make_unsigned_t<std::make_signed_t<wchar_t>>>;
+
+// Given an integral type `T`, returns a type of the same rank and signedness
+// that is guaranteed to not be `wchar_t`.
+template <typename T>
+using MatchingIntegralType = std::conditional_t<std::is_same<T, wchar_t>::value,
+ IntegralTypeForWCharT, T>;
std::string EscCharImpl(int v) {
- if (std::isprint(static_cast<unsigned char>(v))) {
- return std::string(1, static_cast<char>(v));
- }
char buf[64];
- int n = snprintf(buf, sizeof(buf), "\\%#.2x",
- static_cast<unsigned>(v & 0xff));
- assert(n > 0 && n < sizeof(buf));
- return std::string(buf, n);
+ int n = absl::ascii_isprint(static_cast<unsigned char>(v))
+ ? snprintf(buf, sizeof(buf), "'%c'", v)
+ : snprintf(buf, sizeof(buf), "'\\x%.*x'", CHAR_BIT / 4,
+ static_cast<unsigned>(
+ static_cast<std::make_unsigned_t<char>>(v)));
+ assert(n > 0 && static_cast<size_t>(n) < sizeof(buf));
+ return std::string(buf, static_cast<size_t>(n));
}
std::string Esc(char v) { return EscCharImpl(v); }
std::string Esc(signed char v) { return EscCharImpl(v); }
std::string Esc(unsigned char v) { return EscCharImpl(v); }
+std::string Esc(wchar_t v) {
+ char buf[64];
+ int n = std::iswprint(static_cast<wint_t>(v))
+ ? snprintf(buf, sizeof(buf), "L'%lc'", static_cast<wint_t>(v))
+ : snprintf(buf, sizeof(buf), "L'\\x%.*llx'",
+ static_cast<int>(sizeof(wchar_t) * CHAR_BIT / 4),
+ static_cast<unsigned long long>(
+ static_cast<std::make_unsigned_t<wchar_t>>(v)));
+ assert(n > 0 && static_cast<size_t>(n) < sizeof(buf));
+ return std::string(buf, static_cast<size_t>(n));
+}
+
template <typename T>
std::string Esc(const T &v) {
std::ostringstream oss;
@@ -100,7 +186,7 @@ void StrAppendV(std::string *dst, const char *format, va_list ap) {
if (result < kSpaceLength) {
if (result >= 0) {
// Normal case -- everything fit.
- dst->append(space, result);
+ dst->append(space, static_cast<size_t>(result));
return;
}
if (result < 0) {
@@ -111,7 +197,7 @@ void StrAppendV(std::string *dst, const char *format, va_list ap) {
// Increase the buffer size to the size requested by vsnprintf,
// plus one for the closing \0.
- int length = result + 1;
+ size_t length = static_cast<size_t>(result) + 1;
char *buf = new char[length];
// Restore the va_list before we use it again
@@ -119,9 +205,9 @@ void StrAppendV(std::string *dst, const char *format, va_list ap) {
result = vsnprintf(buf, length, format, backup_ap);
va_end(backup_ap);
- if (result >= 0 && result < length) {
+ if (result >= 0 && static_cast<size_t>(result) < length) {
// It fit
- dst->append(buf, result);
+ dst->append(buf, static_cast<size_t>(result));
}
delete[] buf;
}
@@ -230,11 +316,15 @@ void TestStringConvert(const T& str) {
TEST_F(FormatConvertTest, BasicString) {
TestStringConvert("hello"); // As char array.
+ TestStringConvert(L"hello");
TestStringConvert(static_cast<const char*>("hello"));
+ TestStringConvert(static_cast<const wchar_t*>(L"hello"));
TestStringConvert(std::string("hello"));
+ TestStringConvert(std::wstring(L"hello"));
TestStringConvert(string_view("hello"));
#if defined(ABSL_HAVE_STD_STRING_VIEW)
TestStringConvert(std::string_view("hello"));
+ TestStringConvert(std::wstring_view(L"hello"));
#endif // ABSL_HAVE_STD_STRING_VIEW
}
@@ -242,6 +332,10 @@ TEST_F(FormatConvertTest, NullString) {
const char* p = nullptr;
UntypedFormatSpecImpl format("%s");
EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)}));
+
+ const wchar_t* wp = nullptr;
+ UntypedFormatSpecImpl wformat("%ls");
+ EXPECT_EQ("", FormatPack(wformat, {FormatArgImpl(wp)}));
}
TEST_F(FormatConvertTest, StringPrecision) {
@@ -251,10 +345,19 @@ TEST_F(FormatConvertTest, StringPrecision) {
UntypedFormatSpecImpl format("%.1s");
EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));
+ wchar_t wc = L'a';
+ const wchar_t* wp = &wc;
+ UntypedFormatSpecImpl wformat("%.1ls");
+ EXPECT_EQ("a", FormatPack(wformat, {FormatArgImpl(wp)}));
+
// We cap at the NUL-terminator.
p = "ABC";
UntypedFormatSpecImpl format2("%.10s");
EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
+
+ wp = L"ABC";
+ UntypedFormatSpecImpl wformat2("%.10ls");
+ EXPECT_EQ("ABC", FormatPack(wformat2, {FormatArgImpl(wp)}));
}
// Pointer formatting is implementation defined. This checks that the argument
@@ -277,16 +380,25 @@ TEST_F(FormatConvertTest, Pointer) {
char *mcp = &c;
const char *cp = "hi";
const char *cnil = nullptr;
+ wchar_t wc = L'h';
+ wchar_t *mwcp = &wc;
+ const wchar_t *wcp = L"hi";
+ const wchar_t *wcnil = nullptr;
const int *inil = nullptr;
using VoidF = void (*)();
VoidF fp = [] {}, fnil = nullptr;
volatile char vc;
volatile char *vcp = &vc;
volatile char *vcnil = nullptr;
+ volatile wchar_t vwc;
+ volatile wchar_t *vwcp = &vwc;
+ volatile wchar_t *vwcnil = nullptr;
const FormatArgImpl args_array[] = {
- FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil),
- FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
- FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
+ FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(wcp),
+ FormatArgImpl(inil), FormatArgImpl(cnil), FormatArgImpl(wcnil),
+ FormatArgImpl(mcp), FormatArgImpl(mwcp), FormatArgImpl(fp),
+ FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vwcp),
+ FormatArgImpl(vcnil), FormatArgImpl(vwcnil),
};
auto args = absl::MakeConstSpan(args_array);
@@ -312,30 +424,49 @@ TEST_F(FormatConvertTest, Pointer) {
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args),
MatchesPointerString(&x));
+ // const int*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%1$p"), args),
+ MatchesPointerString(xp));
// const char*
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args),
MatchesPointerString(cp));
- // null const int*
+ // const wchar_t*
EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args),
+ MatchesPointerString(wcp));
+ // null const int*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
MatchesPointerString(nullptr));
// null const char*
- EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
+ MatchesPointerString(nullptr));
+ // null const wchar_t*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
MatchesPointerString(nullptr));
// nonconst char*
- EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
MatchesPointerString(mcp));
-
- // function pointers
- EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
- MatchesPointerString(reinterpret_cast<const void*>(fp)));
+ // nonconst wchar_t*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%8$p"), args),
+ MatchesPointerString(mwcp));
+ // function pointer
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
+ MatchesPointerString(reinterpret_cast<const void *>(fp)));
+ // null function pointer
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%10$p"), args),
+ MatchesPointerString(nullptr));
+ // volatile char*
EXPECT_THAT(
- FormatPack(UntypedFormatSpecImpl("%8$p"), args),
+ FormatPack(UntypedFormatSpecImpl("%11$p"), args),
MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));
-
- // null function pointers
- EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
+ // volatile wchar_t*
+ EXPECT_THAT(
+ FormatPack(UntypedFormatSpecImpl("%12$p"), args),
+ MatchesPointerString(reinterpret_cast<volatile const void *>(vwcp)));
+ // null volatile char*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%13$p"), args),
MatchesPointerString(nullptr));
- EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
+ // null volatile wchar_t*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%14$p"), args),
MatchesPointerString(nullptr));
}
@@ -435,12 +566,15 @@ TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) {
// as printf can't do that conversion properly. For those
// cases, we do expect agreement with printf with a "%u"
// and the unsigned equivalent of 'val'.
- UnsignedT uval = val;
- old_fmt += LengthModFor(uval);
+ UnsignedT uval =
+ static_cast<std::remove_volatile_t<UnsignedT>>(val);
+ old_fmt += LengthModFor<
+ MatchingIntegralType<std::remove_cv_t<decltype(uval)>>>();
old_fmt += "u";
old_result = StrPrint(old_fmt.c_str(), uval);
} else {
- old_fmt += LengthModFor(val);
+ old_fmt += LengthModFor<
+ MatchingIntegralType<std::remove_cv_t<decltype(val)>>>();
old_fmt += conv_char;
old_result = StrPrint(old_fmt.c_str(), val);
}
@@ -458,6 +592,47 @@ TYPED_TEST_P(TypedFormatConvertTest, AllIntsWithFlags) {
}
}
+template <typename T>
+absl::optional<std::string> StrPrintChar(T c) {
+ return StrPrint("%c", static_cast<int>(c));
+}
+template <>
+absl::optional<std::string> StrPrintChar(wchar_t c) {
+ // musl libc has a bug where ("%lc", 0) writes no characters, and Android
+ // doesn't support forcing UTF-8 via setlocale(). Hardcode the expected
+ // answers for ASCII inputs to maximize test coverage on these platforms.
+ if (static_cast<std::make_unsigned_t<wchar_t>>(c) < 0x80) {
+ return std::string(1, static_cast<char>(c));
+ }
+
+ // Force a UTF-8 locale to match the expected `StrFormat()` behavior.
+ // It's important to copy the string returned by `old_locale` here, because
+ // its contents are not guaranteed to be valid after the next `setlocale()`
+ // call.
+ std::string old_locale = setlocale(LC_CTYPE, nullptr);
+ if (!setlocale(LC_CTYPE, "en_US.UTF-8")) {
+ return absl::nullopt;
+ }
+ const std::string output = StrPrint("%lc", static_cast<wint_t>(c));
+ setlocale(LC_CTYPE, old_locale.c_str());
+ return output;
+}
+
+template <typename T>
+typename std::remove_volatile<T>::type GetMaxForConversion() {
+ return static_cast<typename std::remove_volatile<T>::type>(
+ std::numeric_limits<int>::max());
+}
+
+template <>
+wchar_t GetMaxForConversion<wchar_t>() {
+ // Don't return values that aren't legal Unicode. For wchar_t conversions in a
+ // UTF-8 locale, conversion behavior for such values is unspecified, and we
+ // don't care about matching it.
+ return (sizeof(wchar_t) * CHAR_BIT <= 16) ? wchar_t{0xffff}
+ : static_cast<wchar_t>(0x10ffff);
+}
+
TYPED_TEST_P(TypedFormatConvertTest, Char) {
// Pass a bunch of values of type TypeParam to both FormatPack and libc's
// vsnprintf("%c", ...) (wrapped in StrPrint) to make sure we get the same
@@ -474,28 +649,50 @@ TYPED_TEST_P(TypedFormatConvertTest, Char) {
// std::numeric_limits::max(), too, but vsnprintf("%c", ...) can't handle
// anything larger than an int. Add in the most extreme values we can without
// exceeding that range.
+ // Special case: Formatting a wchar_t should behave like vsnprintf("%lc").
+ // Technically vsnprintf can accept a wint_t in this case, but since we must
+ // pass a wchar_t to FormatPack, the largest type we can use here is wchar_t.
+ using ArgType =
+ std::conditional_t<std::is_same<T, wchar_t>::value, wchar_t, int>;
static const T kMin =
- static_cast<remove_volatile_t>(std::numeric_limits<int>::min());
- static const T kMax =
- static_cast<remove_volatile_t>(std::numeric_limits<int>::max());
- vals.insert(vals.end(), {kMin + 1, kMin, kMax - 1, kMax});
+ static_cast<remove_volatile_t>(std::numeric_limits<ArgType>::min());
+ static const T kMax = GetMaxForConversion<T>();
+ vals.insert(vals.end(), {static_cast<remove_volatile_t>(kMin + 1), kMin,
+ static_cast<remove_volatile_t>(kMax - 1), kMax});
+ static const auto kMaxWCharT =
+ static_cast<remove_volatile_t>(GetMaxForConversion<wchar_t>());
for (const T c : vals) {
+ SCOPED_TRACE(Esc(c));
const FormatArgImpl args[] = {FormatArgImpl(c)};
UntypedFormatSpecImpl format("%c");
- EXPECT_EQ(StrPrint("%c", static_cast<int>(c)),
- FormatPack(format, absl::MakeSpan(args)));
+ absl::optional<std::string> result = StrPrintChar(c);
+ if (result.has_value()) {
+ EXPECT_EQ(result.value(), FormatPack(format, absl::MakeSpan(args)));
+ }
+
+ // Also test that if the format specifier is "%lc", the argument is treated
+ // as if it's a `wchar_t`.
+ const T wc =
+ std::max(remove_volatile_t{0},
+ std::min(static_cast<remove_volatile_t>(c), kMaxWCharT));
+ SCOPED_TRACE(Esc(wc));
+ const FormatArgImpl wide_args[] = {FormatArgImpl(wc)};
+ UntypedFormatSpecImpl wide_format("%lc");
+ result = StrPrintChar(static_cast<wchar_t>(wc));
+ if (result.has_value()) {
+ EXPECT_EQ(result.value(),
+ FormatPack(wide_format, absl::MakeSpan(wide_args)));
+ }
}
}
REGISTER_TYPED_TEST_SUITE_P(TypedFormatConvertTest, AllIntsWithFlags, Char);
-typedef ::testing::Types<
- int, unsigned, volatile int,
- short, unsigned short,
- long, unsigned long,
- long long, unsigned long long,
- signed char, unsigned char, char>
+typedef ::testing::Types<int, unsigned, volatile int, short, // NOLINT
+ unsigned short, long, unsigned long, // NOLINT
+ long long, unsigned long long, // NOLINT
+ signed char, unsigned char, char, wchar_t>
AllIntTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(TypedFormatConvertTestWithAllIntTypes,
TypedFormatConvertTest, AllIntTypes);
@@ -510,6 +707,22 @@ TEST_F(FormatConvertTest, VectorBool) {
FormatArgImpl(cv[0]), FormatArgImpl(cv[1])})));
}
+TEST_F(FormatConvertTest, UnicodeWideString) {
+ // StrFormat() should be able to convert wide strings containing Unicode
+ // characters (to UTF-8).
+ const FormatArgImpl args[] = {FormatArgImpl(L"\u47e3 \U00011112")};
+ // `u8""` forces UTF-8 encoding; MSVC will default to e.g. CP1252 (and warn)
+ // without it. However, the resulting character type differs between pre-C++20
+ // (`char`) and C++20 (`char8_t`). So deduce the right character type for all
+ // C++ versions, init it with UTF-8, then `memcpy()` to get the result as a
+ // `char*`.
+ using ConstChar8T = std::remove_reference_t<decltype(*u8"a")>;
+ ConstChar8T kOutputUtf8[] = u8"\u47e3 \U00011112";
+ char output[sizeof kOutputUtf8];
+ std::memcpy(output, kOutputUtf8, sizeof kOutputUtf8);
+ EXPECT_EQ(output,
+ FormatPack(UntypedFormatSpecImpl("%ls"), absl::MakeSpan(args)));
+}
TEST_F(FormatConvertTest, Int128) {
absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979;
@@ -684,7 +897,11 @@ TEST_F(FormatConvertTest, Float) {
}
// Remove duplicates to speed up the logic below.
- std::sort(floats.begin(), floats.end());
+ std::sort(floats.begin(), floats.end(), [](const float a, const float b) {
+ if (std::isnan(a)) return false;
+ if (std::isnan(b)) return true;
+ return a < b;
+ });
floats.erase(std::unique(floats.begin(), floats.end()), floats.end());
TestWithMultipleFormatsHelper(floats, {});
@@ -758,7 +975,11 @@ TEST_F(FormatConvertTest, Double) {
}
// Remove duplicates to speed up the logic below.
- std::sort(doubles.begin(), doubles.end());
+ std::sort(doubles.begin(), doubles.end(), [](const double a, const double b) {
+ if (std::isnan(a)) return false;
+ if (std::isnan(b)) return true;
+ return a < b;
+ });
doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());
TestWithMultipleFormatsHelper(doubles, skip_verify);
@@ -1059,7 +1280,7 @@ TEST_F(FormatConvertTest, LongDoubleRoundA) {
// 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) {}
+ friend void AbslFormatFlush(NullSink *, string_view) {}
};
template <typename... T>
@@ -1236,9 +1457,16 @@ TEST_F(FormatConvertTest, ExpectedFailures) {
// 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;
+#if defined(__GLIBC__) && defined(__x86_64__)
+ constexpr bool kIsSupportedGlibc = true;
+#else
+ constexpr bool kIsSupportedGlibc = false;
#endif
+
+ if (!kIsSupportedGlibc) {
+ GTEST_SKIP() << "Test does not support this platform";
+ }
+
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
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index 8de42d2c..173284c6 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -16,16 +16,14 @@
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
-#include <limits.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <ostream>
+#include <string>
#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"
@@ -34,6 +32,7 @@ ABSL_NAMESPACE_BEGIN
enum class FormatConversionChar : uint8_t;
enum class FormatConversionCharSet : uint64_t;
+enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
namespace str_format_internal {
@@ -139,7 +138,8 @@ enum class Flags : uint8_t {
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.
+ // flags are set. This is for when width/precision are specified, or a length
+ // modifier affects the behavior ("%lc").
kNonBasic = 1 << 5,
};
@@ -285,6 +285,8 @@ class FormatConversionSpecImpl {
bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); }
bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); }
+ LengthMod length_mod() const { return length_mod_; }
+
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.
@@ -310,6 +312,7 @@ class FormatConversionSpecImpl {
friend struct str_format_internal::FormatConversionSpecImplFriend;
FormatConversionChar conv_ = FormatConversionCharInternal::kNone;
Flags flags_;
+ LengthMod length_mod_ = LengthMod::none;
int width_;
int precision_;
};
@@ -318,6 +321,9 @@ struct FormatConversionSpecImplFriend final {
static void SetFlags(Flags f, FormatConversionSpecImpl* conv) {
conv->flags_ = f;
}
+ static void SetLengthMod(LengthMod l, FormatConversionSpecImpl* conv) {
+ conv->length_mod_ = l;
+ }
static void SetConversionChar(FormatConversionChar c,
FormatConversionSpecImpl* conv) {
conv->conv_ = c;
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
index 35b6d49c..b1d6d5fd 100644
--- a/absl/strings/internal/str_format/parser.h
+++ b/absl/strings/internal/str_format/parser.h
@@ -15,22 +15,23 @@
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
-#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <cassert>
-#include <cstdint>
+#include <cstring>
#include <initializer_list>
-#include <iosfwd>
-#include <iterator>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
+#include "absl/base/config.h"
+#include "absl/base/optimization.h"
#include "absl/strings/internal/str_format/checker.h"
#include "absl/strings/internal/str_format/constexpr_parser.h"
#include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
index 021f6a87..e2225c60 100644
--- a/absl/strings/internal/str_format/parser_test.cc
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -15,10 +15,18 @@
#include "absl/strings/internal/str_format/parser.h"
#include <string.h>
+#include <algorithm>
+#include <initializer_list>
+#include <string>
+#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
#include "absl/base/macros.h"
+#include "absl/strings/internal/str_format/constexpr_parser.h"
+#include "absl/strings/internal/str_format/extension.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -303,7 +311,7 @@ TEST_F(ConsumeUnboundConversionTest, BasicFlag) {
}
// Flag is off
- for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) {
+ for (const char* fmt : {"3d", ".llx", "-G", "1$#X", "lc"}) {
SCOPED_TRACE(fmt);
EXPECT_TRUE(Run(fmt));
EXPECT_NE(o.flags, Flags::kBasic);
diff --git a/absl/strings/match.cc b/absl/strings/match.cc
index 3b81b2c0..72ae6a43 100644
--- a/absl/strings/match.cc
+++ b/absl/strings/match.cc
@@ -17,10 +17,13 @@
#include <algorithm>
#include <cstdint>
+#include "absl/base/config.h"
#include "absl/base/internal/endian.h"
+#include "absl/base/optimization.h"
#include "absl/numeric/bits.h"
#include "absl/strings/ascii.h"
#include "absl/strings/internal/memutil.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
diff --git a/absl/strings/match_test.cc b/absl/strings/match_test.cc
index 71618f71..6218ce4e 100644
--- a/absl/strings/match_test.cc
+++ b/absl/strings/match_test.cc
@@ -14,7 +14,10 @@
#include "absl/strings/match.h"
+#include <string>
+
#include "gtest/gtest.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index c43c6bcc..882c3a8b 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -20,32 +20,36 @@
#include <algorithm>
#include <cassert>
#include <cfloat> // for DBL_DIG and FLT_DIG
+#include <climits>
#include <cmath> // for HUGE_VAL
+#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <limits>
-#include <memory>
+#include <system_error> // NOLINT(build/c++11)
+#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
#include "absl/base/internal/endian.h"
#include "absl/base/internal/raw_logging.h"
+#include "absl/base/nullability.h"
#include "absl/base/optimization.h"
#include "absl/numeric/bits.h"
+#include "absl/numeric/int128.h"
#include "absl/strings/ascii.h"
#include "absl/strings/charconv.h"
-#include "absl/strings/escaping.h"
-#include "absl/strings/internal/memutil.h"
#include "absl/strings/match.h"
-#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
-bool SimpleAtof(absl::string_view str, float* out) {
+bool SimpleAtof(absl::string_view str, absl::Nonnull<float*> out) {
*out = 0.0;
str = StripAsciiWhitespace(str);
// std::from_chars doesn't accept an initial +, but SimpleAtof does, so if one
@@ -76,7 +80,7 @@ bool SimpleAtof(absl::string_view str, float* out) {
return true;
}
-bool SimpleAtod(absl::string_view str, double* out) {
+bool SimpleAtod(absl::string_view str, absl::Nonnull<double*> out) {
*out = 0.0;
str = StripAsciiWhitespace(str);
// std::from_chars doesn't accept an initial +, but SimpleAtod does, so if one
@@ -107,7 +111,7 @@ bool SimpleAtod(absl::string_view str, double* out) {
return true;
}
-bool SimpleAtob(absl::string_view str, bool* out) {
+bool SimpleAtob(absl::string_view str, absl::Nonnull<bool*> out) {
ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr.");
if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") ||
EqualsIgnoreCase(str, "yes") || EqualsIgnoreCase(str, "y") ||
@@ -155,29 +159,71 @@ constexpr uint32_t kTwoZeroBytes = 0x0101 * '0';
constexpr uint64_t kFourZeroBytes = 0x01010101 * '0';
constexpr uint64_t kEightZeroBytes = 0x0101010101010101ull * '0';
-// * 103 / 1024 is a division by 10 for values from 0 to 99. It's also a
-// division of a structure [k takes 2 bytes][m takes 2 bytes], then * 103 / 1024
-// will be [k / 10][m / 10]. It allows parallel division.
-constexpr uint64_t kDivisionBy10Mul = 103u;
+template <typename T>
+constexpr T Pow(T base, uint32_t n) {
+ // Exponentiation by squaring
+ return static_cast<T>((n > 1 ? Pow(base * base, n >> 1) : static_cast<T>(1)) *
+ ((n & 1) ? base : static_cast<T>(1)));
+}
+
+// Given n, calculates C where the following holds for all 0 <= x < Pow(100, n):
+// x / Pow(10, n) == x * C / Pow(2, n * 10)
+// In other words, it allows us to divide by a power of 10 via a single
+// multiplication and bit shifts, assuming the input will be smaller than the
+// square of that power of 10.
+template <typename T>
+constexpr T ComputePowerOf100DivisionCoefficient(uint32_t n) {
+ if (n > 4) {
+ // This doesn't work for large powers of 100, due to overflow
+ abort();
+ }
+ T denom = 16 - 1;
+ T num = (denom + 1) - 10;
+ T gcd = 3; // Greatest common divisor of numerator and denominator
+ denom = Pow(denom / gcd, n);
+ num = Pow(num / gcd, 9 * n);
+ T quotient = num / denom;
+ if (num % denom >= denom / 2) {
+ // Round up, since the remainder is more than half the denominator
+ ++quotient;
+ }
+ return quotient;
+}
+
+// * kDivisionBy10Mul / kDivisionBy10Div is a division by 10 for values from 0
+// to 99. It's also a division of a structure [k takes 2 bytes][m takes 2
+// bytes], then * kDivisionBy10Mul / kDivisionBy10Div will be [k / 10][m / 10].
+// It allows parallel division.
+constexpr uint64_t kDivisionBy10Mul =
+ ComputePowerOf100DivisionCoefficient<uint64_t>(1);
+static_assert(kDivisionBy10Mul == 103,
+ "division coefficient for 10 is incorrect");
constexpr uint64_t kDivisionBy10Div = 1 << 10;
-// * 10486 / 1048576 is a division by 100 for values from 0 to 9999.
-constexpr uint64_t kDivisionBy100Mul = 10486u;
+// * kDivisionBy100Mul / kDivisionBy100Div is a division by 100 for values from
+// 0 to 9999.
+constexpr uint64_t kDivisionBy100Mul =
+ ComputePowerOf100DivisionCoefficient<uint64_t>(2);
+static_assert(kDivisionBy100Mul == 10486,
+ "division coefficient for 100 is incorrect");
constexpr uint64_t kDivisionBy100Div = 1 << 20;
-// Encode functions write the ASCII output of input `n` to `out_str`.
-inline char* EncodeHundred(uint32_t n, char* out_str) {
- int num_digits = static_cast<int>(n - 10) >> 8;
- uint32_t base = kTwoZeroBytes;
- uint32_t div10 = (n * kDivisionBy10Mul) / kDivisionBy10Div;
- uint32_t mod10 = n - 10u * div10;
- base += div10 + (mod10 << 8);
- base >>= num_digits & 8;
- little_endian::Store16(out_str, static_cast<uint16_t>(base));
- return out_str + 2 + num_digits;
+static_assert(ComputePowerOf100DivisionCoefficient<uint64_t>(3) == 1073742,
+ "division coefficient for 1000 is incorrect");
+
+// Same as `PrepareEightDigits`, but produces 2 digits for integers < 100.
+inline uint32_t PrepareTwoDigitsImpl(uint32_t i, bool reversed) {
+ assert(i < 100);
+ uint32_t div10 = (i * kDivisionBy10Mul) / kDivisionBy10Div;
+ uint32_t mod10 = i - 10u * div10;
+ return (div10 << (reversed ? 8 : 0)) + (mod10 << (reversed ? 0 : 8));
+}
+inline uint32_t PrepareTwoDigits(uint32_t i) {
+ return PrepareTwoDigitsImpl(i, false);
}
-inline char* EncodeTenThousand(uint32_t n, char* out_str) {
+// Same as `PrepareEightDigits`, but produces 4 digits for integers < 10000.
+inline uint32_t PrepareFourDigitsImpl(uint32_t n, bool reversed) {
// We split lower 2 digits and upper 2 digits of n into 2 byte consecutive
// blocks. 123 -> [\0\1][\0\23]. We divide by 10 both blocks
// (it's 1 division + zeroing upper bits), and compute modulo 10 as well "in
@@ -185,146 +231,335 @@ inline char* EncodeTenThousand(uint32_t n, char* out_str) {
// strip trailing zeros, add ASCII '0000' and return.
uint32_t div100 = (n * kDivisionBy100Mul) / kDivisionBy100Div;
uint32_t mod100 = n - 100ull * div100;
- uint32_t hundreds = (mod100 << 16) + div100;
+ uint32_t hundreds =
+ (mod100 << (reversed ? 0 : 16)) + (div100 << (reversed ? 16 : 0));
uint32_t tens = (hundreds * kDivisionBy10Mul) / kDivisionBy10Div;
tens &= (0xFull << 16) | 0xFull;
- tens += (hundreds - 10ull * tens) << 8;
- ABSL_ASSUME(tens != 0);
- // The result can contain trailing zero bits, we need to strip them to a first
- // significant byte in a final representation. For example, for n = 123, we
- // have tens to have representation \0\1\2\3. We do `& -8` to round
- // to a multiple to 8 to strip zero bytes, not all zero bits.
- // countr_zero to help.
- // 0 minus 8 to make MSVC happy.
- uint32_t zeroes = static_cast<uint32_t>(absl::countr_zero(tens)) & (0 - 8ull);
- tens += kFourZeroBytes;
- tens >>= zeroes;
- little_endian::Store32(out_str, tens);
- return out_str + sizeof(tens) - zeroes / 8;
+ tens = (tens << (reversed ? 8 : 0)) +
+ static_cast<uint32_t>((hundreds - 10ull * tens) << (reversed ? 0 : 8));
+ return tens;
+}
+inline uint32_t PrepareFourDigits(uint32_t n) {
+ return PrepareFourDigitsImpl(n, false);
+}
+inline uint32_t PrepareFourDigitsReversed(uint32_t n) {
+ return PrepareFourDigitsImpl(n, true);
}
-// Prepare functions return an integer that should be written to out_str
-// (but possibly include trailing zeros).
-// For hi < 10000, lo < 10000 returns uint64_t as encoded in ASCII with
-// possibly trailing zeroes of the number hi * 10000 + lo.
-inline uint64_t PrepareTenThousands(uint64_t hi, uint64_t lo) {
- uint64_t merged = hi | (lo << 32);
+// Helper function to produce an ASCII representation of `i`.
+//
+// Function returns an 8-byte integer which when summed with `kEightZeroBytes`,
+// can be treated as a printable buffer with ascii representation of `i`,
+// possibly with leading zeros.
+//
+// Example:
+//
+// uint64_t buffer = PrepareEightDigits(102030) + kEightZeroBytes;
+// char* ascii = reinterpret_cast<char*>(&buffer);
+// // Note two leading zeros:
+// EXPECT_EQ(absl::string_view(ascii, 8), "00102030");
+//
+// If `Reversed` is set to true, the result becomes reversed to "03020100".
+//
+// Pre-condition: `i` must be less than 100000000.
+inline uint64_t PrepareEightDigitsImpl(uint32_t i, bool reversed) {
+ ABSL_ASSUME(i < 10000'0000);
+ // Prepare 2 blocks of 4 digits "in parallel".
+ uint32_t hi = i / 10000;
+ uint32_t lo = i % 10000;
+ uint64_t merged = (uint64_t{hi} << (reversed ? 32 : 0)) |
+ (uint64_t{lo} << (reversed ? 0 : 32));
uint64_t div100 = ((merged * kDivisionBy100Mul) / kDivisionBy100Div) &
((0x7Full << 32) | 0x7Full);
uint64_t mod100 = merged - 100ull * div100;
- uint64_t hundreds = (mod100 << 16) + div100;
+ uint64_t hundreds =
+ (mod100 << (reversed ? 0 : 16)) + (div100 << (reversed ? 16 : 0));
uint64_t tens = (hundreds * kDivisionBy10Mul) / kDivisionBy10Div;
tens &= (0xFull << 48) | (0xFull << 32) | (0xFull << 16) | 0xFull;
- tens += (hundreds - 10ull * tens) << 8;
+ tens = (tens << (reversed ? 8 : 0)) +
+ ((hundreds - 10ull * tens) << (reversed ? 0 : 8));
return tens;
}
+inline uint64_t PrepareEightDigits(uint32_t i) {
+ return PrepareEightDigitsImpl(i, false);
+}
+inline uint64_t PrepareEightDigitsReversed(uint32_t i) {
+ return PrepareEightDigitsImpl(i, true);
+}
+
+template <typename T, typename BackwardIt>
+class FastUIntToStringConverter {
+ static_assert(
+ std::is_same<T, decltype(+std::declval<T>())>::value,
+ "to avoid code bloat, only instantiate this for int and larger types");
+ static_assert(std::is_unsigned<T>::value,
+ "this class is only for unsigned types");
+
+ public:
+ // Outputs the given number backward (like with std::copy_backward),
+ // starting from the end of the string.
+ // The number of digits in the number must have been already measured and
+ // passed *exactly*, otherwise the behavior is undefined.
+ // (This is an optimization, as calculating the number of digits again would
+ // slow down the hot path.)
+ // Returns an iterator to the start of the suffix that was appended.
+ static BackwardIt FastIntToBufferBackward(T v, BackwardIt end) {
+ // THIS IS A HOT FUNCTION with a very deliberate structure to exploit branch
+ // prediction and shorten the critical path for smaller numbers.
+ // Do not move around the if/else blocks or attempt to simplify it
+ // without benchmarking any changes.
+
+ if (v < 10) {
+ goto AT_LEAST_1 /* NOTE: mandatory for the 0 case */;
+ }
+ if (v < 1000) {
+ goto AT_LEAST_10;
+ }
+ if (v < 10000000) {
+ goto AT_LEAST_1000;
+ }
+
+ if (v >= 100000000 / 10) {
+ if (v >= 10000000000000000 / 10) {
+ DoFastIntToBufferBackward<8>(v, end);
+ }
+ DoFastIntToBufferBackward<8>(v, end);
+ }
+
+ if (v >= 10000 / 10) {
+ AT_LEAST_1000:
+ DoFastIntToBufferBackward<4>(v, end);
+ }
+
+ if (v >= 100 / 10) {
+ AT_LEAST_10:
+ DoFastIntToBufferBackward<2>(v, end);
+ }
-inline char* EncodeFullU32(uint32_t n, char* out_str) {
- if (n < 100'000'000) {
- uint64_t bottom = PrepareTenThousands(n / 10000, n % 10000);
- ABSL_ASSUME(bottom != 0);
- // 0 minus 8 to make MSVC happy.
- uint32_t zeroes = static_cast<uint32_t>(absl::countr_zero(bottom))
- & (0 - 8ull);
- uint64_t bottom_res = bottom + kEightZeroBytes;
- bottom_res >>= zeroes;
- little_endian::Store64(out_str, bottom_res);
- return out_str + sizeof(bottom) - zeroes / 8;
+ if (v >= 10 / 10) {
+ AT_LEAST_1:
+ end = DoFastIntToBufferBackward(v, end, std::integral_constant<int, 1>());
+ }
+ return end;
}
- uint32_t top = n / 100'000'000;
- n %= 100'000'000;
- uint64_t bottom = PrepareTenThousands(n / 10000, n % 10000);
- uint64_t bottom_res = bottom + kEightZeroBytes;
- out_str = EncodeHundred(top, out_str);
- little_endian::Store64(out_str, bottom_res);
- return out_str + sizeof(bottom);
-}
-} // namespace
+ private:
+ // Only assume pointers are contiguous for now. String and vector iterators
+ // could be special-cased as well, but there's no need for them here.
+ // With C++20 we can probably switch to std::contiguous_iterator_tag.
+ static constexpr bool kIsContiguousIterator =
+ std::is_pointer<BackwardIt>::value;
+
+ template <int Exponent>
+ static void DoFastIntToBufferBackward(T& v, BackwardIt& end) {
+ constexpr T kModulus = Pow<T>(10, Exponent);
+ T remainder = static_cast<T>(v % kModulus);
+ v = static_cast<T>(v / kModulus);
+ end = DoFastIntToBufferBackward(remainder, end,
+ std::integral_constant<int, Exponent>());
+ }
-void numbers_internal::PutTwoDigits(uint32_t i, char* buf) {
- assert(i < 100);
- uint32_t base = kTwoZeroBytes;
- uint32_t div10 = (i * kDivisionBy10Mul) / kDivisionBy10Div;
- uint32_t mod10 = i - 10u * div10;
- base += div10 + (mod10 << 8);
- little_endian::Store16(buf, static_cast<uint16_t>(base));
-}
+ static BackwardIt DoFastIntToBufferBackward(const T&, BackwardIt end,
+ std::integral_constant<int, 0>) {
+ return end;
+ }
-char* numbers_internal::FastIntToBuffer(uint32_t n, char* out_str) {
- if (n < 100) {
- out_str = EncodeHundred(n, out_str);
- goto set_last_zero;
+ static BackwardIt DoFastIntToBufferBackward(T v, BackwardIt end,
+ std::integral_constant<int, 1>) {
+ *--end = static_cast<char>('0' + v);
+ return DoFastIntToBufferBackward(v, end, std::integral_constant<int, 0>());
}
- if (n < 10000) {
- out_str = EncodeTenThousand(n, out_str);
- goto set_last_zero;
+
+ static BackwardIt DoFastIntToBufferBackward(T v, BackwardIt end,
+ std::integral_constant<int, 4>) {
+ if (kIsContiguousIterator) {
+ const uint32_t digits =
+ PrepareFourDigits(static_cast<uint32_t>(v)) + kFourZeroBytes;
+ end -= sizeof(digits);
+ little_endian::Store32(&*end, digits);
+ } else {
+ uint32_t digits =
+ PrepareFourDigitsReversed(static_cast<uint32_t>(v)) + kFourZeroBytes;
+ for (size_t i = 0; i < sizeof(digits); ++i) {
+ *--end = static_cast<char>(digits);
+ digits >>= CHAR_BIT;
+ }
+ }
+ return end;
}
- out_str = EncodeFullU32(n, out_str);
-set_last_zero:
- *out_str = '\0';
- return out_str;
-}
-char* numbers_internal::FastIntToBuffer(int32_t i, char* buffer) {
- uint32_t u = static_cast<uint32_t>(i);
- if (i < 0) {
- *buffer++ = '-';
- // We need to do the negation in modular (i.e., "unsigned")
- // arithmetic; MSVC++ apparently warns for plain "-u", so
- // we write the equivalent expression "0 - u" instead.
- u = 0 - u;
+ static BackwardIt DoFastIntToBufferBackward(T v, BackwardIt end,
+ std::integral_constant<int, 8>) {
+ if (kIsContiguousIterator) {
+ const uint64_t digits =
+ PrepareEightDigits(static_cast<uint32_t>(v)) + kEightZeroBytes;
+ end -= sizeof(digits);
+ little_endian::Store64(&*end, digits);
+ } else {
+ uint64_t digits = PrepareEightDigitsReversed(static_cast<uint32_t>(v)) +
+ kEightZeroBytes;
+ for (size_t i = 0; i < sizeof(digits); ++i) {
+ *--end = static_cast<char>(digits);
+ digits >>= CHAR_BIT;
+ }
+ }
+ return end;
}
- return numbers_internal::FastIntToBuffer(u, buffer);
-}
-char* numbers_internal::FastIntToBuffer(uint64_t i, char* buffer) {
- uint32_t u32 = static_cast<uint32_t>(i);
- if (u32 == i) return numbers_internal::FastIntToBuffer(u32, buffer);
-
- // 10**9 < 2**32 <= i < 10**10, we can do 2+8
- uint64_t div08 = i / 100'000'000ull;
- uint64_t mod08 = i % 100'000'000ull;
- uint64_t mod_result =
- PrepareTenThousands(mod08 / 10000, mod08 % 10000) + kEightZeroBytes;
- if (i < 10'000'000'000ull) {
- buffer = EncodeHundred(static_cast<uint32_t>(div08), buffer);
- little_endian::Store64(buffer, mod_result);
- buffer += 8;
- goto set_last_zero;
+ template <int Digits>
+ static BackwardIt DoFastIntToBufferBackward(
+ T v, BackwardIt end, std::integral_constant<int, Digits>) {
+ constexpr int kLogModulus = Digits - Digits / 2;
+ constexpr T kModulus = Pow(static_cast<T>(10), kLogModulus);
+ bool is_safe_to_use_division_trick = Digits <= 8;
+ T quotient, remainder;
+ if (is_safe_to_use_division_trick) {
+ constexpr uint64_t kCoefficient =
+ ComputePowerOf100DivisionCoefficient<uint64_t>(kLogModulus);
+ quotient = (v * kCoefficient) >> (10 * kLogModulus);
+ remainder = v - quotient * kModulus;
+ } else {
+ quotient = v / kModulus;
+ remainder = v % kModulus;
+ }
+ end = DoFastIntToBufferBackward(remainder, end,
+ std::integral_constant<int, kLogModulus>());
+ return DoFastIntToBufferBackward(
+ quotient, end, std::integral_constant<int, Digits - kLogModulus>());
}
+};
- // i < 10**16, in this case 8+8
- if (i < 10'000'000'000'000'000ull) {
- buffer = EncodeFullU32(static_cast<uint32_t>(div08), buffer);
- little_endian::Store64(buffer, mod_result);
- buffer += 8;
- goto set_last_zero;
- } else {
- // 4 + 8 + 8
- uint64_t div016 = i / 10'000'000'000'000'000ull;
- buffer = EncodeTenThousand(static_cast<uint32_t>(div016), buffer);
- uint64_t mid_result = div08 - div016 * 100'000'000ull;
- mid_result = PrepareTenThousands(mid_result / 10000, mid_result % 10000) +
- kEightZeroBytes;
- little_endian::Store64(buffer, mid_result);
- buffer += 8;
- little_endian::Store64(buffer, mod_result);
- buffer += 8;
- goto set_last_zero;
+// Returns an iterator to the start of the suffix that was appended
+template <typename T, typename BackwardIt>
+std::enable_if_t<std::is_unsigned<T>::value, BackwardIt>
+DoFastIntToBufferBackward(T v, BackwardIt end, uint32_t digits) {
+ using PromotedT = std::decay_t<decltype(+v)>;
+ using Converter = FastUIntToStringConverter<PromotedT, BackwardIt>;
+ (void)digits;
+ return Converter().FastIntToBufferBackward(v, end);
+}
+
+template <typename T, typename BackwardIt>
+std::enable_if_t<std::is_signed<T>::value, BackwardIt>
+DoFastIntToBufferBackward(T v, BackwardIt end, uint32_t digits) {
+ if (absl::numbers_internal::IsNegative(v)) {
+ // Store the minus sign *before* we produce the number itself, not after.
+ // This gets us a tail call.
+ end[-static_cast<ptrdiff_t>(digits) - 1] = '-';
}
-set_last_zero:
- *buffer = '\0';
+ return DoFastIntToBufferBackward(
+ absl::numbers_internal::UnsignedAbsoluteValue(v), end, digits);
+}
+
+template <class T>
+std::enable_if_t<std::is_integral<T>::value, int>
+GetNumDigitsOrNegativeIfNegativeImpl(T v) {
+ const auto /* either bool or std::false_type */ is_negative =
+ absl::numbers_internal::IsNegative(v);
+ const int digits = static_cast<int>(absl::numbers_internal::Base10Digits(
+ absl::numbers_internal::UnsignedAbsoluteValue(v)));
+ return is_negative ? ~digits : digits;
+}
+
+} // namespace
+
+void numbers_internal::PutTwoDigits(uint32_t i, absl::Nonnull<char*> buf) {
+ little_endian::Store16(
+ buf, static_cast<uint16_t>(PrepareTwoDigits(i) + kTwoZeroBytes));
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+ uint32_t i, absl::Nonnull<char*> buffer) {
+ const uint32_t digits = absl::numbers_internal::Base10Digits(i);
+ buffer += digits;
+ *buffer = '\0'; // We're going backward, so store this first
+ FastIntToBufferBackward(i, buffer, digits);
return buffer;
}
-char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) {
- uint64_t u = static_cast<uint64_t>(i);
- if (i < 0) {
- *buffer++ = '-';
- u = 0 - u;
- }
- return numbers_internal::FastIntToBuffer(u, buffer);
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+ int32_t i, absl::Nonnull<char*> buffer) {
+ buffer += static_cast<int>(i < 0);
+ uint32_t digits = absl::numbers_internal::Base10Digits(
+ absl::numbers_internal::UnsignedAbsoluteValue(i));
+ buffer += digits;
+ *buffer = '\0'; // We're going backward, so store this first
+ FastIntToBufferBackward(i, buffer, digits);
+ return buffer;
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+ uint64_t i, absl::Nonnull<char*> buffer) {
+ uint32_t digits = absl::numbers_internal::Base10Digits(i);
+ buffer += digits;
+ *buffer = '\0'; // We're going backward, so store this first
+ FastIntToBufferBackward(i, buffer, digits);
+ return buffer;
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBuffer(
+ int64_t i, absl::Nonnull<char*> buffer) {
+ buffer += static_cast<int>(i < 0);
+ uint32_t digits = absl::numbers_internal::Base10Digits(
+ absl::numbers_internal::UnsignedAbsoluteValue(i));
+ buffer += digits;
+ *buffer = '\0'; // We're going backward, so store this first
+ FastIntToBufferBackward(i, buffer, digits);
+ return buffer;
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+ uint32_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+ return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+ int32_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+ return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+ uint64_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+ return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+absl::Nonnull<char*> numbers_internal::FastIntToBufferBackward(
+ int64_t i, absl::Nonnull<char*> buffer_end, uint32_t exact_digit_count) {
+ return DoFastIntToBufferBackward(i, buffer_end, exact_digit_count);
+}
+
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(signed char v) {
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(unsigned char v) {
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(short v) { // NOLINT
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(
+ unsigned short v) { // NOLINT
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(int v) {
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(unsigned int v) {
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(long v) { // NOLINT
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(
+ unsigned long v) { // NOLINT
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(long long v) { // NOLINT
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
+}
+int numbers_internal::GetNumDigitsOrNegativeIfNegative(
+ unsigned long long v) { // NOLINT
+ return GetNumDigitsOrNegativeIfNegativeImpl(v);
}
// Given a 128-bit number expressed as a pair of uint64_t, high half first,
@@ -534,7 +769,8 @@ static ExpDigits SplitToSix(const double value) {
// Helper function for fast formatting of floating-point.
// The result is the same as "%g", a.k.a. "%.6g".
-size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) {
+size_t numbers_internal::SixDigitsToBuffer(double d,
+ absl::Nonnull<char*> const buffer) {
static_assert(std::numeric_limits<float>::is_iec559,
"IEEE-754/IEC-559 support only");
@@ -681,9 +917,10 @@ static const int8_t kAsciiToInt[256] = {
36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36};
// Parse the sign and optional hex or oct prefix in text.
-inline bool safe_parse_sign_and_base(absl::string_view* text /*inout*/,
- int* base_ptr /*inout*/,
- bool* negative_ptr /*output*/) {
+inline bool safe_parse_sign_and_base(
+ absl::Nonnull<absl::string_view*> text /*inout*/,
+ absl::Nonnull<int*> base_ptr /*inout*/,
+ absl::Nonnull<bool*> negative_ptr /*output*/) {
if (text->data() == nullptr) {
return false;
}
@@ -968,7 +1205,7 @@ ABSL_CONST_INIT const IntType LookupTables<IntType>::kVminOverBase[] =
template <typename IntType>
inline bool safe_parse_positive_int(absl::string_view text, int base,
- IntType* value_p) {
+ absl::Nonnull<IntType*> value_p) {
IntType value = 0;
const IntType vmax = std::numeric_limits<IntType>::max();
assert(vmax > 0);
@@ -1005,7 +1242,7 @@ inline bool safe_parse_positive_int(absl::string_view text, int base,
template <typename IntType>
inline bool safe_parse_negative_int(absl::string_view text, int base,
- IntType* value_p) {
+ absl::Nonnull<IntType*> value_p) {
IntType value = 0;
const IntType vmin = std::numeric_limits<IntType>::min();
assert(vmin < 0);
@@ -1049,8 +1286,8 @@ inline bool safe_parse_negative_int(absl::string_view text, int base,
// Input format based on POSIX.1-2008 strtol
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html
template <typename IntType>
-inline bool safe_int_internal(absl::string_view text, IntType* value_p,
- int base) {
+inline bool safe_int_internal(absl::string_view text,
+ absl::Nonnull<IntType*> value_p, int base) {
*value_p = 0;
bool negative;
if (!safe_parse_sign_and_base(&text, &base, &negative)) {
@@ -1064,8 +1301,8 @@ inline bool safe_int_internal(absl::string_view text, IntType* value_p,
}
template <typename IntType>
-inline bool safe_uint_internal(absl::string_view text, IntType* value_p,
- int base) {
+inline bool safe_uint_internal(absl::string_view text,
+ absl::Nonnull<IntType*> value_p, int base) {
*value_p = 0;
bool negative;
if (!safe_parse_sign_and_base(&text, &base, &negative) || negative) {
@@ -1099,27 +1336,33 @@ ABSL_CONST_INIT ABSL_DLL const char kHexTable[513] =
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
-bool safe_strto32_base(absl::string_view text, int32_t* value, int base) {
+bool safe_strto32_base(absl::string_view text, absl::Nonnull<int32_t*> value,
+ int base) {
return safe_int_internal<int32_t>(text, value, base);
}
-bool safe_strto64_base(absl::string_view text, int64_t* value, int base) {
+bool safe_strto64_base(absl::string_view text, absl::Nonnull<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) {
+bool safe_strto128_base(absl::string_view text, absl::Nonnull<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) {
+bool safe_strtou32_base(absl::string_view text, absl::Nonnull<uint32_t*> value,
+ int base) {
return safe_uint_internal<uint32_t>(text, value, base);
}
-bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) {
+bool safe_strtou64_base(absl::string_view text, absl::Nonnull<uint64_t*> value,
+ int base) {
return safe_uint_internal<uint64_t>(text, value, base);
}
-bool safe_strtou128_base(absl::string_view text, uint128* value, int base) {
+bool safe_strtou128_base(absl::string_view text, absl::Nonnull<uint128*> value,
+ int base) {
return safe_uint_internal<absl::uint128>(text, value, base);
}
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index d7630cef..ad4e66b6 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -32,6 +32,7 @@
#endif
#include <cstddef>
+#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <ctime>
@@ -39,9 +40,12 @@
#include <string>
#include <type_traits>
+#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/endian.h"
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
+#include "absl/base/optimization.h"
#include "absl/base/port.h"
#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
@@ -59,7 +63,8 @@ ABSL_NAMESPACE_BEGIN
// encountered, this function returns `false`, leaving `out` in an unspecified
// state.
template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out);
+ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str,
+ absl::Nonnull<int_type*> out);
// SimpleAtof()
//
@@ -70,7 +75,8 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out);
// allowed formats for `str`, except SimpleAtof() is locale-independent and will
// always use the "C" locale. If any errors are encountered, this function
// returns `false`, leaving `out` in an unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out);
+ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str,
+ absl::Nonnull<float*> out);
// SimpleAtod()
//
@@ -81,7 +87,8 @@ ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out);
// allowed formats for `str`, except SimpleAtod is locale-independent and will
// always use the "C" locale. If any errors are encountered, this function
// returns `false`, leaving `out` in an unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out);
+ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str,
+ absl::Nonnull<double*> out);
// SimpleAtob()
//
@@ -91,7 +98,8 @@ ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out);
// are interpreted as boolean `false`: "false", "f", "no", "n", "0". If any
// errors are encountered, this function returns `false`, leaving `out` in an
// unspecified state.
-ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out);
+ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str,
+ absl::Nonnull<bool*> out);
// SimpleHexAtoi()
//
@@ -104,13 +112,14 @@ ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out);
// 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);
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str,
+ absl::Nonnull<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_MUST_USE_RESULT inline bool SimpleHexAtoi(
+ absl::string_view str, absl::Nonnull<absl::int128*> out);
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(
+ absl::string_view str, absl::Nonnull<absl::uint128*> out);
ABSL_NAMESPACE_END
} // namespace absl
@@ -132,42 +141,136 @@ ABSL_DLL extern const char
// PutTwoDigits(42, buf);
// // buf[0] == '4'
// // buf[1] == '2'
-void PutTwoDigits(uint32_t i, char* buf);
+void PutTwoDigits(uint32_t i, absl::Nonnull<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,
- int base);
+bool safe_strto32_base(absl::string_view text, absl::Nonnull<int32_t*> value,
+ int base);
+bool safe_strto64_base(absl::string_view text, absl::Nonnull<int64_t*> value,
+ int base);
+bool safe_strto128_base(absl::string_view text,
+ absl::Nonnull<absl::int128*> value, int base);
+bool safe_strtou32_base(absl::string_view text, absl::Nonnull<uint32_t*> value,
+ int base);
+bool safe_strtou64_base(absl::string_view text, absl::Nonnull<uint64_t*> value,
+ int base);
+bool safe_strtou128_base(absl::string_view text,
+ absl::Nonnull<absl::uint128*> value, int base);
static const int kFastToBufferSize = 32;
static const int kSixDigitsToBufferSize = 16;
+template <class T>
+std::enable_if_t<!std::is_unsigned<T>::value, bool> IsNegative(const T& v) {
+ return v < T();
+}
+
+template <class T>
+std::enable_if_t<std::is_unsigned<T>::value, std::false_type> IsNegative(
+ const T&) {
+ // The integer is unsigned, so return a compile-time constant.
+ // This can help the optimizer avoid having to prove bool to be false later.
+ return std::false_type();
+}
+
+template <class T>
+std::enable_if_t<std::is_unsigned<std::decay_t<T>>::value, T&&>
+UnsignedAbsoluteValue(T&& v ABSL_ATTRIBUTE_LIFETIME_BOUND) {
+ // The value is unsigned; just return the original.
+ return std::forward<T>(v);
+}
+
+template <class T>
+ABSL_ATTRIBUTE_CONST_FUNCTION
+ std::enable_if_t<!std::is_unsigned<T>::value, std::make_unsigned_t<T>>
+ UnsignedAbsoluteValue(T v) {
+ using U = std::make_unsigned_t<T>;
+ return IsNegative(v) ? U() - static_cast<U>(v) : static_cast<U>(v);
+}
+
+// Returns the number of base-10 digits in the given number.
+// Note that this strictly counts digits. It does not count the sign.
+// The `initial_digits` parameter is the starting point, which is normally equal
+// to 1 because the number of digits in 0 is 1 (a special case).
+// However, callers may e.g. wish to change it to 2 to account for the sign.
+template <typename T>
+std::enable_if_t<std::is_unsigned<T>::value, uint32_t> Base10Digits(
+ T v, const uint32_t initial_digits = 1) {
+ uint32_t r = initial_digits;
+ // If code size becomes an issue, the 'if' stage can be removed for a minor
+ // performance loss.
+ for (;;) {
+ if (ABSL_PREDICT_TRUE(v < 10 * 10)) {
+ r += (v >= 10);
+ break;
+ }
+ if (ABSL_PREDICT_TRUE(v < 1000 * 10)) {
+ r += (v >= 1000) + 2;
+ break;
+ }
+ if (ABSL_PREDICT_TRUE(v < 100000 * 10)) {
+ r += (v >= 100000) + 4;
+ break;
+ }
+ r += 6;
+ v = static_cast<T>(v / 1000000);
+ }
+ return r;
+}
+
+template <typename T>
+std::enable_if_t<std::is_signed<T>::value, uint32_t> Base10Digits(
+ T v, uint32_t r = 1) {
+ // Branchlessly add 1 to account for a minus sign.
+ r += static_cast<uint32_t>(IsNegative(v));
+ return Base10Digits(UnsignedAbsoluteValue(v), r);
+}
+
+// These functions return the number of base-10 digits, but multiplied by -1 if
+// the input itself is negative. This is handy and efficient for later usage,
+// since the bitwise complement of the result becomes equal to the number of
+// characters required.
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ signed char v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ unsigned char v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ short v); // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ unsigned short v); // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(int v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ unsigned int v);
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ long v); // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ unsigned long v); // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ long long v); // NOLINT
+ABSL_ATTRIBUTE_CONST_FUNCTION int GetNumDigitsOrNegativeIfNegative(
+ unsigned long long v); // NOLINT
+
// Helper function for fast formatting of floating-point values.
// The result is the same as printf's "%g", a.k.a. "%.6g"; that is, six
// significant digits are returned, trailing zeros are removed, and numbers
// outside the range 0.0001-999999 are output using scientific notation
// (1.23456e+06). This routine is heavily optimized.
// Required buffer size is `kSixDigitsToBufferSize`.
-size_t SixDigitsToBuffer(double d, char* buffer);
+size_t SixDigitsToBuffer(double d, absl::Nonnull<char*> buffer);
-// These functions are intended for speed. All functions take an output buffer
+// All of these functions take an output buffer
// as an argument and return a pointer to the last byte they wrote, which is the
// terminating '\0'. At most `kFastToBufferSize` bytes are written.
-char* FastIntToBuffer(int32_t, char*);
-char* FastIntToBuffer(uint32_t, char*);
-char* FastIntToBuffer(int64_t, char*);
-char* FastIntToBuffer(uint64_t, char*);
+absl::Nonnull<char*> FastIntToBuffer(int32_t i, absl::Nonnull<char*> buffer);
+absl::Nonnull<char*> FastIntToBuffer(uint32_t i, absl::Nonnull<char*> buffer);
+absl::Nonnull<char*> FastIntToBuffer(int64_t i, absl::Nonnull<char*> buffer);
+absl::Nonnull<char*> FastIntToBuffer(uint64_t i, absl::Nonnull<char*> buffer);
// For enums and integer types that are not an exact match for the types above,
// use templates to call the appropriate one of the four overloads above.
template <typename int_type>
-char* FastIntToBuffer(int_type i, char* buffer) {
+absl::Nonnull<char*> FastIntToBuffer(int_type i, absl::Nonnull<char*> buffer) {
static_assert(sizeof(i) <= 64 / 8,
"FastIntToBuffer works only with 64-bit-or-less integers.");
// TODO(jorg): This signed-ness check is used because it works correctly
@@ -191,10 +294,63 @@ char* FastIntToBuffer(int_type i, char* buffer) {
}
}
+// These functions do NOT add any null-terminator.
+// They return a pointer to the beginning of the written string.
+// The digit counts provided must *exactly* match the number of base-10 digits
+// in the number, or the behavior is undefined.
+// (i.e. do NOT count the minus sign, or over- or under-count the digits.)
+absl::Nonnull<char*> FastIntToBufferBackward(int32_t i,
+ absl::Nonnull<char*> buffer_end,
+ uint32_t exact_digit_count);
+absl::Nonnull<char*> FastIntToBufferBackward(uint32_t i,
+ absl::Nonnull<char*> buffer_end,
+ uint32_t exact_digit_count);
+absl::Nonnull<char*> FastIntToBufferBackward(int64_t i,
+ absl::Nonnull<char*> buffer_end,
+ uint32_t exact_digit_count);
+absl::Nonnull<char*> FastIntToBufferBackward(uint64_t i,
+ absl::Nonnull<char*> buffer_end,
+ uint32_t exact_digit_count);
+
+// For enums and integer types that are not an exact match for the types above,
+// use templates to call the appropriate one of the four overloads above.
+template <typename int_type>
+absl::Nonnull<char*> FastIntToBufferBackward(int_type i,
+ absl::Nonnull<char*> buffer_end,
+ uint32_t exact_digit_count) {
+ static_assert(
+ sizeof(i) <= 64 / 8,
+ "FastIntToBufferBackward works only with 64-bit-or-less integers.");
+ // This signed-ness check is used because it works correctly
+ // with enums, and it also serves to check that int_type is not a pointer.
+ // If one day something like std::is_signed<enum E> works, switch to it.
+ // These conditions are constexpr bools to suppress MSVC warning C4127.
+ constexpr bool kIsSigned = static_cast<int_type>(1) - 2 < 0;
+ constexpr bool kUse64Bit = sizeof(i) > 32 / 8;
+ if (kIsSigned) {
+ if (kUse64Bit) {
+ return FastIntToBufferBackward(static_cast<int64_t>(i), buffer_end,
+ exact_digit_count);
+ } else {
+ return FastIntToBufferBackward(static_cast<int32_t>(i), buffer_end,
+ exact_digit_count);
+ }
+ } else {
+ if (kUse64Bit) {
+ return FastIntToBufferBackward(static_cast<uint64_t>(i), buffer_end,
+ exact_digit_count);
+ } else {
+ return FastIntToBufferBackward(static_cast<uint32_t>(i), buffer_end,
+ exact_digit_count);
+ }
+ }
+}
+
// Implementation of SimpleAtoi, generalized to support arbitrary base (used
// with base different from 10 elsewhere in Abseil implementation).
template <typename int_type>
-ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
+ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s,
+ absl::Nonnull<int_type*> out,
int base) {
static_assert(sizeof(*out) == 4 || sizeof(*out) == 8,
"SimpleAtoi works only with 32-bit or 64-bit integers.");
@@ -237,7 +393,7 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
// without the terminating null character. Thus `out` must be of length >= 16.
// Returns the number of non-pad digits of the output (it can never be zero
// since 0 has one digit).
-inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) {
+inline size_t FastHexToBufferZeroPad16(uint64_t val, absl::Nonnull<char*> out) {
#ifdef ABSL_INTERNAL_HAVE_SSSE3
uint64_t be = absl::big_endian::FromHost64(val);
const auto kNibbleMask = _mm_set1_epi8(0xf);
@@ -263,32 +419,34 @@ inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) {
} // namespace numbers_internal
template <typename int_type>
-ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) {
+ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str,
+ absl::Nonnull<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) {
+ absl::Nonnull<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) {
+ absl::Nonnull<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) {
+ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str,
+ absl::Nonnull<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) {
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(
+ absl::string_view str, absl::Nonnull<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) {
+ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(
+ absl::string_view str, absl::Nonnull<absl::uint128*> out) {
return numbers_internal::safe_strtou128_base(str, out, 16);
}
diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc
index 6e79b3e8..e7cb60a4 100644
--- a/absl/strings/numbers_benchmark.cc
+++ b/absl/strings/numbers_benchmark.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include <cstdint>
+#include <limits>
#include <random>
#include <string>
#include <type_traits>
@@ -23,6 +24,7 @@
#include "absl/random/distributions.h"
#include "absl/random/random.h"
#include "absl/strings/numbers.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index 2864bda2..1ceff70f 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -28,6 +28,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
+#include <ios>
#include <limits>
#include <numeric>
#include <random>
@@ -38,12 +39,14 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/log/log.h"
+#include "absl/numeric/int128.h"
#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"
+#include "absl/strings/string_view.h"
namespace {
@@ -60,6 +63,7 @@ using absl::strings_internal::strtouint32_test_cases;
using absl::strings_internal::strtouint64_test_cases;
using testing::Eq;
using testing::MatchesRegex;
+using testing::Pointee;
// Number of floats to test with.
// 5,000,000 is a reasonable default for a test that only takes a few seconds.
@@ -227,10 +231,15 @@ TEST(Numbers, TestFastPrints) {
CheckInt32(INT_MIN);
CheckInt32(INT_MAX);
CheckInt64(LONG_MIN);
+ CheckInt64(uint64_t{10000000});
+ CheckInt64(uint64_t{100000000});
CheckInt64(uint64_t{1000000000});
CheckInt64(uint64_t{9999999999});
CheckInt64(uint64_t{100000000000000});
CheckInt64(uint64_t{999999999999999});
+ CheckInt64(uint64_t{1000000000000000});
+ CheckInt64(uint64_t{10000000000000000});
+ CheckInt64(uint64_t{100000000000000000});
CheckInt64(uint64_t{1000000000000000000});
CheckInt64(uint64_t{1199999999999999999});
CheckInt64(int64_t{-700000000000000000});
@@ -242,6 +251,8 @@ TEST(Numbers, TestFastPrints) {
CheckUInt64(uint64_t{999999999999999});
CheckUInt64(uint64_t{1000000000000000000});
CheckUInt64(uint64_t{1199999999999999999});
+ CheckUInt64(uint64_t{10000000000000000000u});
+ CheckUInt64(uint64_t{10200300040000500006u});
CheckUInt64(std::numeric_limits<uint64_t>::max());
for (int i = 0; i < 10000; i++) {
@@ -1712,4 +1723,25 @@ TEST(FastHexToBufferZeroPad16, Smoke) {
}
}
+template <typename Int>
+void ExpectWritesNull() {
+ {
+ char buf[absl::numbers_internal::kFastToBufferSize];
+ Int x = std::numeric_limits<Int>::min();
+ EXPECT_THAT(absl::numbers_internal::FastIntToBuffer(x, buf), Pointee('\0'));
+ }
+ {
+ char buf[absl::numbers_internal::kFastToBufferSize];
+ Int x = std::numeric_limits<Int>::max();
+ EXPECT_THAT(absl::numbers_internal::FastIntToBuffer(x, buf), Pointee('\0'));
+ }
+}
+
+TEST(FastIntToBuffer, WritesNull) {
+ ExpectWritesNull<int32_t>();
+ ExpectWritesNull<uint32_t>();
+ ExpectWritesNull<int64_t>();
+ ExpectWritesNull<uint32_t>();
+}
+
} // namespace
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 2e49c31b..098ab183 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -16,13 +16,15 @@
#include <assert.h>
-#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
+#include <initializer_list>
#include <string>
+#include <type_traits>
-#include "absl/strings/ascii.h"
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
@@ -30,6 +32,7 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
+
// ----------------------------------------------------------------------
// StrCat()
// This merges the given strings or integers, with no delimiter. This
@@ -37,9 +40,10 @@ ABSL_NAMESPACE_BEGIN
// of a mix of raw C strings, string_views, strings, and integer values.
// ----------------------------------------------------------------------
+namespace {
// Append is merely a version of memcpy that returns the address of the byte
// after the area just overwritten.
-static char* Append(char* out, const AlphaNum& x) {
+absl::Nonnull<char*> Append(absl::Nonnull<char*> out, const AlphaNum& x) {
// memcpy is allowed to overwrite arbitrary memory, so doing this after the
// call would force an extra fetch of x.size().
char* after = out + x.size();
@@ -49,6 +53,8 @@ static char* Append(char* out, const AlphaNum& x) {
return after;
}
+} // namespace
+
std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
std::string result;
absl::strings_internal::STLStringResizeUninitialized(&result,
@@ -92,6 +98,130 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
namespace strings_internal {
// Do not call directly - these are not part of the public API.
+void STLStringAppendUninitializedAmortized(std::string* dest,
+ size_t to_append) {
+ strings_internal::AppendUninitializedTraits<std::string>::Append(dest,
+ to_append);
+}
+
+template <typename Integer>
+std::enable_if_t<std::is_integral<Integer>::value, std::string> IntegerToString(
+ Integer i) {
+ std::string str;
+ const auto /* either bool or std::false_type */ is_negative =
+ absl::numbers_internal::IsNegative(i);
+ const uint32_t digits = absl::numbers_internal::Base10Digits(
+ absl::numbers_internal::UnsignedAbsoluteValue(i));
+ absl::strings_internal::STLStringResizeUninitialized(
+ &str, digits + static_cast<uint32_t>(is_negative));
+ absl::numbers_internal::FastIntToBufferBackward(i, &str[str.size()], digits);
+ return str;
+}
+
+template <>
+std::string IntegerToString(long i) { // NOLINT
+ if (sizeof(i) <= sizeof(int)) {
+ return IntegerToString(static_cast<int>(i));
+ } else {
+ return IntegerToString(static_cast<long long>(i)); // NOLINT
+ }
+}
+
+template <>
+std::string IntegerToString(unsigned long i) { // NOLINT
+ if (sizeof(i) <= sizeof(unsigned int)) {
+ return IntegerToString(static_cast<unsigned int>(i));
+ } else {
+ return IntegerToString(static_cast<unsigned long long>(i)); // NOLINT
+ }
+}
+
+template <typename Float>
+std::enable_if_t<std::is_floating_point<Float>::value, std::string>
+FloatToString(Float f) {
+ std::string result;
+ strings_internal::STLStringResizeUninitialized(
+ &result, numbers_internal::kSixDigitsToBufferSize);
+ char* start = &result[0];
+ result.erase(numbers_internal::SixDigitsToBuffer(f, start));
+ return result;
+}
+
+std::string SingleArgStrCat(int x) { return IntegerToString(x); }
+std::string SingleArgStrCat(unsigned int x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(unsigned long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(long long x) { return IntegerToString(x); }
+// NOLINTNEXTLINE
+std::string SingleArgStrCat(unsigned long long x) { return IntegerToString(x); }
+std::string SingleArgStrCat(float x) { return FloatToString(x); }
+std::string SingleArgStrCat(double x) { return FloatToString(x); }
+
+template <class Integer>
+std::enable_if_t<std::is_integral<Integer>::value, void> AppendIntegerToString(
+ std::string& str, Integer i) {
+ const auto /* either bool or std::false_type */ is_negative =
+ absl::numbers_internal::IsNegative(i);
+ const uint32_t digits = absl::numbers_internal::Base10Digits(
+ absl::numbers_internal::UnsignedAbsoluteValue(i));
+ absl::strings_internal::STLStringAppendUninitializedAmortized(
+ &str, digits + static_cast<uint32_t>(is_negative));
+ absl::numbers_internal::FastIntToBufferBackward(i, &str[str.size()], digits);
+}
+
+template <>
+void AppendIntegerToString(std::string& str, long i) { // NOLINT
+ if (sizeof(i) <= sizeof(int)) {
+ return AppendIntegerToString(str, static_cast<int>(i));
+ } else {
+ return AppendIntegerToString(str, static_cast<long long>(i)); // NOLINT
+ }
+}
+
+template <>
+void AppendIntegerToString(std::string& str,
+ unsigned long i) { // NOLINT
+ if (sizeof(i) <= sizeof(unsigned int)) {
+ return AppendIntegerToString(str, static_cast<unsigned int>(i));
+ } else {
+ return AppendIntegerToString(str,
+ static_cast<unsigned long long>(i)); // NOLINT
+ }
+}
+
+// `SingleArgStrAppend` overloads are defined here for the same reasons as with
+// `SingleArgStrCat` above.
+void SingleArgStrAppend(std::string& str, int x) {
+ return AppendIntegerToString(str, x);
+}
+
+void SingleArgStrAppend(std::string& str, unsigned int x) {
+ return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, long x) {
+ return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, unsigned long x) {
+ return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, long long x) {
+ return AppendIntegerToString(str, x);
+}
+
+// NOLINTNEXTLINE
+void SingleArgStrAppend(std::string& str, unsigned long long x) {
+ return AppendIntegerToString(str, x);
+}
+
std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
std::string result;
size_t total_size = 0;
@@ -120,15 +250,15 @@ std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
assert(((src).size() == 0) || \
(uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size())))
-void AppendPieces(std::string* dest,
+void AppendPieces(absl::Nonnull<std::string*> dest,
std::initializer_list<absl::string_view> pieces) {
size_t old_size = dest->size();
- size_t total_size = old_size;
+ size_t to_append = 0;
for (absl::string_view piece : pieces) {
ASSERT_NO_OVERLAP(*dest, piece);
- total_size += piece.size();
+ to_append += piece.size();
}
- strings_internal::STLStringResizeUninitializedAmortized(dest, total_size);
+ strings_internal::STLStringAppendUninitializedAmortized(dest, to_append);
char* const begin = &(*dest)[0];
char* out = begin + old_size;
@@ -144,23 +274,23 @@ void AppendPieces(std::string* dest,
} // namespace strings_internal
-void StrAppend(std::string* dest, const AlphaNum& a) {
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a) {
ASSERT_NO_OVERLAP(*dest, a);
std::string::size_type old_size = dest->size();
- strings_internal::STLStringResizeUninitializedAmortized(dest,
- old_size + a.size());
+ strings_internal::STLStringAppendUninitializedAmortized(dest, a.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
assert(out == begin + dest->size());
}
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
+void StrAppend(absl::Nonnull<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::STLStringResizeUninitializedAmortized(
- dest, old_size + a.size() + b.size());
+ strings_internal::STLStringAppendUninitializedAmortized(dest,
+ a.size() + b.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
@@ -168,14 +298,14 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
assert(out == begin + dest->size());
}
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
- const AlphaNum& c) {
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+ const AlphaNum& b, const AlphaNum& c) {
ASSERT_NO_OVERLAP(*dest, a);
ASSERT_NO_OVERLAP(*dest, b);
ASSERT_NO_OVERLAP(*dest, c);
std::string::size_type old_size = dest->size();
- strings_internal::STLStringResizeUninitializedAmortized(
- dest, old_size + a.size() + b.size() + c.size());
+ strings_internal::STLStringAppendUninitializedAmortized(
+ dest, a.size() + b.size() + c.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
@@ -184,15 +314,15 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
assert(out == begin + dest->size());
}
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
- const AlphaNum& c, const AlphaNum& d) {
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+ const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) {
ASSERT_NO_OVERLAP(*dest, a);
ASSERT_NO_OVERLAP(*dest, b);
ASSERT_NO_OVERLAP(*dest, c);
ASSERT_NO_OVERLAP(*dest, d);
std::string::size_type old_size = dest->size();
- strings_internal::STLStringResizeUninitializedAmortized(
- dest, old_size + a.size() + b.size() + c.size() + d.size());
+ strings_internal::STLStringAppendUninitializedAmortized(
+ dest, a.size() + b.size() + c.size() + d.size());
char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index d5f71ff0..ea2c4dca 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -89,6 +89,8 @@
#include <algorithm>
#include <array>
+#include <cassert>
+#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
@@ -97,8 +99,11 @@
#include <vector>
#include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
#include "absl/base/port.h"
-#include "absl/strings/internal/has_absl_stringify.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/has_absl_stringify.h"
+#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/stringify_sink.h"
#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
@@ -201,7 +206,7 @@ struct Hex {
!std::is_pointer<Int>::value>::type* = nullptr)
: Hex(spec, static_cast<uint64_t>(v)) {}
template <typename Pointee>
- explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad)
+ explicit Hex(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad)
: Hex(spec, reinterpret_cast<uintptr_t>(v)) {}
template <typename S>
@@ -253,10 +258,9 @@ struct Dec {
typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
: value(v >= 0 ? static_cast<uint64_t>(v)
: uint64_t{0} - static_cast<uint64_t>(v)),
- width(spec == absl::kNoPad
- ? 1
- : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
- : spec - absl::kZeroPad2 + 2),
+ width(spec == absl::kNoPad ? 1
+ : spec >= absl::kSpacePad2 ? spec - absl::kSpacePad2 + 2
+ : spec - absl::kZeroPad2 + 2),
fill(spec >= absl::kSpacePad2 ? ' ' : '0'),
neg(v < 0) {}
@@ -344,7 +348,7 @@ class AlphaNum {
ABSL_ATTRIBUTE_LIFETIME_BOUND)
: piece_(&buf.data[0], buf.size) {}
- AlphaNum(const char* c_str // NOLINT(runtime/explicit)
+ AlphaNum(absl::Nullable<const char*> c_str // NOLINT(runtime/explicit)
ABSL_ATTRIBUTE_LIFETIME_BOUND)
: piece_(NullSafeStringView(c_str)) {}
AlphaNum(absl::string_view pc // NOLINT(runtime/explicit)
@@ -352,7 +356,7 @@ class AlphaNum {
: piece_(pc) {}
template <typename T, typename = typename std::enable_if<
- strings_internal::HasAbslStringify<T>::value>::type>
+ HasAbslStringify<T>::value>::type>
AlphaNum( // NOLINT(runtime/explicit)
const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
@@ -371,7 +375,7 @@ class AlphaNum {
AlphaNum& operator=(const AlphaNum&) = delete;
absl::string_view::size_type size() const { return piece_.size(); }
- const char* data() const { return piece_.data(); }
+ absl::Nullable<const char*> data() const { return piece_.data(); }
absl::string_view Piece() const { return piece_; }
// Match unscoped enums. Use integral promotion so that a `char`-backed
@@ -379,17 +383,17 @@ class AlphaNum {
template <typename T,
typename = typename std::enable_if<
std::is_enum<T>{} && std::is_convertible<T, int>{} &&
- !strings_internal::HasAbslStringify<T>::value>::type>
+ !HasAbslStringify<T>::value>::type>
AlphaNum(T e) // NOLINT(runtime/explicit)
: AlphaNum(+e) {}
// This overload matches scoped enums. We must explicitly cast to the
// underlying type, but use integral promotion for the same reason as above.
template <typename T,
- typename std::enable_if<
- std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
- !strings_internal::HasAbslStringify<T>::value,
- char*>::type = nullptr>
+ typename std::enable_if<std::is_enum<T>{} &&
+ !std::is_convertible<T, int>{} &&
+ !HasAbslStringify<T>::value,
+ char*>::type = nullptr>
AlphaNum(T e) // NOLINT(runtime/explicit)
: AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}
@@ -441,13 +445,48 @@ namespace strings_internal {
// Do not call directly - this is not part of the public API.
std::string CatPieces(std::initializer_list<absl::string_view> pieces);
-void AppendPieces(std::string* dest,
+void AppendPieces(absl::Nonnull<std::string*> dest,
std::initializer_list<absl::string_view> pieces);
+void STLStringAppendUninitializedAmortized(std::string* dest, size_t to_append);
+
+// `SingleArgStrCat` overloads take built-in `int`, `long` and `long long` types
+// (signed / unsigned) to avoid ambiguity on the call side. If we used int32_t
+// and int64_t, then at least one of the three (`int` / `long` / `long long`)
+// would have been ambiguous when passed to `SingleArgStrCat`.
+std::string SingleArgStrCat(int x);
+std::string SingleArgStrCat(unsigned int x);
+std::string SingleArgStrCat(long x); // NOLINT
+std::string SingleArgStrCat(unsigned long x); // NOLINT
+std::string SingleArgStrCat(long long x); // NOLINT
+std::string SingleArgStrCat(unsigned long long x); // NOLINT
+std::string SingleArgStrCat(float x);
+std::string SingleArgStrCat(double x);
+
+// `SingleArgStrAppend` overloads are defined here for the same reasons as with
+// `SingleArgStrCat` above.
+void SingleArgStrAppend(std::string& str, int x);
+void SingleArgStrAppend(std::string& str, unsigned int x);
+void SingleArgStrAppend(std::string& str, long x); // NOLINT
+void SingleArgStrAppend(std::string& str, unsigned long x); // NOLINT
+void SingleArgStrAppend(std::string& str, long long x); // NOLINT
+void SingleArgStrAppend(std::string& str, unsigned long long x); // NOLINT
+
+template <typename T,
+ typename = std::enable_if_t<std::is_arithmetic<T>::value &&
+ !std::is_same<T, char>::value &&
+ !std::is_same<T, bool>::value>>
+using EnableIfFastCase = T;
+
} // namespace strings_internal
ABSL_MUST_USE_RESULT inline std::string StrCat() { return std::string(); }
+template <typename T>
+ABSL_MUST_USE_RESULT inline std::string StrCat(
+ strings_internal::EnableIfFastCase<T> a) {
+ return strings_internal::SingleArgStrCat(a);
+}
ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) {
return std::string(a.data(), a.size());
}
@@ -495,24 +534,87 @@ ABSL_MUST_USE_RESULT inline std::string StrCat(
// absl::string_view p = s;
// StrAppend(&s, p);
-inline void StrAppend(std::string*) {}
-void StrAppend(std::string* dest, const AlphaNum& a);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
- const AlphaNum& c);
-void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
- const AlphaNum& c, const AlphaNum& d);
+inline void StrAppend(absl::Nonnull<std::string*>) {}
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a);
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+ const AlphaNum& b);
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+ const AlphaNum& b, const AlphaNum& c);
+void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+ const AlphaNum& b, const AlphaNum& c, const AlphaNum& d);
// Support 5 or more arguments
template <typename... AV>
-inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
- const AlphaNum& c, const AlphaNum& d, const AlphaNum& e,
- const AV&... args) {
+inline void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a,
+ const AlphaNum& b, const AlphaNum& c, const AlphaNum& d,
+ const AlphaNum& e, const AV&... args) {
strings_internal::AppendPieces(
dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(),
static_cast<const AlphaNum&>(args).Piece()...});
}
+template <class String, class T>
+std::enable_if_t<
+ std::is_integral<absl::strings_internal::EnableIfFastCase<T>>::value, void>
+StrAppend(absl::Nonnull<String*> result, T i) {
+ return absl::strings_internal::SingleArgStrAppend(*result, i);
+}
+
+// This overload is only selected if all the parameters are numbers that can be
+// handled quickly.
+// Later we can look into how we can extend this to more general argument
+// mixtures without bloating codegen too much, or copying unnecessarily.
+template <typename String, typename... T>
+std::enable_if_t<
+ (sizeof...(T) > 1),
+ std::common_type_t<std::conditional_t<
+ true, void, absl::strings_internal::EnableIfFastCase<T>>...>>
+StrAppend(absl::Nonnull<String*> str, T... args) {
+ // Do not add unnecessary variables, logic, or even "free" lambdas here.
+ // They can add overhead for the compiler and/or at run time.
+ // Furthermore, assume this function will be inlined.
+ // This function is carefully tailored to be able to be largely optimized away
+ // so that it becomes near-equivalent to the caller handling each argument
+ // individually while minimizing register pressure, so that the compiler
+ // can inline it with minimal overhead.
+
+ // First, calculate the total length, so we can perform just a single resize.
+ // Save all the lengths for later.
+ size_t total_length = 0;
+ const ptrdiff_t lengths[] = {
+ absl::numbers_internal::GetNumDigitsOrNegativeIfNegative(args)...};
+ for (const ptrdiff_t possibly_negative_length : lengths) {
+ // Lengths are negative for negative numbers. Keep them for later use, but
+ // take their absolute values for calculating total lengths;
+ total_length += possibly_negative_length < 0
+ ? static_cast<size_t>(-possibly_negative_length)
+ : static_cast<size_t>(possibly_negative_length);
+ }
+
+ // Now reserve space for all the arguments.
+ const size_t old_size = str->size();
+ absl::strings_internal::STLStringAppendUninitializedAmortized(str,
+ total_length);
+
+ // Finally, output each argument one-by-one, from left to right.
+ size_t i = 0; // The current argument we're processing
+ ptrdiff_t n; // The length of the current argument
+ typename String::pointer pos = &(*str)[old_size];
+ using SomeTrivialEmptyType = std::false_type;
+ // Ugly code due to the lack of C++14 fold expression makes us.
+ const SomeTrivialEmptyType dummy1;
+ for (const SomeTrivialEmptyType& dummy2 :
+ {(/* Comma expressions are poor man's C++17 fold expression for C++14 */
+ (void)(n = lengths[i]),
+ (void)(n < 0 ? (void)(*pos++ = '-'), (n = ~n) : 0),
+ (void)absl::numbers_internal::FastIntToBufferBackward(
+ absl::numbers_internal::UnsignedAbsoluteValue(std::move(args)),
+ pos += n, static_cast<uint32_t>(n)),
+ (void)++i, dummy1)...}) {
+ (void)dummy2; // Remove & migrate to fold expressions in C++17
+ }
+}
+
// Helper function for the future StrCat default floating-point format, %.6g
// This is fast.
inline strings_internal::AlphaNumBuffer<
diff --git a/absl/strings/str_cat_benchmark.cc b/absl/strings/str_cat_benchmark.cc
index 02c4dbe6..0a851e7b 100644
--- a/absl/strings/str_cat_benchmark.cc
+++ b/absl/strings/str_cat_benchmark.cc
@@ -12,12 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "absl/strings/str_cat.h"
-
+#include <array>
#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include <string>
+#include <tuple>
+#include <utility>
#include "benchmark/benchmark.h"
+#include "absl/random/log_uniform_int_distribution.h"
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
namespace {
@@ -137,39 +145,65 @@ 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) {
+template <typename Table, size_t... Index>
+void BM_StrAppendImpl(benchmark::State& state, Table table, size_t total_bytes,
+ std::index_sequence<Index...>) {
for (auto s : state) {
+ const size_t table_size = table.size();
+ size_t i = 0;
std::string result;
while (result.size() < total_bytes) {
- absl::StrAppend(&result, chunks...);
+ absl::StrAppend(&result, std::get<Index>(table[i])...);
benchmark::DoNotOptimize(result);
+ ++i;
+ i -= i >= table_size ? table_size : 0;
}
}
}
-void BM_StrAppend(benchmark::State& state) {
- const int total_bytes = state.range(0);
+template <typename Array>
+void BM_StrAppend(benchmark::State& state, Array&& table) {
+ const size_t 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);
+ return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+ std::make_index_sequence<1>());
case 2:
- return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk);
+ return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+ std::make_index_sequence<2>());
case 4:
- return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk,
- kChunk);
+ return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+ std::make_index_sequence<4>());
case 8:
- return BM_StrAppendImpl(state, total_bytes, kChunk, kChunk, kChunk,
- kChunk, kChunk, kChunk, kChunk, kChunk);
+ return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes,
+ std::make_index_sequence<8>());
default:
std::abort();
}
}
+void BM_StrAppendStr(benchmark::State& state) {
+ using T = absl::string_view;
+ using Row = std::tuple<T, T, T, T, T, T, T, T>;
+ constexpr absl::string_view kChunk = "0123456789";
+ Row row = {kChunk, kChunk, kChunk, kChunk, kChunk, kChunk, kChunk, kChunk};
+ return BM_StrAppend(state, std::array<Row, 1>({row}));
+}
+
+template <typename T>
+void BM_StrAppendInt(benchmark::State& state) {
+ absl::BitGen rng;
+ absl::log_uniform_int_distribution<T> dist;
+ std::array<std::tuple<T, T, T, T, T, T, T, T>, (1 << 7)> table;
+ for (size_t i = 0; i < table.size(); ++i) {
+ table[i] = {dist(rng), dist(rng), dist(rng), dist(rng),
+ dist(rng), dist(rng), dist(rng), dist(rng)};
+ }
+ return BM_StrAppend(state, table);
+}
+
template <typename B>
void StrAppendConfig(B* benchmark) {
for (int bytes : {10, 100, 1000, 10000}) {
@@ -182,6 +216,50 @@ void StrAppendConfig(B* benchmark) {
}
}
-BENCHMARK(BM_StrAppend)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendStr)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<int64_t>)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<uint64_t>)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<int32_t>)->Apply(StrAppendConfig);
+BENCHMARK(BM_StrAppendInt<uint32_t>)->Apply(StrAppendConfig);
+
+template <typename... Chunks>
+void BM_StrCatImpl(benchmark::State& state,
+ Chunks... chunks) {
+ for (auto s : state) {
+ std::string result = absl::StrCat(chunks...);
+ benchmark::DoNotOptimize(result);
+ }
+}
+
+void BM_StrCat(benchmark::State& state) {
+ const int chunks_at_a_time = state.range(0);
+ const absl::string_view kChunk = "0123456789";
+
+ switch (chunks_at_a_time) {
+ case 1:
+ return BM_StrCatImpl(state, kChunk);
+ case 2:
+ return BM_StrCatImpl(state, kChunk, kChunk);
+ case 3:
+ return BM_StrCatImpl(state, kChunk, kChunk, kChunk);
+ case 4:
+ return BM_StrCatImpl(state, kChunk, kChunk, kChunk, kChunk);
+ default:
+ std::abort();
+ }
+}
+
+BENCHMARK(BM_StrCat)->Arg(1)->Arg(2)->Arg(3)->Arg(4);
+
+void BM_StrCat_int(benchmark::State& state) {
+ int i = 0;
+ for (auto s : state) {
+ std::string result = absl::StrCat(i);
+ benchmark::DoNotOptimize(result);
+ i = IncrementAlternatingSign(i);
+ }
+}
+
+BENCHMARK(BM_StrCat_int);
} // namespace
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index 2d74245e..b30a86fe 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -16,13 +16,16 @@
#include "absl/strings/str_cat.h"
+#include <cstddef>
#include <cstdint>
+#include <cstdlib>
+#include <limits>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "absl/strings/str_format.h"
-#include "absl/strings/substitute.h"
+#include "absl/strings/string_view.h"
#ifdef __ANDROID__
// Android assert messages only go to system log, so death tests cannot inspect
@@ -36,6 +39,24 @@
namespace {
+template <typename Integer>
+void VerifyInteger(Integer value) {
+ const std::string expected = std::to_string(value);
+
+ EXPECT_EQ(absl::StrCat(value), expected);
+
+ const char* short_prefix = "x";
+ const char* long_prefix = "2;k.msabxiuow2[09i;o3k21-93-9=29]";
+
+ std::string short_str = short_prefix;
+ absl::StrAppend(&short_str, value);
+ EXPECT_EQ(short_str, short_prefix + expected);
+
+ std::string long_str = long_prefix;
+ absl::StrAppend(&long_str, value);
+ EXPECT_EQ(long_str, long_prefix + expected);
+}
+
// Test absl::StrCat of ints and longs of various sizes and signdedness.
TEST(StrCat, Ints) {
const short s = -1; // NOLINT(runtime/int)
@@ -65,6 +86,34 @@ TEST(StrCat, Ints) {
EXPECT_EQ(answer, "-9-12");
answer = absl::StrCat(uintptr, 0);
EXPECT_EQ(answer, "130");
+
+ for (const uint32_t base : {2u, 10u}) {
+ for (const int extra_shift : {0, 12}) {
+ for (uint64_t i = 0; i < (1 << 8); ++i) {
+ uint64_t j = i;
+ while (true) {
+ uint64_t v = j ^ (extra_shift != 0 ? (j << extra_shift) * base : 0);
+ VerifyInteger(static_cast<bool>(v));
+ VerifyInteger(static_cast<wchar_t>(v));
+ VerifyInteger(static_cast<signed char>(v));
+ VerifyInteger(static_cast<unsigned char>(v));
+ VerifyInteger(static_cast<short>(v)); // NOLINT
+ VerifyInteger(static_cast<unsigned short>(v)); // NOLINT
+ VerifyInteger(static_cast<int>(v)); // NOLINT
+ VerifyInteger(static_cast<unsigned int>(v)); // NOLINT
+ VerifyInteger(static_cast<long>(v)); // NOLINT
+ VerifyInteger(static_cast<unsigned long>(v)); // NOLINT
+ VerifyInteger(static_cast<long long>(v)); // NOLINT
+ VerifyInteger(static_cast<unsigned long long>(v)); // NOLINT
+ const uint64_t next = j == 0 ? 1 : j * base;
+ if (next <= j) {
+ break;
+ }
+ j = next;
+ }
+ }
+ }
+ }
}
TEST(StrCat, Enums) {
@@ -662,4 +711,20 @@ TEST(StrCat, AbslStringifyWithEnum) {
EXPECT_EQ(absl::StrCat(e), "Choices");
}
+template <typename Integer>
+void CheckSingleArgumentIntegerLimits() {
+ Integer max = std::numeric_limits<Integer>::max();
+ Integer min = std::numeric_limits<Integer>::min();
+
+ EXPECT_EQ(absl::StrCat(max), std::to_string(max));
+ EXPECT_EQ(absl::StrCat(min), std::to_string(min));
+}
+
+TEST(StrCat, SingleArgumentLimits) {
+ CheckSingleArgumentIntegerLimits<int32_t>();
+ CheckSingleArgumentIntegerLimits<uint32_t>();
+ CheckSingleArgumentIntegerLimits<int64_t>();
+ CheckSingleArgumentIntegerLimits<uint64_t>();
+}
+
} // namespace
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
index 023e4350..66b6af58 100644
--- a/absl/strings/str_format.h
+++ b/absl/strings/str_format.h
@@ -72,14 +72,21 @@
#ifndef ABSL_STRINGS_STR_FORMAT_H_
#define ABSL_STRINGS_STR_FORMAT_H_
+#include <cstdint>
#include <cstdio>
#include <string>
+#include <type_traits>
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
#include "absl/strings/internal/str_format/arg.h" // IWYU pragma: export
#include "absl/strings/internal/str_format/bind.h" // IWYU pragma: export
#include "absl/strings/internal/str_format/checker.h" // IWYU pragma: export
#include "absl/strings/internal/str_format/extension.h" // IWYU pragma: export
#include "absl/strings/internal/str_format/parser.h" // IWYU pragma: export
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -104,7 +111,8 @@ class UntypedFormatSpec {
explicit UntypedFormatSpec(string_view s) : spec_(s) {}
protected:
- explicit UntypedFormatSpec(const str_format_internal::ParsedFormatBase* pc)
+ explicit UntypedFormatSpec(
+ absl::Nonnull<const str_format_internal::ParsedFormatBase*> pc)
: spec_(pc) {}
private:
@@ -144,7 +152,7 @@ str_format_internal::StreamedWrapper<T> FormatStreamed(const T& v) {
// EXPECT_EQ(8, n);
class FormatCountCapture {
public:
- explicit FormatCountCapture(int* p) : p_(p) {}
+ explicit FormatCountCapture(absl::Nonnull<int*> p) : p_(p) {}
private:
// FormatCountCaptureHelper is used to define FormatConvertImpl() for this
@@ -153,8 +161,8 @@ class FormatCountCapture {
// Unused() is here because of the false positive from -Wunused-private-field
// p_ is used in the templated function of the friend FormatCountCaptureHelper
// class.
- int* Unused() { return p_; }
- int* p_;
+ absl::Nonnull<int*> Unused() { return p_; }
+ absl::Nonnull<int*> p_;
};
// FormatSpec
@@ -250,13 +258,13 @@ class FormatCountCapture {
// `v` uses `d` for signed integer values, `u` for unsigned integer values, `g`
// for floating point values, and formats boolean values as "true"/"false"
// (instead of 1 or 0 for booleans formatted using d). `const char*` is not
-// supported; please use `std:string` and `string_view`. `char` is also not
+// supported; please use `std::string` and `string_view`. `char` is also not
// supported due to ambiguity of the type. This specifier does not support
// modifiers.
//
// The `FormatSpec` intrinsically supports all of these fundamental C++ types:
//
-// * Characters: `char`, `signed char`, `unsigned char`
+// * Characters: `char`, `signed char`, `unsigned char`, `wchar_t`
// * Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`,
// `unsigned long`, `long long`, `unsigned long long`
// * Enums: printed as their underlying integral value
@@ -264,9 +272,9 @@ class FormatCountCapture {
//
// However, in the `str_format` library, a format conversion specifies a broader
// C++ conceptual category instead of an exact type. For example, `%s` binds to
-// any string-like argument, so `std::string`, `absl::string_view`, and
-// `const char*` are all accepted. Likewise, `%d` accepts any integer-like
-// argument, etc.
+// any string-like argument, so `std::string`, `std::wstring`,
+// `absl::string_view`, `const char*`, and `const wchar_t*` are all accepted.
+// Likewise, `%d` accepts any integer-like argument, etc.
template <typename... Args>
using FormatSpec = str_format_internal::FormatSpecTemplate<
@@ -287,8 +295,8 @@ using FormatSpec = str_format_internal::FormatSpecTemplate<
// Example:
//
// // Verified at compile time.
-// absl::ParsedFormat<'s', 'd'> formatString("Welcome to %s, Number %d!");
-// absl::StrFormat(formatString, "TheVillage", 6);
+// absl::ParsedFormat<'s', 'd'> format_string("Welcome to %s, Number %d!");
+// absl::StrFormat(format_string, "TheVillage", 6);
//
// // Verified at runtime.
// auto format_runtime = absl::ParsedFormat<'d'>::New(format_string);
@@ -369,7 +377,7 @@ ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec<Args...>& format,
// std::string orig("For example PI is approximately ");
// std::cout << StrAppendFormat(&orig, "%12.6f", 3.14);
template <typename... Args>
-std::string& StrAppendFormat(std::string* dst,
+std::string& StrAppendFormat(absl::Nonnull<std::string*> dst,
const FormatSpec<Args...>& format,
const Args&... args) {
return str_format_internal::AppendPack(
@@ -381,7 +389,7 @@ std::string& StrAppendFormat(std::string* dst,
//
// Writes to an output stream given a format string and zero or more arguments,
// generally in a manner that is more efficient than streaming the result of
-// `absl:: StrFormat()`. The returned object must be streamed before the full
+// `absl::StrFormat()`. The returned object must be streamed before the full
// expression ends.
//
// Example:
@@ -429,7 +437,7 @@ int PrintF(const FormatSpec<Args...>& format, const Args&... args) {
// Outputs: "The capital of Mongolia is Ulaanbaatar"
//
template <typename... Args>
-int FPrintF(std::FILE* output, const FormatSpec<Args...>& format,
+int FPrintF(absl::Nonnull<std::FILE*> output, const FormatSpec<Args...>& format,
const Args&... args) {
return str_format_internal::FprintF(
output, str_format_internal::UntypedFormatSpecImpl::Extract(format),
@@ -458,8 +466,8 @@ int FPrintF(std::FILE* output, const FormatSpec<Args...>& format,
// Post-condition: output == "The capital of Mongolia is Ulaanbaatar"
//
template <typename... Args>
-int SNPrintF(char* output, std::size_t size, const FormatSpec<Args...>& format,
- const Args&... args) {
+int SNPrintF(absl::Nonnull<char*> output, std::size_t size,
+ const FormatSpec<Args...>& format, const Args&... args) {
return str_format_internal::SnprintF(
output, size, str_format_internal::UntypedFormatSpecImpl::Extract(format),
{str_format_internal::FormatArgImpl(args)...});
@@ -492,7 +500,7 @@ class FormatRawSink {
template <typename T,
typename = typename std::enable_if<std::is_constructible<
str_format_internal::FormatRawSinkImpl, T*>::value>::type>
- FormatRawSink(T* raw) // NOLINT
+ FormatRawSink(absl::Nonnull<T*> raw) // NOLINT
: sink_(raw) {}
private:
@@ -849,14 +857,16 @@ class FormatSink {
}
// Support `absl::Format(&sink, format, args...)`.
- friend void AbslFormatFlush(FormatSink* sink, absl::string_view v) {
+ friend void AbslFormatFlush(absl::Nonnull<FormatSink*> sink,
+ absl::string_view v) {
sink->Append(v);
}
private:
friend str_format_internal::FormatSinkImpl;
- explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {}
- str_format_internal::FormatSinkImpl* sink_;
+ explicit FormatSink(absl::Nonnull<str_format_internal::FormatSinkImpl*> s)
+ : sink_(s) {}
+ absl::Nonnull<str_format_internal::FormatSinkImpl*> sink_;
};
// FormatConvertResult
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 20fd0289..3c52be1e 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -14,16 +14,22 @@
#include "absl/strings/str_format.h"
+#include <cerrno>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
+#include <ostream>
+#include <sstream>
#include <string>
+#include <type_traits>
-#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/str_cat.h"
#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -628,6 +634,10 @@ TEST(StrFormat, BehavesAsDocumented) {
const int& something = *reinterpret_cast<const int*>(ptr_value);
EXPECT_EQ(StrFormat("%p", &something), StrFormat("0x%x", ptr_value));
+ // The output of formatting a null pointer is not documented as being a
+ // specific thing, but the attempt should at least compile.
+ (void)StrFormat("%p", nullptr);
+
// Output widths are supported, with optional flags.
EXPECT_EQ(StrFormat("%3d", 1), " 1");
EXPECT_EQ(StrFormat("%3d", 123456), "123456");
diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h
index ee5ae7ef..6a92c0f5 100644
--- a/absl/strings/str_join.h
+++ b/absl/strings/str_join.h
@@ -80,7 +80,7 @@ ABSL_NAMESPACE_BEGIN
// absl::StrJoin(v, ", ", [](std::string* out, absl::Duration dur) {
// absl::StrAppend(out, absl::FormatDuration(dur));
// });
-// EXPECT_EQ("1s, 10ms", s);
+// EXPECT_EQ(s, "1s, 10ms");
//
// The following standard formatters are provided within this file:
//
@@ -164,21 +164,21 @@ DereferenceFormatter() {
// // of `absl::string_view` or even `const char*`.
// std::vector<std::string> v = {"foo", "bar", "baz"};
// std::string s = absl::StrJoin(v, "-");
-// EXPECT_EQ("foo-bar-baz", s);
+// EXPECT_EQ(s, "foo-bar-baz");
//
// Example 2:
// // Joins the values in the given `std::initializer_list<>` specified using
// // brace initialization. This pattern also works with an initializer_list
// // of ints or `absl::string_view` -- any `AlphaNum`-compatible type.
// std::string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
-// EXPECT_EQ("foo-bar-baz", s);
+// EXPECT_EQs, "foo-bar-baz");
//
// Example 3:
// // Joins a collection of ints. This pattern also works with floats,
// // doubles, int64s -- any `StrCat()`-compatible type.
// std::vector<int> v = {1, 2, 3, -4};
// std::string s = absl::StrJoin(v, "-");
-// EXPECT_EQ("1-2-3--4", s);
+// EXPECT_EQ(s, "1-2-3--4");
//
// Example 4:
// // Joins a collection of pointer-to-int. By default, pointers are
@@ -189,7 +189,7 @@ DereferenceFormatter() {
// int x = 1, y = 2, z = 3;
// std::vector<int*> v = {&x, &y, &z};
// std::string s = absl::StrJoin(v, "-");
-// EXPECT_EQ("1-2-3", s);
+// EXPECT_EQ(s, "1-2-3");
//
// Example 5:
// // Dereferencing of `std::unique_ptr<>` is also supported:
@@ -198,42 +198,42 @@ DereferenceFormatter() {
// v.emplace_back(new int(2));
// v.emplace_back(new int(3));
// std::string s = absl::StrJoin(v, "-");
-// EXPECT_EQ("1-2-3", s);
+// EXPECT_EQ(s, "1-2-3");
//
// Example 6:
// // Joins a `std::map`, with each key-value pair separated by an equals
// // sign. This pattern would also work with, say, a
// // `std::vector<std::pair<>>`.
// std::map<std::string, int> m = {
-// std::make_pair("a", 1),
-// std::make_pair("b", 2),
-// std::make_pair("c", 3)};
+// {"a", 1},
+// {"b", 2},
+// {"c", 3}};
// std::string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
-// EXPECT_EQ("a=1,b=2,c=3", s);
+// EXPECT_EQ(s, "a=1,b=2,c=3");
//
// Example 7:
// // These examples show how `absl::StrJoin()` handles a few common edge
// // cases:
// std::vector<std::string> v_empty;
-// EXPECT_EQ("", absl::StrJoin(v_empty, "-"));
+// EXPECT_EQ(absl::StrJoin(v_empty, "-"), "");
//
// std::vector<std::string> v_one_item = {"foo"};
-// EXPECT_EQ("foo", absl::StrJoin(v_one_item, "-"));
+// EXPECT_EQ(absl::StrJoin(v_one_item, "-"), "foo");
//
// std::vector<std::string> v_empty_string = {""};
-// EXPECT_EQ("", absl::StrJoin(v_empty_string, "-"));
+// EXPECT_EQ(absl::StrJoin(v_empty_string, "-"), "");
//
// std::vector<std::string> v_one_item_empty_string = {"a", ""};
-// EXPECT_EQ("a-", absl::StrJoin(v_one_item_empty_string, "-"));
+// EXPECT_EQ(absl::StrJoin(v_one_item_empty_string, "-"), "a-");
//
// std::vector<std::string> v_two_empty_string = {"", ""};
-// EXPECT_EQ("-", absl::StrJoin(v_two_empty_string, "-"));
+// EXPECT_EQ(absl::StrJoin(v_two_empty_string, "-"), "-");
//
// Example 8:
// // Joins a `std::tuple<T...>` of heterogeneous types, converting each to
// // a std::string using the `absl::AlphaNum` class.
// std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-");
-// EXPECT_EQ("123-abc-0.456", s);
+// EXPECT_EQ(s, "123-abc-0.456");
template <typename Iterator, typename Formatter>
std::string StrJoin(Iterator start, Iterator end, absl::string_view sep,
diff --git a/absl/strings/str_join_test.cc b/absl/strings/str_join_test.cc
index c986e863..449f95be 100644
--- a/absl/strings/str_join_test.cc
+++ b/absl/strings/str_join_test.cc
@@ -25,8 +25,9 @@
#include <map>
#include <memory>
#include <ostream>
+#include <string>
#include <tuple>
-#include <type_traits>
+#include <utility>
#include <vector>
#include "gtest/gtest.h"
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc
index 2bd5fa98..a7ab52fe 100644
--- a/absl/strings/str_replace.cc
+++ b/absl/strings/str_replace.cc
@@ -14,7 +14,16 @@
#include "absl/strings/str_replace.h"
+#include <cstddef>
+#include <initializer_list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/config.h"
+#include "absl/base/nullability.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -28,8 +37,8 @@ using FixedMapping =
// occurred.
int ApplySubstitutions(
absl::string_view s,
- std::vector<strings_internal::ViableSubstitution>* subs_ptr,
- std::string* result_ptr) {
+ absl::Nonnull<std::vector<strings_internal::ViableSubstitution>*> subs_ptr,
+ absl::Nonnull<std::string*> result_ptr) {
auto& subs = *subs_ptr;
int substitutions = 0;
size_t pos = 0;
@@ -74,7 +83,7 @@ std::string StrReplaceAll(absl::string_view s,
}
int StrReplaceAll(strings_internal::FixedMapping replacements,
- std::string* target) {
+ absl::Nonnull<std::string*> target) {
return StrReplaceAll<strings_internal::FixedMapping>(replacements, target);
}
diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h
index 273c7077..e77ced3e 100644
--- a/absl/strings/str_replace.h
+++ b/absl/strings/str_replace.h
@@ -43,6 +43,7 @@
#include <vector>
#include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
#include "absl/strings/string_view.h"
namespace absl {
@@ -113,7 +114,7 @@ std::string StrReplaceAll(absl::string_view s,
int StrReplaceAll(
std::initializer_list<std::pair<absl::string_view, absl::string_view>>
replacements,
- std::string* target);
+ absl::Nonnull<std::string*> target);
// Overload of `StrReplaceAll()` to replace patterns within a given output
// string *in place* with replacements provided within a container of key/value
@@ -128,7 +129,8 @@ int StrReplaceAll(
// EXPECT_EQ(count, 2);
// EXPECT_EQ("if (ptr &lt; &amp;foo)", s);
template <typename StrToStrMapping>
-int StrReplaceAll(const StrToStrMapping& replacements, std::string* target);
+int StrReplaceAll(const StrToStrMapping& replacements,
+ absl::Nonnull<std::string*> target);
// Implementation details only, past this point.
namespace strings_internal {
@@ -185,8 +187,8 @@ std::vector<ViableSubstitution> FindSubstitutions(
}
int ApplySubstitutions(absl::string_view s,
- std::vector<ViableSubstitution>* subs_ptr,
- std::string* result_ptr);
+ absl::Nonnull<std::vector<ViableSubstitution>*> subs_ptr,
+ absl::Nonnull<std::string*> result_ptr);
} // namespace strings_internal
@@ -201,7 +203,8 @@ std::string StrReplaceAll(absl::string_view s,
}
template <typename StrToStrMapping>
-int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) {
+int StrReplaceAll(const StrToStrMapping& replacements,
+ absl::Nonnull<std::string*> target) {
auto subs = strings_internal::FindSubstitutions(*target, replacements);
if (subs.empty()) return 0;
diff --git a/absl/strings/str_replace_test.cc b/absl/strings/str_replace_test.cc
index 9d8c7f75..04b23af6 100644
--- a/absl/strings/str_replace_test.cc
+++ b/absl/strings/str_replace_test.cc
@@ -16,11 +16,15 @@
#include <list>
#include <map>
+#include <string>
#include <tuple>
+#include <utility>
+#include <vector>
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
TEST(StrReplaceAll, OneReplacement) {
std::string s;
@@ -175,7 +179,7 @@ TEST(StrReplaceAll, ReplacementsInPlaceInMap) {
}
struct Cont {
- Cont() {}
+ Cont() = default;
explicit Cont(absl::string_view src) : data(src) {}
absl::string_view data;
diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc
index 72ba7c02..abe486b9 100644
--- a/absl/strings/str_split.cc
+++ b/absl/strings/str_split.cc
@@ -15,16 +15,13 @@
#include "absl/strings/str_split.h"
#include <algorithm>
-#include <cassert>
-#include <cstdint>
+#include <cstddef>
#include <cstdlib>
#include <cstring>
-#include <iterator>
-#include <limits>
-#include <memory>
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/ascii.h"
+#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -99,6 +96,11 @@ absl::string_view ByString::Find(absl::string_view text, size_t pos) const {
return GenericFind(text, delimiter_, pos, LiteralPolicy());
}
+absl::string_view ByAsciiWhitespace::Find(absl::string_view text,
+ size_t pos) const {
+ return GenericFind(text, " \t\v\f\r\n", pos, AnyOfPolicy());
+}
+
//
// ByChar
//
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index 7bbb68a3..87540278 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -130,6 +130,24 @@ class ByString {
const std::string delimiter_;
};
+// ByAsciiWhitespace
+//
+// A sub-string delimiter that splits by ASCII whitespace
+// (space, tab, vertical tab, formfeed, linefeed, or carriage return).
+// Note: you probably want to use absl::SkipEmpty() as well!
+//
+// This class is equivalent to ByAnyChar with ASCII whitespace chars.
+//
+// Example:
+//
+// std::vector<std::string> v = absl::StrSplit(
+// "a b\tc\n d \n", absl::ByAsciiWhitespace(), absl::SkipEmpty());
+// // v[0] == "a", v[1] == "b", v[2] == "c", v[3] == "d"
+class ByAsciiWhitespace {
+ public:
+ absl::string_view Find(absl::string_view text, size_t pos) const;
+};
+
// ByChar
//
// A single character delimiter. `ByChar` is functionally equivalent to a
diff --git a/absl/strings/str_split_benchmark.cc b/absl/strings/str_split_benchmark.cc
index f38dfcfe..003a66b5 100644
--- a/absl/strings/str_split_benchmark.cc
+++ b/absl/strings/str_split_benchmark.cc
@@ -14,6 +14,7 @@
#include "absl/strings/str_split.h"
+#include <cstddef>
#include <iterator>
#include <string>
#include <unordered_map>
diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc
index 04a64a42..df6c460f 100644
--- a/absl/strings/str_split_test.cc
+++ b/absl/strings/str_split_test.cc
@@ -14,30 +14,33 @@
#include "absl/strings/str_split.h"
+#include <cstddef>
+#include <cstdint>
#include <deque>
#include <initializer_list>
#include <list>
#include <map>
#include <memory>
+#include <set>
#include <string>
-#include <type_traits>
#include <unordered_map>
#include <unordered_set>
+#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#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"
+#include "absl/strings/string_view.h"
namespace {
using ::testing::ElementsAre;
+using ::testing::IsEmpty;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
@@ -922,6 +925,45 @@ TEST(Delimiter, ByAnyChar) {
}
//
+// Tests for ByAsciiWhitespace
+//
+TEST(Split, ByAsciiWhitespace) {
+ using absl::ByAsciiWhitespace;
+ using absl::SkipEmpty;
+ std::vector<absl::string_view> results;
+
+ results = absl::StrSplit("aaaa\n", ByAsciiWhitespace());
+ EXPECT_THAT(results, ElementsAre("aaaa", ""));
+
+ results = absl::StrSplit("aaaa\n", ByAsciiWhitespace(), SkipEmpty());
+ EXPECT_THAT(results, ElementsAre("aaaa"));
+
+ results = absl::StrSplit(" ", ByAsciiWhitespace());
+ EXPECT_THAT(results, ElementsAre("", ""));
+
+ results = absl::StrSplit(" ", ByAsciiWhitespace(), SkipEmpty());
+ EXPECT_THAT(results, IsEmpty());
+
+ results = absl::StrSplit("a", ByAsciiWhitespace());
+ EXPECT_THAT(results, ElementsAre("a"));
+
+ results = absl::StrSplit("", ByAsciiWhitespace());
+ EXPECT_THAT(results, ElementsAre(""));
+
+ results = absl::StrSplit("", ByAsciiWhitespace(), SkipEmpty());
+ EXPECT_THAT(results, IsEmpty());
+
+ results = absl::StrSplit("a b\tc\n d\n", ByAsciiWhitespace());
+ EXPECT_THAT(results, ElementsAre("a", "b", "c", "", "", "d", ""));
+
+ results = absl::StrSplit("a b\tc\n d \n", ByAsciiWhitespace(), SkipEmpty());
+ EXPECT_THAT(results, ElementsAre("a", "b", "c", "d"));
+
+ results = absl::StrSplit("a\t\n\v\f\r b", ByAsciiWhitespace(), SkipEmpty());
+ EXPECT_THAT(results, ElementsAre("a", "b"));
+}
+
+//
// Tests for ByLength
//
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
index f20ff530..97025c32 100644
--- a/absl/strings/string_view.cc
+++ b/absl/strings/string_view.cc
@@ -21,6 +21,8 @@
#include <cstring>
#include <ostream>
+#include "absl/base/nullability.h"
+
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -28,8 +30,10 @@ namespace {
// This is significantly faster for case-sensitive matches with very
// few possible matches.
-const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
- size_t neelen) {
+absl::Nullable<const char*> memmatch(absl::Nullable<const char*> phaystack,
+ size_t haylen,
+ absl::Nullable<const char*> pneedle,
+ size_t neelen) {
if (0 == neelen) {
return phaystack; // even if haylen is 0
}
@@ -37,8 +41,8 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
const char* match;
const char* hayend = phaystack + haylen - neelen + 1;
- // A static cast is used here to work around the fact that memchr returns
- // a void* on Posix-compliant systems and const void* on Windows.
+ // A static cast is used here as memchr returns a const void *, and pointer
+ // arithmetic is not allowed on pointers to void.
while (
(match = static_cast<const char*>(memchr(
phaystack, pneedle[0], static_cast<size_t>(hayend - phaystack))))) {
@@ -229,7 +233,6 @@ string_view::size_type string_view::find_last_not_of(
return npos;
}
-
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
constexpr string_view::size_type string_view::npos;
constexpr string_view::size_type string_view::kMaxSize;
@@ -238,4 +241,22 @@ constexpr string_view::size_type string_view::kMaxSize;
ABSL_NAMESPACE_END
} // namespace absl
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+extern const char kAvoidEmptyStringViewLibraryWarning;
+const char kAvoidEmptyStringViewLibraryWarning = 0;
+} // namespace strings_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // __APPLE__
+
#endif // ABSL_USES_STD_STRING_VIEW
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index eae11b2a..04ca0a38 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -37,6 +37,7 @@
#include <string>
#include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
#include "absl/base/config.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
@@ -162,11 +163,11 @@ class string_view {
public:
using traits_type = std::char_traits<char>;
using value_type = char;
- using pointer = char*;
- using const_pointer = const char*;
+ using pointer = absl::Nullable<char*>;
+ using const_pointer = absl::Nullable<const char*>;
using reference = char&;
using const_reference = const char&;
- using const_iterator = const char*;
+ using const_iterator = absl::Nullable<const char*>;
using iterator = const_iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using reverse_iterator = const_reverse_iterator;
@@ -194,11 +195,12 @@ class string_view {
// 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)
+ constexpr string_view( // NOLINT(runtime/explicit)
+ absl::Nonnull<const char*> str)
: 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)
+ constexpr string_view(absl::Nullable<const char*> data, size_type len)
: ptr_(data), length_(CheckLengthInternal(len)) {}
// NOTE: Harmlessly omitted to work around gdb bug.
@@ -427,18 +429,21 @@ class string_view {
// Overload of `string_view::compare()` for comparing a `string_view` and a
// a different C-style string `s`.
- constexpr int compare(const char* s) const { return compare(string_view(s)); }
+ constexpr int compare(absl::Nonnull<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 string C-style string `s`.
- constexpr int compare(size_type pos1, size_type count1, const char* s) const {
+ constexpr int compare(size_type pos1, size_type count1,
+ absl::Nonnull<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 string `s`.
- constexpr int compare(size_type pos1, size_type count1, const char* s,
- size_type count2) const {
+ constexpr int compare(size_type pos1, size_type count1,
+ absl::Nonnull<const char*> s, size_type count2) const {
return substr(pos1, count1).compare(string_view(s, count2));
}
@@ -457,13 +462,14 @@ class string_view {
// 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 {
+ size_type find(absl::Nonnull<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 {
+ size_type find(absl::Nonnull<const char *> s, size_type pos = 0) const {
return find(string_view(s), pos);
}
@@ -480,13 +486,14 @@ class string_view {
// 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 {
+ size_type rfind(absl::Nonnull<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 {
+ size_type rfind(absl::Nonnull<const char*> s, size_type pos = npos) const {
return rfind(string_view(s), pos);
}
@@ -505,14 +512,15 @@ class string_view {
// 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 {
+ size_type find_first_of(absl::Nonnull<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 {
+ size_type find_first_of(absl::Nonnull<const char*> s,
+ size_type pos = 0) const {
return find_first_of(string_view(s), pos);
}
@@ -531,13 +539,15 @@ class string_view {
// 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 {
+ size_type find_last_of(absl::Nonnull<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 {
+ size_type find_last_of(absl::Nonnull<const char*> s,
+ size_type pos = npos) const {
return find_last_of(string_view(s), pos);
}
@@ -554,14 +564,15 @@ class string_view {
// 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 find_first_not_of(absl::Nonnull<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 {
+ size_type find_first_not_of(absl::Nonnull<const char*> s,
+ size_type pos = 0) const {
return find_first_not_of(string_view(s), pos);
}
@@ -579,22 +590,76 @@ class string_view {
// 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 find_last_not_of(absl::Nonnull<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 {
+ size_type find_last_not_of(absl::Nonnull<const char*> s,
+ size_type pos = npos) const {
return find_last_not_of(string_view(s), pos);
}
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+ // string_view::starts_with()
+ //
+ // Returns true if the `string_view` starts with the prefix `s`.
+ //
+ // This method only exists when targeting at least C++20.
+ // If support for C++ prior to C++20 is required, use `absl::StartsWith()`
+ // from `//absl/strings/match.h` for compatibility.
+ constexpr bool starts_with(string_view s) const noexcept {
+ return s.empty() ||
+ (size() >= s.size() &&
+ ABSL_INTERNAL_STRING_VIEW_MEMCMP(data(), s.data(), s.size()) == 0);
+ }
+
+ // Overload of `string_view::starts_with()` that returns true if `c` is the
+ // first character of the `string_view`.
+ constexpr bool starts_with(char c) const noexcept {
+ return !empty() && front() == c;
+ }
+
+ // Overload of `string_view::starts_with()` that returns true if the
+ // `string_view` starts with the C-style prefix `s`.
+ constexpr bool starts_with(const char* s) const {
+ return starts_with(string_view(s));
+ }
+
+ // string_view::ends_with()
+ //
+ // Returns true if the `string_view` ends with the suffix `s`.
+ //
+ // This method only exists when targeting at least C++20.
+ // If support for C++ prior to C++20 is required, use `absl::EndsWith()`
+ // from `//absl/strings/match.h` for compatibility.
+ constexpr bool ends_with(string_view s) const noexcept {
+ return s.empty() || (size() >= s.size() && ABSL_INTERNAL_STRING_VIEW_MEMCMP(
+ data() + (size() - s.size()),
+ s.data(), s.size()) == 0);
+ }
+
+ // Overload of `string_view::ends_with()` that returns true if `c` is the
+ // last character of the `string_view`.
+ constexpr bool ends_with(char c) const noexcept {
+ return !empty() && back() == c;
+ }
+
+ // Overload of `string_view::ends_with()` that returns true if the
+ // `string_view` ends with the C-style suffix `s`.
+ constexpr bool ends_with(const char* s) const {
+ return ends_with(string_view(s));
+ }
+#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+
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
+ string_view(absl::Nullable<const char*> data, size_type len,
+ SkipCheckLengthTag) noexcept
: ptr_(data), length_(len) {}
static constexpr size_type kMaxSize =
@@ -604,7 +669,7 @@ class string_view {
return ABSL_HARDENING_ASSERT(len <= kMaxSize), len;
}
- static constexpr size_type StrlenInternal(const char* str) {
+ static constexpr size_type StrlenInternal(absl::Nonnull<const char*> str) {
#if defined(_MSC_VER) && _MSC_VER >= 1910 && !defined(__clang__)
// MSVC 2017+ can evaluate this at compile-time.
const char* begin = str;
@@ -633,7 +698,7 @@ class string_view {
: (compare_result < 0 ? -1 : 1);
}
- const char* ptr_;
+ absl::Nullable<const char*> ptr_;
size_type length_;
};
@@ -694,7 +759,7 @@ inline string_view ClippedSubstr(string_view s, size_t pos,
// Creates an `absl::string_view` from a pointer `p` even if it's null-valued.
// This function should be used where an `absl::string_view` can be created from
// a possibly-null pointer.
-constexpr string_view NullSafeStringView(const char* p) {
+constexpr string_view NullSafeStringView(absl::Nullable<const char*> p) {
return p ? string_view(p) : string_view();
}
diff --git a/absl/strings/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc
index 0d74e23e..98f747ca 100644
--- a/absl/strings/string_view_benchmark.cc
+++ b/absl/strings/string_view_benchmark.cc
@@ -15,6 +15,7 @@
#include "absl/strings/string_view.h"
#include <algorithm>
+#include <cstddef>
#include <cstdint>
#include <map>
#include <random>
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index 990c211a..5b1eb01a 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -15,20 +15,23 @@
#include "absl/strings/string_view.h"
#include <stdlib.h>
+
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
#include <iomanip>
+#include <ios>
#include <iterator>
#include <limits>
#include <map>
+#include <memory>
#include <sstream>
-#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#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.
@@ -948,6 +951,76 @@ TEST(StringViewTest, At) {
#endif
}
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+TEST(StringViewTest, StartsWith) {
+ const absl::string_view a("foobar");
+ const absl::string_view b("123\0abc", 7);
+ const absl::string_view e;
+ EXPECT_TRUE(a.starts_with(a));
+ EXPECT_TRUE(a.starts_with("foo"));
+ EXPECT_TRUE(a.starts_with('f'));
+ EXPECT_TRUE(a.starts_with(e));
+ EXPECT_TRUE(b.starts_with(b));
+ EXPECT_TRUE(b.starts_with('1'));
+ EXPECT_TRUE(b.starts_with(e));
+ EXPECT_TRUE(e.starts_with(""));
+ EXPECT_FALSE(a.starts_with(b));
+ EXPECT_FALSE(b.starts_with(a));
+ EXPECT_FALSE(e.starts_with(a));
+ EXPECT_FALSE(a.starts_with('r'));
+ EXPECT_FALSE(a.starts_with('\0'));
+ EXPECT_FALSE(e.starts_with('r'));
+ EXPECT_FALSE(e.starts_with('\0'));
+
+ // Test that constexpr compiles.
+ constexpr absl::string_view kFooBar("foobar");
+ constexpr absl::string_view kFoo("foo");
+ constexpr absl::string_view kBar("bar");
+ constexpr bool k1 = kFooBar.starts_with(kFoo);
+ EXPECT_TRUE(k1);
+ constexpr bool k2 = kFooBar.starts_with(kBar);
+ EXPECT_FALSE(k2);
+ constexpr bool k3 = kFooBar.starts_with('f');
+ EXPECT_TRUE(k3);
+ constexpr bool k4 = kFooBar.starts_with("fo");
+ EXPECT_TRUE(k4);
+}
+
+TEST(StringViewTest, EndsWith) {
+ const absl::string_view a("foobar");
+ const absl::string_view b("123\0abc", 7);
+ const absl::string_view e;
+ EXPECT_TRUE(a.ends_with(a));
+ EXPECT_TRUE(a.ends_with('r'));
+ EXPECT_TRUE(a.ends_with("bar"));
+ EXPECT_TRUE(a.ends_with(e));
+ EXPECT_TRUE(b.ends_with(b));
+ EXPECT_TRUE(b.ends_with('c'));
+ EXPECT_TRUE(b.ends_with(e));
+ EXPECT_TRUE(e.ends_with(""));
+ EXPECT_FALSE(a.ends_with(b));
+ EXPECT_FALSE(b.ends_with(a));
+ EXPECT_FALSE(e.ends_with(a));
+ EXPECT_FALSE(a.ends_with('f'));
+ EXPECT_FALSE(a.ends_with('\0'));
+ EXPECT_FALSE(e.ends_with('r'));
+ EXPECT_FALSE(e.ends_with('\0'));
+
+ // Test that constexpr compiles.
+ constexpr absl::string_view kFooBar("foobar");
+ constexpr absl::string_view kFoo("foo");
+ constexpr absl::string_view kBar("bar");
+ constexpr bool k1 = kFooBar.ends_with(kFoo);
+ EXPECT_FALSE(k1);
+ constexpr bool k2 = kFooBar.ends_with(kBar);
+ EXPECT_TRUE(k2);
+ constexpr bool k3 = kFooBar.ends_with('r');
+ EXPECT_TRUE(k3);
+ constexpr bool k4 = kFooBar.ends_with("ar");
+ EXPECT_TRUE(k4);
+}
+#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+
struct MyCharAlloc : std::allocator<char> {};
TEST(StringViewTest, ExplicitConversionOperator) {
diff --git a/absl/strings/strip.h b/absl/strings/strip.h
index 341e66fc..e3cda5ba 100644
--- a/absl/strings/strip.h
+++ b/absl/strings/strip.h
@@ -25,6 +25,7 @@
#include <string>
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
@@ -43,7 +44,8 @@ ABSL_NAMESPACE_BEGIN
// absl::string_view input("abc");
// EXPECT_TRUE(absl::ConsumePrefix(&input, "a"));
// EXPECT_EQ(input, "bc");
-inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) {
+inline bool ConsumePrefix(absl::Nonnull<absl::string_view*> str,
+ absl::string_view expected) {
if (!absl::StartsWith(*str, expected)) return false;
str->remove_prefix(expected.size());
return true;
@@ -59,7 +61,8 @@ inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) {
// absl::string_view input("abcdef");
// EXPECT_TRUE(absl::ConsumeSuffix(&input, "def"));
// EXPECT_EQ(input, "abc");
-inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) {
+inline bool ConsumeSuffix(absl::Nonnull<absl::string_view*> str,
+ absl::string_view expected) {
if (!absl::EndsWith(*str, expected)) return false;
str->remove_suffix(expected.size());
return true;
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 33a39305..dd32c75f 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -15,20 +15,28 @@
#include "absl/strings/substitute.h"
#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
+#include "absl/base/nullability.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
#include "absl/strings/internal/resize_uninitialized.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace substitute_internal {
-void SubstituteAndAppendArray(std::string* output, absl::string_view format,
- const absl::string_view* args_array,
- size_t num_args) {
+void SubstituteAndAppendArray(
+ absl::Nonnull<std::string*> output, absl::string_view format,
+ absl::Nullable<const absl::string_view*> args_array, size_t num_args) {
// Determine total size needed.
size_t size = 0;
for (size_t i = 0; i < format.size(); i++) {
@@ -97,7 +105,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format,
assert(target == output->data() + output->size());
}
-Arg::Arg(const void* value) {
+Arg::Arg(absl::Nullable<const void*> value) {
static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
"fix sizeof(scratch_)");
if (value == nullptr) {
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index d6a5a690..6c7cba4b 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -78,6 +78,7 @@
#include <vector>
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
#include "absl/base/port.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
@@ -105,7 +106,7 @@ class Arg {
// Overloads for string-y things
//
// Explicitly overload `const char*` so the compiler doesn't cast to `bool`.
- Arg(const char* value) // NOLINT(google-explicit-constructor)
+ Arg(absl::Nullable<const char*> value) // NOLINT(google-explicit-constructor)
: piece_(absl::NullSafeStringView(value)) {}
template <typename Allocator>
Arg( // NOLINT
@@ -176,7 +177,7 @@ class Arg {
: piece_(value ? "true" : "false") {}
template <typename T, typename = typename std::enable_if<
- strings_internal::HasAbslStringify<T>::value>::type>
+ HasAbslStringify<T>::value>::type>
Arg( // NOLINT(google-explicit-constructor)
const T& v, strings_internal::StringifySink&& sink = {})
: piece_(strings_internal::ExtractStringification(sink, v)) {}
@@ -197,14 +198,15 @@ class Arg {
// `void*` values, with the exception of `char*`, are printed as
// "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
- Arg(const void* value); // NOLINT(google-explicit-constructor)
+ Arg( // NOLINT(google-explicit-constructor)
+ absl::Nullable<const void*> value);
// Normal enums are already handled by the integer formatters.
// This overload matches only scoped enums.
template <typename T,
typename = typename std::enable_if<
std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
- !strings_internal::HasAbslStringify<T>::value>::type>
+ !HasAbslStringify<T>::value>::type>
Arg(T value) // NOLINT(google-explicit-constructor)
: Arg(static_cast<typename std::underlying_type<T>::type>(value)) {}
@@ -220,12 +222,12 @@ class Arg {
// Internal helper function. Don't call this from outside this implementation.
// This interface may change without notice.
-void SubstituteAndAppendArray(std::string* output, absl::string_view format,
- const absl::string_view* args_array,
- size_t num_args);
+void SubstituteAndAppendArray(
+ absl::Nonnull<std::string*> output, absl::string_view format,
+ absl::Nullable<const absl::string_view*> args_array, size_t num_args);
#if defined(ABSL_BAD_CALL_IF)
-constexpr int CalculateOneBit(const char* format) {
+constexpr int CalculateOneBit(absl::Nonnull<const char*> format) {
// Returns:
// * 2^N for '$N' when N is in [0-9]
// * 0 for correct '$' escaping: '$$'.
@@ -234,11 +236,11 @@ constexpr int CalculateOneBit(const char* format) {
: (1 << (*format - '0'));
}
-constexpr const char* SkipNumber(const char* format) {
+constexpr const char* SkipNumber(absl::Nonnull<const char*> format) {
return !*format ? format : (format + 1);
}
-constexpr int PlaceholderBitmask(const char* format) {
+constexpr int PlaceholderBitmask(absl::Nonnull<const char*> format) {
return !*format
? 0
: *format != '$' ? PlaceholderBitmask(format + 1)
@@ -271,18 +273,21 @@ constexpr int PlaceholderBitmask(const char* format) {
// absl::SubstituteAndAppend(boilerplate, format, args...);
// }
//
-inline void SubstituteAndAppend(std::string* output, absl::string_view format) {
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::string_view format) {
substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0);
}
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::string_view format,
const substitute_internal::Arg& a0) {
const absl::string_view args[] = {a0.piece()};
substitute_internal::SubstituteAndAppendArray(output, format, args,
ABSL_ARRAYSIZE(args));
}
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::string_view format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1) {
const absl::string_view args[] = {a0.piece(), a1.piece()};
@@ -290,7 +295,8 @@ inline void SubstituteAndAppend(std::string* output, absl::string_view format,
ABSL_ARRAYSIZE(args));
}
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::string_view format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2) {
@@ -299,7 +305,8 @@ inline void SubstituteAndAppend(std::string* output, absl::string_view format,
ABSL_ARRAYSIZE(args));
}
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::string_view format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2,
@@ -310,7 +317,8 @@ inline void SubstituteAndAppend(std::string* output, absl::string_view format,
ABSL_ARRAYSIZE(args));
}
-inline void SubstituteAndAppend(std::string* output, absl::string_view format,
+inline void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::string_view format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2,
@@ -322,27 +330,23 @@ inline void SubstituteAndAppend(std::string* output, absl::string_view format,
ABSL_ARRAYSIZE(args));
}
-inline void SubstituteAndAppend(std::string* output, absl::string_view 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) {
+inline void SubstituteAndAppend(
+ absl::Nonnull<std::string*> output, absl::string_view 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 absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
a3.piece(), a4.piece(), a5.piece()};
substitute_internal::SubstituteAndAppendArray(output, format, args,
ABSL_ARRAYSIZE(args));
}
-inline void SubstituteAndAppend(std::string* output, absl::string_view 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) {
+inline void SubstituteAndAppend(
+ absl::Nonnull<std::string*> output, absl::string_view 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) {
const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(),
a3.piece(), a4.piece(), a5.piece(),
a6.piece()};
@@ -351,7 +355,7 @@ inline void SubstituteAndAppend(std::string* output, absl::string_view format,
}
inline void SubstituteAndAppend(
- std::string* output, absl::string_view format,
+ absl::Nonnull<std::string*> output, absl::string_view 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,
@@ -364,7 +368,7 @@ inline void SubstituteAndAppend(
}
inline void SubstituteAndAppend(
- std::string* output, absl::string_view format,
+ absl::Nonnull<std::string*> output, absl::string_view 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,
@@ -378,7 +382,7 @@ inline void SubstituteAndAppend(
}
inline void SubstituteAndAppend(
- std::string* output, absl::string_view format,
+ absl::Nonnull<std::string*> output, absl::string_view 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,
@@ -394,14 +398,16 @@ inline void SubstituteAndAppend(
#if defined(ABSL_BAD_CALL_IF)
// 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)
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::Nonnull<const char*> format)
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,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::Nonnull<const char*> format,
const substitute_internal::Arg& a0)
ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1,
"There was 1 substitution argument given, but "
@@ -409,7 +415,8 @@ void SubstituteAndAppend(std::string* output, const char* format,
"one of $1-$9, or contains an unescaped $ character (use "
"$$ instead)");
-void SubstituteAndAppend(std::string* output, const char* format,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::Nonnull<const char*> format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1)
ABSL_BAD_CALL_IF(
@@ -418,7 +425,8 @@ void SubstituteAndAppend(std::string* output, const char* format,
"missing its $0/$1, contains one of $2-$9, or contains an "
"unescaped $ character (use $$ instead)");
-void SubstituteAndAppend(std::string* output, const char* format,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::Nonnull<const char*> format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2)
@@ -428,7 +436,8 @@ void SubstituteAndAppend(std::string* output, const char* format,
"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,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::Nonnull<const char*> format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2,
@@ -439,7 +448,8 @@ void SubstituteAndAppend(std::string* output, const char* format,
"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,
+void SubstituteAndAppend(absl::Nonnull<std::string*> output,
+ absl::Nonnull<const char*> format,
const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2,
@@ -451,13 +461,11 @@ void SubstituteAndAppend(std::string* output, const char* format,
"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,
- 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)
+void SubstituteAndAppend(
+ absl::Nonnull<std::string*> output, absl::Nonnull<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)
ABSL_BAD_CALL_IF(
substitute_internal::PlaceholderBitmask(format) != 63,
"There were 6 substitution arguments given, but "
@@ -465,10 +473,11 @@ void SubstituteAndAppend(std::string* output, const char* format,
"$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::Nonnull<std::string*> output, absl::Nonnull<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 "
@@ -476,11 +485,11 @@ void SubstituteAndAppend(
"$7-$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,
- const substitute_internal::Arg& a7)
+ absl::Nonnull<std::string*> output, absl::Nonnull<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, const substitute_internal::Arg& a7)
ABSL_BAD_CALL_IF(
substitute_internal::PlaceholderBitmask(format) != 255,
"There were 8 substitution arguments given, but "
@@ -488,11 +497,12 @@ void SubstituteAndAppend(
"$8-$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,
- const substitute_internal::Arg& a7, const substitute_internal::Arg& a8)
+ absl::Nonnull<std::string*> output, absl::Nonnull<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, const substitute_internal::Arg& a7,
+ const substitute_internal::Arg& a8)
ABSL_BAD_CALL_IF(
substitute_internal::PlaceholderBitmask(format) != 511,
"There were 9 substitution arguments given, but "
@@ -500,12 +510,12 @@ void SubstituteAndAppend(
"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,
- const substitute_internal::Arg& a7, const substitute_internal::Arg& a8,
- const substitute_internal::Arg& a9)
+ absl::Nonnull<std::string*> output, absl::Nonnull<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, 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 "
@@ -633,20 +643,22 @@ ABSL_MUST_USE_RESULT inline std::string Substitute(
#if defined(ABSL_BAD_CALL_IF)
// This body of functions catches cases where the number of placeholders
// doesn't match the number of data arguments.
-std::string Substitute(const char* format)
+std::string Substitute(absl::Nonnull<const char*> format)
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)");
-std::string Substitute(const char* format, const substitute_internal::Arg& a0)
+std::string Substitute(absl::Nonnull<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 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,
+std::string Substitute(absl::Nonnull<const char*> format,
+ const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1)
ABSL_BAD_CALL_IF(
substitute_internal::PlaceholderBitmask(format) != 3,
@@ -654,7 +666,8 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0,
"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,
+std::string Substitute(absl::Nonnull<const char*> format,
+ const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2)
ABSL_BAD_CALL_IF(
@@ -663,7 +676,8 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0,
"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,
+std::string Substitute(absl::Nonnull<const char*> format,
+ const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2,
const substitute_internal::Arg& a3)
@@ -673,7 +687,8 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0,
"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,
+std::string Substitute(absl::Nonnull<const char*> format,
+ const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2,
const substitute_internal::Arg& a3,
@@ -684,7 +699,8 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0,
"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,
+std::string Substitute(absl::Nonnull<const char*> format,
+ const substitute_internal::Arg& a0,
const substitute_internal::Arg& a1,
const substitute_internal::Arg& a2,
const substitute_internal::Arg& a3,
@@ -696,27 +712,23 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0,
"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,
- 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)
+std::string Substitute(
+ absl::Nonnull<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 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,
- 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,
- const substitute_internal::Arg& a7)
+std::string Substitute(
+ absl::Nonnull<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,
+ const substitute_internal::Arg& a7)
ABSL_BAD_CALL_IF(
substitute_internal::PlaceholderBitmask(format) != 255,
"There were 8 substitution arguments given, but "
@@ -724,7 +736,7 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0,
"$8-$9, or contains an unescaped $ character (use $$ instead)");
std::string Substitute(
- const char* format, const substitute_internal::Arg& a0,
+ absl::Nonnull<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,
@@ -736,7 +748,7 @@ std::string Substitute(
"contains an unescaped $ character (use $$ instead)");
std::string Substitute(
- const char* format, const substitute_internal::Arg& a0,
+ absl::Nonnull<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,
diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc
index ecf78d6b..70f9119b 100644
--- a/absl/strings/substitute_test.cc
+++ b/absl/strings/substitute_test.cc
@@ -15,10 +15,13 @@
#include "absl/strings/substitute.h"
#include <cstdint>
+#include <cstring>
+#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index 0ca94e01..de06ebdd 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:private"])
+package(
+ default_visibility = ["//visibility:private"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -76,6 +83,7 @@ cc_test(
"//absl/base:config",
"//absl/random",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -151,6 +159,7 @@ cc_test(
deps = [
":synchronization",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -167,6 +176,7 @@ cc_test(
deps = [
":synchronization",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -181,6 +191,7 @@ cc_binary(
deps = [
":synchronization",
":thread_pool",
+ "//absl/base:no_destructor",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -196,6 +207,7 @@ cc_test(
"//absl/base:core_headers",
"//absl/log",
"//absl/log:check",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -212,6 +224,7 @@ cc_test(
":graphcycles_internal",
"//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -248,6 +261,7 @@ cc_test(
"//absl/log:check",
"//absl/memory",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -260,6 +274,7 @@ cc_test(
deps = [
":synchronization",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -277,6 +292,7 @@ cc_library(
":thread_pool",
"//absl/base",
"//absl/base:config",
+ "//absl/base:no_destructor",
"@com_github_google_benchmark//:benchmark_main",
],
alwayslink = 1,
@@ -303,6 +319,7 @@ cc_test(
deps = [
":synchronization",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -356,6 +373,7 @@ cc_test(
"//absl/base:config",
"//absl/random",
"//absl/time",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/synchronization/blocking_counter_benchmark.cc b/absl/synchronization/blocking_counter_benchmark.cc
index b504d1a5..ea2bf9f9 100644
--- a/absl/synchronization/blocking_counter_benchmark.cc
+++ b/absl/synchronization/blocking_counter_benchmark.cc
@@ -14,6 +14,7 @@
#include <limits>
+#include "absl/base/no_destructor.h"
#include "absl/synchronization/blocking_counter.h"
#include "absl/synchronization/internal/thread_pool.h"
#include "benchmark/benchmark.h"
@@ -39,8 +40,8 @@ BENCHMARK(BM_BlockingCounter_SingleThread)
->Arg(256);
void BM_BlockingCounter_DecrementCount(benchmark::State& state) {
- static absl::BlockingCounter* counter =
- new absl::BlockingCounter{std::numeric_limits<int>::max()};
+ static absl::NoDestructor<absl::BlockingCounter> counter(
+ std::numeric_limits<int>::max());
for (auto _ : state) {
counter->DecrementCount();
}
diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc
index 92ed2691..bc546719 100644
--- a/absl/synchronization/internal/kernel_timeout_test.cc
+++ b/absl/synchronization/internal/kernel_timeout_test.cc
@@ -47,11 +47,10 @@ extern "C" int clock_gettime(clockid_t c, struct timespec* ts) {
namespace {
-#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
- defined(ABSL_HAVE_MEMORY_SANITIZER) || \
- defined(ABSL_HAVE_THREAD_SANITIZER) || \
- defined(__ANDROID__) || \
- defined(_WIN32) || defined(_WIN64)
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
+ defined(ABSL_HAVE_MEMORY_SANITIZER) || \
+ defined(ABSL_HAVE_THREAD_SANITIZER) || defined(__ANDROID__) || \
+ defined(__APPLE__) || defined(_WIN32) || defined(_WIN64)
constexpr absl::Duration kTimingBound = absl::Milliseconds(5);
#else
constexpr absl::Duration kTimingBound = absl::Microseconds(250);
diff --git a/absl/synchronization/internal/pthread_waiter.h b/absl/synchronization/internal/pthread_waiter.h
index 206aefa4..23db76ad 100644
--- a/absl/synchronization/internal/pthread_waiter.h
+++ b/absl/synchronization/internal/pthread_waiter.h
@@ -16,7 +16,7 @@
#ifndef ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
#define ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__MINGW32__)
#include <pthread.h>
#include "absl/base/config.h"
@@ -55,6 +55,6 @@ class PthreadWaiter : public WaiterCrtp<PthreadWaiter> {
ABSL_NAMESPACE_END
} // namespace absl
-#endif // ndef _WIN32
+#endif // !defined(_WIN32) && !defined(__MINGW32__)
#endif // ABSL_SYNCHRONIZATION_INTERNAL_PTHREAD_WAITER_H_
diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h
index 1a8b0b83..6ba204be 100644
--- a/absl/synchronization/internal/waiter.h
+++ b/absl/synchronization/internal/waiter.h
@@ -40,6 +40,8 @@
#define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM
#elif defined(ABSL_INTERNAL_HAVE_PTHREAD_WAITER)
#define ABSL_WAITER_MODE ABSL_WAITER_MODE_CONDVAR
+#elif defined(ABSL_INTERNAL_HAVE_STDCPP_WAITER)
+#define ABSL_WAITER_MODE ABSL_WAITER_MODE_STDCPP
#else
#error ABSL_WAITER_MODE is undefined
#endif
diff --git a/absl/synchronization/internal/win32_waiter.h b/absl/synchronization/internal/win32_waiter.h
index 87eb617c..fdab264e 100644
--- a/absl/synchronization/internal/win32_waiter.h
+++ b/absl/synchronization/internal/win32_waiter.h
@@ -20,7 +20,8 @@
#include <sdkddkver.h>
#endif
-#if defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#if defined(_WIN32) && !defined(__MINGW32__) && \
+ _WIN32_WINNT >= _WIN32_WINNT_VISTA
#include "absl/base/config.h"
#include "absl/synchronization/internal/kernel_timeout.h"
@@ -65,6 +66,7 @@ class Win32Waiter : public WaiterCrtp<Win32Waiter> {
ABSL_NAMESPACE_END
} // namespace absl
-#endif // defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#endif // defined(_WIN32) && !defined(__MINGW32__) &&
+ // _WIN32_WINNT >= _WIN32_WINNT_VISTA
#endif // ABSL_SYNCHRONIZATION_INTERNAL_WIN32_WAITER_H_
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index 3aa5560a..cb3c7e74 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -129,11 +129,15 @@ enum DelayMode { AGGRESSIVE, GENTLE };
struct ABSL_CACHELINE_ALIGNED MutexGlobals {
absl::once_flag once;
- int spinloop_iterations = 0;
+ // Note: this variable is initialized separately in Mutex::LockSlow,
+ // so that Mutex::Lock does not have a stack frame in optimized build.
+ std::atomic<int> spinloop_iterations{0};
int32_t mutex_sleep_spins[2] = {};
absl::Duration mutex_sleep_time;
};
+ABSL_CONST_INIT static MutexGlobals globals;
+
absl::Duration MeasureTimeToYield() {
absl::Time before = absl::Now();
ABSL_INTERNAL_C_SYMBOL(AbslInternalMutexYield)();
@@ -141,33 +145,30 @@ absl::Duration MeasureTimeToYield() {
}
const MutexGlobals& GetMutexGlobals() {
- ABSL_CONST_INIT static MutexGlobals data;
- absl::base_internal::LowLevelCallOnce(&data.once, [&]() {
+ absl::base_internal::LowLevelCallOnce(&globals.once, [&]() {
if (absl::base_internal::NumCPUs() > 1) {
- // If this is multiprocessor, allow spinning. 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.
- data.spinloop_iterations = 1500;
- data.mutex_sleep_spins[AGGRESSIVE] = 5000;
- data.mutex_sleep_spins[GENTLE] = 250;
- data.mutex_sleep_time = absl::Microseconds(10);
+ // 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.
+ globals.mutex_sleep_spins[AGGRESSIVE] = 5000;
+ globals.mutex_sleep_spins[GENTLE] = 250;
+ globals.mutex_sleep_time = absl::Microseconds(10);
} else {
// If this a uniprocessor, only yield/sleep. Real-time threads are often
// unable to yield, so the sleep time needs to be long enough to keep
// the calling thread asleep until scheduling happens.
- data.spinloop_iterations = 0;
- data.mutex_sleep_spins[AGGRESSIVE] = 0;
- data.mutex_sleep_spins[GENTLE] = 0;
- data.mutex_sleep_time = MeasureTimeToYield() * 5;
- data.mutex_sleep_time =
- std::min(data.mutex_sleep_time, absl::Milliseconds(1));
- data.mutex_sleep_time =
- std::max(data.mutex_sleep_time, absl::Microseconds(10));
+ globals.mutex_sleep_spins[AGGRESSIVE] = 0;
+ globals.mutex_sleep_spins[GENTLE] = 0;
+ globals.mutex_sleep_time = MeasureTimeToYield() * 5;
+ globals.mutex_sleep_time =
+ std::min(globals.mutex_sleep_time, absl::Milliseconds(1));
+ globals.mutex_sleep_time =
+ std::max(globals.mutex_sleep_time, absl::Microseconds(10));
}
});
- return data;
+ return globals;
}
} // namespace
@@ -202,31 +203,23 @@ int MutexDelay(int32_t c, int mode) {
// Ensure that "(*pv & bits) == bits" by doing an atomic update of "*pv" to
// "*pv | bits" if necessary. Wait until (*pv & wait_until_clear)==0
// before making any change.
+// Returns true if bits were previously unset and set by the call.
// This is used to set flags in mutex and condition variable words.
-static void AtomicSetBits(std::atomic<intptr_t>* pv, intptr_t bits,
+static bool AtomicSetBits(std::atomic<intptr_t>* pv, intptr_t bits,
intptr_t wait_until_clear) {
- intptr_t v;
- do {
- v = pv->load(std::memory_order_relaxed);
- } while ((v & bits) != bits &&
- ((v & wait_until_clear) != 0 ||
- !pv->compare_exchange_weak(v, v | bits, std::memory_order_release,
- std::memory_order_relaxed)));
-}
-
-// Ensure that "(*pv & bits) == 0" by doing an atomic update of "*pv" to
-// "*pv & ~bits" if necessary. Wait until (*pv & wait_until_clear)==0
-// before making any change.
-// This is used to unset flags in mutex and condition variable words.
-static void AtomicClearBits(std::atomic<intptr_t>* pv, intptr_t bits,
- intptr_t wait_until_clear) {
- intptr_t v;
- do {
- v = pv->load(std::memory_order_relaxed);
- } while ((v & bits) != 0 &&
- ((v & wait_until_clear) != 0 ||
- !pv->compare_exchange_weak(v, v & ~bits, std::memory_order_release,
- std::memory_order_relaxed)));
+ for (;;) {
+ intptr_t v = pv->load(std::memory_order_relaxed);
+ if ((v & bits) == bits) {
+ return false;
+ }
+ if ((v & wait_until_clear) != 0) {
+ continue;
+ }
+ if (pv->compare_exchange_weak(v, v | bits, std::memory_order_release,
+ std::memory_order_relaxed)) {
+ return true;
+ }
+ }
}
//------------------------------------------------------------------
@@ -337,12 +330,49 @@ static SynchEvent* EnsureSynchEvent(std::atomic<intptr_t>* addr,
const char* name, intptr_t bits,
intptr_t lockbit) {
uint32_t h = reinterpret_cast<uintptr_t>(addr) % kNSynchEvent;
- SynchEvent* e;
- // first look for existing SynchEvent struct..
synch_event_mu.Lock();
- for (e = synch_event[h];
- e != nullptr && e->masked_addr != base_internal::HidePtr(addr);
- e = e->next) {
+ // When a Mutex/CondVar is destroyed, we don't remove the associated
+ // SynchEvent to keep destructors empty in release builds for performance
+ // reasons. If the current call is the first to set bits (kMuEvent/kCVEvent),
+ // we don't look up the existing even because (if it exists, it must be for
+ // the previous Mutex/CondVar that existed at the same address).
+ // The leaking events must not be a problem for tests, which should create
+ // bounded amount of events. And debug logging is not supposed to be enabled
+ // in production. However, if it's accidentally enabled, or briefly enabled
+ // for some debugging, we don't want to crash the program. Instead we drop
+ // all events, if we accumulated too many of them. Size of a single event
+ // is ~48 bytes, so 100K events is ~5 MB.
+ // Additionally we could delete the old event for the same address,
+ // but it would require a better hashmap (if we accumulate too many events,
+ // linked lists will grow and traversing them will be very slow).
+ constexpr size_t kMaxSynchEventCount = 100 << 10;
+ // Total number of live synch events.
+ static size_t synch_event_count ABSL_GUARDED_BY(synch_event_mu);
+ if (++synch_event_count > kMaxSynchEventCount) {
+ synch_event_count = 0;
+ ABSL_RAW_LOG(ERROR,
+ "Accumulated %zu Mutex debug objects. If you see this"
+ " in production, it may mean that the production code"
+ " accidentally calls "
+ "Mutex/CondVar::EnableDebugLog/EnableInvariantDebugging.",
+ kMaxSynchEventCount);
+ for (auto*& head : synch_event) {
+ for (auto* e = head; e != nullptr;) {
+ SynchEvent* next = e->next;
+ if (--(e->refcount) == 0) {
+ base_internal::LowLevelAlloc::Free(e);
+ }
+ e = next;
+ }
+ head = nullptr;
+ }
+ }
+ SynchEvent* e = nullptr;
+ if (!AtomicSetBits(addr, bits, lockbit)) {
+ for (e = synch_event[h];
+ e != nullptr && e->masked_addr != base_internal::HidePtr(addr);
+ e = e->next) {
+ }
}
if (e == nullptr) { // no SynchEvent struct found; make one.
if (name == nullptr) {
@@ -358,7 +388,6 @@ static SynchEvent* EnsureSynchEvent(std::atomic<intptr_t>* addr,
e->log = false;
strcpy(e->name, name); // NOLINT(runtime/printf)
e->next = synch_event[h];
- AtomicSetBits(addr, bits, lockbit);
synch_event[h] = e;
} else {
e->refcount++; // for return value
@@ -367,11 +396,6 @@ static SynchEvent* EnsureSynchEvent(std::atomic<intptr_t>* addr,
return e;
}
-// Deallocate the SynchEvent *e, whose refcount has fallen to zero.
-static void DeleteSynchEvent(SynchEvent* e) {
- base_internal::LowLevelAlloc::Free(e);
-}
-
// Decrement the reference count of *e, or do nothing if e==null.
static void UnrefSynchEvent(SynchEvent* e) {
if (e != nullptr) {
@@ -379,36 +403,11 @@ static void UnrefSynchEvent(SynchEvent* e) {
bool del = (--(e->refcount) == 0);
synch_event_mu.Unlock();
if (del) {
- DeleteSynchEvent(e);
+ base_internal::LowLevelAlloc::Free(e);
}
}
}
-// Forget the mapping from the object (Mutex or CondVar) at address addr
-// to SynchEvent object, and clear "bits" in its word (waiting until lockbit
-// is clear before doing so).
-static void ForgetSynchEvent(std::atomic<intptr_t>* addr, intptr_t bits,
- intptr_t lockbit) {
- uint32_t h = reinterpret_cast<uintptr_t>(addr) % kNSynchEvent;
- SynchEvent** pe;
- SynchEvent* e;
- synch_event_mu.Lock();
- for (pe = &synch_event[h];
- (e = *pe) != nullptr && e->masked_addr != base_internal::HidePtr(addr);
- pe = &e->next) {
- }
- bool del = false;
- if (e != nullptr) {
- *pe = e->next;
- del = (--(e->refcount) == 0);
- }
- AtomicClearBits(addr, bits, lockbit);
- synch_event_mu.Unlock();
- if (del) {
- DeleteSynchEvent(e);
- }
-}
-
// Return a refcounted reference to the SynchEvent of the object at address
// "addr", if any. The pointer returned is valid until the UnrefSynchEvent() is
// called.
@@ -579,31 +578,25 @@ static SynchLocksHeld* Synch_GetAllLocks() {
// Post on "w"'s associated PerThreadSem.
void Mutex::IncrementSynchSem(Mutex* mu, PerThreadSynch* w) {
- if (mu) {
- ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
- // We miss synchronization around passing PerThreadSynch between threads
- // since it happens inside of the Mutex code, so we need to ignore all
- // accesses to the object.
- ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
- PerThreadSem::Post(w->thread_identity());
- ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END();
- ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
- } else {
- PerThreadSem::Post(w->thread_identity());
- }
+ static_cast<void>(mu); // Prevent unused param warning in non-TSAN builds.
+ ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
+ // We miss synchronization around passing PerThreadSynch between threads
+ // since it happens inside of the Mutex code, so we need to ignore all
+ // accesses to the object.
+ ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
+ PerThreadSem::Post(w->thread_identity());
+ ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END();
+ ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
}
// Wait on "w"'s associated PerThreadSem; returns false if timeout expired.
bool Mutex::DecrementSynchSem(Mutex* mu, PerThreadSynch* w, KernelTimeout t) {
- if (mu) {
- ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
- }
+ static_cast<void>(mu); // Prevent unused param warning in non-TSAN builds.
+ ABSL_TSAN_MUTEX_PRE_DIVERT(mu, 0);
assert(w == Synch_GetPerThread());
static_cast<void>(w);
bool res = PerThreadSem::Wait(t);
- if (mu) {
- ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
- }
+ ABSL_TSAN_MUTEX_POST_DIVERT(mu, 0);
return res;
}
@@ -682,6 +675,7 @@ static const intptr_t kMuOne = 0x0100; // a count of one reader
// flags passed to Enqueue and LockSlow{,WithTimeout,Loop}
static const int kMuHasBlocked = 0x01; // already blocked (MUST == 1)
static const int kMuIsCond = 0x02; // conditional waiter (CV or Condition)
+static const int kMuIsFer = 0x04; // wait morphing from a CondVar
static_assert(PerThreadSynch::kAlignment > kMuLow,
"PerThreadSynch::kAlignment must be greater than kMuLow");
@@ -737,25 +731,43 @@ static unsigned TsanFlags(Mutex::MuHow how) {
}
#endif
-static bool DebugOnlyIsExiting() {
- return false;
-}
+#if defined(__APPLE__) || defined(ABSL_BUILD_DLL)
+// When building a dll symbol export lists may reference the destructor
+// and want it to be an exported symbol rather than an inline function.
+// Some apple builds also do dynamic library build but don't say it explicitly.
+Mutex::~Mutex() { Dtor(); }
+#endif
-Mutex::~Mutex() {
- intptr_t v = mu_.load(std::memory_order_relaxed);
- if ((v & kMuEvent) != 0 && !DebugOnlyIsExiting()) {
- ForgetSynchEvent(&this->mu_, kMuEvent, kMuSpin);
- }
+#if !defined(NDEBUG) || defined(ABSL_HAVE_THREAD_SANITIZER)
+void Mutex::Dtor() {
if (kDebugMode) {
this->ForgetDeadlockInfo();
}
ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static);
}
+#endif
void Mutex::EnableDebugLog(const char* name) {
+ // Need to disable writes here and in EnableInvariantDebugging to prevent
+ // false race reports on SynchEvent objects. TSan ignores synchronization
+ // on synch_event_mu in Lock/Unlock/etc methods due to mutex annotations,
+ // but it sees few accesses to SynchEvent in EvalConditionAnnotated.
+ // If we don't ignore accesses here, it can result in false races
+ // between EvalConditionAnnotated and SynchEvent reuse in EnsureSynchEvent.
+ ABSL_ANNOTATE_IGNORE_WRITES_BEGIN();
SynchEvent* e = EnsureSynchEvent(&this->mu_, name, kMuEvent, kMuSpin);
e->log = true;
UnrefSynchEvent(e);
+ // This prevents "error: undefined symbol: absl::Mutex::~Mutex()"
+ // in a release build (NDEBUG defined) when a test does "#undef NDEBUG"
+ // to use assert macro. In such case, the test does not get the dtor
+ // definition because it's supposed to be outline when NDEBUG is not defined,
+ // and this source file does not define one either because NDEBUG is defined.
+ // Since it's not possible to take address of a destructor, we move the
+ // actual destructor code into the separate Dtor function and force the
+ // compiler to emit this function even if it's inline by taking its address.
+ ABSL_ATTRIBUTE_UNUSED volatile auto dtor = &Mutex::Dtor;
+ ABSL_ANNOTATE_IGNORE_WRITES_END();
}
void EnableMutexInvariantDebugging(bool enabled) {
@@ -763,6 +775,7 @@ void EnableMutexInvariantDebugging(bool enabled) {
}
void Mutex::EnableInvariantDebugging(void (*invariant)(void*), void* arg) {
+ ABSL_ANNOTATE_IGNORE_WRITES_BEGIN();
if (synch_check_invariants.load(std::memory_order_acquire) &&
invariant != nullptr) {
SynchEvent* e = EnsureSynchEvent(&this->mu_, nullptr, kMuEvent, kMuSpin);
@@ -770,6 +783,7 @@ void Mutex::EnableInvariantDebugging(void (*invariant)(void*), void* arg) {
e->arg = arg;
UnrefSynchEvent(e);
}
+ ABSL_ANNOTATE_IGNORE_WRITES_END();
}
void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode) {
@@ -920,20 +934,23 @@ static PerThreadSynch* Enqueue(PerThreadSynch* head, SynchWaitParams* waitp,
s->wake = false; // not being woken
s->cond_waiter = ((flags & kMuIsCond) != 0);
#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
- int64_t now_cycles = CycleClock::Now();
- if (s->next_priority_read_cycles < now_cycles) {
- // Every so often, update our idea of the thread's priority.
- // pthread_getschedparam() is 5% of the block/wakeup time;
- // CycleClock::Now() is 0.5%.
- int policy;
- struct sched_param param;
- const int err = pthread_getschedparam(pthread_self(), &policy, &param);
- if (err != 0) {
- ABSL_RAW_LOG(ERROR, "pthread_getschedparam failed: %d", err);
- } else {
- s->priority = param.sched_priority;
- s->next_priority_read_cycles =
- now_cycles + static_cast<int64_t>(CycleClock::Frequency());
+ if ((flags & kMuIsFer) == 0) {
+ assert(s == Synch_GetPerThread());
+ int64_t now_cycles = CycleClock::Now();
+ if (s->next_priority_read_cycles < now_cycles) {
+ // Every so often, update our idea of the thread's priority.
+ // pthread_getschedparam() is 5% of the block/wakeup time;
+ // CycleClock::Now() is 0.5%.
+ int policy;
+ struct sched_param param;
+ const int err = pthread_getschedparam(pthread_self(), &policy, &param);
+ if (err != 0) {
+ ABSL_RAW_LOG(ERROR, "pthread_getschedparam failed: %d", err);
+ } else {
+ s->priority = param.sched_priority;
+ s->next_priority_read_cycles =
+ now_cycles + static_cast<int64_t>(CycleClock::Frequency());
+ }
}
}
#endif
@@ -962,8 +979,7 @@ static PerThreadSynch* Enqueue(PerThreadSynch* head, SynchWaitParams* waitp,
} while (s->priority <= advance_to->priority);
// termination guaranteed because s->priority > head->priority
// and head is the end of a skip chain
- } else if (waitp->how == kExclusive &&
- Condition::GuaranteedEqual(waitp->cond, nullptr)) {
+ } else if (waitp->how == kExclusive && waitp->cond == nullptr) {
// An unlocker could be scanning the queue, but we know it will recheck
// the queue front for writers that have no condition, which is what s
// is, so an insert at front is safe.
@@ -994,6 +1010,24 @@ static PerThreadSynch* Enqueue(PerThreadSynch* head, SynchWaitParams* waitp,
if (MuEquivalentWaiter(s, s->next)) { // s->may_skip is known to be true
s->skip = s->next; // s may skip to its successor
}
+ } else if ((flags & kMuHasBlocked) &&
+ (s->priority >= head->next->priority) &&
+ (!head->maybe_unlocking ||
+ (waitp->how == kExclusive &&
+ Condition::GuaranteedEqual(waitp->cond, nullptr)))) {
+ // This thread has already waited, then was woken, then failed to acquire
+ // the mutex and now tries to requeue. Try to requeue it at head,
+ // otherwise it can suffer bad latency (wait whole queue several times).
+ // However, we need to be conservative. First, we need to ensure that we
+ // respect priorities. Then, we need to be careful to not break wait
+ // queue invariants: we require either that unlocker is not scanning
+ // the queue or that the current thread is a writer with no condition
+ // (unlocker will recheck the queue for such waiters).
+ s->next = head->next;
+ head->next = s;
+ 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
// we're inserting s at the back
// s will become new head; copy data from head into it
@@ -1472,7 +1506,7 @@ 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 = GetMutexGlobals().spinloop_iterations;
+ int c = globals.spinloop_iterations.load(std::memory_order_relaxed);
do { // do/while somewhat faster on AMD
intptr_t v = mu->load(std::memory_order_relaxed);
if ((v & (kMuReader | kMuEvent)) != 0) {
@@ -1492,11 +1526,12 @@ void Mutex::Lock() {
GraphId id = DebugOnlyDeadlockCheck(this);
intptr_t v = mu_.load(std::memory_order_relaxed);
// try fast acquire, then spin loop
- if ((v & (kMuWriter | kMuReader | kMuEvent)) != 0 ||
- !mu_.compare_exchange_strong(v, kMuWriter | v, std::memory_order_acquire,
- std::memory_order_relaxed)) {
+ if (ABSL_PREDICT_FALSE((v & (kMuWriter | kMuReader | kMuEvent)) != 0) ||
+ ABSL_PREDICT_FALSE(!mu_.compare_exchange_strong(
+ v, kMuWriter | v, std::memory_order_acquire,
+ std::memory_order_relaxed))) {
// try spin acquire, then slow loop
- if (!TryAcquireWithSpinning(&this->mu_)) {
+ if (ABSL_PREDICT_FALSE(!TryAcquireWithSpinning(&this->mu_))) {
this->LockSlow(kExclusive, nullptr, 0);
}
}
@@ -1508,159 +1543,95 @@ void Mutex::ReaderLock() {
ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
GraphId id = DebugOnlyDeadlockCheck(this);
intptr_t v = mu_.load(std::memory_order_relaxed);
- // try fast acquire, then slow loop
- if ((v & (kMuWriter | kMuWait | kMuEvent)) != 0 ||
- !mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
- std::memory_order_acquire,
- std::memory_order_relaxed)) {
- this->LockSlow(kShared, nullptr, 0);
+ for (;;) {
+ // If there are non-readers holding the lock, use the slow loop.
+ if (ABSL_PREDICT_FALSE(v & (kMuWriter | kMuWait | kMuEvent)) != 0) {
+ this->LockSlow(kShared, nullptr, 0);
+ break;
+ }
+ // We can avoid the loop and only use the CAS when the lock is free or
+ // only held by readers.
+ if (ABSL_PREDICT_TRUE(mu_.compare_exchange_weak(
+ v, (kMuReader | v) + kMuOne, std::memory_order_acquire,
+ std::memory_order_relaxed))) {
+ break;
+ }
}
DebugOnlyLockEnter(this, id);
ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
}
-void Mutex::LockWhen(const Condition& cond) {
- ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
- GraphId id = DebugOnlyDeadlockCheck(this);
- this->LockSlow(kExclusive, &cond, 0);
- DebugOnlyLockEnter(this, id);
- ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
-}
-
-bool Mutex::LockWhenWithTimeout(const Condition& cond, absl::Duration timeout) {
- ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
- GraphId id = DebugOnlyDeadlockCheck(this);
- bool res = LockSlowWithDeadline(kExclusive, &cond, KernelTimeout(timeout), 0);
- DebugOnlyLockEnter(this, id);
- ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
- return res;
-}
-
-bool Mutex::LockWhenWithDeadline(const Condition& cond, absl::Time deadline) {
- ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
- GraphId id = DebugOnlyDeadlockCheck(this);
- bool res =
- LockSlowWithDeadline(kExclusive, &cond, KernelTimeout(deadline), 0);
- DebugOnlyLockEnter(this, id);
- ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0);
- return res;
-}
-
-void Mutex::ReaderLockWhen(const Condition& cond) {
- ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
- GraphId id = DebugOnlyDeadlockCheck(this);
- this->LockSlow(kShared, &cond, 0);
- DebugOnlyLockEnter(this, id);
- ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
-}
-
-bool Mutex::ReaderLockWhenWithTimeout(const Condition& cond,
- absl::Duration timeout) {
- ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
- GraphId id = DebugOnlyDeadlockCheck(this);
- bool res = LockSlowWithDeadline(kShared, &cond, KernelTimeout(timeout), 0);
- DebugOnlyLockEnter(this, id);
- ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
- return res;
-}
-
-bool Mutex::ReaderLockWhenWithDeadline(const Condition& cond,
- absl::Time deadline) {
- ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_read_lock);
+bool Mutex::LockWhenCommon(const Condition& cond,
+ synchronization_internal::KernelTimeout t,
+ bool write) {
+ MuHow how = write ? kExclusive : kShared;
+ ABSL_TSAN_MUTEX_PRE_LOCK(this, TsanFlags(how));
GraphId id = DebugOnlyDeadlockCheck(this);
- bool res = LockSlowWithDeadline(kShared, &cond, KernelTimeout(deadline), 0);
+ bool res = LockSlowWithDeadline(how, &cond, t, 0);
DebugOnlyLockEnter(this, id);
- ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_read_lock, 0);
+ ABSL_TSAN_MUTEX_POST_LOCK(this, TsanFlags(how), 0);
return res;
}
-void Mutex::Await(const Condition& cond) {
- if (cond.Eval()) { // condition already true; nothing to do
- if (kDebugMode) {
- this->AssertReaderHeld();
- }
- } else { // normal case
- ABSL_RAW_CHECK(this->AwaitCommon(cond, KernelTimeout::Never()),
- "condition untrue on return from Await");
- }
-}
-
-bool Mutex::AwaitWithTimeout(const Condition& cond, absl::Duration timeout) {
- if (cond.Eval()) { // condition already true; nothing to do
- if (kDebugMode) {
- this->AssertReaderHeld();
- }
- return true;
+bool Mutex::AwaitCommon(const Condition& cond, KernelTimeout t) {
+ if (kDebugMode) {
+ this->AssertReaderHeld();
}
-
- KernelTimeout t{timeout};
- bool res = this->AwaitCommon(cond, t);
- ABSL_RAW_CHECK(res || t.has_timeout(),
- "condition untrue on return from Await");
- return res;
-}
-
-bool Mutex::AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
if (cond.Eval()) { // condition already true; nothing to do
- if (kDebugMode) {
- this->AssertReaderHeld();
- }
return true;
}
-
- KernelTimeout t{deadline};
- bool res = this->AwaitCommon(cond, t);
- ABSL_RAW_CHECK(res || t.has_timeout(),
- "condition untrue on return from Await");
- return res;
-}
-
-bool Mutex::AwaitCommon(const Condition& cond, KernelTimeout t) {
- this->AssertReaderHeld();
MuHow how =
(mu_.load(std::memory_order_relaxed) & kMuWriter) ? kExclusive : kShared;
ABSL_TSAN_MUTEX_PRE_UNLOCK(this, TsanFlags(how));
SynchWaitParams waitp(how, &cond, t, nullptr /*no cvmu*/,
Synch_GetPerThreadAnnotated(this),
nullptr /*no cv_word*/);
- int flags = kMuHasBlocked;
- if (!Condition::GuaranteedEqual(&cond, nullptr)) {
- flags |= kMuIsCond;
- }
this->UnlockSlow(&waitp);
this->Block(waitp.thread);
ABSL_TSAN_MUTEX_POST_UNLOCK(this, TsanFlags(how));
ABSL_TSAN_MUTEX_PRE_LOCK(this, TsanFlags(how));
- this->LockSlowLoop(&waitp, flags);
+ this->LockSlowLoop(&waitp, kMuHasBlocked | kMuIsCond);
bool res = waitp.cond != nullptr || // => cond known true from LockSlowLoop
EvalConditionAnnotated(&cond, this, true, false, how == kShared);
ABSL_TSAN_MUTEX_POST_LOCK(this, TsanFlags(how), 0);
+ ABSL_RAW_CHECK(res || t.has_timeout(),
+ "condition untrue on return from Await");
return res;
}
bool Mutex::TryLock() {
ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
intptr_t v = mu_.load(std::memory_order_relaxed);
- if ((v & (kMuWriter | kMuReader | kMuEvent)) == 0 && // try fast acquire
- mu_.compare_exchange_strong(v, kMuWriter | v, std::memory_order_acquire,
- std::memory_order_relaxed)) {
- DebugOnlyLockEnter(this);
- ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
- return true;
- }
- if ((v & kMuEvent) != 0) { // we're recording events
- if ((v & kExclusive->slow_need_zero) == 0 && // try fast acquire
- mu_.compare_exchange_strong(
- v, (kExclusive->fast_or | v) + kExclusive->fast_add,
- std::memory_order_acquire, std::memory_order_relaxed)) {
+ // Try fast acquire.
+ if (ABSL_PREDICT_TRUE((v & (kMuWriter | kMuReader | kMuEvent)) == 0)) {
+ if (ABSL_PREDICT_TRUE(mu_.compare_exchange_strong(
+ v, kMuWriter | v, std::memory_order_acquire,
+ std::memory_order_relaxed))) {
DebugOnlyLockEnter(this);
- PostSynchEvent(this, SYNCH_EV_TRYLOCK_SUCCESS);
ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
return true;
- } else {
- PostSynchEvent(this, SYNCH_EV_TRYLOCK_FAILED);
}
+ } else if (ABSL_PREDICT_FALSE((v & kMuEvent) != 0)) {
+ // We're recording events.
+ return TryLockSlow();
+ }
+ ABSL_TSAN_MUTEX_POST_LOCK(
+ this, __tsan_mutex_try_lock | __tsan_mutex_try_lock_failed, 0);
+ return false;
+}
+
+ABSL_ATTRIBUTE_NOINLINE bool Mutex::TryLockSlow() {
+ intptr_t v = mu_.load(std::memory_order_relaxed);
+ if ((v & kExclusive->slow_need_zero) == 0 && // try fast acquire
+ mu_.compare_exchange_strong(
+ v, (kExclusive->fast_or | v) + kExclusive->fast_add,
+ std::memory_order_acquire, std::memory_order_relaxed)) {
+ DebugOnlyLockEnter(this);
+ PostSynchEvent(this, SYNCH_EV_TRYLOCK_SUCCESS);
+ ABSL_TSAN_MUTEX_POST_LOCK(this, __tsan_mutex_try_lock, 0);
+ return true;
}
+ PostSynchEvent(this, SYNCH_EV_TRYLOCK_FAILED);
ABSL_TSAN_MUTEX_POST_LOCK(
this, __tsan_mutex_try_lock | __tsan_mutex_try_lock_failed, 0);
return false;
@@ -1670,41 +1641,57 @@ bool Mutex::ReaderTryLock() {
ABSL_TSAN_MUTEX_PRE_LOCK(this,
__tsan_mutex_read_lock | __tsan_mutex_try_lock);
intptr_t v = mu_.load(std::memory_order_relaxed);
+ // Clang tends to unroll the loop when compiling with optimization.
+ // But in this case it just unnecessary increases code size.
+ // If CAS is failing due to contention, the jump cost is negligible.
+#if defined(__clang__)
+#pragma nounroll
+#endif
// The while-loops (here and below) iterate only if the mutex word keeps
- // changing (typically because the reader count changes) under the CAS. We
- // limit the number of attempts to avoid having to think about livelock.
- int loop_limit = 5;
- while ((v & (kMuWriter | kMuWait | kMuEvent)) == 0 && loop_limit != 0) {
- if (mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
- std::memory_order_acquire,
- std::memory_order_relaxed)) {
+ // changing (typically because the reader count changes) under the CAS.
+ // We limit the number of attempts to avoid having to think about livelock.
+ for (int loop_limit = 5; loop_limit != 0; loop_limit--) {
+ if (ABSL_PREDICT_FALSE((v & (kMuWriter | kMuWait | kMuEvent)) != 0)) {
+ break;
+ }
+ if (ABSL_PREDICT_TRUE(mu_.compare_exchange_strong(
+ v, (kMuReader | v) + kMuOne, std::memory_order_acquire,
+ std::memory_order_relaxed))) {
DebugOnlyLockEnter(this);
ABSL_TSAN_MUTEX_POST_LOCK(
this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
return true;
}
- loop_limit--;
- v = mu_.load(std::memory_order_relaxed);
}
- if ((v & kMuEvent) != 0) { // we're recording events
- loop_limit = 5;
- while ((v & kShared->slow_need_zero) == 0 && loop_limit != 0) {
- if (mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
- std::memory_order_acquire,
- std::memory_order_relaxed)) {
- DebugOnlyLockEnter(this);
- PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_SUCCESS);
- ABSL_TSAN_MUTEX_POST_LOCK(
- this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
- return true;
- }
- loop_limit--;
- v = mu_.load(std::memory_order_relaxed);
- }
- if ((v & kMuEvent) != 0) {
- PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_FAILED);
+ if (ABSL_PREDICT_TRUE((v & kMuEvent) == 0)) {
+ ABSL_TSAN_MUTEX_POST_LOCK(this,
+ __tsan_mutex_read_lock | __tsan_mutex_try_lock |
+ __tsan_mutex_try_lock_failed,
+ 0);
+ return false;
+ }
+ // we're recording events
+ return ReaderTryLockSlow();
+}
+
+ABSL_ATTRIBUTE_NOINLINE bool Mutex::ReaderTryLockSlow() {
+ intptr_t v = mu_.load(std::memory_order_relaxed);
+#if defined(__clang__)
+#pragma nounroll
+#endif
+ for (int loop_limit = 5; loop_limit != 0; loop_limit--) {
+ if ((v & kShared->slow_need_zero) == 0 &&
+ mu_.compare_exchange_strong(v, (kMuReader | v) + kMuOne,
+ std::memory_order_acquire,
+ std::memory_order_relaxed)) {
+ DebugOnlyLockEnter(this);
+ PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_SUCCESS);
+ ABSL_TSAN_MUTEX_POST_LOCK(
+ this, __tsan_mutex_read_lock | __tsan_mutex_try_lock, 0);
+ return true;
}
}
+ PostSynchEvent(this, SYNCH_EV_READERTRYLOCK_FAILED);
ABSL_TSAN_MUTEX_POST_LOCK(this,
__tsan_mutex_read_lock | __tsan_mutex_try_lock |
__tsan_mutex_try_lock_failed,
@@ -1768,16 +1755,20 @@ void Mutex::ReaderUnlock() {
DebugOnlyLockLeave(this);
intptr_t v = mu_.load(std::memory_order_relaxed);
assert((v & (kMuWriter | kMuReader)) == kMuReader);
- if ((v & (kMuReader | kMuWait | kMuEvent)) == kMuReader) {
+ for (;;) {
+ if (ABSL_PREDICT_FALSE((v & (kMuReader | kMuWait | kMuEvent)) !=
+ kMuReader)) {
+ this->UnlockSlow(nullptr /*no waitp*/); // take slow path
+ break;
+ }
// fast reader release (reader with no waiters)
intptr_t clear = ExactlyOneReader(v) ? kMuReader | kMuOne : kMuOne;
- if (mu_.compare_exchange_strong(v, v - clear, std::memory_order_release,
- std::memory_order_relaxed)) {
- ABSL_TSAN_MUTEX_POST_UNLOCK(this, __tsan_mutex_read_lock);
- return;
+ if (ABSL_PREDICT_TRUE(
+ mu_.compare_exchange_strong(v, v - clear, std::memory_order_release,
+ std::memory_order_relaxed))) {
+ break;
}
}
- this->UnlockSlow(nullptr /*no waitp*/); // take slow path
ABSL_TSAN_MUTEX_POST_UNLOCK(this, __tsan_mutex_read_lock);
}
@@ -1812,6 +1803,22 @@ static intptr_t IgnoreWaitingWritersMask(int flag) {
// Internal version of LockWhen(). See LockSlowWithDeadline()
ABSL_ATTRIBUTE_NOINLINE void Mutex::LockSlow(MuHow how, const Condition* cond,
int flags) {
+ // Note: we specifically initialize spinloop_iterations after the first use
+ // in TryAcquireWithSpinning so that Lock function does not have any non-tail
+ // calls and consequently a stack frame. It's fine to have spinloop_iterations
+ // uninitialized (meaning no spinning) in all initial uncontended Lock calls
+ // and in the first contended call. After that we will have
+ // spinloop_iterations properly initialized.
+ if (ABSL_PREDICT_FALSE(
+ globals.spinloop_iterations.load(std::memory_order_relaxed) == 0)) {
+ if (absl::base_internal::NumCPUs() > 1) {
+ // If this is multiprocessor, allow spinning.
+ globals.spinloop_iterations.store(1500, std::memory_order_relaxed);
+ } else {
+ // If this a uniprocessor, only yield/sleep.
+ globals.spinloop_iterations.store(-1, std::memory_order_relaxed);
+ }
+ }
ABSL_RAW_CHECK(
this->LockSlowWithDeadline(how, cond, KernelTimeout::Never(), flags),
"condition untrue on return from LockSlow");
@@ -1913,7 +1920,7 @@ bool Mutex::LockSlowWithDeadline(MuHow how, const Condition* cond,
SynchWaitParams waitp(how, cond, t, nullptr /*no cvmu*/,
Synch_GetPerThreadAnnotated(this),
nullptr /*no cv_word*/);
- if (!Condition::GuaranteedEqual(cond, nullptr)) {
+ if (cond != nullptr) {
flags |= kMuIsCond;
}
if (unlock) {
@@ -2103,7 +2110,6 @@ ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams* waitp) {
// head of the list searched previously, or zero
PerThreadSynch* old_h = nullptr;
// a condition that's known to be false.
- const Condition* known_false = nullptr;
PerThreadSynch* wake_list = kPerThreadSynchNull; // list of threads to wake
intptr_t wr_wait = 0; // set to kMuWrWait if we wake a reader and a
// later writer could have acquired the lock
@@ -2207,7 +2213,7 @@ ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams* waitp) {
}
}
if (h->next->waitp->how == kExclusive &&
- Condition::GuaranteedEqual(h->next->waitp->cond, nullptr)) {
+ h->next->waitp->cond == nullptr) {
// easy case: writer with no condition; no need to search
pw = h; // wake w, the successor of h (=pw)
w = h->next;
@@ -2290,10 +2296,8 @@ ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams* waitp) {
w_walk->wake = false;
if (w_walk->waitp->cond ==
nullptr || // no condition => vacuously true OR
- (w_walk->waitp->cond != known_false &&
- // this thread's condition is not known false, AND
- // is in fact true
- EvalConditionIgnored(this, w_walk->waitp->cond))) {
+ // this thread's condition is true
+ EvalConditionIgnored(this, w_walk->waitp->cond)) {
if (w == nullptr) {
w_walk->wake = true; // can wake this waiter
w = w_walk;
@@ -2307,8 +2311,6 @@ ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams* waitp) {
} else { // writer with true condition
wr_wait = kMuWrWait;
}
- } else { // can't wake; condition false
- known_false = w_walk->waitp->cond; // remember last false condition
}
if (w_walk->wake) { // we're waking reader w_walk
pw_walk = w_walk; // don't skip similar waiters
@@ -2414,10 +2416,10 @@ void Mutex::Fer(PerThreadSynch* w) {
int c = 0;
ABSL_RAW_CHECK(w->waitp->cond == nullptr,
"Mutex::Fer while waiting on Condition");
- ABSL_RAW_CHECK(!w->waitp->timeout.has_timeout(),
- "Mutex::Fer while in timed wait");
ABSL_RAW_CHECK(w->waitp->cv_word == nullptr,
"Mutex::Fer with pending CondVar queueing");
+ // The CondVar timeout is not relevant for the Mutex wait.
+ w->waitp->timeout = {};
for (;;) {
intptr_t v = mu_.load(std::memory_order_relaxed);
// Note: must not queue if the mutex is unlocked (nobody will wake it).
@@ -2436,7 +2438,8 @@ void Mutex::Fer(PerThreadSynch* w) {
} else {
if ((v & (kMuSpin | kMuWait)) == 0) { // no waiters
// This thread tries to become the one and only waiter.
- PerThreadSynch* new_h = Enqueue(nullptr, w->waitp, v, kMuIsCond);
+ PerThreadSynch* new_h =
+ Enqueue(nullptr, w->waitp, v, kMuIsCond | kMuIsFer);
ABSL_RAW_CHECK(new_h != nullptr,
"Enqueue failed"); // we must queue ourselves
if (mu_.compare_exchange_strong(
@@ -2447,7 +2450,7 @@ void Mutex::Fer(PerThreadSynch* w) {
} else if ((v & kMuSpin) == 0 &&
mu_.compare_exchange_strong(v, v | kMuSpin | kMuWait)) {
PerThreadSynch* h = GetPerThreadSynch(v);
- PerThreadSynch* new_h = Enqueue(h, w->waitp, v, kMuIsCond);
+ PerThreadSynch* new_h = Enqueue(h, w->waitp, v, kMuIsCond | kMuIsFer);
ABSL_RAW_CHECK(new_h != nullptr,
"Enqueue failed"); // we must queue ourselves
do {
@@ -2503,12 +2506,6 @@ void CondVar::EnableDebugLog(const char* name) {
UnrefSynchEvent(e);
}
-CondVar::~CondVar() {
- if ((cv_.load(std::memory_order_relaxed) & kCvEvent) != 0) {
- ForgetSynchEvent(&this->cv_, kCvEvent, kCvSpin);
- }
-}
-
// Remove thread s from the list of waiters on this condition variable.
void CondVar::Remove(PerThreadSynch* s) {
SchedulingGuard::ScopedDisable disable_rescheduling;
@@ -2659,33 +2656,6 @@ bool CondVar::WaitCommon(Mutex* mutex, KernelTimeout t) {
return rc;
}
-bool CondVar::WaitWithTimeout(Mutex* mu, absl::Duration timeout) {
- return WaitCommon(mu, KernelTimeout(timeout));
-}
-
-bool CondVar::WaitWithDeadline(Mutex* mu, absl::Time deadline) {
- return WaitCommon(mu, KernelTimeout(deadline));
-}
-
-void CondVar::Wait(Mutex* mu) { WaitCommon(mu, KernelTimeout::Never()); }
-
-// Wake thread w
-// If it was a timed wait, w will be waiting on w->cv
-// Otherwise, if it was not a Mutex mutex, w will be waiting on w->sem
-// Otherwise, w is transferred to the Mutex mutex via Mutex::Fer().
-void CondVar::Wakeup(PerThreadSynch* w) {
- if (w->waitp->timeout.has_timeout() || w->waitp->cvmu == nullptr) {
- // The waiting thread only needs to observe "w->state == kAvailable" to be
- // released, we must cache "cvmu" before clearing "next".
- Mutex* mu = w->waitp->cvmu;
- w->next = nullptr;
- w->state.store(PerThreadSynch::kAvailable, std::memory_order_release);
- Mutex::IncrementSynchSem(mu, w);
- } else {
- w->waitp->cvmu->Fer(w);
- }
-}
-
void CondVar::Signal() {
SchedulingGuard::ScopedDisable disable_rescheduling;
ABSL_TSAN_MUTEX_PRE_SIGNAL(nullptr, 0);
@@ -2710,7 +2680,7 @@ void CondVar::Signal() {
cv_.store((v & kCvEvent) | reinterpret_cast<intptr_t>(h),
std::memory_order_release);
if (w != nullptr) {
- CondVar::Wakeup(w); // wake waiter, if there was one
+ w->waitp->cvmu->Fer(w); // wake waiter, if there was one
cond_var_tracer("Signal wakeup", this);
}
if ((v & kCvEvent) != 0) {
@@ -2746,7 +2716,7 @@ void CondVar::SignalAll() {
do { // for every thread, wake it up
w = n;
n = n->next;
- CondVar::Wakeup(w);
+ w->waitp->cvmu->Fer(w);
} while (w != h);
cond_var_tracer("SignalAll wakeup", this);
}
@@ -2810,17 +2780,11 @@ Condition::Condition(const bool* cond)
StoreCallback(dereference);
}
-bool Condition::Eval() const {
- // eval_ == null for kTrue
- return (this->eval_ == nullptr) || (*this->eval_)(this);
-}
+bool Condition::Eval() const { return (*this->eval_)(this); }
bool Condition::GuaranteedEqual(const Condition* a, const Condition* b) {
- // kTrue logic.
- if (a == nullptr || a->eval_ == nullptr) {
- return b == nullptr || b->eval_ == nullptr;
- } else if (b == nullptr || b->eval_ == nullptr) {
- return false;
+ if (a == nullptr || b == nullptr) {
+ return a == b;
}
// Check equality of the representative fields.
return a->eval_ == b->eval_ && a->arg_ == b->arg_ &&
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index 645c26d9..d53a22bb 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -64,6 +64,7 @@
#include <iterator>
#include <string>
+#include "absl/base/attributes.h"
#include "absl/base/const_init.h"
#include "absl/base/internal/identity.h"
#include "absl/base/internal/low_level_alloc.h"
@@ -324,7 +325,9 @@ class ABSL_LOCKABLE Mutex {
// `true`, `Await()` *may* skip the release/re-acquire step.
//
// `Await()` requires that this thread holds this `Mutex` in some mode.
- void Await(const Condition& cond);
+ void Await(const Condition& cond) {
+ AwaitCommon(cond, synchronization_internal::KernelTimeout::Never());
+ }
// Mutex::LockWhen()
// Mutex::ReaderLockWhen()
@@ -334,9 +337,15 @@ class ABSL_LOCKABLE Mutex {
// be acquired, then atomically acquires this `Mutex`. `LockWhen()` is
// logically equivalent to `*Lock(); Await();` though they may have different
// performance characteristics.
- void LockWhen(const Condition& cond) ABSL_EXCLUSIVE_LOCK_FUNCTION();
+ void LockWhen(const Condition& cond) ABSL_EXCLUSIVE_LOCK_FUNCTION() {
+ LockWhenCommon(cond, synchronization_internal::KernelTimeout::Never(),
+ true);
+ }
- void ReaderLockWhen(const Condition& cond) ABSL_SHARED_LOCK_FUNCTION();
+ void ReaderLockWhen(const Condition& cond) ABSL_SHARED_LOCK_FUNCTION() {
+ LockWhenCommon(cond, synchronization_internal::KernelTimeout::Never(),
+ false);
+ }
void WriterLockWhen(const Condition& cond) ABSL_EXCLUSIVE_LOCK_FUNCTION() {
this->LockWhen(cond);
@@ -363,9 +372,13 @@ class ABSL_LOCKABLE Mutex {
// Negative timeouts are equivalent to a zero timeout.
//
// This method requires that this thread holds this `Mutex` in some mode.
- bool AwaitWithTimeout(const Condition& cond, absl::Duration timeout);
+ bool AwaitWithTimeout(const Condition& cond, absl::Duration timeout) {
+ return AwaitCommon(cond, synchronization_internal::KernelTimeout{timeout});
+ }
- bool AwaitWithDeadline(const Condition& cond, absl::Time deadline);
+ bool AwaitWithDeadline(const Condition& cond, absl::Time deadline) {
+ return AwaitCommon(cond, synchronization_internal::KernelTimeout{deadline});
+ }
// Mutex::LockWhenWithTimeout()
// Mutex::ReaderLockWhenWithTimeout()
@@ -379,9 +392,15 @@ class ABSL_LOCKABLE Mutex {
//
// Negative timeouts are equivalent to a zero timeout.
bool LockWhenWithTimeout(const Condition& cond, absl::Duration timeout)
- ABSL_EXCLUSIVE_LOCK_FUNCTION();
+ ABSL_EXCLUSIVE_LOCK_FUNCTION() {
+ return LockWhenCommon(
+ cond, synchronization_internal::KernelTimeout{timeout}, true);
+ }
bool ReaderLockWhenWithTimeout(const Condition& cond, absl::Duration timeout)
- ABSL_SHARED_LOCK_FUNCTION();
+ ABSL_SHARED_LOCK_FUNCTION() {
+ return LockWhenCommon(
+ cond, synchronization_internal::KernelTimeout{timeout}, false);
+ }
bool WriterLockWhenWithTimeout(const Condition& cond, absl::Duration timeout)
ABSL_EXCLUSIVE_LOCK_FUNCTION() {
return this->LockWhenWithTimeout(cond, timeout);
@@ -399,9 +418,15 @@ class ABSL_LOCKABLE Mutex {
//
// Deadlines in the past are equivalent to an immediate deadline.
bool LockWhenWithDeadline(const Condition& cond, absl::Time deadline)
- ABSL_EXCLUSIVE_LOCK_FUNCTION();
+ ABSL_EXCLUSIVE_LOCK_FUNCTION() {
+ return LockWhenCommon(
+ cond, synchronization_internal::KernelTimeout{deadline}, true);
+ }
bool ReaderLockWhenWithDeadline(const Condition& cond, absl::Time deadline)
- ABSL_SHARED_LOCK_FUNCTION();
+ ABSL_SHARED_LOCK_FUNCTION() {
+ return LockWhenCommon(
+ cond, synchronization_internal::KernelTimeout{deadline}, false);
+ }
bool WriterLockWhenWithDeadline(const Condition& cond, absl::Time deadline)
ABSL_EXCLUSIVE_LOCK_FUNCTION() {
return this->LockWhenWithDeadline(cond, deadline);
@@ -497,15 +522,22 @@ class ABSL_LOCKABLE Mutex {
int flags) ABSL_ATTRIBUTE_COLD;
// slow path release
void UnlockSlow(SynchWaitParams* waitp) ABSL_ATTRIBUTE_COLD;
+ // TryLock slow path.
+ bool TryLockSlow();
+ // ReaderTryLock slow path.
+ bool ReaderTryLockSlow();
// Common code between Await() and AwaitWithTimeout/Deadline()
bool AwaitCommon(const Condition& cond,
synchronization_internal::KernelTimeout t);
+ bool LockWhenCommon(const Condition& cond,
+ synchronization_internal::KernelTimeout t, bool write);
// Attempt to remove thread s from queue.
void TryRemove(base_internal::PerThreadSynch* s);
// Block a thread on mutex.
void Block(base_internal::PerThreadSynch* s);
// Wake a thread; return successor.
base_internal::PerThreadSynch* Wakeup(base_internal::PerThreadSynch* w);
+ void Dtor();
friend class CondVar; // for access to Trans()/Fer().
void Trans(MuHow how); // used for CondVar->Mutex transfer
@@ -708,23 +740,25 @@ class Condition {
// a function template is passed as `func`. Also, the dummy `typename = void`
// template parameter exists just to work around a MSVC mangling bug.
template <typename T, typename = void>
- Condition(bool (*func)(T*), typename absl::internal::identity<T>::type* arg);
+ Condition(bool (*func)(T*),
+ typename absl::internal::type_identity<T>::type* arg);
// Templated version for invoking a method that returns a `bool`.
//
// `Condition(object, &Class::Method)` constructs a `Condition` that evaluates
// `object->Method()`.
//
- // Implementation Note: `absl::internal::identity` is used to allow methods to
- // come from base classes. A simpler signature like
+ // Implementation Note: `absl::internal::type_identity` is used to allow
+ // methods to come from base classes. A simpler signature like
// `Condition(T*, bool (T::*)())` does not suffice.
template <typename T>
- Condition(T* object, bool (absl::internal::identity<T>::type::*method)());
+ Condition(T* object,
+ bool (absl::internal::type_identity<T>::type::*method)());
// Same as above, for const members
template <typename T>
Condition(const T* object,
- bool (absl::internal::identity<T>::type::*method)() const);
+ bool (absl::internal::type_identity<T>::type::*method)() const);
// A Condition that returns the value of `*cond`
explicit Condition(const bool* cond);
@@ -816,7 +850,7 @@ class Condition {
static bool CallVoidPtrFunction(const Condition*);
template <typename T>
static bool CastAndCallFunction(const Condition* c);
- template <typename T>
+ template <typename T, typename ConditionMethodPtr>
static bool CastAndCallMethod(const Condition* c);
// Helper methods for storing, validating, and reading callback arguments.
@@ -833,8 +867,10 @@ class Condition {
std::memcpy(callback, callback_, sizeof(*callback));
}
+ static bool AlwaysTrue(const Condition*) { return true; }
+
// Used only to create kTrue.
- constexpr Condition() = default;
+ constexpr Condition() : eval_(AlwaysTrue), arg_(nullptr) {}
};
// -----------------------------------------------------------------------------
@@ -877,7 +913,6 @@ class CondVar {
// A `CondVar` allocated on the heap or on the stack can use the this
// constructor.
CondVar();
- ~CondVar();
// CondVar::Wait()
//
@@ -886,7 +921,9 @@ class CondVar {
// spurious wakeup), then reacquires the `Mutex` and returns.
//
// Requires and ensures that the current thread holds the `Mutex`.
- void Wait(Mutex* mu);
+ void Wait(Mutex* mu) {
+ WaitCommon(mu, synchronization_internal::KernelTimeout::Never());
+ }
// CondVar::WaitWithTimeout()
//
@@ -901,7 +938,9 @@ class CondVar {
// to return `true` or `false`.
//
// Requires and ensures that the current thread holds the `Mutex`.
- bool WaitWithTimeout(Mutex* mu, absl::Duration timeout);
+ bool WaitWithTimeout(Mutex* mu, absl::Duration timeout) {
+ return WaitCommon(mu, synchronization_internal::KernelTimeout(timeout));
+ }
// CondVar::WaitWithDeadline()
//
@@ -918,7 +957,9 @@ class CondVar {
// to return `true` or `false`.
//
// Requires and ensures that the current thread holds the `Mutex`.
- bool WaitWithDeadline(Mutex* mu, absl::Time deadline);
+ bool WaitWithDeadline(Mutex* mu, absl::Time deadline) {
+ return WaitCommon(mu, synchronization_internal::KernelTimeout(deadline));
+ }
// CondVar::Signal()
//
@@ -940,7 +981,6 @@ class CondVar {
private:
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.
CondVar(const CondVar&) = delete;
CondVar& operator=(const CondVar&) = delete;
@@ -1024,15 +1064,30 @@ inline Mutex::Mutex() : mu_(0) {
inline constexpr Mutex::Mutex(absl::ConstInitType) : mu_(0) {}
+#if !defined(__APPLE__) && !defined(ABSL_BUILD_DLL)
+ABSL_ATTRIBUTE_ALWAYS_INLINE
+inline Mutex::~Mutex() { Dtor(); }
+#endif
+
+#if defined(NDEBUG) && !defined(ABSL_HAVE_THREAD_SANITIZER)
+// Use default (empty) destructor in release build for performance reasons.
+// We need to mark both Dtor and ~Mutex as always inline for inconsistent
+// builds that use both NDEBUG and !NDEBUG with dynamic libraries. In these
+// cases we want the empty functions to dissolve entirely rather than being
+// exported from dynamic libraries and potentially override the non-empty ones.
+ABSL_ATTRIBUTE_ALWAYS_INLINE
+inline void Mutex::Dtor() {}
+#endif
+
inline CondVar::CondVar() : cv_(0) {}
// static
-template <typename T>
+template <typename T, typename ConditionMethodPtr>
bool Condition::CastAndCallMethod(const Condition* c) {
T* object = static_cast<T*>(c->arg_);
- bool (T::*method_pointer)();
- c->ReadCallback(&method_pointer);
- return (object->*method_pointer)();
+ ConditionMethodPtr condition_method_pointer;
+ c->ReadCallback(&condition_method_pointer);
+ return (object->*condition_method_pointer)();
}
// static
@@ -1054,25 +1109,25 @@ inline Condition::Condition(bool (*func)(T*), T* arg)
}
template <typename T, typename>
-inline Condition::Condition(bool (*func)(T*),
- typename absl::internal::identity<T>::type* arg)
+inline Condition::Condition(
+ bool (*func)(T*), typename absl::internal::type_identity<T>::type* arg)
// Just delegate to the overload above.
: Condition(func, arg) {}
template <typename T>
-inline Condition::Condition(T* object,
- bool (absl::internal::identity<T>::type::*method)())
- : eval_(&CastAndCallMethod<T>), arg_(object) {
+inline Condition::Condition(
+ T* object, bool (absl::internal::type_identity<T>::type::*method)())
+ : eval_(&CastAndCallMethod<T, decltype(method)>), arg_(object) {
static_assert(sizeof(&method) <= sizeof(callback_),
"An overlarge method pointer was passed to Condition.");
StoreCallback(method);
}
template <typename T>
-inline Condition::Condition(const T* object,
- bool (absl::internal::identity<T>::type::*method)()
- const)
- : eval_(&CastAndCallMethod<T>),
+inline Condition::Condition(
+ const T* object,
+ bool (absl::internal::type_identity<T>::type::*method)() const)
+ : eval_(&CastAndCallMethod<const T, decltype(method)>),
arg_(reinterpret_cast<void*>(const_cast<T*>(object))) {
StoreCallback(method);
}
diff --git a/absl/synchronization/mutex_benchmark.cc b/absl/synchronization/mutex_benchmark.cc
index b5d2fbc4..06888dfe 100644
--- a/absl/synchronization/mutex_benchmark.cc
+++ b/absl/synchronization/mutex_benchmark.cc
@@ -19,6 +19,7 @@
#include "absl/base/config.h"
#include "absl/base/internal/cycleclock.h"
#include "absl/base/internal/spinlock.h"
+#include "absl/base/no_destructor.h"
#include "absl/synchronization/blocking_counter.h"
#include "absl/synchronization/internal/thread_pool.h"
#include "absl/synchronization/mutex.h"
@@ -27,13 +28,41 @@
namespace {
void BM_Mutex(benchmark::State& state) {
- static absl::Mutex* mu = new absl::Mutex;
+ static absl::NoDestructor<absl::Mutex> mu;
for (auto _ : state) {
- absl::MutexLock lock(mu);
+ absl::MutexLock lock(mu.get());
}
}
BENCHMARK(BM_Mutex)->UseRealTime()->Threads(1)->ThreadPerCpu();
+void BM_ReaderLock(benchmark::State& state) {
+ static absl::NoDestructor<absl::Mutex> mu;
+ for (auto _ : state) {
+ absl::ReaderMutexLock lock(mu.get());
+ }
+}
+BENCHMARK(BM_ReaderLock)->UseRealTime()->Threads(1)->ThreadPerCpu();
+
+void BM_TryLock(benchmark::State& state) {
+ absl::Mutex mu;
+ for (auto _ : state) {
+ if (mu.TryLock()) {
+ mu.Unlock();
+ }
+ }
+}
+BENCHMARK(BM_TryLock);
+
+void BM_ReaderTryLock(benchmark::State& state) {
+ static absl::NoDestructor<absl::Mutex> mu;
+ for (auto _ : state) {
+ if (mu->ReaderTryLock()) {
+ mu->ReaderUnlock();
+ }
+ }
+}
+BENCHMARK(BM_ReaderTryLock)->UseRealTime()->Threads(1)->ThreadPerCpu();
+
static void DelayNs(int64_t ns, int* data) {
int64_t end = absl::base_internal::CycleClock::Now() +
ns * absl::base_internal::CycleClock::Frequency() / 1e9;
@@ -105,7 +134,7 @@ void BM_MutexEnqueue(benchmark::State& state) {
std::atomic<int> blocked_threads{0};
std::atomic<bool> thread_has_mutex{false};
};
- static Shared* shared = new Shared;
+ static absl::NoDestructor<Shared> shared;
// Set up 'blocked_threads' to count how many threads are currently blocked
// in Abseil synchronization code.
@@ -183,7 +212,7 @@ void BM_Contended(benchmark::State& state) {
MutexType mu;
int data = 0;
};
- static auto* shared = new Shared;
+ static absl::NoDestructor<Shared> shared;
int local = 0;
for (auto _ : state) {
// Here we model both local work outside of the critical section as well as
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index b585c342..bb93abc9 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -36,10 +36,16 @@
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
+#include "absl/synchronization/internal/create_thread_identity.h"
#include "absl/synchronization/internal/thread_pool.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
+#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+#include <pthread.h>
+#include <string.h>
+#endif
+
namespace {
// TODO(dmauro): Replace with a commandline flag.
@@ -66,6 +72,11 @@ static void ScheduleAfter(absl::synchronization_internal::ThreadPool *tp,
});
}
+struct ScopedInvariantDebugging {
+ ScopedInvariantDebugging() { absl::EnableMutexInvariantDebugging(true); }
+ ~ScopedInvariantDebugging() { absl::EnableMutexInvariantDebugging(false); }
+};
+
struct TestContext {
int iterations;
int threads;
@@ -389,13 +400,12 @@ static int RunTestWithInvariantDebugging(void (*test)(TestContext *cxt, int),
int threads, int iterations,
int operations,
void (*invariant)(void *)) {
- absl::EnableMutexInvariantDebugging(true);
+ ScopedInvariantDebugging scoped_debugging;
SetInvariantChecked(false);
TestContext cxt;
cxt.mu.EnableInvariantDebugging(invariant, &cxt);
int ret = RunTestCommon(&cxt, test, threads, iterations, operations);
CHECK(GetInvariantChecked()) << "Invariant not checked";
- absl::EnableMutexInvariantDebugging(false); // Restore.
return ret;
}
#endif
@@ -971,6 +981,15 @@ TEST(Mutex, FunctionPointerConditionWithDerivedToBaseConversion) {
const Derived *>::value));
}
+struct Constable {
+ bool WotsAllThisThen() const { return true; }
+};
+
+TEST(Mutex, FunctionPointerConditionWithConstMethod) {
+ const Constable chapman;
+ EXPECT_TRUE(absl::Condition(&chapman, &Constable::WotsAllThisThen).Eval());
+}
+
struct True {
template <class... Args>
bool operator()(Args...) const {
@@ -1019,6 +1038,19 @@ TEST(Mutex, FunctorCondition) {
}
}
+TEST(Mutex, ConditionSwap) {
+ // Ensure that Conditions can be swap'ed.
+ bool b1 = true;
+ absl::Condition c1(&b1);
+ bool b2 = false;
+ absl::Condition c2(&b2);
+ EXPECT_TRUE(c1.Eval());
+ EXPECT_FALSE(c2.Eval());
+ std::swap(c1, c2);
+ EXPECT_FALSE(c1.Eval());
+ EXPECT_TRUE(c2.Eval());
+}
+
// --------------------------------------------------------
// 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
@@ -1690,6 +1722,61 @@ TEST(Mutex, Logging) {
logged_cv.SignalAll();
}
+TEST(Mutex, LoggingAddressReuse) {
+ // Repeatedly re-create a Mutex with debug logging at the same address.
+ ScopedInvariantDebugging scoped_debugging;
+ alignas(absl::Mutex) char storage[sizeof(absl::Mutex)];
+ auto invariant =
+ +[](void *alive) { EXPECT_TRUE(*static_cast<bool *>(alive)); };
+ constexpr size_t kIters = 10;
+ bool alive[kIters] = {};
+ for (size_t i = 0; i < kIters; ++i) {
+ absl::Mutex *mu = new (storage) absl::Mutex;
+ alive[i] = true;
+ mu->EnableDebugLog("Mutex");
+ mu->EnableInvariantDebugging(invariant, &alive[i]);
+ mu->Lock();
+ mu->Unlock();
+ mu->~Mutex();
+ alive[i] = false;
+ }
+}
+
+TEST(Mutex, LoggingBankrupcy) {
+ // Test the case with too many live Mutexes with debug logging.
+ ScopedInvariantDebugging scoped_debugging;
+ std::vector<absl::Mutex> mus(1 << 20);
+ for (auto &mu : mus) {
+ mu.EnableDebugLog("Mutex");
+ }
+}
+
+TEST(Mutex, SynchEventRace) {
+ // Regression test for a false TSan race report in
+ // EnableInvariantDebugging/EnableDebugLog related to SynchEvent reuse.
+ ScopedInvariantDebugging scoped_debugging;
+ std::vector<std::thread> threads;
+ for (size_t i = 0; i < 5; i++) {
+ threads.emplace_back([&] {
+ for (size_t j = 0; j < (1 << 17); j++) {
+ {
+ absl::Mutex mu;
+ mu.EnableInvariantDebugging([](void *) {}, nullptr);
+ mu.Lock();
+ mu.Unlock();
+ }
+ {
+ absl::Mutex mu;
+ mu.EnableDebugLog("Mutex");
+ }
+ }
+ });
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+
// --------------------------------------------------------
// Generate the vector of thread counts for tests parameterized on thread count.
@@ -1868,6 +1955,60 @@ TEST(Mutex, WriterPriority) {
EXPECT_TRUE(saw_wrote.load());
}
+#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
+TEST(Mutex, CondVarPriority) {
+ // A regression test for a bug in condition variable wait morphing,
+ // which resulted in the waiting thread getting priority of the waking thread.
+ int err = 0;
+ sched_param param;
+ param.sched_priority = 7;
+ std::thread test([&]() {
+ err = pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
+ });
+ test.join();
+ if (err) {
+ // Setting priority usually requires special privileges.
+ GTEST_SKIP() << "failed to set priority: " << strerror(err);
+ }
+ absl::Mutex mu;
+ absl::CondVar cv;
+ bool locked = false;
+ bool notified = false;
+ bool waiting = false;
+ bool morph = false;
+ std::thread th([&]() {
+ EXPECT_EQ(0, pthread_setschedparam(pthread_self(), SCHED_FIFO, &param));
+ mu.Lock();
+ locked = true;
+ mu.Await(absl::Condition(&notified));
+ mu.Unlock();
+ EXPECT_EQ(absl::synchronization_internal::GetOrCreateCurrentThreadIdentity()
+ ->per_thread_synch.priority,
+ param.sched_priority);
+ mu.Lock();
+ mu.Await(absl::Condition(&waiting));
+ morph = true;
+ absl::SleepFor(absl::Seconds(1));
+ cv.Signal();
+ mu.Unlock();
+ });
+ mu.Lock();
+ mu.Await(absl::Condition(&locked));
+ notified = true;
+ mu.Unlock();
+ mu.Lock();
+ waiting = true;
+ while (!morph) {
+ cv.Wait(&mu);
+ }
+ mu.Unlock();
+ th.join();
+ EXPECT_NE(absl::synchronization_internal::GetOrCreateCurrentThreadIdentity()
+ ->per_thread_synch.priority,
+ param.sched_priority);
+}
+#endif
+
TEST(Mutex, LockWhenWithTimeoutResult) {
// Check various corner cases for Await/LockWhen return value
// with always true/always false conditions.
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index 88d20887..e3fe705b 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -93,6 +100,7 @@ cc_test(
"//absl/numeric:int128",
"//absl/strings:str_format",
"//absl/time/internal/cctz:time_zone",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -117,6 +125,7 @@ cc_test(
":time",
"//absl/flags:flag",
"//absl/flags:reflection",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h
index 5855bc73..3e904a11 100644
--- a/absl/time/civil_time.h
+++ b/absl/time/civil_time.h
@@ -462,6 +462,32 @@ std::string FormatCivilTime(CivilDay c);
std::string FormatCivilTime(CivilMonth c);
std::string FormatCivilTime(CivilYear c);
+// Support for StrFormat(), StrCat(), etc
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilSecond c) {
+ sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilMinute c) {
+ sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilHour c) {
+ sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilDay c) {
+ sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilMonth c) {
+ sink.Append(FormatCivilTime(c));
+}
+template <typename Sink>
+void AbslStringify(Sink& sink, CivilYear c) {
+ sink.Append(FormatCivilTime(c));
+}
+
// absl::ParseCivilTime()
//
// Parses a civil-time value from the specified `absl::string_view` into the
diff --git a/absl/time/civil_time_benchmark.cc b/absl/time/civil_time_benchmark.cc
index f04dbe20..2de0233d 100644
--- a/absl/time/civil_time_benchmark.cc
+++ b/absl/time/civil_time_benchmark.cc
@@ -14,7 +14,9 @@
#include "absl/time/civil_time.h"
+#include <cstddef>
#include <numeric>
+#include <string>
#include <vector>
#include "absl/hash/hash.h"
@@ -42,7 +44,7 @@ void BM_Difference_Days(benchmark::State& state) {
const absl::CivilDay c(2014, 8, 22);
const absl::CivilDay epoch(1970, 1, 1);
while (state.KeepRunning()) {
- const absl::civil_diff_t n = c - epoch;
+ absl::civil_diff_t n = c - epoch;
benchmark::DoNotOptimize(n);
}
}
@@ -60,7 +62,7 @@ BENCHMARK(BM_Step_Days);
void BM_Format(benchmark::State& state) {
const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
while (state.KeepRunning()) {
- const std::string s = absl::FormatCivilTime(c);
+ std::string s = absl::FormatCivilTime(c);
benchmark::DoNotOptimize(s);
}
}
@@ -70,7 +72,7 @@ void BM_Parse(benchmark::State& state) {
const std::string f = "2014-01-02T03:04:05";
absl::CivilSecond c;
while (state.KeepRunning()) {
- const bool b = absl::ParseCivilTime(f, &c);
+ bool b = absl::ParseCivilTime(f, &c);
benchmark::DoNotOptimize(b);
}
}
@@ -80,7 +82,7 @@ void BM_RoundTripFormatParse(benchmark::State& state) {
const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
absl::CivilSecond out;
while (state.KeepRunning()) {
- const bool b = absl::ParseCivilTime(absl::FormatCivilTime(c), &out);
+ bool b = absl::ParseCivilTime(absl::FormatCivilTime(c), &out);
benchmark::DoNotOptimize(b);
}
}
@@ -95,7 +97,8 @@ void BM_CivilTimeAbslHash(benchmark::State& state) {
absl::Hash<T> absl_hasher;
while (state.KeepRunningBatch(kSize)) {
for (const T civil_time : civil_times) {
- benchmark::DoNotOptimize(absl_hasher(civil_time));
+ size_t hash = absl_hasher(civil_time);
+ benchmark::DoNotOptimize(hash);
}
}
}
diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc
index ec435ac7..19292a9c 100644
--- a/absl/time/civil_time_test.cc
+++ b/absl/time/civil_time_test.cc
@@ -14,12 +14,14 @@
#include "absl/time/civil_time.h"
+#include <iomanip>
#include <limits>
#include <sstream>
#include <type_traits>
-#include "absl/base/macros.h"
#include "gtest/gtest.h"
+#include "absl/base/macros.h"
+#include "absl/strings/str_format.h"
namespace {
@@ -868,6 +870,23 @@ TEST(CivilTime, ParseEdgeCases) {
EXPECT_FALSE(absl::ParseLenientCivilTime("9223372036854775808", &y)) << y;
}
+TEST(CivilTime, AbslStringify) {
+ EXPECT_EQ("2015-01-02T03:04:05",
+ absl::StrFormat("%v", absl::CivilSecond(2015, 1, 2, 3, 4, 5)));
+
+ EXPECT_EQ("2015-01-02T03:04",
+ absl::StrFormat("%v", absl::CivilMinute(2015, 1, 2, 3, 4)));
+
+ EXPECT_EQ("2015-01-02T03",
+ absl::StrFormat("%v", absl::CivilHour(2015, 1, 2, 3)));
+
+ EXPECT_EQ("2015-01-02", absl::StrFormat("%v", absl::CivilDay(2015, 1, 2)));
+
+ EXPECT_EQ("2015-01", absl::StrFormat("%v", absl::CivilMonth(2015, 1)));
+
+ EXPECT_EQ("2015", absl::StrFormat("%v", absl::CivilYear(2015)));
+}
+
TEST(CivilTime, OutputStream) {
absl::CivilSecond cs(2016, 2, 3, 4, 5, 6);
{
diff --git a/absl/time/clock.h b/absl/time/clock.h
index 5fe244d6..41d2cf27 100644
--- a/absl/time/clock.h
+++ b/absl/time/clock.h
@@ -22,6 +22,9 @@
#ifndef ABSL_TIME_CLOCK_H_
#define ABSL_TIME_CLOCK_H_
+#include <cstdint>
+
+#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/time/time.h"
@@ -64,7 +67,8 @@ ABSL_NAMESPACE_END
// By changing our extension points to be extern "C", we dodge this
// check.
extern "C" {
-void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(absl::Duration duration);
+ABSL_DLL void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(
+ absl::Duration duration);
} // extern "C"
inline void absl::SleepFor(absl::Duration duration) {
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 634e5d58..bdb16e21 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -55,8 +55,7 @@
#include <algorithm>
#include <cassert>
-#include <cctype>
-#include <cerrno>
+#include <chrono> // NOLINT(build/c++11)
#include <cmath>
#include <cstdint>
#include <cstdlib>
@@ -66,8 +65,9 @@
#include <limits>
#include <string>
+#include "absl/base/attributes.h"
#include "absl/base/casts.h"
-#include "absl/base/macros.h"
+#include "absl/base/config.h"
#include "absl/numeric/int128.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index 4c5ad075..0b43bb12 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -12,7 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-package(features = ["-parse_headers"])
+package(features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+])
licenses(["notice"])
@@ -83,6 +87,7 @@ cc_test(
deps = [
":civil_time",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -103,6 +108,7 @@ cc_test(
":civil_time",
":time_zone",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -124,6 +130,7 @@ cc_test(
":civil_time",
":time_zone",
"//absl/base:config",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
index a5b084e6..2b0aed56 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -401,11 +401,11 @@ class civil_time {
: civil_time(ct.f_) {}
// Factories for the maximum/minimum representable civil_time.
- static CONSTEXPR_F civil_time(max)() {
+ static CONSTEXPR_F auto(max)() -> civil_time {
const auto max_year = (std::numeric_limits<std::int_least64_t>::max)();
return civil_time(max_year, 12, 31, 23, 59, 59);
}
- static CONSTEXPR_F civil_time(min)() {
+ static CONSTEXPR_F auto(min)() -> civil_time {
const auto min_year = (std::numeric_limits<std::int_least64_t>::min)();
return civil_time(min_year, 1, 1, 0, 0, 0);
}
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index c64f3801..11f9ba6c 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -110,7 +110,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmara",
- "Africa/Asmera",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
@@ -166,7 +165,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Araguaina",
"America/Argentina/Buenos_Aires",
"America/Argentina/Catamarca",
- "America/Argentina/ComodRivadavia",
"America/Argentina/Cordoba",
"America/Argentina/Jujuy",
"America/Argentina/La_Rioja",
@@ -190,18 +188,16 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
- "America/Buenos_Aires",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
- "America/Catamarca",
"America/Cayenne",
"America/Cayman",
"America/Chicago",
"America/Chihuahua",
+ "America/Ciudad_Juarez",
"America/Coral_Harbour",
- "America/Cordoba",
"America/Costa_Rica",
"America/Creston",
"America/Cuiaba",
@@ -217,7 +213,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/El_Salvador",
"America/Ensenada",
"America/Fort_Nelson",
- "America/Fort_Wayne",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
@@ -239,20 +234,16 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
- "America/Indianapolis",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
- "America/Jujuy",
"America/Juneau",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
- "America/Knox_IN",
"America/Kralendijk",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
- "America/Louisville",
"America/Lower_Princes",
"America/Maceio",
"America/Managua",
@@ -261,7 +252,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
- "America/Mendoza",
"America/Menominee",
"America/Merida",
"America/Metlakatla",
@@ -298,7 +288,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
- "America/Rosario",
"America/Santa_Isabel",
"America/Santarem",
"America/Santiago",
@@ -334,7 +323,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Antarctica/McMurdo",
"Antarctica/Palmer",
"Antarctica/Rothera",
- "Antarctica/South_Pole",
"Antarctica/Syowa",
"Antarctica/Troll",
"Antarctica/Vostok",
@@ -346,7 +334,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
- "Asia/Ashkhabad",
"Asia/Atyrau",
"Asia/Baghdad",
"Asia/Bahrain",
@@ -356,13 +343,10 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
- "Asia/Calcutta",
"Asia/Chita",
"Asia/Choibalsan",
"Asia/Chongqing",
- "Asia/Chungking",
"Asia/Colombo",
- "Asia/Dacca",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
@@ -385,14 +369,12 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Karachi",
"Asia/Kashgar",
"Asia/Kathmandu",
- "Asia/Katmandu",
"Asia/Khandyga",
"Asia/Kolkata",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Kuwait",
- "Asia/Macao",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
@@ -409,9 +391,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Qatar",
"Asia/Qostanay",
"Asia/Qyzylorda",
- "Asia/Rangoon",
"Asia/Riyadh",
- "Asia/Saigon",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
@@ -423,13 +403,10 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Tel_Aviv",
- "Asia/Thimbu",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Tomsk",
- "Asia/Ujung_Pandang",
"Asia/Ulaanbaatar",
- "Asia/Ulan_Bator",
"Asia/Urumqi",
"Asia/Ust-Nera",
"Asia/Vientiane",
@@ -442,7 +419,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
- "Atlantic/Faeroe",
"Atlantic/Faroe",
"Atlantic/Jan_Mayen",
"Atlantic/Madeira",
@@ -450,7 +426,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Atlantic/South_Georgia",
"Atlantic/St_Helena",
"Atlantic/Stanley",
- "Australia/ACT",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
@@ -459,42 +434,12 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
- "Australia/LHI",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
- "Australia/NSW",
- "Australia/North",
"Australia/Perth",
- "Australia/Queensland",
- "Australia/South",
"Australia/Sydney",
- "Australia/Tasmania",
- "Australia/Victoria",
- "Australia/West",
"Australia/Yancowinna",
- "Brazil/Acre",
- "Brazil/DeNoronha",
- "Brazil/East",
- "Brazil/West",
- "CET",
- "CST6CDT",
- "Canada/Atlantic",
- "Canada/Central",
- "Canada/Eastern",
- "Canada/Mountain",
- "Canada/Newfoundland",
- "Canada/Pacific",
- "Canada/Saskatchewan",
- "Canada/Yukon",
- "Chile/Continental",
- "Chile/EasterIsland",
- "Cuba",
- "EET",
- "EST",
- "EST5EDT",
- "Egypt",
- "Eire",
"Etc/GMT",
"Etc/GMT+0",
"Etc/GMT+1",
@@ -552,7 +497,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kaliningrad",
- "Europe/Kiev",
"Europe/Kirov",
"Europe/Kyiv",
"Europe/Lisbon",
@@ -584,7 +528,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Europe/Tirane",
"Europe/Tiraspol",
"Europe/Ulyanovsk",
- "Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
@@ -592,19 +535,8 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zagreb",
- "Europe/Zaporozhye",
"Europe/Zurich",
"Factory",
- "GB",
- "GB-Eire",
- "GMT",
- "GMT+0",
- "GMT-0",
- "GMT0",
- "Greenwich",
- "HST",
- "Hongkong",
- "Iceland",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
@@ -616,23 +548,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
- "Iran",
- "Israel",
- "Jamaica",
- "Japan",
- "Kwajalein",
- "Libya",
- "MET",
- "MST",
- "MST7MDT",
- "Mexico/BajaNorte",
- "Mexico/BajaSur",
- "Mexico/General",
- "NZ",
- "NZ-CHAT",
- "Navajo",
- "PRC",
- "PST8PDT",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Bougainville",
@@ -640,7 +555,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Chuuk",
"Pacific/Easter",
"Pacific/Efate",
- "Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
@@ -665,7 +579,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Pohnpei",
- "Pacific/Ponape",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Saipan",
@@ -673,34 +586,10 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
- "Pacific/Truk",
"Pacific/Wake",
"Pacific/Wallis",
"Pacific/Yap",
- "Poland",
- "Portugal",
- "ROC",
- "ROK",
- "Singapore",
- "Turkey",
- "UCT",
- "US/Alaska",
- "US/Aleutian",
- "US/Arizona",
- "US/Central",
- "US/East-Indiana",
- "US/Eastern",
- "US/Hawaii",
- "US/Indiana-Starke",
- "US/Michigan",
- "US/Mountain",
- "US/Pacific",
- "US/Samoa",
"UTC",
- "Universal",
- "W-SU",
- "WET",
- "Zulu",
nullptr};
std::vector<std::string> AllTimeZoneNames() {
diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc
index 9b91f61c..e7e30a2f 100644
--- a/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/absl/time/internal/cctz/src/time_zone_format.cc
@@ -19,7 +19,7 @@
#endif
#if defined(HAS_STRPTIME) && HAS_STRPTIME
-#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__)
+#if !defined(_XOPEN_SOURCE) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
#define _XOPEN_SOURCE 500 // Exposes definitions for SUSv2 (UNIX 98).
#endif
#endif
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index f46198ff..b7178a6b 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -474,7 +474,8 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
// See Android's libc/tzcode/bionic.cpp for additional information.
- for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
+ for (const char* tzdata : {"/apex/com.android.tzdata/etc/tz/tzdata",
+ "/data/misc/zoneinfo/current/tzdata",
"/system/usr/share/zoneinfo/tzdata"}) {
auto fp = FOpen(tzdata, "rb");
if (fp == nullptr) continue;
@@ -539,9 +540,16 @@ std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
// Prefixes where a Fuchsia component might find zoneinfo files,
// in descending order of preference.
const auto kTzdataPrefixes = {
+ // The tzdata from `config-data`.
"/config/data/tzdata/",
+ // The tzdata bundled in the component's package.
"/pkg/data/tzdata/",
+ // General data storage.
"/data/tzdata/",
+ // The recommended path for routed-in tzdata files.
+ // See for details:
+ // https://fuchsia.dev/fuchsia-src/concepts/process/namespaces?hl=en#typical_directory_structure
+ "/config/tzdata/",
};
const auto kEmptyPrefix = {""};
const bool name_absolute = (pos != name.size() && name[pos] == '/');
@@ -744,19 +752,6 @@ bool TimeZoneInfo::Load(ZoneInfoSource* zip) {
version_ = zip->Version();
}
- // Trim redundant transitions. zic may have added these to work around
- // differences between the glibc and reference implementations (see
- // zic.c:dontmerge) or to avoid bugs in old readers. For us, they just
- // get in the way when we do future_spec_ extension.
- while (hdr.timecnt > 1) {
- if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index,
- transitions_[hdr.timecnt - 2].type_index)) {
- break;
- }
- hdr.timecnt -= 1;
- }
- transitions_.resize(hdr.timecnt);
-
// Ensure that there is always a transition in the first half of the
// time line (the second half is handled below) so that the signed
// difference between a civil_second and the civil_second of its
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 4884c32e..6f7e5cfe 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -45,7 +45,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmara",
- "Africa/Asmera",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
@@ -101,7 +100,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Araguaina",
"America/Argentina/Buenos_Aires",
"America/Argentina/Catamarca",
- "America/Argentina/ComodRivadavia",
"America/Argentina/Cordoba",
"America/Argentina/Jujuy",
"America/Argentina/La_Rioja",
@@ -125,19 +123,16 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
- "America/Buenos_Aires",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
- "America/Catamarca",
"America/Cayenne",
"America/Cayman",
"America/Chicago",
"America/Chihuahua",
"America/Ciudad_Juarez",
"America/Coral_Harbour",
- "America/Cordoba",
"America/Costa_Rica",
"America/Creston",
"America/Cuiaba",
@@ -153,7 +148,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/El_Salvador",
"America/Ensenada",
"America/Fort_Nelson",
- "America/Fort_Wayne",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
@@ -175,20 +169,16 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
- "America/Indianapolis",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
- "America/Jujuy",
"America/Juneau",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
- "America/Knox_IN",
"America/Kralendijk",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
- "America/Louisville",
"America/Lower_Princes",
"America/Maceio",
"America/Managua",
@@ -197,7 +187,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
- "America/Mendoza",
"America/Menominee",
"America/Merida",
"America/Metlakatla",
@@ -234,7 +223,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
- "America/Rosario",
"America/Santa_Isabel",
"America/Santarem",
"America/Santiago",
@@ -270,7 +258,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Antarctica/McMurdo",
"Antarctica/Palmer",
"Antarctica/Rothera",
- "Antarctica/South_Pole",
"Antarctica/Syowa",
"Antarctica/Troll",
"Antarctica/Vostok",
@@ -282,7 +269,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
- "Asia/Ashkhabad",
"Asia/Atyrau",
"Asia/Baghdad",
"Asia/Bahrain",
@@ -292,13 +278,10 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
- "Asia/Calcutta",
"Asia/Chita",
"Asia/Choibalsan",
"Asia/Chongqing",
- "Asia/Chungking",
"Asia/Colombo",
- "Asia/Dacca",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
@@ -321,14 +304,12 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Karachi",
"Asia/Kashgar",
"Asia/Kathmandu",
- "Asia/Katmandu",
"Asia/Khandyga",
"Asia/Kolkata",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Kuwait",
- "Asia/Macao",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
@@ -345,9 +326,7 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Qatar",
"Asia/Qostanay",
"Asia/Qyzylorda",
- "Asia/Rangoon",
"Asia/Riyadh",
- "Asia/Saigon",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
@@ -359,13 +338,10 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Tel_Aviv",
- "Asia/Thimbu",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Tomsk",
- "Asia/Ujung_Pandang",
"Asia/Ulaanbaatar",
- "Asia/Ulan_Bator",
"Asia/Urumqi",
"Asia/Ust-Nera",
"Asia/Vientiane",
@@ -378,7 +354,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
- "Atlantic/Faeroe",
"Atlantic/Faroe",
"Atlantic/Jan_Mayen",
"Atlantic/Madeira",
@@ -386,7 +361,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Atlantic/South_Georgia",
"Atlantic/St_Helena",
"Atlantic/Stanley",
- "Australia/ACT",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
@@ -395,42 +369,12 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
- "Australia/LHI",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
- "Australia/NSW",
- "Australia/North",
"Australia/Perth",
- "Australia/Queensland",
- "Australia/South",
"Australia/Sydney",
- "Australia/Tasmania",
- "Australia/Victoria",
- "Australia/West",
"Australia/Yancowinna",
- "Brazil/Acre",
- "Brazil/DeNoronha",
- "Brazil/East",
- "Brazil/West",
- "CET",
- "CST6CDT",
- "Canada/Atlantic",
- "Canada/Central",
- "Canada/Eastern",
- "Canada/Mountain",
- "Canada/Newfoundland",
- "Canada/Pacific",
- "Canada/Saskatchewan",
- "Canada/Yukon",
- "Chile/Continental",
- "Chile/EasterIsland",
- "Cuba",
- "EET",
- "EST",
- "EST5EDT",
- "Egypt",
- "Eire",
"Etc/GMT",
"Etc/GMT+0",
"Etc/GMT+1",
@@ -488,7 +432,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kaliningrad",
- "Europe/Kiev",
"Europe/Kirov",
"Europe/Kyiv",
"Europe/Lisbon",
@@ -520,7 +463,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Europe/Tirane",
"Europe/Tiraspol",
"Europe/Ulyanovsk",
- "Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
@@ -528,19 +470,8 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zagreb",
- "Europe/Zaporozhye",
"Europe/Zurich",
"Factory",
- "GB",
- "GB-Eire",
- "GMT",
- "GMT+0",
- "GMT-0",
- "GMT0",
- "Greenwich",
- "HST",
- "Hongkong",
- "Iceland",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
@@ -552,23 +483,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
- "Iran",
- "Israel",
- "Jamaica",
- "Japan",
- "Kwajalein",
- "Libya",
- "MET",
- "MST",
- "MST7MDT",
- "Mexico/BajaNorte",
- "Mexico/BajaSur",
- "Mexico/General",
- "NZ",
- "NZ-CHAT",
- "Navajo",
- "PRC",
- "PST8PDT",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Bougainville",
@@ -576,7 +490,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Chuuk",
"Pacific/Easter",
"Pacific/Efate",
- "Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
@@ -601,7 +514,6 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Pohnpei",
- "Pacific/Ponape",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Saipan",
@@ -609,34 +521,10 @@ const char* const kTimeZoneNames[] = {"Africa/Abidjan",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
- "Pacific/Truk",
"Pacific/Wake",
"Pacific/Wallis",
"Pacific/Yap",
- "Poland",
- "Portugal",
- "ROC",
- "ROK",
- "Singapore",
- "Turkey",
- "UCT",
- "US/Alaska",
- "US/Aleutian",
- "US/Arizona",
- "US/Central",
- "US/East-Indiana",
- "US/Eastern",
- "US/Hawaii",
- "US/Indiana-Starke",
- "US/Michigan",
- "US/Mountain",
- "US/Pacific",
- "US/Samoa",
"UTC",
- "Universal",
- "W-SU",
- "WET",
- "Zulu",
nullptr};
// Helper to return a loaded time zone by value (UTC on error).
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h
index 9613055d..114026d0 100644
--- a/absl/time/internal/cctz/src/tzfile.h
+++ b/absl/time/internal/cctz/src/tzfile.h
@@ -21,14 +21,6 @@
** Information about time zone files.
*/
-#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
-#endif /* !defined TZDIR */
-
-#ifndef TZDEFAULT
-#define TZDEFAULT "/etc/localtime"
-#endif /* !defined TZDEFAULT */
-
#ifndef TZDEFRULES
#define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
diff --git a/absl/time/internal/cctz/testdata/README.zoneinfo b/absl/time/internal/cctz/testdata/README.zoneinfo
index 67e9c404..0fe55858 100644
--- a/absl/time/internal/cctz/testdata/README.zoneinfo
+++ b/absl/time/internal/cctz/testdata/README.zoneinfo
@@ -22,7 +22,7 @@ New versions can be generated using the following shell script.
LOCALTIME=Factory \
TZDATA_TEXT= \
PACKRATDATA=backzone PACKRATLIST=zone.tab \
- ZONETABLES=zone1970.tab
+ ZONETABLES=zone1970.tab\ zonenow.tab
tar --create --dereference --hard-dereference --file tzfile.tar \
--directory=tz tzfile.h
tar --create --dereference --hard-dereference --file zoneinfo.tar \
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index 7daa77e0..cd9c3f6d 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2023c
+2023d
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
index e8be26b1..42087af4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ensenada
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
index 00b57bb1..310774ea 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Godthab
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay b/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
index 820e0dd2..e2cc3eef 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Goose_Bay
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
index 8700ed9f..679d321e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Winamac
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
index 88cabcd1..993ac475 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
index 9fefee38..71b0eab0 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton b/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
index ecb69ef2..020e33d9 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Moncton
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
index 00b57bb1..310774ea 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
index 2fc74e94..f7e40c08 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
index e8be26b1..42087af4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santa_Isabel
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund b/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
index 6db49124..fc1b11cb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Scoresbysund
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
index e5f2aec2..94d790ba 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Johns
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
index e8be26b1..42087af4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tijuana
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
index 30315cc0..84f1c61e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Casey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
index 3fc1f231..99a8e60e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
index 4e31affb..2359c44b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Troll
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
index 6e329071..4ce8f747 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
index 7e833898..6241b4e7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
index fcf923bd..5267de96 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
index c210d0a5..390347f4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Nicosia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
index e5f2aec2..94d790ba 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Newfoundland
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
index 323cd381..b9e95d92 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
index efa689ba..c4a391e7 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
index 6970b14c..9152e685 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
index d40bcaa3..f2d87c6b 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
index b0a37e7e..9f5aaffd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
index 9a10a2ec..c83814dd 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
index 4e026859..753a6c86 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kiev
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv
index 4e026859..753a6c86 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kyiv
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
index 323cd381..b9e95d92 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
index c210d0a5..390347f4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Nicosia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
index 26af4c90..d99170b6 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
index eabc972a..89450685 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
index 5321bbd4..fbebdc62 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
index 6970b14c..9152e685 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
index 4e026859..753a6c86 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
index 75b2eebb..43c3d7f1 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
index 4e026859..753a6c86 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zaporozhye
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB b/absl/time/internal/cctz/testdata/zoneinfo/GB
index 323cd381..b9e95d92 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GB
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GB
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
index 323cd381..b9e95d92 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
+++ b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
index e8be26b1..42087af4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaNorte
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
index 79e2a941..0c0bdbda 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
index be3348d1..402c015e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab
@@ -3,17 +3,22 @@
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
#
-# From Paul Eggert (2022-11-18):
+# From Paul Eggert (2023-09-06):
# This file contains a table of two-letter country codes. Columns are
# separated by a single tab. Lines beginning with '#' are comments.
# All text uses UTF-8 encoding. The columns of the table are as follows:
#
# 1. ISO 3166-1 alpha-2 country code, current as of
-# ISO 3166-1 N1087 (2022-09-02). See: Updates on ISO 3166-1
-# https://isotc.iso.org/livelink/livelink/Open/16944257
-# 2. The usual English name for the coded region,
-# chosen so that alphabetic sorting of subsets produces helpful lists.
-# This is not the same as the English name in the ISO 3166 tables.
+# ISO/TC 46 N1108 (2023-04-05). See: ISO/TC 46 Documents
+# https://www.iso.org/committee/48750.html?view=documents
+# 2. The usual English name for the coded region. This sometimes
+# departs from ISO-listed names, sometimes so that sorted subsets
+# of names are useful (e.g., "Samoa (American)" and "Samoa
+# (western)" rather than "American Samoa" and "Samoa"),
+# sometimes to avoid confusion among non-experts (e.g.,
+# "Czech Republic" and "Turkey" rather than "Czechia" and "Türkiye"),
+# and sometimes to omit needless detail or churn (e.g., "Netherlands"
+# rather than "Netherlands (the)" or "Netherlands (Kingdom of the)").
#
# The table is sorted by country code.
#
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
index 1f1cecb8..abd94897 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -37,7 +37,7 @@
#country-
#codes coordinates TZ comments
AD +4230+00131 Europe/Andorra
-AE,OM,RE,SC,TF +2518+05518 Asia/Dubai Crozet, Scattered Is
+AE,OM,RE,SC,TF +2518+05518 Asia/Dubai Crozet
AF +3431+06912 Asia/Kabul
AL +4120+01950 Europe/Tirane
AM +4011+04430 Asia/Yerevan
@@ -47,12 +47,13 @@ AQ -6736+06253 Antarctica/Mawson Mawson
AQ -6448-06406 Antarctica/Palmer Palmer
AQ -6734-06808 Antarctica/Rothera Rothera
AQ -720041+0023206 Antarctica/Troll Troll
+AQ -7824+10654 Antarctica/Vostok Vostok
AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF)
AR -3124-06411 America/Argentina/Cordoba most areas: CB, CC, CN, ER, FM, MN, SE, SF
AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN)
AR -2411-06518 America/Argentina/Jujuy Jujuy (JY)
AR -2649-06513 America/Argentina/Tucuman Tucumán (TM)
-AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH)
+AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH)
AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR)
AR -3132-06831 America/Argentina/San_Juan San Juan (SJ)
AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ)
@@ -81,7 +82,7 @@ BG +4241+02319 Europe/Sofia
BM +3217-06446 Atlantic/Bermuda
BO -1630-06809 America/La_Paz
BR -0351-03225 America/Noronha Atlantic islands
-BR -0127-04829 America/Belem Pará (east); Amapá
+BR -0127-04829 America/Belem Pará (east), Amapá
BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB)
BR -0803-03454 America/Recife Pernambuco
BR -0712-04812 America/Araguaina Tocantins
@@ -99,19 +100,19 @@ BR -0958-06748 America/Rio_Branco Acre
BT +2728+08939 Asia/Thimphu
BY +5354+02734 Europe/Minsk
BZ +1730-08812 America/Belize
-CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast)
-CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE
+CA +4734-05243 America/St_Johns Newfoundland, Labrador (SE)
+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,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas)
+CA,BS +4339-07923 America/Toronto Eastern - ON & QC (most areas)
CA +6344-06828 America/Iqaluit Eastern - NU (most areas)
-CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba
+CA +4953-09709 America/Winnipeg Central - ON (west), Manitoba
CA +744144-0944945 America/Resolute Central - NU (Resolute)
CA +624900-0920459 America/Rankin_Inlet Central - NU (central)
CA +5024-10439 America/Regina CST - SK (most areas)
CA +5017-10750 America/Swift_Current CST - SK (midwest)
-CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W)
+CA +5333-11328 America/Edmonton Mountain - AB, BC(E), NT(E), SK(W)
CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west)
CA +682059-1334300 America/Inuvik Mountain - NT (west)
CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
@@ -126,7 +127,7 @@ CL -3327-07040 America/Santiago most of Chile
CL -5309-07055 America/Punta_Arenas Region of Magallanes
CL -2709-10926 Pacific/Easter Easter Island
CN +3114+12128 Asia/Shanghai Beijing Time
-CN,AQ +4348+08735 Asia/Urumqi Xinjiang Time, Vostok
+CN +4348+08735 Asia/Urumqi Xinjiang Time
CO +0436-07405 America/Bogota
CR +0956-08405 America/Costa_Rica
CU +2308-08222 America/Havana
@@ -171,8 +172,8 @@ HT +1832-07220 America/Port-au-Prince
HU +4730+01905 Europe/Budapest
ID -0610+10648 Asia/Jakarta Java, Sumatra
ID -0002+10920 Asia/Pontianak Borneo (west, central)
-ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west)
-ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas
+ID -0507+11924 Asia/Makassar Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west)
+ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya), Malukus/Moluccas
IE +5320-00615 Europe/Dublin
IL +314650+0351326 Asia/Jerusalem
IN +2232+08822 Asia/Kolkata
@@ -251,7 +252,7 @@ PK +2452+06703 Asia/Karachi
PL +5215+02100 Europe/Warsaw
PM +4703-05620 America/Miquelon
PN -2504-13005 Pacific/Pitcairn
-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
+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 - QC (Lower North Shore)
PS +3130+03428 Asia/Gaza Gaza Strip
PS +313200+0350542 Asia/Hebron West Bank
PT +3843-00908 Europe/Lisbon Portugal (mainland)
@@ -287,7 +288,7 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River
RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky
RU +5934+15048 Asia/Magadan MSK+08 - Magadan
RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island
-RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is
+RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E), N Kuril Is
RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka
RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea
SA,AQ,KW,YE +2438+04643 Asia/Riyadh Syowa
@@ -329,7 +330,7 @@ US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver)
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 +433649-1161209 America/Boise Mountain - ID (south), OR (east)
US,CA +332654-1120424 America/Phoenix MST - AZ (most areas), Creston BC
US +340308-1181434 America/Los_Angeles Pacific
US +611305-1495401 America/Anchorage Alaska (most areas)
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
new file mode 100644
index 00000000..2dbe8f00
--- /dev/null
+++ b/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab
@@ -0,0 +1,301 @@
+# tzdb timezone descriptions, for users who do not care about old timestamps
+#
+# This file is in the public domain.
+#
+# From Paul Eggert (2023-12-18):
+# This file contains a table where each row stands for a timezone
+# where civil timestamps are predicted to agree from now on.
+# This file is like zone1970.tab (see zone1970.tab's coments),
+# but with the following changes:
+#
+# 1. Each timezone corresponds to a set of clocks that are planned
+# to agree from now on. This is a larger set of clocks than in
+# zone1970.tab, where each timezone's clocks must agree from 1970 on.
+# 2. The first column is irrelevant and ignored.
+# 3. The table is sorted in a different way:
+# first by standard time UTC offset;
+# then, if DST is used, by daylight saving UTC offset;
+# then by time zone abbreviation.
+# 4. Every timezone has a nonempty comments column, with wording
+# distinguishing the timezone only from other timezones with the
+# same UTC offset at some point during the year.
+#
+# The format of this table is experimental, and may change in future versions.
+#
+# This table is intended as an aid for users, to help them select timezones
+# appropriate for their practical needs. It is not intended to take or
+# endorse any position on legal or territorial claims.
+#
+#XX coordinates TZ comments
+#
+# -11 - SST
+XX -1416-17042 Pacific/Pago_Pago Midway; Samoa ("SST")
+#
+# -11
+XX -1901-16955 Pacific/Niue Niue
+#
+# -10 - HST
+XX +211825-1575130 Pacific/Honolulu Hawaii ("HST")
+#
+# -10
+XX -1732-14934 Pacific/Tahiti Tahiti; Cook Islands
+#
+# -10/-09 - HST / HDT (North America DST)
+XX +515248-1763929 America/Adak western Aleutians in Alaska ("HST/HDT")
+#
+# -09:30
+XX -0900-13930 Pacific/Marquesas Marquesas
+#
+# -09
+XX -2308-13457 Pacific/Gambier Gambier
+#
+# -09/-08 - AKST/AKDT (North America DST)
+XX +611305-1495401 America/Anchorage most of Alaska ("AKST/AKDT")
+#
+# -08
+XX -2504-13005 Pacific/Pitcairn Pitcairn
+#
+# -08/-07 - PST/PDT (North America DST)
+XX +340308-1181434 America/Los_Angeles Pacific ("PST/PDT") - US & Canada; Mexico near US border
+#
+# -07 - MST
+XX +332654-1120424 America/Phoenix Mountain Standard ("MST") - Arizona; western Mexico; Yukon
+#
+# -07/-06 - MST/MDT (North America DST)
+XX +394421-1045903 America/Denver Mountain ("MST/MDT") - US & Canada; Mexico near US border
+#
+# -06
+XX -0054-08936 Pacific/Galapagos Galápagos
+#
+# -06 - CST
+XX +1924-09909 America/Mexico_City Central Standard ("CST") - Saskatchewan; central Mexico; Central America
+#
+# -06/-05 (Chile DST)
+XX -2709-10926 Pacific/Easter Easter Island
+#
+# -06/-05 - CST/CDT (North America DST)
+XX +415100-0873900 America/Chicago Central ("CST/CDT") - US & Canada; Mexico near US border
+#
+# -05
+XX -1203-07703 America/Lima eastern South America
+#
+# -05 - EST
+XX +175805-0764736 America/Jamaica Eastern Standard ("EST") - Caymans; Jamaica; eastern Mexico; Panama
+#
+# -05/-04 - CST/CDT (Cuba DST)
+XX +2308-08222 America/Havana Cuba
+#
+# -05/-04 - EST/EDT (North America DST)
+XX +404251-0740023 America/New_York Eastern ("EST/EDT") - US & Canada
+#
+# -04
+XX +1030-06656 America/Caracas western South America
+#
+# -04 - AST
+XX +1828-06954 America/Santo_Domingo Atlantic Standard ("AST") - eastern Caribbean
+#
+# -04/-03 (Chile DST)
+XX -3327-07040 America/Santiago most of Chile
+#
+# -04/-03 (Paraguay DST)
+XX -2516-05740 America/Asuncion Paraguay
+#
+# -04/-03 - AST/ADT (North America DST)
+XX +4439-06336 America/Halifax Atlantic ("AST/ADT") - Canada; Bermuda
+#
+# -03:30/-02:30 - NST/NDT (North America DST)
+XX +4734-05243 America/St_Johns Newfoundland ("NST/NDT")
+#
+# -03
+XX -2332-04637 America/Sao_Paulo eastern South America
+#
+# -03/-02 (North America DST)
+XX +4703-05620 America/Miquelon St Pierre & Miquelon
+#
+# -02
+XX -0351-03225 America/Noronha Fernando de Noronha; South Georgia
+#
+# -02/-01 (EU DST)
+XX +6411-05144 America/Nuuk most of Greenland
+#
+# -01
+XX +1455-02331 Atlantic/Cape_Verde Cape Verde
+#
+# -01/+00 (EU DST)
+XX +3744-02540 Atlantic/Azores Azores
+# -01/+00 (EU DST) until 2024-03-31; then -02/-01 (EU DST)
+XX +7029-02158 America/Scoresbysund Ittoqqortoormiit
+#
+# +00 - GMT
+XX +0519-00402 Africa/Abidjan far western Africa; Iceland ("GMT")
+#
+# +00/+01 - GMT/BST (EU DST)
+XX +513030-0000731 Europe/London United Kingdom ("GMT/BST")
+#
+# +00/+01 - WET/WEST (EU DST)
+XX +3843-00908 Europe/Lisbon western Europe ("WET/WEST")
+#
+# +00/+02 - Troll DST
+XX -720041+0023206 Antarctica/Troll Troll Station in Antarctica
+#
+# +01 - CET
+XX +3647+00303 Africa/Algiers Algeria, Tunisia ("CET")
+#
+# +01 - WAT
+XX +0627+00324 Africa/Lagos western Africa ("WAT")
+#
+# +01/+00 - IST/GMT (EU DST in reverse)
+XX +5320-00615 Europe/Dublin Ireland ("IST/GMT")
+#
+# +01/+00 - (Morocco DST)
+XX +3339-00735 Africa/Casablanca Morocco
+#
+# +01/+02 - CET/CEST (EU DST)
+XX +4852+00220 Europe/Paris central Europe ("CET/CEST")
+#
+# +02 - CAT
+XX -2558+03235 Africa/Maputo central Africa ("CAT")
+#
+# +02 - EET
+XX +3254+01311 Africa/Tripoli Libya; Kaliningrad ("EET")
+#
+# +02 - SAST
+XX -2615+02800 Africa/Johannesburg southern Africa ("SAST")
+#
+# +02/+03 - EET/EEST (EU DST)
+XX +3758+02343 Europe/Athens eastern Europe ("EET/EEST")
+#
+# +02/+03 - EET/EEST (Egypt DST)
+XX +3003+03115 Africa/Cairo Egypt
+#
+# +02/+03 - EET/EEST (Lebanon DST)
+XX +3353+03530 Asia/Beirut Lebanon
+#
+# +02/+03 - EET/EEST (Moldova DST)
+XX +4700+02850 Europe/Chisinau Moldova
+#
+# +02/+03 - EET/EEST (Palestine DST)
+XX +3130+03428 Asia/Gaza Palestine
+#
+# +02/+03 - IST/IDT (Israel DST)
+XX +314650+0351326 Asia/Jerusalem Israel
+#
+# +03
+XX +4101+02858 Europe/Istanbul Near East; Belarus
+#
+# +03 - EAT
+XX -0117+03649 Africa/Nairobi eastern Africa ("EAT")
+#
+# +03 - MSK
+XX +554521+0373704 Europe/Moscow Moscow ("MSK")
+#
+# +03:30
+XX +3540+05126 Asia/Tehran Iran
+#
+# +04
+XX +2518+05518 Asia/Dubai Russia; Caucasus; Persian Gulf; Seychelles; Réunion
+#
+# +04:30
+XX +3431+06912 Asia/Kabul Afghanistan
+#
+# +05
+XX +4120+06918 Asia/Tashkent Russia; Tajikistan; Turkmenistan; Uzbekistan; Maldives
+#
+# +05 - PKT
+XX +2452+06703 Asia/Karachi Pakistan ("PKT")
+#
+# +05:30
+XX +0656+07951 Asia/Colombo Sri Lanka
+#
+# +05:30 - IST
+XX +2232+08822 Asia/Kolkata India ("IST")
+#
+# +05:45
+XX +2743+08519 Asia/Kathmandu Nepal
+#
+# +06
+XX +2343+09025 Asia/Dhaka Russia; Kyrgyzstan; Bhutan; Bangladesh; Chagos
+#
+# +06:30
+XX +1647+09610 Asia/Yangon Myanmar; Cocos
+#
+# +07
+XX +1345+10031 Asia/Bangkok Russia; Indochina; Christmas Island
+#
+# +07 - WIB
+XX -0610+10648 Asia/Jakarta Indonesia ("WIB")
+#
+# +08
+XX +0117+10351 Asia/Singapore Russia; Brunei; Malaysia; Singapore
+#
+# +08 - AWST
+XX -3157+11551 Australia/Perth Western Australia ("AWST")
+#
+# +08 - CST
+XX +3114+12128 Asia/Shanghai China ("CST")
+#
+# +08 - HKT
+XX +2217+11409 Asia/Hong_Kong Hong Kong ("HKT")
+#
+# +08 - PHT
+XX +1435+12100 Asia/Manila Philippines ("PHT")
+#
+# +08 - WITA
+XX -0507+11924 Asia/Makassar Indonesia ("WITA")
+#
+# +08:45
+XX -3143+12852 Australia/Eucla Eucla
+#
+# +09
+XX +5203+11328 Asia/Chita Russia; Palau; East Timor
+#
+# +09 - JST
+XX +353916+1394441 Asia/Tokyo Japan ("JST")
+#
+# +09 - KST
+XX +3733+12658 Asia/Seoul Korea ("KST")
+#
+# +09 - WIT
+XX -0232+14042 Asia/Jayapura Indonesia ("WIT")
+#
+# +09:30 - ACST
+XX -1228+13050 Australia/Darwin Northern Territory ("ACST")
+#
+# +09:30/+10:30 - ACST/ACDT (Australia DST)
+XX -3455+13835 Australia/Adelaide South Australia ("ACST/ACDT")
+#
+# +10
+XX +4310+13156 Asia/Vladivostok Russia; Yap; Chuuk; Papua New Guinea; Dumont d'Urville
+#
+# +10 - AEST
+XX -2728+15302 Australia/Brisbane Queensland ("AEST")
+#
+# +10 - ChST
+XX +1328+14445 Pacific/Guam Mariana Islands ("ChST")
+#
+# +10/+11 - AEST/AEDT (Australia DST)
+XX -3352+15113 Australia/Sydney southeast Australia ("AEST/AEDT")
+#
+# +10:30/+11
+XX -3133+15905 Australia/Lord_Howe Lord Howe Island
+#
+# +11
+XX -0613+15534 Pacific/Bougainville Russia; Kosrae; Bougainville; Solomons
+#
+# +11/+12 (Australia DST)
+XX -2903+16758 Pacific/Norfolk Norfolk Island
+#
+# +12
+XX +5301+15839 Asia/Kamchatka Russia; Tuvalu; Fiji; etc.
+#
+# +12/+13 (New Zealand DST)
+XX -3652+17446 Pacific/Auckland New Zealand ("NZST/NZDT")
+#
+# +12:45/+13:45 (Chatham DST)
+XX -4357-17633 Pacific/Chatham Chatham Islands
+#
+# +13
+XX -210800-1751200 Pacific/Tongatapu Kanton; Tokelau; Samoa (western); Tonga
+#
+# +14
+XX +0152-15720 Pacific/Kiritimati Kiritimati
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index b57d3b9b..ce8f605b 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -20,7 +20,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -79,6 +86,7 @@ cc_test(
"//absl/base:exception_testing",
"//absl/container:test_instance_tracker",
"//absl/log",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -92,6 +100,7 @@ cc_test(
":any",
"//absl/base:config",
"//absl/base:exception_safety_testing",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -109,6 +118,7 @@ cc_library(
deps = [
"//absl/algorithm",
"//absl/base:core_headers",
+ "//absl/base:nullability",
"//absl/base:throw_delegate",
"//absl/meta:type_traits",
],
@@ -129,6 +139,7 @@ cc_test(
"//absl/container:inlined_vector",
"//absl/hash:hash_testing",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -144,6 +155,7 @@ cc_library(
"//absl/base:base_internal",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:nullability",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/utility",
@@ -188,6 +200,7 @@ cc_test(
"//absl/log",
"//absl/meta:type_traits",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -203,44 +216,7 @@ cc_test(
":optional",
"//absl/base:config",
"//absl/base:exception_safety_testing",
- "@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_library(
- name = "conformance_testing",
- testonly = 1,
- hdrs = [
- "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/algorithm:container",
- "//absl/meta:type_traits",
- "//absl/strings",
- "//absl/utility",
"@com_google_googletest//:gtest",
- ],
-)
-
-cc_test(
- name = "conformance_testing_test",
- size = "small",
- srcs = [
- "internal/conformance_testing_test.cc",
- ],
- copts = ABSL_TEST_COPTS,
- linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = [
- ":conformance_testing",
- "//absl/meta:type_traits",
"@com_google_googletest//:gtest_main",
],
)
@@ -274,6 +250,7 @@ cc_test(
"//absl/memory",
"//absl/meta:type_traits",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -290,6 +267,7 @@ cc_test(
":variant",
"//absl/utility",
"@com_github_google_benchmark//:benchmark_main",
+ "@com_google_googletest//:gtest",
],
)
@@ -306,6 +284,7 @@ cc_test(
"//absl/base:config",
"//absl/base:exception_safety_testing",
"//absl/memory",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -316,6 +295,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/meta:type_traits",
],
@@ -331,6 +311,7 @@ cc_test(
deps = [
":compare",
"//absl/base",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index c0dcee79..92b4ae49 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -115,6 +115,7 @@ absl_cc_library(
DEPS
absl::algorithm
absl::core_headers
+ absl::nullability
absl::throw_delegate
absl::type_traits
PUBLIC
@@ -175,6 +176,7 @@ absl_cc_library(
absl::config
absl::core_headers
absl::memory
+ absl::nullability
absl::type_traits
absl::utility
PUBLIC
@@ -240,59 +242,6 @@ absl_cc_test(
GTest::gmock_main
)
-# Internal-only target, do not depend on directly.
-absl_cc_library(
- NAME
- conformance_testing
- HDRS
- "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
- GTest::gmock_main
- TESTONLY
-)
-
-absl_cc_test(
- NAME
- conformance_testing_test
- SRCS
- "internal/conformance_testing_test.cc"
- COPTS
- ${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
- DEPS
- absl::conformance_testing
- absl::type_traits
- GTest::gmock_main
-)
-
-absl_cc_test(
- NAME
- conformance_testing_test_no_exceptions
- SRCS
- "internal/conformance_testing_test.cc"
- COPTS
- ${ABSL_TEST_COPTS}
- DEPS
- absl::conformance_testing
- absl::type_traits
- GTest::gmock_main
-)
-
absl_cc_library(
NAME
variant
@@ -337,6 +286,7 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::config
absl::core_headers
absl::type_traits
PUBLIC
diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc
index b0592cc9..22558b48 100644
--- a/absl/types/bad_any_cast.cc
+++ b/absl/types/bad_any_cast.cc
@@ -43,4 +43,22 @@ void ThrowBadAnyCast() {
ABSL_NAMESPACE_END
} // namespace absl
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace types_internal {
+extern const char kAvoidEmptyBadAnyCastLibraryWarning;
+const char kAvoidEmptyBadAnyCastLibraryWarning = 0;
+} // namespace types_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // __APPLE__
+
#endif // ABSL_USES_STD_ANY
diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc
index 26aca70d..2552cc85 100644
--- a/absl/types/bad_optional_access.cc
+++ b/absl/types/bad_optional_access.cc
@@ -45,4 +45,22 @@ void throw_bad_optional_access() {
ABSL_NAMESPACE_END
} // namespace absl
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace types_internal {
+extern const char kAvoidEmptyBadOptionalAccessLibraryWarning;
+const char kAvoidEmptyBadOptionalAccessLibraryWarning = 0;
+} // namespace types_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // __APPLE__
+
#endif // ABSL_USES_STD_OPTIONAL
diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc
index 3dc88cc0..a76aa80d 100644
--- a/absl/types/bad_variant_access.cc
+++ b/absl/types/bad_variant_access.cc
@@ -61,4 +61,22 @@ void Rethrow() {
ABSL_NAMESPACE_END
} // namespace absl
+#else
+
+// https://github.com/abseil/abseil-cpp/issues/1465
+// CMake builds on Apple platforms error when libraries are empty.
+// Our CMake configuration can avoid this error on header-only libraries,
+// but since this library is conditionally empty, including a single
+// variable is an easy workaround.
+#ifdef __APPLE__
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace types_internal {
+extern const char kAvoidEmptyBadVariantAccessLibraryWarning;
+const char kAvoidEmptyBadVariantAccessLibraryWarning = 0;
+} // namespace types_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // __APPLE__
+
#endif // ABSL_USES_STD_VARIANT
diff --git a/absl/types/compare.h b/absl/types/compare.h
index 2b89b69c..3cf4a917 100644
--- a/absl/types/compare.h
+++ b/absl/types/compare.h
@@ -16,20 +16,31 @@
// compare.h
// -----------------------------------------------------------------------------
//
-// This header file defines the `absl::weak_equality`, `absl::strong_equality`,
-// `absl::partial_ordering`, `absl::weak_ordering`, and `absl::strong_ordering`
-// types for storing the results of three way comparisons.
+// This header file defines the `absl::partial_ordering`, `absl::weak_ordering`,
+// and `absl::strong_ordering` types for storing the results of three way
+// comparisons.
//
// Example:
// absl::weak_ordering compare(const std::string& a, const std::string& b);
//
// These are C++11 compatible versions of the C++20 corresponding types
-// (`std::weak_equality`, etc.) and are designed to be drop-in replacements
+// (`std::partial_ordering`, etc.) and are designed to be drop-in replacements
// for code compliant with C++20.
#ifndef ABSL_TYPES_COMPARE_H_
#define ABSL_TYPES_COMPARE_H_
+#include "absl/base/config.h"
+
+#ifdef ABSL_USES_STD_ORDERING
+
+#include <compare> // IWYU pragma: export
+#include <type_traits>
+
+#include "absl/meta/type_traits.h"
+
+#else
+
#include <cstddef>
#include <cstdint>
#include <cstdlib>
@@ -39,8 +50,19 @@
#include "absl/base/macros.h"
#include "absl/meta/type_traits.h"
+#endif
+
namespace absl {
ABSL_NAMESPACE_BEGIN
+
+#ifdef ABSL_USES_STD_ORDERING
+
+using std::partial_ordering;
+using std::strong_ordering;
+using std::weak_ordering;
+
+#else
+
namespace compare_internal {
using value_type = int8_t;
@@ -122,20 +144,6 @@ enum class ncmp : value_type { unordered = -127 };
// in the header file (for performance) without using inline variables (which
// aren't available in C++11).
template <typename T>
-struct weak_equality_base {
- ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
- ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent);
-};
-
-template <typename T>
-struct strong_equality_base {
- ABSL_COMPARE_INLINE_BASECLASS_DECL(equal);
- ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequal);
- ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
- ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent);
-};
-
-template <typename T>
struct partial_ordering_base {
ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
@@ -160,104 +168,6 @@ struct strong_ordering_base {
} // namespace compare_internal
-class weak_equality
- : public compare_internal::weak_equality_base<weak_equality> {
- explicit constexpr weak_equality(compare_internal::eq v) noexcept
- : value_(static_cast<compare_internal::value_type>(v)) {}
- friend struct compare_internal::weak_equality_base<weak_equality>;
-
- public:
- ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, equivalent);
- ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, nonequivalent);
-
- // Comparisons
- friend constexpr bool operator==(
- weak_equality v, compare_internal::OnlyLiteralZero) noexcept {
- return v.value_ == 0;
- }
- friend constexpr bool operator!=(
- weak_equality v, compare_internal::OnlyLiteralZero) noexcept {
- return v.value_ != 0;
- }
- friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
- weak_equality v) noexcept {
- return 0 == v.value_;
- }
- friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
- weak_equality v) noexcept {
- return 0 != v.value_;
- }
- friend constexpr bool operator==(weak_equality v1,
- weak_equality v2) noexcept {
- return v1.value_ == v2.value_;
- }
- friend constexpr bool operator!=(weak_equality v1,
- weak_equality v2) noexcept {
- return v1.value_ != v2.value_;
- }
-
- private:
- compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(weak_equality, equivalent,
- compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(weak_equality, nonequivalent,
- compare_internal::eq::nonequivalent);
-
-class strong_equality
- : public compare_internal::strong_equality_base<strong_equality> {
- explicit constexpr strong_equality(compare_internal::eq v) noexcept
- : value_(static_cast<compare_internal::value_type>(v)) {}
- friend struct compare_internal::strong_equality_base<strong_equality>;
-
- public:
- ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equal);
- ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequal);
- ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equivalent);
- ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequivalent);
-
- // Conversion
- constexpr operator weak_equality() const noexcept { // NOLINT
- return value_ == 0 ? weak_equality::equivalent
- : weak_equality::nonequivalent;
- }
- // Comparisons
- friend constexpr bool operator==(
- strong_equality v, compare_internal::OnlyLiteralZero) noexcept {
- return v.value_ == 0;
- }
- friend constexpr bool operator!=(
- strong_equality v, compare_internal::OnlyLiteralZero) noexcept {
- return v.value_ != 0;
- }
- friend constexpr bool operator==(compare_internal::OnlyLiteralZero,
- strong_equality v) noexcept {
- return 0 == v.value_;
- }
- friend constexpr bool operator!=(compare_internal::OnlyLiteralZero,
- strong_equality v) noexcept {
- return 0 != v.value_;
- }
- friend constexpr bool operator==(strong_equality v1,
- strong_equality v2) noexcept {
- return v1.value_ == v2.value_;
- }
- friend constexpr bool operator!=(strong_equality v1,
- strong_equality v2) noexcept {
- return v1.value_ != v2.value_;
- }
-
- private:
- compare_internal::value_type value_;
-};
-ABSL_COMPARE_INLINE_INIT(strong_equality, equal, compare_internal::eq::equal);
-ABSL_COMPARE_INLINE_INIT(strong_equality, nonequal,
- compare_internal::eq::nonequal);
-ABSL_COMPARE_INLINE_INIT(strong_equality, equivalent,
- compare_internal::eq::equivalent);
-ABSL_COMPARE_INLINE_INIT(strong_equality, nonequivalent,
- compare_internal::eq::nonequivalent);
-
class partial_ordering
: public compare_internal::partial_ordering_base<partial_ordering> {
explicit constexpr partial_ordering(compare_internal::eq v) noexcept
@@ -279,11 +189,6 @@ class partial_ordering
ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater);
ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered);
- // Conversion
- constexpr operator weak_equality() const noexcept { // NOLINT
- return value_ == 0 ? weak_equality::equivalent
- : weak_equality::nonequivalent;
- }
// Comparisons
friend constexpr bool operator==(
partial_ordering v, compare_internal::OnlyLiteralZero) noexcept {
@@ -367,10 +272,6 @@ class weak_ordering
ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater);
// Conversions
- constexpr operator weak_equality() const noexcept { // NOLINT
- return value_ == 0 ? weak_equality::equivalent
- : weak_equality::nonequivalent;
- }
constexpr operator partial_ordering() const noexcept { // NOLINT
return value_ == 0 ? partial_ordering::equivalent
: (value_ < 0 ? partial_ordering::less
@@ -458,13 +359,6 @@ class strong_ordering
ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater);
// Conversions
- constexpr operator weak_equality() const noexcept { // NOLINT
- return value_ == 0 ? weak_equality::equivalent
- : weak_equality::nonequivalent;
- }
- constexpr operator strong_equality() const noexcept { // NOLINT
- return value_ == 0 ? strong_equality::equal : strong_equality::nonequal;
- }
constexpr operator partial_ordering() const noexcept { // NOLINT
return value_ == 0 ? partial_ordering::equivalent
: (value_ < 0 ? partial_ordering::less
@@ -547,6 +441,8 @@ ABSL_COMPARE_INLINE_INIT(strong_ordering, greater,
#undef ABSL_COMPARE_INLINE_SUBCLASS_DECL
#undef ABSL_COMPARE_INLINE_INIT
+#endif // ABSL_USES_STD_ORDERING
+
namespace compare_internal {
// We also provide these comparator adapter functions for internal absl use.
diff --git a/absl/types/compare_test.cc b/absl/types/compare_test.cc
index 8095baf9..455cdbba 100644
--- a/absl/types/compare_test.cc
+++ b/absl/types/compare_test.cc
@@ -26,45 +26,6 @@ namespace {
// to an int, which can't be converted to the unspecified zero type.
bool Identity(bool b) { return b; }
-TEST(Compare, WeakEquality) {
- EXPECT_TRUE(Identity(weak_equality::equivalent == 0));
- EXPECT_TRUE(Identity(0 == weak_equality::equivalent));
- EXPECT_TRUE(Identity(weak_equality::nonequivalent != 0));
- EXPECT_TRUE(Identity(0 != weak_equality::nonequivalent));
- const weak_equality values[] = {weak_equality::equivalent,
- weak_equality::nonequivalent};
- for (const auto& lhs : values) {
- for (const auto& rhs : values) {
- const bool are_equal = &lhs == &rhs;
- EXPECT_EQ(lhs == rhs, are_equal);
- EXPECT_EQ(lhs != rhs, !are_equal);
- }
- }
-}
-
-TEST(Compare, StrongEquality) {
- EXPECT_TRUE(Identity(strong_equality::equal == 0));
- EXPECT_TRUE(Identity(0 == strong_equality::equal));
- EXPECT_TRUE(Identity(strong_equality::nonequal != 0));
- EXPECT_TRUE(Identity(0 != strong_equality::nonequal));
- EXPECT_TRUE(Identity(strong_equality::equivalent == 0));
- EXPECT_TRUE(Identity(0 == strong_equality::equivalent));
- EXPECT_TRUE(Identity(strong_equality::nonequivalent != 0));
- EXPECT_TRUE(Identity(0 != strong_equality::nonequivalent));
- const strong_equality values[] = {strong_equality::equal,
- strong_equality::nonequal};
- for (const auto& lhs : values) {
- for (const auto& rhs : values) {
- const bool are_equal = &lhs == &rhs;
- EXPECT_EQ(lhs == rhs, are_equal);
- EXPECT_EQ(lhs != rhs, !are_equal);
- }
- }
- EXPECT_TRUE(Identity(strong_equality::equivalent == strong_equality::equal));
- EXPECT_TRUE(
- Identity(strong_equality::nonequivalent == strong_equality::nonequal));
-}
-
TEST(Compare, PartialOrdering) {
EXPECT_TRUE(Identity(partial_ordering::less < 0));
EXPECT_TRUE(Identity(0 > partial_ordering::less));
@@ -147,30 +108,6 @@ TEST(Compare, StrongOrdering) {
TEST(Compare, Conversions) {
EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(strong_equality::equal) == 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(strong_equality::nonequal) != 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(strong_equality::equivalent) == 0));
- EXPECT_TRUE(Identity(
- implicit_cast<weak_equality>(strong_equality::nonequivalent) != 0));
-
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(partial_ordering::less) != 0));
- EXPECT_TRUE(Identity(
- implicit_cast<weak_equality>(partial_ordering::equivalent) == 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(partial_ordering::greater) != 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(partial_ordering::unordered) != 0));
-
- EXPECT_TRUE(implicit_cast<weak_equality>(weak_ordering::less) != 0);
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(weak_ordering::equivalent) == 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(weak_ordering::greater) != 0));
-
- EXPECT_TRUE(
Identity(implicit_cast<partial_ordering>(weak_ordering::less) != 0));
EXPECT_TRUE(
Identity(implicit_cast<partial_ordering>(weak_ordering::less) < 0));
@@ -186,24 +123,6 @@ TEST(Compare, Conversions) {
Identity(implicit_cast<partial_ordering>(weak_ordering::greater) >= 0));
EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(strong_ordering::less) != 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(strong_ordering::equal) == 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(strong_ordering::equivalent) == 0));
- EXPECT_TRUE(
- Identity(implicit_cast<weak_equality>(strong_ordering::greater) != 0));
-
- EXPECT_TRUE(
- Identity(implicit_cast<strong_equality>(strong_ordering::less) != 0));
- EXPECT_TRUE(
- Identity(implicit_cast<strong_equality>(strong_ordering::equal) == 0));
- EXPECT_TRUE(Identity(
- implicit_cast<strong_equality>(strong_ordering::equivalent) == 0));
- EXPECT_TRUE(
- Identity(implicit_cast<strong_equality>(strong_ordering::greater) != 0));
-
- EXPECT_TRUE(
Identity(implicit_cast<partial_ordering>(strong_ordering::less) != 0));
EXPECT_TRUE(
Identity(implicit_cast<partial_ordering>(strong_ordering::less) < 0));
@@ -360,14 +279,6 @@ TEST(DoThreeWayComparison, SanityTest) {
#ifdef __cpp_inline_variables
TEST(Compare, StaticAsserts) {
- static_assert(weak_equality::equivalent == 0, "");
- static_assert(weak_equality::nonequivalent != 0, "");
-
- static_assert(strong_equality::equal == 0, "");
- static_assert(strong_equality::nonequal != 0, "");
- static_assert(strong_equality::equivalent == 0, "");
- static_assert(strong_equality::nonequivalent != 0, "");
-
static_assert(partial_ordering::less < 0, "");
static_assert(partial_ordering::equivalent == 0, "");
static_assert(partial_ordering::greater > 0, "");
diff --git a/absl/types/internal/conformance_aliases.h b/absl/types/internal/conformance_aliases.h
deleted file mode 100644
index 0cc6884e..00000000
--- a/absl/types/internal/conformance_aliases.h
+++ /dev/null
@@ -1,447 +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.
-//
-// -----------------------------------------------------------------------------
-// regularity_aliases.h
-// -----------------------------------------------------------------------------
-//
-// This file contains type aliases of common ConformanceProfiles and Archetypes
-// so that they can be directly used by name without creating them from scratch.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
-
-#include "absl/types/internal/conformance_archetype.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// Creates both a Profile and a corresponding Archetype with root name "name".
-#define ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(name, ...) \
- struct name##Profile : __VA_ARGS__ {}; \
- \
- using name##Archetype = ::absl::types_internal::Archetype<name##Profile>; \
- \
- template <class AbslInternalProfileTag> \
- using name##Archetype##_ = ::absl::types_internal::Archetype< \
- ::absl::types_internal::StrongProfileTypedef<name##Profile, \
- AbslInternalProfileTag>>
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasTrivialDefaultConstructor,
- ConformanceProfile<default_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowDefaultConstructor,
- ConformanceProfile<default_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasDefaultConstructor, ConformanceProfile<default_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasTrivialMoveConstructor, ConformanceProfile<default_constructible::maybe,
- move_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowMoveConstructor, ConformanceProfile<default_constructible::maybe,
- move_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasMoveConstructor,
- ConformanceProfile<default_constructible::maybe, move_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasTrivialCopyConstructor,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowCopyConstructor,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasCopyConstructor,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasTrivialMoveAssign,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowMoveAssign,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasMoveAssign,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasTrivialCopyAssign,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowCopyAssign,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasCopyAssign,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasTrivialDestructor,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::trivial>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowDestructor,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasDestructor,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowEquality,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasEquality,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowInequality,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::maybe,
- inequality_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasInequality,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::maybe, inequality_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowLessThan,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::maybe, inequality_comparable::maybe,
- less_than_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasLessThan,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::maybe, inequality_comparable::maybe,
- less_than_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowLessEqual,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::maybe, inequality_comparable::maybe,
- less_than_comparable::maybe,
- less_equal_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasLessEqual,
- ConformanceProfile<default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe,
- equality_comparable::maybe, inequality_comparable::maybe,
- less_than_comparable::maybe,
- less_equal_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowGreaterEqual,
- ConformanceProfile<
- default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
- inequality_comparable::maybe, less_than_comparable::maybe,
- less_equal_comparable::maybe, greater_equal_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasGreaterEqual,
- ConformanceProfile<
- default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
- inequality_comparable::maybe, less_than_comparable::maybe,
- less_equal_comparable::maybe, greater_equal_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowGreaterThan,
- ConformanceProfile<
- default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
- inequality_comparable::maybe, less_than_comparable::maybe,
- less_equal_comparable::maybe, greater_equal_comparable::maybe,
- greater_than_comparable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasGreaterThan,
- ConformanceProfile<
- default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
- inequality_comparable::maybe, less_than_comparable::maybe,
- less_equal_comparable::maybe, greater_equal_comparable::maybe,
- greater_than_comparable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasNothrowSwap,
- ConformanceProfile<
- default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
- inequality_comparable::maybe, less_than_comparable::maybe,
- less_equal_comparable::maybe, greater_equal_comparable::maybe,
- greater_than_comparable::maybe, swappable::nothrow>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasSwap,
- ConformanceProfile<
- default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
- inequality_comparable::maybe, less_than_comparable::maybe,
- less_equal_comparable::maybe, greater_equal_comparable::maybe,
- greater_than_comparable::maybe, swappable::yes>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HasStdHashSpecialization,
- ConformanceProfile<
- default_constructible::maybe, move_constructible::maybe,
- copy_constructible::maybe, move_assignable::maybe,
- copy_assignable::maybe, destructible::maybe, equality_comparable::maybe,
- inequality_comparable::maybe, less_than_comparable::maybe,
- less_equal_comparable::maybe, greater_equal_comparable::maybe,
- greater_than_comparable::maybe, swappable::maybe, hashable::yes>);
-
-////////////////////////////////////////////////////////////////////////////////
-//// The remaining aliases are combinations of the previous aliases. ////
-////////////////////////////////////////////////////////////////////////////////
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- Equatable, CombineProfiles<HasEqualityProfile, HasInequalityProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- Comparable,
- CombineProfiles<EquatableProfile, HasLessThanProfile, HasLessEqualProfile,
- HasGreaterEqualProfile, HasGreaterThanProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- NothrowEquatable,
- CombineProfiles<HasNothrowEqualityProfile, HasNothrowInequalityProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- NothrowComparable,
- CombineProfiles<NothrowEquatableProfile, HasNothrowLessThanProfile,
- HasNothrowLessEqualProfile, HasNothrowGreaterEqualProfile,
- HasNothrowGreaterThanProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- Value,
- CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile,
- HasNothrowMoveAssignProfile, HasCopyAssignProfile,
- HasNothrowDestructorProfile, HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- EquatableValue, CombineProfiles<EquatableProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- ComparableValue, CombineProfiles<ComparableProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- DefaultConstructibleValue,
- CombineProfiles<HasDefaultConstructorProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- NothrowMoveConstructible, CombineProfiles<HasNothrowMoveConstructorProfile,
- HasNothrowDestructorProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- EquatableNothrowMoveConstructible,
- CombineProfiles<EquatableProfile, NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- ComparableNothrowMoveConstructible,
- CombineProfiles<ComparableProfile, NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- DefaultConstructibleNothrowMoveConstructible,
- CombineProfiles<HasDefaultConstructorProfile,
- NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- CopyConstructible,
- CombineProfiles<HasNothrowMoveConstructorProfile, HasCopyConstructorProfile,
- HasNothrowDestructorProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- EquatableCopyConstructible,
- CombineProfiles<EquatableProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- ComparableCopyConstructible,
- CombineProfiles<ComparableProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- DefaultConstructibleCopyConstructible,
- CombineProfiles<HasDefaultConstructorProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- NothrowMovable,
- CombineProfiles<HasNothrowMoveConstructorProfile,
- HasNothrowMoveAssignProfile, HasNothrowDestructorProfile,
- HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- EquatableNothrowMovable,
- CombineProfiles<EquatableProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- ComparableNothrowMovable,
- CombineProfiles<ComparableProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- DefaultConstructibleNothrowMovable,
- CombineProfiles<HasDefaultConstructorProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- TrivialSpecialMemberFunctions,
- CombineProfiles<HasTrivialDefaultConstructorProfile,
- HasTrivialMoveConstructorProfile,
- HasTrivialCopyConstructorProfile,
- HasTrivialMoveAssignProfile, HasTrivialCopyAssignProfile,
- HasTrivialDestructorProfile, HasNothrowSwapProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- TriviallyComplete,
- CombineProfiles<TrivialSpecialMemberFunctionsProfile, ComparableProfile,
- HasStdHashSpecializationProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HashableNothrowMoveConstructible,
- CombineProfiles<HasStdHashSpecializationProfile,
- NothrowMoveConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HashableCopyConstructible,
- CombineProfiles<HasStdHashSpecializationProfile, CopyConstructibleProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HashableNothrowMovable,
- CombineProfiles<HasStdHashSpecializationProfile, NothrowMovableProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- HashableValue,
- CombineProfiles<HasStdHashSpecializationProfile, ValueProfile>);
-
-ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(
- ComparableHashableValue,
- CombineProfiles<HashableValueProfile, ComparableProfile>);
-
-// The "preferred" profiles that we support in Abseil.
-template <template <class...> class Receiver>
-using ExpandBasicProfiles =
- Receiver<NothrowMoveConstructibleProfile, CopyConstructibleProfile,
- NothrowMovableProfile, ValueProfile>;
-
-// The basic profiles except that they are also all Equatable.
-template <template <class...> class Receiver>
-using ExpandBasicEquatableProfiles =
- Receiver<EquatableNothrowMoveConstructibleProfile,
- EquatableCopyConstructibleProfile, EquatableNothrowMovableProfile,
- EquatableValueProfile>;
-
-// The basic profiles except that they are also all Comparable.
-template <template <class...> class Receiver>
-using ExpandBasicComparableProfiles =
- Receiver<ComparableNothrowMoveConstructibleProfile,
- ComparableCopyConstructibleProfile,
- ComparableNothrowMovableProfile, ComparableValueProfile>;
-
-// The basic profiles except that they are also all Hashable.
-template <template <class...> class Receiver>
-using ExpandBasicHashableProfiles =
- Receiver<HashableNothrowMoveConstructibleProfile,
- HashableCopyConstructibleProfile, HashableNothrowMovableProfile,
- HashableValueProfile>;
-
-// The basic profiles except that they are also all DefaultConstructible.
-template <template <class...> class Receiver>
-using ExpandBasicDefaultConstructibleProfiles =
- Receiver<DefaultConstructibleNothrowMoveConstructibleProfile,
- DefaultConstructibleCopyConstructibleProfile,
- DefaultConstructibleNothrowMovableProfile,
- DefaultConstructibleValueProfile>;
-
-// The type profiles that we support in Abseil (all of the previous lists).
-template <template <class...> class Receiver>
-using ExpandSupportedProfiles = Receiver<
- NothrowMoveConstructibleProfile, CopyConstructibleProfile,
- NothrowMovableProfile, ValueProfile,
- EquatableNothrowMoveConstructibleProfile, EquatableCopyConstructibleProfile,
- EquatableNothrowMovableProfile, EquatableValueProfile,
- ComparableNothrowMoveConstructibleProfile,
- ComparableCopyConstructibleProfile, ComparableNothrowMovableProfile,
- ComparableValueProfile, DefaultConstructibleNothrowMoveConstructibleProfile,
- DefaultConstructibleCopyConstructibleProfile,
- DefaultConstructibleNothrowMovableProfile, DefaultConstructibleValueProfile,
- HashableNothrowMoveConstructibleProfile, HashableCopyConstructibleProfile,
- HashableNothrowMovableProfile, HashableValueProfile>;
-
-// TODO(calabrese) Include types that have throwing move constructors, since in
-// practice we still need to support them because of standard library types with
-// (potentially) non-noexcept moves.
-
-} // namespace types_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-#undef ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS
-
-#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_
diff --git a/absl/types/internal/conformance_archetype.h b/absl/types/internal/conformance_archetype.h
deleted file mode 100644
index 2349e0f7..00000000
--- a/absl/types/internal/conformance_archetype.h
+++ /dev/null
@@ -1,978 +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.
-//
-// -----------------------------------------------------------------------------
-// conformance_archetype.h
-// -----------------------------------------------------------------------------
-//
-// This file contains a facility for generating "archetypes" of out of
-// "Conformance Profiles" (see "conformance_profiles.h" for more information
-// about Conformance Profiles). An archetype is a type that aims to support the
-// bare minimum requirements of a given Conformance Profile. For instance, an
-// archetype that corresponds to an ImmutableProfile has exactly a nothrow
-// move-constructor, a potentially-throwing copy constructor, a nothrow
-// destructor, with all other special-member-functions deleted. These archetypes
-// are useful for testing to make sure that templates are able to work with the
-// kinds of types that they claim to support (i.e. that they do not accidentally
-// under-constrain),
-//
-// The main type template in this file is the Archetype template, which takes
-// a Conformance Profile as a template argument and its instantiations are a
-// minimum-conforming model of that profile.
-
-#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
-#define ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
-
-#include <cstddef>
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-#include "absl/meta/type_traits.h"
-#include "absl/types/internal/conformance_profile.h"
-
-namespace absl {
-ABSL_NAMESPACE_BEGIN
-namespace types_internal {
-
-// A minimum-conforming implementation of a type with properties specified in
-// `Prof`, where `Prof` is a valid Conformance Profile.
-template <class Prof, class /*Enabler*/ = void>
-class Archetype;
-
-// Given an Archetype, obtain the properties of the profile associated with that
-// archetype.
-template <class Archetype>
-struct PropertiesOfArchetype;
-
-template <class Prof>
-struct PropertiesOfArchetype<Archetype<Prof>> {
- using type = PropertiesOfT<Prof>;
-};
-
-template <class Archetype>
-using PropertiesOfArchetypeT = typename PropertiesOfArchetype<Archetype>::type;
-
-// A metafunction to determine if a type is an `Archetype`.
-template <class T>
-struct IsArchetype : std::false_type {};
-
-template <class Prof>
-struct IsArchetype<Archetype<Prof>> : std::true_type {};
-
-// A constructor tag type used when creating an Archetype with internal state.
-struct MakeArchetypeState {};
-
-// Data stored within an archetype that is copied/compared/hashed when the
-// corresponding operations are used.
-using ArchetypeState = std::size_t;
-
-////////////////////////////////////////////////////////////////////////////////
-// This section of the file defines a chain of base classes for Archetype, //
-// where each base defines a specific special member function with the //
-// appropriate properties (deleted, noexcept(false), noexcept, or trivial). //
-////////////////////////////////////////////////////////////////////////////////
-
-// The bottom-most base, which contains the state and the default constructor.
-template <default_constructible DefaultConstructibleValue>
-struct ArchetypeStateBase {
- static_assert(DefaultConstructibleValue == default_constructible::yes ||
- DefaultConstructibleValue == default_constructible::nothrow,
- "");
-
- ArchetypeStateBase() noexcept(
- DefaultConstructibleValue ==
- default_constructible::
- nothrow) /*Vacuous archetype_state initialization*/ {}
- explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
- : archetype_state(state) {}
-
- ArchetypeState archetype_state;
-};
-
-template <>
-struct ArchetypeStateBase<default_constructible::maybe> {
- explicit ArchetypeStateBase() = delete;
- explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
- : archetype_state(state) {}
-
- ArchetypeState archetype_state;
-};
-
-template <>
-struct ArchetypeStateBase<default_constructible::trivial> {
- ArchetypeStateBase() = default;
- explicit ArchetypeStateBase(MakeArchetypeState, ArchetypeState state) noexcept
- : archetype_state(state) {}
-
- ArchetypeState archetype_state;
-};
-
-// The move-constructor base
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue>
-struct ArchetypeMoveConstructor
- : ArchetypeStateBase<DefaultConstructibleValue> {
- static_assert(MoveConstructibleValue == move_constructible::yes ||
- MoveConstructibleValue == move_constructible::nothrow,
- "");
-
- explicit ArchetypeMoveConstructor(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
- state) {}
-
- ArchetypeMoveConstructor() = default;
- ArchetypeMoveConstructor(ArchetypeMoveConstructor&& other) noexcept(
- MoveConstructibleValue == move_constructible::nothrow)
- : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
- other.archetype_state) {}
- ArchetypeMoveConstructor(const ArchetypeMoveConstructor&) = default;
- ArchetypeMoveConstructor& operator=(ArchetypeMoveConstructor&&) = default;
- ArchetypeMoveConstructor& operator=(const ArchetypeMoveConstructor&) =
- default;
-};
-
-template <default_constructible DefaultConstructibleValue>
-struct ArchetypeMoveConstructor<DefaultConstructibleValue,
- move_constructible::trivial>
- : ArchetypeStateBase<DefaultConstructibleValue> {
- explicit ArchetypeMoveConstructor(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeStateBase<DefaultConstructibleValue>(MakeArchetypeState(),
- state) {}
-
- ArchetypeMoveConstructor() = default;
-};
-
-// The copy-constructor base
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue>
-struct ArchetypeCopyConstructor
- : ArchetypeMoveConstructor<DefaultConstructibleValue,
- MoveConstructibleValue> {
- static_assert(CopyConstructibleValue == copy_constructible::yes ||
- CopyConstructibleValue == copy_constructible::nothrow,
- "");
- explicit ArchetypeCopyConstructor(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeMoveConstructor<DefaultConstructibleValue,
- MoveConstructibleValue>(MakeArchetypeState(),
- state) {}
-
- ArchetypeCopyConstructor() = default;
- ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default;
- ArchetypeCopyConstructor(const ArchetypeCopyConstructor& other) noexcept(
- CopyConstructibleValue == copy_constructible::nothrow)
- : ArchetypeMoveConstructor<DefaultConstructibleValue,
- MoveConstructibleValue>(
- MakeArchetypeState(), other.archetype_state) {}
- ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default;
- ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) =
- default;
-};
-
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue>
-struct ArchetypeCopyConstructor<DefaultConstructibleValue,
- MoveConstructibleValue,
- copy_constructible::maybe>
- : ArchetypeMoveConstructor<DefaultConstructibleValue,
- MoveConstructibleValue> {
- explicit ArchetypeCopyConstructor(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeMoveConstructor<DefaultConstructibleValue,
- MoveConstructibleValue>(MakeArchetypeState(),
- state) {}
-
- ArchetypeCopyConstructor() = default;
- ArchetypeCopyConstructor(ArchetypeCopyConstructor&&) = default;
- ArchetypeCopyConstructor(const ArchetypeCopyConstructor&) = delete;
- ArchetypeCopyConstructor& operator=(ArchetypeCopyConstructor&&) = default;
- ArchetypeCopyConstructor& operator=(const ArchetypeCopyConstructor&) =
- default;
-};
-
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue>
-struct ArchetypeCopyConstructor<DefaultConstructibleValue,
- MoveConstructibleValue,
- copy_constructible::trivial>
- : ArchetypeMoveConstructor<DefaultConstructibleValue,
- MoveConstructibleValue> {
- explicit ArchetypeCopyConstructor(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeMoveConstructor<DefaultConstructibleValue,
- MoveConstructibleValue>(MakeArchetypeState(),
- state) {}
-
- ArchetypeCopyConstructor() = default;
-};
-
-// The move-assign base
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue,
- move_assignable MoveAssignableValue>
-struct ArchetypeMoveAssign
- : ArchetypeCopyConstructor<DefaultConstructibleValue,
- MoveConstructibleValue, CopyConstructibleValue> {
- static_assert(MoveAssignableValue == move_assignable::yes ||
- MoveAssignableValue == move_assignable::nothrow,
- "");
- explicit ArchetypeMoveAssign(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeCopyConstructor<DefaultConstructibleValue,
- MoveConstructibleValue,
- CopyConstructibleValue>(MakeArchetypeState(),
- state) {}
-
- ArchetypeMoveAssign() = default;
- ArchetypeMoveAssign(ArchetypeMoveAssign&&) = default;
- ArchetypeMoveAssign(const ArchetypeMoveAssign&) = default;
- ArchetypeMoveAssign& operator=(ArchetypeMoveAssign&& other) noexcept(
- MoveAssignableValue == move_assignable::nothrow) {
- this->archetype_state = other.archetype_state;
- return *this;
- }
-
- ArchetypeMoveAssign& operator=(const ArchetypeMoveAssign&) = default;
-};
-
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue>
-struct ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, move_assignable::trivial>
- : ArchetypeCopyConstructor<DefaultConstructibleValue,
- MoveConstructibleValue, CopyConstructibleValue> {
- explicit ArchetypeMoveAssign(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeCopyConstructor<DefaultConstructibleValue,
- MoveConstructibleValue,
- CopyConstructibleValue>(MakeArchetypeState(),
- state) {}
-
- ArchetypeMoveAssign() = default;
-};
-
-// The copy-assign base
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue,
- move_assignable MoveAssignableValue,
- copy_assignable CopyAssignableValue>
-struct ArchetypeCopyAssign
- : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue> {
- static_assert(CopyAssignableValue == copy_assignable::yes ||
- CopyAssignableValue == copy_assignable::nothrow,
- "");
- explicit ArchetypeCopyAssign(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue>(
- MakeArchetypeState(), state) {}
-
- ArchetypeCopyAssign() = default;
- ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default;
- ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default;
- ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default;
-
- ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign& other) noexcept(
- CopyAssignableValue == copy_assignable::nothrow) {
- this->archetype_state = other.archetype_state;
- return *this;
- }
-};
-
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue,
- move_assignable MoveAssignableValue>
-struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue,
- copy_assignable::maybe>
- : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue> {
- explicit ArchetypeCopyAssign(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue>(
- MakeArchetypeState(), state) {}
-
- ArchetypeCopyAssign() = default;
- ArchetypeCopyAssign(ArchetypeCopyAssign&&) = default;
- ArchetypeCopyAssign(const ArchetypeCopyAssign&) = default;
- ArchetypeCopyAssign& operator=(ArchetypeCopyAssign&&) = default;
- ArchetypeCopyAssign& operator=(const ArchetypeCopyAssign&) = delete;
-};
-
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue,
- move_assignable MoveAssignableValue>
-struct ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue,
- copy_assignable::trivial>
- : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue> {
- explicit ArchetypeCopyAssign(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeMoveAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue>(
- MakeArchetypeState(), state) {}
-
- ArchetypeCopyAssign() = default;
-};
-
-// The destructor base
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue,
- move_assignable MoveAssignableValue,
- copy_assignable CopyAssignableValue, destructible DestructibleValue>
-struct ArchetypeDestructor
- : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue,
- CopyAssignableValue> {
- static_assert(DestructibleValue == destructible::yes ||
- DestructibleValue == destructible::nothrow,
- "");
-
- explicit ArchetypeDestructor(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue,
- CopyAssignableValue>(MakeArchetypeState(), state) {}
-
- ArchetypeDestructor() = default;
- ArchetypeDestructor(ArchetypeDestructor&&) = default;
- ArchetypeDestructor(const ArchetypeDestructor&) = default;
- ArchetypeDestructor& operator=(ArchetypeDestructor&&) = default;
- ArchetypeDestructor& operator=(const ArchetypeDestructor&) = default;
- ~ArchetypeDestructor() noexcept(DestructibleValue == destructible::nothrow) {}
-};
-
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue,
- move_assignable MoveAssignableValue,
- copy_assignable CopyAssignableValue>
-struct ArchetypeDestructor<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue,
- CopyAssignableValue, destructible::trivial>
- : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue,
- CopyAssignableValue> {
- explicit ArchetypeDestructor(MakeArchetypeState,
- ArchetypeState state) noexcept
- : ArchetypeCopyAssign<DefaultConstructibleValue, MoveConstructibleValue,
- CopyConstructibleValue, MoveAssignableValue,
- CopyAssignableValue>(MakeArchetypeState(), state) {}
-
- ArchetypeDestructor() = default;
-};
-
-// An alias to the top of the chain of bases for special-member functions.
-// NOTE: move_constructible::maybe, move_assignable::maybe, and
-// destructible::maybe are handled in the top-level type by way of SFINAE.
-// Because of this, we never instantiate the base classes with
-// move_constructible::maybe, move_assignable::maybe, or destructible::maybe so
-// that we minimize the number of different possible type-template
-// instantiations.
-template <default_constructible DefaultConstructibleValue,
- move_constructible MoveConstructibleValue,
- copy_constructible CopyConstructibleValue,
- move_assignable MoveAssignableValue,
- copy_assignable CopyAssignableValue, destructible DestructibleValue>
-using ArchetypeSpecialMembersBase = ArchetypeDestructor<
- DefaultConstructibleValue,
- MoveConstructibleValue != move_constructible::maybe
- ? MoveConstructibleValue
- : move_constructible::nothrow,
- CopyConstructibleValue,
- MoveAssignableValue != move_assignable::maybe ? MoveAssignableValue
- : move_assignable::nothrow,
- CopyAssignableValue,
- DestructibleValue != destructible::maybe ? DestructibleValue
- : destructible::nothrow>;
-
-// A function that is used to create an archetype with some associated state.
-template <class Arch>
-Arch MakeArchetype(ArchetypeState state) noexcept {
- static_assert(IsArchetype<Arch>::value,
- "The explicit template argument to MakeArchetype is required "
- "to be an Archetype.");
- return Arch(MakeArchetypeState(), state);
-}
-
-// This is used to conditionally delete "copy" and "move" constructors in a way
-// that is consistent with what the ConformanceProfile requires and that also
-// strictly enforces the arguments to the copy/move to not come from implicit
-// conversions when dealing with the Archetype.
-template <class Prof, class T>
-constexpr bool ShouldDeleteConstructor() {
- return !((PropertiesOfT<Prof>::move_constructible_support !=
- move_constructible::maybe &&
- std::is_same<T, Archetype<Prof>>::value) ||
- (PropertiesOfT<Prof>::copy_constructible_support !=
- copy_constructible::maybe &&
- (std::is_same<T, const Archetype<Prof>&>::value ||
- std::is_same<T, Archetype<Prof>&>::value ||
- std::is_same<T, const Archetype<Prof>>::value)));
-}
-
-// This is used to conditionally delete "copy" and "move" assigns in a way
-// that is consistent with what the ConformanceProfile requires and that also
-// strictly enforces the arguments to the copy/move to not come from implicit
-// conversions when dealing with the Archetype.
-template <class Prof, class T>
-constexpr bool ShouldDeleteAssign() {
- return !(
- (PropertiesOfT<Prof>::move_assignable_support != move_assignable::maybe &&
- std::is_same<T, Archetype<Prof>>::value) ||
- (PropertiesOfT<Prof>::copy_assignable_support != copy_assignable::maybe &&
- (std::is_same<T, const Archetype<Prof>&>::value ||
- std::is_same<T, Archetype<Prof>&>::value ||
- std::is_same<T, const Archetype<Prof>>::value)));
-}
-
-// TODO(calabrese) Inherit from a chain of secondary bases to pull in the
-// associated functions of other concepts.
-template <class Prof, class Enabler>
-class Archetype : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support> {
- static_assert(std::is_same<Enabler, void>::value,
- "An explicit type must not be passed as the second template "
- "argument to 'Archetype`.");
-
- // The cases mentioned in these static_asserts are expected to be handled in
- // the partial template specializations of Archetype that follow this
- // definition.
- static_assert(PropertiesOfT<Prof>::destructible_support !=
- destructible::maybe,
- "");
- static_assert(PropertiesOfT<Prof>::move_constructible_support !=
- move_constructible::maybe ||
- PropertiesOfT<Prof>::copy_constructible_support ==
- copy_constructible::maybe,
- "");
- static_assert(PropertiesOfT<Prof>::move_assignable_support !=
- move_assignable::maybe ||
- PropertiesOfT<Prof>::copy_assignable_support ==
- copy_assignable::maybe,
- "");
-
- public:
- Archetype() = default;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
- Archetype(T&&) = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
- Archetype& operator=(T&&) = delete;
-
- using ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
- explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
- state) {}
-
- friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
- PropertiesOfT<Prof>::move_constructible_support !=
- move_constructible::maybe &&
- PropertiesOfT<Prof>::move_assignable_support ==
- move_assignable::maybe &&
- PropertiesOfT<Prof>::destructible_support !=
- destructible::maybe>::type>
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support> {
- public:
- Archetype() = default;
- Archetype(Archetype&&) = default;
- Archetype(const Archetype&) = default;
- Archetype& operator=(Archetype&&) = delete;
- Archetype& operator=(const Archetype&) = default;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
- Archetype(T&&) = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
- Archetype& operator=(T&&) = delete;
-
- using ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
- explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
- state) {}
-
- friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
- PropertiesOfT<Prof>::move_constructible_support ==
- move_constructible::maybe &&
- PropertiesOfT<Prof>::move_assignable_support ==
- move_assignable::maybe &&
- PropertiesOfT<Prof>::destructible_support !=
- destructible::maybe>::type>
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support> {
- public:
- Archetype() = default;
- Archetype(Archetype&&) = delete;
- Archetype(const Archetype&) = default;
- Archetype& operator=(Archetype&&) = delete;
- Archetype& operator=(const Archetype&) = default;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
- Archetype(T&&) = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
- Archetype& operator=(T&&) = delete;
-
- using ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
- explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
- state) {}
-
- friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
- PropertiesOfT<Prof>::move_constructible_support ==
- move_constructible::maybe &&
- PropertiesOfT<Prof>::move_assignable_support !=
- move_assignable::maybe &&
- PropertiesOfT<Prof>::destructible_support !=
- destructible::maybe>::type>
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support> {
- public:
- Archetype() = default;
- Archetype(Archetype&&) = delete;
- Archetype(const Archetype&) = default;
- Archetype& operator=(Archetype&&) = default;
- Archetype& operator=(const Archetype&) = default;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
- Archetype(T&&) = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
- Archetype& operator=(T&&) = delete;
-
- using ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
- explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
- state) {}
-
- friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
- PropertiesOfT<Prof>::move_constructible_support !=
- move_constructible::maybe &&
- PropertiesOfT<Prof>::move_assignable_support ==
- move_assignable::maybe &&
- PropertiesOfT<Prof>::destructible_support ==
- destructible::maybe>::type>
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support> {
- public:
- Archetype() = default;
- Archetype(Archetype&&) = default;
- Archetype(const Archetype&) = default;
- Archetype& operator=(Archetype&&) = delete;
- Archetype& operator=(const Archetype&) = default;
- ~Archetype() = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
- Archetype(T&&) = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
- Archetype& operator=(T&&) = delete;
-
- using ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
- explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
- state) {}
-
- friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
- PropertiesOfT<Prof>::move_constructible_support ==
- move_constructible::maybe &&
- PropertiesOfT<Prof>::move_assignable_support ==
- move_assignable::maybe &&
- PropertiesOfT<Prof>::destructible_support ==
- destructible::maybe>::type>
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support> {
- public:
- Archetype() = default;
- Archetype(Archetype&&) = delete;
- Archetype(const Archetype&) = default;
- Archetype& operator=(Archetype&&) = delete;
- Archetype& operator=(const Archetype&) = default;
- ~Archetype() = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
- Archetype(T&&) = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
- Archetype& operator=(T&&) = delete;
-
- using ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
- explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
- state) {}
-
- friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-template <class Prof>
-class Archetype<Prof, typename std::enable_if<
- PropertiesOfT<Prof>::move_constructible_support ==
- move_constructible::maybe &&
- PropertiesOfT<Prof>::move_assignable_support !=
- move_assignable::maybe &&
- PropertiesOfT<Prof>::destructible_support ==
- destructible::maybe>::type>
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support> {
- public:
- Archetype() = default;
- Archetype(Archetype&&) = delete;
- Archetype(const Archetype&) = default;
- Archetype& operator=(Archetype&&) = default;
- Archetype& operator=(const Archetype&) = default;
- ~Archetype() = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteConstructor<Prof, T>()>::type* = nullptr>
- Archetype(T&&) = delete;
-
- // Disallow moves when requested, and disallow implicit conversions.
- template <class T, typename std::enable_if<
- ShouldDeleteAssign<Prof, T>()>::type* = nullptr>
- Archetype& operator=(T&&) = delete;
-
- using ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>::archetype_state;
-
- private:
- explicit Archetype(MakeArchetypeState, ArchetypeState state) noexcept
- : ArchetypeSpecialMembersBase<
- PropertiesOfT<Prof>::default_constructible_support,
- PropertiesOfT<Prof>::move_constructible_support,
- PropertiesOfT<Prof>::copy_constructible_support,
- PropertiesOfT<Prof>::move_assignable_support,
- PropertiesOfT<Prof>::copy_assignable_support,
- PropertiesOfT<Prof>::destructible_support>(MakeArchetypeState(),
- state) {}
-
- friend Archetype MakeArchetype<Archetype>(ArchetypeState) noexcept;
-};
-
-// Explicitly deleted swap for Archetype if the profile does not require swap.
-// It is important to delete it rather than simply leave it out so that the
-// "using std::swap;" idiom will result in this deleted overload being picked.
-template <class Prof,
- absl::enable_if_t<!PropertiesOfT<Prof>::is_swappable, int> = 0>
-void swap(Archetype<Prof>&, Archetype<Prof>&) = delete; // NOLINT
-
-// A conditionally-noexcept swap implementation for Archetype when the profile
-// supports swap.
-template <class Prof,
- absl::enable_if_t<PropertiesOfT<Prof>::is_swappable, int> = 0>
-void swap(Archetype<Prof>& lhs, Archetype<Prof>& rhs) // NOLINT
- noexcept(PropertiesOfT<Prof>::swappable_support != swappable::yes) {
- std::swap(lhs.archetype_state, rhs.archetype_state);
-}
-
-// A convertible-to-bool type that is used as the return type of comparison
-// operators since the standard doesn't always require exactly bool.
-struct NothrowBool {
- explicit NothrowBool() = delete;
- ~NothrowBool() = default;
-
- // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed
- // elision makes it not required when returning from a function.
- // NothrowBool(NothrowBool const&) = delete;
-
- NothrowBool& operator=(NothrowBool const&) = delete;
-
- explicit operator bool() const noexcept { return value; }
-
- static NothrowBool make(bool const value) noexcept {
- return NothrowBool(value);
- }
-
- private:
- explicit NothrowBool(bool const value) noexcept : value(value) {}
-
- bool value;
-};
-
-// A convertible-to-bool type that is used as the return type of comparison
-// operators since the standard doesn't always require exactly bool.
-// Note: ExceptionalBool has a conversion operator that is not noexcept, so
-// that even when a comparison operator is noexcept, that operation may still
-// potentially throw when converted to bool.
-struct ExceptionalBool {
- explicit ExceptionalBool() = delete;
- ~ExceptionalBool() = default;
-
- // TODO(calabrese) Delete the copy constructor in C++17 mode since guaranteed
- // elision makes it not required when returning from a function.
- // ExceptionalBool(ExceptionalBool const&) = delete;
-
- ExceptionalBool& operator=(ExceptionalBool const&) = delete;
-
- explicit operator bool() const { return value; } // NOLINT
-
- static ExceptionalBool make(bool const value) noexcept {
- return ExceptionalBool(value);
- }
-
- private:
- explicit ExceptionalBool(bool const value) noexcept : value(value) {}
-
- bool value;
-};
-
-// The following macro is only used as a helper in this file to stamp out
-// comparison operator definitions. It is undefined after usage.
-//
-// NOTE: Non-nothrow operators throw via their result's conversion to bool even
-// though the operation itself is noexcept.
-#define ABSL_TYPES_INTERNAL_OP(enum_name, op) \
- template <class Prof> \
- absl::enable_if_t<!PropertiesOfT<Prof>::is_##enum_name, bool> operator op( \
- const Archetype<Prof>&, const Archetype<Prof>&) = delete; \
- \
- template <class Prof> \
- typename absl::enable_if_t< \
- PropertiesOfT<Prof>::is_##enum_name, \
- std::conditional<PropertiesOfT<Prof>::enum_name##_support == \
- enum_name::nothrow, \
- NothrowBool, ExceptionalBool>>::type \
- operator op(const Archetype<Prof>& lhs, \
- const Archetype<Prof>& rhs) noexcept { \
- return absl::conditional_t< \
- PropertiesOfT<Prof>::enum_name##_support == enum_name::nothrow, \
- NothrowBool, ExceptionalBool>::make(lhs.archetype_state op \
- rhs.archetype_state); \
- }
-
-ABSL_TYPES_INTERNAL_OP(equality_comparable, ==);
-ABSL_TYPES_INTERNAL_OP(inequality_comparable, !=);
-ABSL_TYPES_INTERNAL_OP(less_than_comparable, <);
-ABSL_TYPES_INTERNAL_OP(less_equal_comparable, <=);
-ABSL_TYPES_INTERNAL_OP(greater_equal_comparable, >=);
-ABSL_TYPES_INTERNAL_OP(greater_than_comparable, >);
-
-#undef ABSL_TYPES_INTERNAL_OP
-
-// Base class for std::hash specializations when an Archetype doesn't support
-// hashing.
-struct PoisonedHash {
- PoisonedHash() = delete;
- PoisonedHash(const PoisonedHash&) = delete;
- PoisonedHash& operator=(const PoisonedHash&) = delete;
-};
-
-// Base class for std::hash specializations when an Archetype supports hashing.
-template <class Prof>
-struct EnabledHash {
- using argument_type = Archetype<Prof>;
- using result_type = std::size_t;
- result_type operator()(const argument_type& arg) const {
- return std::hash<ArchetypeState>()(arg.archetype_state);
- }
-};
-
-} // namespace types_internal
-ABSL_NAMESPACE_END
-} // namespace absl
-
-namespace std {
-
-template <class Prof> // NOLINT
-struct hash<::absl::types_internal::Archetype<Prof>>
- : conditional<::absl::types_internal::PropertiesOfT<Prof>::is_hashable,
- ::absl::types_internal::EnabledHash<Prof>,
- ::absl::types_internal::PoisonedHash>::type {};
-
-} // namespace std
-
-#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_ARCHETYPE_H_
diff --git a/absl/types/internal/conformance_profile.h b/absl/types/internal/conformance_profile.h
deleted file mode 100644
index 37b017db..00000000
--- a/absl/types/internal/conformance_profile.h
+++ /dev/null
@@ -1,933 +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.
-//
-// -----------------------------------------------------------------------------
-// conformance_profiles.h
-// -----------------------------------------------------------------------------
-//
-// This file contains templates for representing "Regularity Profiles" and
-// concisely-named versions of commonly used Regularity Profiles.
-//
-// A Regularity Profile is a compile-time description of the types of operations
-// that a given type supports, along with properties of those operations when
-// they do exist. For instance, a Regularity Profile may describe a type that
-// has a move-constructor that is noexcept and a copy constructor that is not
-// noexcept. This description can then be examined and passed around to other
-// templates for the purposes of asserting expectations on user-defined types
-// via a series trait checks, or for determining what kinds of run-time tests
-// are able to be performed.
-//
-// Regularity Profiles are also used when creating "archetypes," which are
-// minimum-conforming types that meet all of the requirements of a given
-// Regularity Profile. For more information regarding archetypes, see
-// "conformance_archetypes.h".
-
-#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.
-
-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 {};
-
-template <class T>
-struct PropertiesOfImpl<T, absl::void_t<typename T::properties>> {
- using type = typename T::properties;
-};
-
-template <class T>
-struct PropertiesOfImpl<T, absl::void_t<typename T::profile_alias_of>> {
- using type = typename PropertiesOfImpl<typename T::profile_alias_of>::type;
-};
-
-template <class T>
-struct PropertiesOf : PropertiesOfImpl<T> {};
-
-template <class T>
-using PropertiesOfT = typename PropertiesOf<T>::type;
-
-// NOTE: These enums use this naming convention to be consistent with the
-// standard trait names, which is useful since it allows us to match up each
-// enum name with a corresponding trait name in macro definitions.
-
-// 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;
-}
-
-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));
- }
-}
-
-#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(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("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("support for swap", swappable);
-
-#undef ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM
-
-enum class hashable { maybe, yes };
-
-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,
- move_constructible MoveConstructibleValue = move_constructible::maybe,
- copy_constructible CopyConstructibleValue = copy_constructible::maybe,
- move_assignable MoveAssignableValue = move_assignable::maybe,
- copy_assignable CopyAssignableValue = copy_assignable::maybe,
- destructible DestructibleValue = destructible::maybe,
- equality_comparable EqualityComparableValue = equality_comparable::maybe,
- inequality_comparable InequalityComparableValue =
- inequality_comparable::maybe,
- less_than_comparable LessThanComparableValue = less_than_comparable::maybe,
- less_equal_comparable LessEqualComparableValue =
- less_equal_comparable::maybe,
- greater_equal_comparable GreaterEqualComparableValue =
- greater_equal_comparable::maybe,
- greater_than_comparable GreaterThanComparableValue =
- greater_than_comparable::maybe,
- swappable SwappableValue = swappable::maybe,
- hashable HashableValue = hashable::maybe>
-struct ConformanceProfile {
- using properties = ConformanceProfile;
-
- static constexpr default_constructible
- default_constructible_support = // NOLINT
- DefaultConstructibleValue;
-
- static constexpr move_constructible move_constructible_support = // NOLINT
- MoveConstructibleValue;
-
- static constexpr copy_constructible copy_constructible_support = // NOLINT
- CopyConstructibleValue;
-
- static constexpr move_assignable move_assignable_support = // NOLINT
- MoveAssignableValue;
-
- static constexpr copy_assignable copy_assignable_support = // NOLINT
- CopyAssignableValue;
-
- static constexpr destructible destructible_support = // NOLINT
- DestructibleValue;
-
- static constexpr equality_comparable equality_comparable_support = // NOLINT
- EqualityComparableValue;
-
- static constexpr inequality_comparable
- inequality_comparable_support = // NOLINT
- InequalityComparableValue;
-
- static constexpr less_than_comparable
- less_than_comparable_support = // NOLINT
- LessThanComparableValue;
-
- static constexpr less_equal_comparable
- less_equal_comparable_support = // NOLINT
- LessEqualComparableValue;
-
- static constexpr greater_equal_comparable
- greater_equal_comparable_support = // NOLINT
- GreaterEqualComparableValue;
-
- static constexpr greater_than_comparable
- greater_than_comparable_support = // NOLINT
- GreaterThanComparableValue;
-
- static constexpr swappable swappable_support = SwappableValue; // NOLINT
-
- static constexpr hashable hashable_support = HashableValue; // NOLINT
-
- static constexpr bool is_default_constructible = // NOLINT
- DefaultConstructibleValue != default_constructible::maybe;
-
- static constexpr bool is_move_constructible = // NOLINT
- MoveConstructibleValue != move_constructible::maybe;
-
- static constexpr bool is_copy_constructible = // NOLINT
- CopyConstructibleValue != copy_constructible::maybe;
-
- static constexpr bool is_move_assignable = // NOLINT
- MoveAssignableValue != move_assignable::maybe;
-
- static constexpr bool is_copy_assignable = // NOLINT
- CopyAssignableValue != copy_assignable::maybe;
-
- static constexpr bool is_destructible = // NOLINT
- DestructibleValue != destructible::maybe;
-
- static constexpr bool is_equality_comparable = // NOLINT
- EqualityComparableValue != equality_comparable::maybe;
-
- static constexpr bool is_inequality_comparable = // NOLINT
- InequalityComparableValue != inequality_comparable::maybe;
-
- static constexpr bool is_less_than_comparable = // NOLINT
- LessThanComparableValue != less_than_comparable::maybe;
-
- static constexpr bool is_less_equal_comparable = // NOLINT
- LessEqualComparableValue != less_equal_comparable::maybe;
-
- static constexpr bool is_greater_equal_comparable = // NOLINT
- GreaterEqualComparableValue != greater_equal_comparable::maybe;
-
- static constexpr bool is_greater_than_comparable = // NOLINT
- GreaterThanComparableValue != greater_than_comparable::maybe;
-
- static constexpr bool is_swappable = // NOLINT
- SwappableValue != swappable::maybe;
-
- static constexpr bool is_hashable = // NOLINT
- 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, \
- copy_constructible CopyConstructibleValue, \
- move_assignable MoveAssignableValue, \
- copy_assignable CopyAssignableValue, \
- destructible DestructibleValue, \
- equality_comparable EqualityComparableValue, \
- inequality_comparable InequalityComparableValue, \
- less_than_comparable LessThanComparableValue, \
- less_equal_comparable LessEqualComparableValue, \
- greater_equal_comparable GreaterEqualComparableValue, \
- greater_than_comparable GreaterThanComparableValue, \
- swappable SwappableValue, hashable HashableValue> \
- constexpr type ConformanceProfile< \
- DefaultConstructibleValue, MoveConstructibleValue, \
- CopyConstructibleValue, MoveAssignableValue, CopyAssignableValue, \
- DestructibleValue, EqualityComparableValue, InequalityComparableValue, \
- LessThanComparableValue, LessEqualComparableValue, \
- GreaterEqualComparableValue, GreaterThanComparableValue, SwappableValue, \
- HashableValue>::name
-
-#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(type) \
- ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, \
- type##_support); \
- ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(bool, is_##type)
-
-#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(default_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_constructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_assignable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_assignable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(destructible);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(equality_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(inequality_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_than_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_equal_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_equal_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_than_comparable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(swappable);
-ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable);
-#endif
-
-#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF
-#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL
-
-// 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>
-constexpr H MaxEnum(H head) {
- return head;
-}
-
-template <class H, class N, class... T>
-constexpr H MaxEnum(H head, N next, T... tail) {
- return (UnderlyingValue)(next) < (UnderlyingValue)(head)
- ? (MaxEnum)(head, tail...)
- : (MaxEnum)(next, tail...);
-}
-
-template <class... Profs>
-struct CombineProfilesImpl {
- static constexpr default_constructible
- default_constructible_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::default_constructible_support...);
-
- static constexpr move_constructible move_constructible_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::move_constructible_support...);
-
- static constexpr copy_constructible copy_constructible_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::copy_constructible_support...);
-
- static constexpr move_assignable move_assignable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::move_assignable_support...);
-
- static constexpr copy_assignable copy_assignable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::copy_assignable_support...);
-
- static constexpr destructible destructible_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::destructible_support...);
-
- static constexpr equality_comparable equality_comparable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::equality_comparable_support...);
-
- static constexpr inequality_comparable
- inequality_comparable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::inequality_comparable_support...);
-
- static constexpr less_than_comparable
- less_than_comparable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::less_than_comparable_support...);
-
- static constexpr less_equal_comparable
- less_equal_comparable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...);
-
- static constexpr greater_equal_comparable
- greater_equal_comparable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...);
-
- static constexpr greater_than_comparable
- greater_than_comparable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...);
-
- static constexpr swappable swappable_support = // NOLINT
- (MaxEnum)(PropertiesOfT<Profs>::swappable_support...);
-
- static constexpr hashable hashable_support = // NOLINT
- (MaxEnum)(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>;
-};
-
-// NOTE: We use this as opposed to a direct alias of CombineProfilesImpl so that
-// when named aliases of CombineProfiles are created (such as in
-// conformance_aliases.h), we only pay for the combination algorithm on the
-// profiles that are actually used.
-template <class... Profs>
-struct CombineProfiles {
- using profile_alias_of = CombineProfilesImpl<Profs...>;
-};
-
-template <>
-struct CombineProfiles<> {
- using properties = ConformanceProfile<>;
-};
-
-template <class Profile, class Tag>
-struct StrongProfileTypedef {
- using properties = PropertiesOfT<Profile>;
-};
-
-template <class T, class /*Enabler*/ = void>
-struct IsProfileImpl : std::false_type {};
-
-template <class T>
-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
-
-#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
diff --git a/absl/types/internal/conformance_testing.h b/absl/types/internal/conformance_testing.h
deleted file mode 100644
index 487b0f78..00000000
--- a/absl/types/internal/conformance_testing.h
+++ /dev/null
@@ -1,1386 +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.
-//
-// -----------------------------------------------------------------------------
-// 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/absl/types/internal/conformance_testing_helpers.h b/absl/types/internal/conformance_testing_helpers.h
deleted file mode 100644
index 00775f96..00000000
--- a/absl/types/internal/conformance_testing_helpers.h
+++ /dev/null
@@ -1,391 +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_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/absl/types/internal/conformance_testing_test.cc b/absl/types/internal/conformance_testing_test.cc
deleted file mode 100644
index cf262fa6..00000000
--- a/absl/types/internal/conformance_testing_test.cc
+++ /dev/null
@@ -1,1556 +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/types/internal/conformance_testing.h"
-
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#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 {
-
-namespace ti = absl::types_internal;
-
-template <class T>
-using DefaultConstructibleWithNewImpl = decltype(::new (std::nothrow) T);
-
-template <class T>
-using DefaultConstructibleWithNew =
- absl::type_traits_internal::is_detected<DefaultConstructibleWithNewImpl, T>;
-
-template <class T>
-using MoveConstructibleWithNewImpl =
- decltype(::new (std::nothrow) T(std::declval<T>()));
-
-template <class T>
-using MoveConstructibleWithNew =
- absl::type_traits_internal::is_detected<MoveConstructibleWithNewImpl, T>;
-
-template <class T>
-using CopyConstructibleWithNewImpl =
- decltype(::new (std::nothrow) T(std::declval<const T&>()));
-
-template <class T>
-using CopyConstructibleWithNew =
- absl::type_traits_internal::is_detected<CopyConstructibleWithNewImpl, T>;
-
-template <class T,
- class Result =
- std::integral_constant<bool, noexcept(::new (std::nothrow) T)>>
-using NothrowDefaultConstructibleWithNewImpl =
- typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowDefaultConstructibleWithNew =
- absl::type_traits_internal::is_detected<
- NothrowDefaultConstructibleWithNewImpl, T>;
-
-template <class T,
- class Result = std::integral_constant<
- bool, noexcept(::new (std::nothrow) T(std::declval<T>()))>>
-using NothrowMoveConstructibleWithNewImpl =
- typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowMoveConstructibleWithNew =
- absl::type_traits_internal::is_detected<NothrowMoveConstructibleWithNewImpl,
- T>;
-
-template <class T,
- class Result = std::integral_constant<
- bool, noexcept(::new (std::nothrow) T(std::declval<const T&>()))>>
-using NothrowCopyConstructibleWithNewImpl =
- typename std::enable_if<Result::value>::type;
-
-template <class T>
-using NothrowCopyConstructibleWithNew =
- absl::type_traits_internal::is_detected<NothrowCopyConstructibleWithNewImpl,
- T>;
-
-// NOTE: ?: is used to verify contextually-convertible to bool and not simply
-// implicit or explicit convertibility.
-#define ABSL_INTERNAL_COMPARISON_OP_EXPR(op) \
- ((std::declval<const T&>() op std::declval<const T&>()) ? true : true)
-
-#define ABSL_INTERNAL_COMPARISON_OP_TRAIT(name, op) \
- template <class T> \
- using name##Impl = decltype(ABSL_INTERNAL_COMPARISON_OP_EXPR(op)); \
- \
- template <class T> \
- using name = absl::type_traits_internal::is_detected<name##Impl, T>; \
- \
- template <class T, \
- class Result = std::integral_constant< \
- bool, noexcept(ABSL_INTERNAL_COMPARISON_OP_EXPR(op))>> \
- using Nothrow##name##Impl = typename std::enable_if<Result::value>::type; \
- \
- template <class T> \
- using Nothrow##name = \
- absl::type_traits_internal::is_detected<Nothrow##name##Impl, T>
-
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(EqualityComparable, ==);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(InequalityComparable, !=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessThanComparable, <);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(LessEqualComparable, <=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterEqualComparable, >=);
-ABSL_INTERNAL_COMPARISON_OP_TRAIT(GreaterThanComparable, >);
-
-#undef ABSL_INTERNAL_COMPARISON_OP_TRAIT
-
-template <class T>
-class ProfileTest : public ::testing::Test {};
-
-TYPED_TEST_SUITE_P(ProfileTest);
-
-TYPED_TEST_P(ProfileTest, HasAppropriateConstructionProperties) {
- using profile = typename TypeParam::profile;
- using arch = typename TypeParam::arch;
- using expected_profile = typename TypeParam::expected_profile;
-
- using props = ti::PropertiesOfT<profile>;
- using arch_props = ti::PropertiesOfArchetypeT<arch>;
- using expected_props = ti::PropertiesOfT<expected_profile>;
-
- // Make sure all of the properties are as expected.
- // There are seemingly redundant tests here to make it easier to diagnose
- // the specifics of the failure if something were to go wrong.
- EXPECT_TRUE((std::is_same<props, arch_props>::value));
- EXPECT_TRUE((std::is_same<props, expected_props>::value));
- EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
- EXPECT_EQ(props::default_constructible_support,
- expected_props::default_constructible_support);
-
- EXPECT_EQ(props::move_constructible_support,
- expected_props::move_constructible_support);
-
- EXPECT_EQ(props::copy_constructible_support,
- expected_props::copy_constructible_support);
-
- EXPECT_EQ(props::destructible_support, expected_props::destructible_support);
-
- // Avoid additional error message noise when profile and archetype match with
- // each other but were not what was expected.
- if (!std::is_same<props, arch_props>::value) {
- EXPECT_EQ(arch_props::default_constructible_support,
- expected_props::default_constructible_support);
-
- EXPECT_EQ(arch_props::move_constructible_support,
- expected_props::move_constructible_support);
-
- EXPECT_EQ(arch_props::copy_constructible_support,
- expected_props::copy_constructible_support);
-
- EXPECT_EQ(arch_props::destructible_support,
- expected_props::destructible_support);
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Default constructor checks //
- //////////////////////////////////////////////////////////////////////////////
- EXPECT_EQ(props::default_constructible_support,
- expected_props::default_constructible_support);
-
- switch (expected_props::default_constructible_support) {
- case ti::default_constructible::maybe:
- EXPECT_FALSE(DefaultConstructibleWithNew<arch>::value);
- EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_FALSE(std::is_default_constructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
- }
- break;
- case ti::default_constructible::yes:
- EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
- EXPECT_FALSE(NothrowDefaultConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_default_constructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_default_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
- }
- break;
- case ti::default_constructible::nothrow:
- EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
- EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_default_constructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_default_constructible<arch>::value);
-
- // Constructor traits also check the destructor.
- if (std::is_nothrow_destructible<arch>::value) {
- EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
- }
- }
- break;
- case ti::default_constructible::trivial:
- EXPECT_TRUE(DefaultConstructibleWithNew<arch>::value);
- EXPECT_TRUE(NothrowDefaultConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_default_constructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_default_constructible<arch>::value);
-
- // Constructor triviality traits require trivially destructible types.
- if (absl::is_trivially_destructible<arch>::value) {
- EXPECT_TRUE(absl::is_trivially_default_constructible<arch>::value);
- }
- }
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Move constructor checks //
- //////////////////////////////////////////////////////////////////////////////
- EXPECT_EQ(props::move_constructible_support,
- expected_props::move_constructible_support);
-
- switch (expected_props::move_constructible_support) {
- case ti::move_constructible::maybe:
- EXPECT_FALSE(MoveConstructibleWithNew<arch>::value);
- EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_FALSE(std::is_move_constructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
- }
- break;
- case ti::move_constructible::yes:
- EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
- EXPECT_FALSE(NothrowMoveConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_move_constructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_move_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
- }
- break;
- case ti::move_constructible::nothrow:
- EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
- EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_move_constructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_move_constructible<arch>::value);
-
- // Constructor traits also check the destructor.
- if (std::is_nothrow_destructible<arch>::value) {
- EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
- }
- }
- break;
- case ti::move_constructible::trivial:
- EXPECT_TRUE(MoveConstructibleWithNew<arch>::value);
- EXPECT_TRUE(NothrowMoveConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_move_constructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_move_constructible<arch>::value);
-
- // Constructor triviality traits require trivially destructible types.
- if (absl::is_trivially_destructible<arch>::value) {
- EXPECT_TRUE(absl::is_trivially_move_constructible<arch>::value);
- }
- }
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Copy constructor checks //
- //////////////////////////////////////////////////////////////////////////////
- EXPECT_EQ(props::copy_constructible_support,
- expected_props::copy_constructible_support);
-
- switch (expected_props::copy_constructible_support) {
- case ti::copy_constructible::maybe:
- EXPECT_FALSE(CopyConstructibleWithNew<arch>::value);
- EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_FALSE(std::is_copy_constructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
- }
- break;
- case ti::copy_constructible::yes:
- EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
- EXPECT_FALSE(NothrowCopyConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_copy_constructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_copy_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
- }
- break;
- case ti::copy_constructible::nothrow:
- EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
- EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_copy_constructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_copy_constructible<arch>::value);
-
- // Constructor traits also check the destructor.
- if (std::is_nothrow_destructible<arch>::value) {
- EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
- }
- }
- break;
- case ti::copy_constructible::trivial:
- EXPECT_TRUE(CopyConstructibleWithNew<arch>::value);
- EXPECT_TRUE(NothrowCopyConstructibleWithNew<arch>::value);
-
- // Standard constructible traits depend on the destructor.
- if (std::is_destructible<arch>::value) {
- EXPECT_TRUE(std::is_copy_constructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_copy_constructible<arch>::value);
-
- // Constructor triviality traits require trivially destructible types.
- if (absl::is_trivially_destructible<arch>::value) {
- EXPECT_TRUE(absl::is_trivially_copy_constructible<arch>::value);
- }
- }
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Destructible checks //
- //////////////////////////////////////////////////////////////////////////////
- EXPECT_EQ(props::destructible_support, expected_props::destructible_support);
-
- switch (expected_props::destructible_support) {
- case ti::destructible::maybe:
- EXPECT_FALSE(std::is_destructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_destructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
- break;
- case ti::destructible::yes:
- EXPECT_TRUE(std::is_destructible<arch>::value);
- EXPECT_FALSE(std::is_nothrow_destructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
- break;
- case ti::destructible::nothrow:
- EXPECT_TRUE(std::is_destructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_destructible<arch>::value);
- EXPECT_FALSE(absl::is_trivially_destructible<arch>::value);
- break;
- case ti::destructible::trivial:
- EXPECT_TRUE(std::is_destructible<arch>::value);
- EXPECT_TRUE(std::is_nothrow_destructible<arch>::value);
- EXPECT_TRUE(absl::is_trivially_destructible<arch>::value);
- break;
- }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateAssignmentProperties) {
- using profile = typename TypeParam::profile;
- using arch = typename TypeParam::arch;
- using expected_profile = typename TypeParam::expected_profile;
-
- using props = ti::PropertiesOfT<profile>;
- using arch_props = ti::PropertiesOfArchetypeT<arch>;
- using expected_props = ti::PropertiesOfT<expected_profile>;
-
- // Make sure all of the properties are as expected.
- // There are seemingly redundant tests here to make it easier to diagnose
- // the specifics of the failure if something were to go wrong.
- EXPECT_TRUE((std::is_same<props, arch_props>::value));
- EXPECT_TRUE((std::is_same<props, expected_props>::value));
- EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
- EXPECT_EQ(props::move_assignable_support,
- expected_props::move_assignable_support);
-
- EXPECT_EQ(props::copy_assignable_support,
- expected_props::copy_assignable_support);
-
- // Avoid additional error message noise when profile and archetype match with
- // each other but were not what was expected.
- if (!std::is_same<props, arch_props>::value) {
- EXPECT_EQ(arch_props::move_assignable_support,
- expected_props::move_assignable_support);
-
- EXPECT_EQ(arch_props::copy_assignable_support,
- expected_props::copy_assignable_support);
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Move assignment checks //
- //////////////////////////////////////////////////////////////////////////////
- EXPECT_EQ(props::move_assignable_support,
- expected_props::move_assignable_support);
-
- switch (expected_props::move_assignable_support) {
- case ti::move_assignable::maybe:
- EXPECT_FALSE(std::is_move_assignable<arch>::value);
- EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value);
- EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
- break;
- case ti::move_assignable::yes:
- EXPECT_TRUE(std::is_move_assignable<arch>::value);
- EXPECT_FALSE(std::is_nothrow_move_assignable<arch>::value);
- EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
- break;
- case ti::move_assignable::nothrow:
- EXPECT_TRUE(std::is_move_assignable<arch>::value);
- EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value);
- EXPECT_FALSE(absl::is_trivially_move_assignable<arch>::value);
- break;
- case ti::move_assignable::trivial:
- EXPECT_TRUE(std::is_move_assignable<arch>::value);
- EXPECT_TRUE(std::is_nothrow_move_assignable<arch>::value);
- EXPECT_TRUE(absl::is_trivially_move_assignable<arch>::value);
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Copy assignment checks //
- //////////////////////////////////////////////////////////////////////////////
- EXPECT_EQ(props::copy_assignable_support,
- expected_props::copy_assignable_support);
-
- switch (expected_props::copy_assignable_support) {
- case ti::copy_assignable::maybe:
- EXPECT_FALSE(std::is_copy_assignable<arch>::value);
- EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value);
- EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
- break;
- case ti::copy_assignable::yes:
- EXPECT_TRUE(std::is_copy_assignable<arch>::value);
- EXPECT_FALSE(std::is_nothrow_copy_assignable<arch>::value);
- EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
- break;
- case ti::copy_assignable::nothrow:
- EXPECT_TRUE(std::is_copy_assignable<arch>::value);
- EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value);
- EXPECT_FALSE(absl::is_trivially_copy_assignable<arch>::value);
- break;
- case ti::copy_assignable::trivial:
- EXPECT_TRUE(std::is_copy_assignable<arch>::value);
- EXPECT_TRUE(std::is_nothrow_copy_assignable<arch>::value);
- EXPECT_TRUE(absl::is_trivially_copy_assignable<arch>::value);
- break;
- }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateComparisonProperties) {
- using profile = typename TypeParam::profile;
- using arch = typename TypeParam::arch;
- using expected_profile = typename TypeParam::expected_profile;
-
- using props = ti::PropertiesOfT<profile>;
- using arch_props = ti::PropertiesOfArchetypeT<arch>;
- using expected_props = ti::PropertiesOfT<expected_profile>;
-
- // Make sure all of the properties are as expected.
- // There are seemingly redundant tests here to make it easier to diagnose
- // the specifics of the failure if something were to go wrong.
- EXPECT_TRUE((std::is_same<props, arch_props>::value));
- EXPECT_TRUE((std::is_same<props, expected_props>::value));
- EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
- EXPECT_EQ(props::equality_comparable_support,
- expected_props::equality_comparable_support);
-
- EXPECT_EQ(props::inequality_comparable_support,
- expected_props::inequality_comparable_support);
-
- EXPECT_EQ(props::less_than_comparable_support,
- expected_props::less_than_comparable_support);
-
- EXPECT_EQ(props::less_equal_comparable_support,
- expected_props::less_equal_comparable_support);
-
- EXPECT_EQ(props::greater_equal_comparable_support,
- expected_props::greater_equal_comparable_support);
-
- EXPECT_EQ(props::greater_than_comparable_support,
- expected_props::greater_than_comparable_support);
-
- // Avoid additional error message noise when profile and archetype match with
- // each other but were not what was expected.
- if (!std::is_same<props, arch_props>::value) {
- EXPECT_EQ(arch_props::equality_comparable_support,
- expected_props::equality_comparable_support);
-
- EXPECT_EQ(arch_props::inequality_comparable_support,
- expected_props::inequality_comparable_support);
-
- EXPECT_EQ(arch_props::less_than_comparable_support,
- expected_props::less_than_comparable_support);
-
- EXPECT_EQ(arch_props::less_equal_comparable_support,
- expected_props::less_equal_comparable_support);
-
- EXPECT_EQ(arch_props::greater_equal_comparable_support,
- expected_props::greater_equal_comparable_support);
-
- EXPECT_EQ(arch_props::greater_than_comparable_support,
- expected_props::greater_than_comparable_support);
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Equality comparable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::equality_comparable_support) {
- case ti::equality_comparable::maybe:
- EXPECT_FALSE(EqualityComparable<arch>::value);
- EXPECT_FALSE(NothrowEqualityComparable<arch>::value);
- break;
- case ti::equality_comparable::yes:
- EXPECT_TRUE(EqualityComparable<arch>::value);
- EXPECT_FALSE(NothrowEqualityComparable<arch>::value);
- break;
- case ti::equality_comparable::nothrow:
- EXPECT_TRUE(EqualityComparable<arch>::value);
- EXPECT_TRUE(NothrowEqualityComparable<arch>::value);
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Inequality comparable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::inequality_comparable_support) {
- case ti::inequality_comparable::maybe:
- EXPECT_FALSE(InequalityComparable<arch>::value);
- EXPECT_FALSE(NothrowInequalityComparable<arch>::value);
- break;
- case ti::inequality_comparable::yes:
- EXPECT_TRUE(InequalityComparable<arch>::value);
- EXPECT_FALSE(NothrowInequalityComparable<arch>::value);
- break;
- case ti::inequality_comparable::nothrow:
- EXPECT_TRUE(InequalityComparable<arch>::value);
- EXPECT_TRUE(NothrowInequalityComparable<arch>::value);
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Less than comparable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::less_than_comparable_support) {
- case ti::less_than_comparable::maybe:
- EXPECT_FALSE(LessThanComparable<arch>::value);
- EXPECT_FALSE(NothrowLessThanComparable<arch>::value);
- break;
- case ti::less_than_comparable::yes:
- EXPECT_TRUE(LessThanComparable<arch>::value);
- EXPECT_FALSE(NothrowLessThanComparable<arch>::value);
- break;
- case ti::less_than_comparable::nothrow:
- EXPECT_TRUE(LessThanComparable<arch>::value);
- EXPECT_TRUE(NothrowLessThanComparable<arch>::value);
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Less equal comparable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::less_equal_comparable_support) {
- case ti::less_equal_comparable::maybe:
- EXPECT_FALSE(LessEqualComparable<arch>::value);
- EXPECT_FALSE(NothrowLessEqualComparable<arch>::value);
- break;
- case ti::less_equal_comparable::yes:
- EXPECT_TRUE(LessEqualComparable<arch>::value);
- EXPECT_FALSE(NothrowLessEqualComparable<arch>::value);
- break;
- case ti::less_equal_comparable::nothrow:
- EXPECT_TRUE(LessEqualComparable<arch>::value);
- EXPECT_TRUE(NothrowLessEqualComparable<arch>::value);
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Greater equal comparable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::greater_equal_comparable_support) {
- case ti::greater_equal_comparable::maybe:
- EXPECT_FALSE(GreaterEqualComparable<arch>::value);
- EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value);
- break;
- case ti::greater_equal_comparable::yes:
- EXPECT_TRUE(GreaterEqualComparable<arch>::value);
- EXPECT_FALSE(NothrowGreaterEqualComparable<arch>::value);
- break;
- case ti::greater_equal_comparable::nothrow:
- EXPECT_TRUE(GreaterEqualComparable<arch>::value);
- EXPECT_TRUE(NothrowGreaterEqualComparable<arch>::value);
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Greater than comparable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::greater_than_comparable_support) {
- case ti::greater_than_comparable::maybe:
- EXPECT_FALSE(GreaterThanComparable<arch>::value);
- EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value);
- break;
- case ti::greater_than_comparable::yes:
- EXPECT_TRUE(GreaterThanComparable<arch>::value);
- EXPECT_FALSE(NothrowGreaterThanComparable<arch>::value);
- break;
- case ti::greater_than_comparable::nothrow:
- EXPECT_TRUE(GreaterThanComparable<arch>::value);
- EXPECT_TRUE(NothrowGreaterThanComparable<arch>::value);
- break;
- }
-}
-
-TYPED_TEST_P(ProfileTest, HasAppropriateAuxilliaryProperties) {
- using profile = typename TypeParam::profile;
- using arch = typename TypeParam::arch;
- using expected_profile = typename TypeParam::expected_profile;
-
- using props = ti::PropertiesOfT<profile>;
- using arch_props = ti::PropertiesOfArchetypeT<arch>;
- using expected_props = ti::PropertiesOfT<expected_profile>;
-
- // Make sure all of the properties are as expected.
- // There are seemingly redundant tests here to make it easier to diagnose
- // the specifics of the failure if something were to go wrong.
- EXPECT_TRUE((std::is_same<props, arch_props>::value));
- EXPECT_TRUE((std::is_same<props, expected_props>::value));
- EXPECT_TRUE((std::is_same<arch_props, expected_props>::value));
-
- EXPECT_EQ(props::swappable_support, expected_props::swappable_support);
-
- EXPECT_EQ(props::hashable_support, expected_props::hashable_support);
-
- // Avoid additional error message noise when profile and archetype match with
- // each other but were not what was expected.
- if (!std::is_same<props, arch_props>::value) {
- EXPECT_EQ(arch_props::swappable_support, expected_props::swappable_support);
-
- EXPECT_EQ(arch_props::hashable_support, expected_props::hashable_support);
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Swappable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::swappable_support) {
- case ti::swappable::maybe:
- EXPECT_FALSE(absl::type_traits_internal::IsSwappable<arch>::value);
- EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
- break;
- case ti::swappable::yes:
- EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value);
- EXPECT_FALSE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
- break;
- case ti::swappable::nothrow:
- EXPECT_TRUE(absl::type_traits_internal::IsSwappable<arch>::value);
- EXPECT_TRUE(absl::type_traits_internal::IsNothrowSwappable<arch>::value);
- break;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Hashable checks //
- //////////////////////////////////////////////////////////////////////////////
- switch (expected_props::hashable_support) {
- case ti::hashable::maybe:
-#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
- EXPECT_FALSE(absl::type_traits_internal::IsHashable<arch>::value);
-#endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
- break;
- case ti::hashable::yes:
- EXPECT_TRUE(absl::type_traits_internal::IsHashable<arch>::value);
- break;
- }
-}
-
-REGISTER_TYPED_TEST_SUITE_P(ProfileTest, HasAppropriateConstructionProperties,
- HasAppropriateAssignmentProperties,
- HasAppropriateComparisonProperties,
- HasAppropriateAuxilliaryProperties);
-
-template <class Profile, class Arch, class ExpectedProfile>
-struct ProfileAndExpectation {
- using profile = Profile;
- using arch = Arch;
- using expected_profile = ExpectedProfile;
-};
-
-using CoreProfilesToTest = ::testing::Types<
- // The terminating case of combine (all properties are "maybe").
- ProfileAndExpectation<ti::CombineProfiles<>,
- ti::Archetype<ti::CombineProfiles<>>,
- ti::ConformanceProfile<>>,
-
- // Core default constructor profiles
- ProfileAndExpectation<
- ti::HasDefaultConstructorProfile, ti::HasDefaultConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowDefaultConstructorProfile,
- ti::HasNothrowDefaultConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::nothrow>>,
- ProfileAndExpectation<
- ti::HasTrivialDefaultConstructorProfile,
- ti::HasTrivialDefaultConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::trivial>>,
-
- // Core move constructor profiles
- ProfileAndExpectation<
- ti::HasMoveConstructorProfile, ti::HasMoveConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::maybe,
- ti::move_constructible::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowMoveConstructorProfile,
- ti::HasNothrowMoveConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::maybe,
- ti::move_constructible::nothrow>>,
- ProfileAndExpectation<
- ti::HasTrivialMoveConstructorProfile,
- ti::HasTrivialMoveConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::maybe,
- ti::move_constructible::trivial>>,
-
- // Core copy constructor profiles
- ProfileAndExpectation<
- ti::HasCopyConstructorProfile, ti::HasCopyConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::maybe,
- ti::move_constructible::maybe,
- ti::copy_constructible::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowCopyConstructorProfile,
- ti::HasNothrowCopyConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::maybe,
- ti::move_constructible::maybe,
- ti::copy_constructible::nothrow>>,
- ProfileAndExpectation<
- ti::HasTrivialCopyConstructorProfile,
- ti::HasTrivialCopyConstructorArchetype,
- ti::ConformanceProfile<ti::default_constructible::maybe,
- ti::move_constructible::maybe,
- ti::copy_constructible::trivial>>,
-
- // Core move assignment profiles
- ProfileAndExpectation<
- ti::HasMoveAssignProfile, ti::HasMoveAssignArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowMoveAssignProfile, ti::HasNothrowMoveAssignArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::nothrow>>,
- ProfileAndExpectation<
- ti::HasTrivialMoveAssignProfile, ti::HasTrivialMoveAssignArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::trivial>>,
-
- // Core copy assignment profiles
- ProfileAndExpectation<
- ti::HasCopyAssignProfile, ti::HasCopyAssignArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowCopyAssignProfile, ti::HasNothrowCopyAssignArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::nothrow>>,
- ProfileAndExpectation<
- ti::HasTrivialCopyAssignProfile, ti::HasTrivialCopyAssignArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::trivial>>,
-
- // Core destructor profiles
- ProfileAndExpectation<
- ti::HasDestructorProfile, ti::HasDestructorArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowDestructorProfile, ti::HasNothrowDestructorArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::nothrow>>,
- ProfileAndExpectation<
- ti::HasTrivialDestructorProfile, ti::HasTrivialDestructorArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::trivial>>,
-
- // Core equality comparable profiles
- ProfileAndExpectation<
- ti::HasEqualityProfile, ti::HasEqualityArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowEqualityProfile, ti::HasNothrowEqualityArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::nothrow>>,
-
- // Core inequality comparable profiles
- ProfileAndExpectation<
- ti::HasInequalityProfile, ti::HasInequalityArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowInequalityProfile, ti::HasNothrowInequalityArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe,
- ti::inequality_comparable::nothrow>>,
-
- // Core less than comparable profiles
- ProfileAndExpectation<
- ti::HasLessThanProfile, ti::HasLessThanArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowLessThanProfile, ti::HasNothrowLessThanArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::nothrow>>,
-
- // Core less equal comparable profiles
- ProfileAndExpectation<
- ti::HasLessEqualProfile, ti::HasLessEqualArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowLessEqualProfile, ti::HasNothrowLessEqualArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe,
- ti::less_equal_comparable::nothrow>>,
-
- // Core greater equal comparable profiles
- ProfileAndExpectation<
- ti::HasGreaterEqualProfile, ti::HasGreaterEqualArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowGreaterEqualProfile, ti::HasNothrowGreaterEqualArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::nothrow>>,
-
- // Core greater than comparable profiles
- ProfileAndExpectation<
- ti::HasGreaterThanProfile, ti::HasGreaterThanArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowGreaterThanProfile, ti::HasNothrowGreaterThanArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::nothrow>>,
-
- // Core swappable profiles
- ProfileAndExpectation<
- ti::HasSwapProfile, ti::HasSwapArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::yes>>,
- ProfileAndExpectation<
- ti::HasNothrowSwapProfile, ti::HasNothrowSwapArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
- // Core hashable profiles
- ProfileAndExpectation<
- ti::HasStdHashSpecializationProfile,
- ti::HasStdHashSpecializationArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::maybe,
- ti::hashable::yes>>>;
-
-using CommonProfilesToTest = ::testing::Types<
- // NothrowMoveConstructible
- ProfileAndExpectation<
- ti::NothrowMoveConstructibleProfile,
- ti::NothrowMoveConstructibleArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
- // CopyConstructible
- ProfileAndExpectation<
- ti::CopyConstructibleProfile, ti::CopyConstructibleArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::yes, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
- // NothrowMovable
- ProfileAndExpectation<
- ti::NothrowMovableProfile, ti::NothrowMovableArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::maybe, ti::move_assignable::nothrow,
- ti::copy_assignable::maybe, ti::destructible::nothrow,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
- // Value
- ProfileAndExpectation<
- ti::ValueProfile, ti::ValueArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::yes, ti::move_assignable::nothrow,
- ti::copy_assignable::yes, ti::destructible::nothrow,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
- ////////////////////////////////////////////////////////////////////////////
- // Common but also DefaultConstructible //
- ////////////////////////////////////////////////////////////////////////////
-
- // DefaultConstructibleNothrowMoveConstructible
- ProfileAndExpectation<
- ti::DefaultConstructibleNothrowMoveConstructibleProfile,
- ti::DefaultConstructibleNothrowMoveConstructibleArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::yes, ti::move_constructible::nothrow,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
- // DefaultConstructibleCopyConstructible
- ProfileAndExpectation<
- ti::DefaultConstructibleCopyConstructibleProfile,
- ti::DefaultConstructibleCopyConstructibleArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::yes, ti::move_constructible::nothrow,
- ti::copy_constructible::yes, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::nothrow>>,
-
- // DefaultConstructibleNothrowMovable
- ProfileAndExpectation<
- ti::DefaultConstructibleNothrowMovableProfile,
- ti::DefaultConstructibleNothrowMovableArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::yes, ti::move_constructible::nothrow,
- ti::copy_constructible::maybe, ti::move_assignable::nothrow,
- ti::copy_assignable::maybe, ti::destructible::nothrow,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
- // DefaultConstructibleValue
- ProfileAndExpectation<
- ti::DefaultConstructibleValueProfile,
- ti::DefaultConstructibleValueArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::yes, ti::move_constructible::nothrow,
- ti::copy_constructible::yes, ti::move_assignable::nothrow,
- ti::copy_assignable::yes, ti::destructible::nothrow,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::nothrow>>>;
-
-using ComparableHelpersProfilesToTest = ::testing::Types<
- // Equatable
- ProfileAndExpectation<
- ti::EquatableProfile, ti::EquatableArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::yes, ti::inequality_comparable::yes>>,
-
- // Comparable
- ProfileAndExpectation<
- ti::ComparableProfile, ti::ComparableArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::yes, ti::inequality_comparable::yes,
- ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
- ti::greater_equal_comparable::yes,
- ti::greater_than_comparable::yes>>,
-
- // NothrowEquatable
- ProfileAndExpectation<
- ti::NothrowEquatableProfile, ti::NothrowEquatableArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::nothrow,
- ti::inequality_comparable::nothrow>>,
-
- // NothrowComparable
- ProfileAndExpectation<
- ti::NothrowComparableProfile, ti::NothrowComparableArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::maybe,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::maybe,
- ti::equality_comparable::nothrow,
- ti::inequality_comparable::nothrow,
- ti::less_than_comparable::nothrow,
- ti::less_equal_comparable::nothrow,
- ti::greater_equal_comparable::nothrow,
- ti::greater_than_comparable::nothrow>>>;
-
-using CommonComparableProfilesToTest = ::testing::Types<
- // ComparableNothrowMoveConstructible
- ProfileAndExpectation<
- ti::ComparableNothrowMoveConstructibleProfile,
- ti::ComparableNothrowMoveConstructibleArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::maybe, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::nothrow,
- ti::equality_comparable::yes, ti::inequality_comparable::yes,
- ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
- ti::greater_equal_comparable::yes,
- ti::greater_than_comparable::yes>>,
-
- // ComparableCopyConstructible
- ProfileAndExpectation<
- ti::ComparableCopyConstructibleProfile,
- ti::ComparableCopyConstructibleArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::yes, ti::move_assignable::maybe,
- ti::copy_assignable::maybe, ti::destructible::nothrow,
- ti::equality_comparable::yes, ti::inequality_comparable::yes,
- ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
- ti::greater_equal_comparable::yes,
- ti::greater_than_comparable::yes>>,
-
- // ComparableNothrowMovable
- ProfileAndExpectation<
- ti::ComparableNothrowMovableProfile,
- ti::ComparableNothrowMovableArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::maybe, ti::move_assignable::nothrow,
- ti::copy_assignable::maybe, ti::destructible::nothrow,
- ti::equality_comparable::yes, ti::inequality_comparable::yes,
- ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
- ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
- ti::swappable::nothrow>>,
-
- // ComparableValue
- ProfileAndExpectation<
- ti::ComparableValueProfile, ti::ComparableValueArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::maybe, ti::move_constructible::nothrow,
- ti::copy_constructible::yes, ti::move_assignable::nothrow,
- ti::copy_assignable::yes, ti::destructible::nothrow,
- ti::equality_comparable::yes, ti::inequality_comparable::yes,
- ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
- ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
- ti::swappable::nothrow>>>;
-
-using TrivialProfilesToTest = ::testing::Types<
- ProfileAndExpectation<
- ti::TrivialSpecialMemberFunctionsProfile,
- ti::TrivialSpecialMemberFunctionsArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::trivial, ti::move_constructible::trivial,
- ti::copy_constructible::trivial, ti::move_assignable::trivial,
- ti::copy_assignable::trivial, ti::destructible::trivial,
- ti::equality_comparable::maybe, ti::inequality_comparable::maybe,
- ti::less_than_comparable::maybe, ti::less_equal_comparable::maybe,
- ti::greater_equal_comparable::maybe,
- ti::greater_than_comparable::maybe, ti::swappable::nothrow>>,
-
- ProfileAndExpectation<
- ti::TriviallyCompleteProfile, ti::TriviallyCompleteArchetype,
- ti::ConformanceProfile<
- ti::default_constructible::trivial, ti::move_constructible::trivial,
- ti::copy_constructible::trivial, ti::move_assignable::trivial,
- ti::copy_assignable::trivial, ti::destructible::trivial,
- ti::equality_comparable::yes, ti::inequality_comparable::yes,
- ti::less_than_comparable::yes, ti::less_equal_comparable::yes,
- ti::greater_equal_comparable::yes, ti::greater_than_comparable::yes,
- ti::swappable::nothrow, ti::hashable::yes>>>;
-
-INSTANTIATE_TYPED_TEST_SUITE_P(Core, ProfileTest, CoreProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(Common, ProfileTest, CommonProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(ComparableHelpers, ProfileTest,
- ComparableHelpersProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(CommonComparable, ProfileTest,
- CommonComparableProfilesToTest);
-INSTANTIATE_TYPED_TEST_SUITE_P(Trivial, ProfileTest, TrivialProfilesToTest);
-
-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/absl/types/internal/parentheses.h b/absl/types/internal/parentheses.h
deleted file mode 100644
index 5aebee8f..00000000
--- a/absl/types/internal/parentheses.h
+++ /dev/null
@@ -1,34 +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.
-//
-// -----------------------------------------------------------------------------
-// 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/absl/types/internal/transform_args.h b/absl/types/internal/transform_args.h
deleted file mode 100644
index 4a0ab42a..00000000
--- a/absl/types/internal/transform_args.h
+++ /dev/null
@@ -1,246 +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.
-//
-// -----------------------------------------------------------------------------
-// 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/absl/types/internal/variant.h b/absl/types/internal/variant.h
index fc8829e5..263d7b09 100644
--- a/absl/types/internal/variant.h
+++ b/absl/types/internal/variant.h
@@ -1047,11 +1047,11 @@ class VariantStateBase {
std::size_t index_;
};
-using absl::internal::identity;
+using absl::internal::type_identity;
// OverloadSet::Overload() is a unary function which is overloaded to
// take any of the element types of the variant, by reference-to-const.
-// The return type of the overload on T is identity<T>, so that you
+// The return type of the overload on T is type_identity<T>, so that you
// can statically determine which overload was called.
//
// Overload() is not defined, so it can only be called in unevaluated
@@ -1062,7 +1062,7 @@ struct OverloadSet;
template <typename T, typename... Ts>
struct OverloadSet<T, Ts...> : OverloadSet<Ts...> {
using Base = OverloadSet<Ts...>;
- static identity<T> Overload(const T&);
+ static type_identity<T> Overload(const T&);
using Base::Overload;
};
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 0a8080dc..395fe62f 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -61,6 +61,7 @@ ABSL_NAMESPACE_END
#include <utility>
#include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
#include "absl/base/internal/inline_variable.h"
#include "absl/meta/type_traits.h"
#include "absl/types/bad_optional_access.h"
@@ -415,11 +416,11 @@ class optional : private optional_internal::optional_data<T>,
// `optional` is empty, behavior is undefined.
//
// If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
- const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
+ absl::Nonnull<const T*> operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(this->engaged_);
return std::addressof(this->data_);
}
- T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND {
+ absl::Nonnull<T*> operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND {
ABSL_HARDENING_ASSERT(this->engaged_);
return std::addressof(this->data_);
}
diff --git a/absl/types/span.h b/absl/types/span.h
index 70ed8eb6..88cd7595 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -63,6 +63,7 @@
#include "absl/base/attributes.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
+#include "absl/base/nullability.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h" // TODO(strel): remove this include
#include "absl/meta/type_traits.h"
@@ -172,6 +173,8 @@ class Span {
public:
using element_type = T;
using value_type = absl::remove_cv_t<T>;
+ // TODO(b/316099902) - pointer should be Nullable<T*>, but this makes it hard
+ // to recognize foreach loops as safe.
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
@@ -679,12 +682,12 @@ bool operator>=(Span<T> a, const U& b) {
// }
//
template <int&... ExplicitArgumentBarrier, typename T>
-constexpr Span<T> MakeSpan(T* ptr, size_t size) noexcept {
+constexpr Span<T> MakeSpan(absl::Nullable<T*> ptr, size_t size) noexcept {
return Span<T>(ptr, size);
}
template <int&... ExplicitArgumentBarrier, typename T>
-Span<T> MakeSpan(T* begin, T* end) noexcept {
+Span<T> MakeSpan(absl::Nullable<T*> begin, absl::Nullable<T*> end) noexcept {
return ABSL_HARDENING_ASSERT(begin <= end),
Span<T>(begin, static_cast<size_t>(end - begin));
}
@@ -725,12 +728,14 @@ constexpr Span<T> MakeSpan(T (&array)[N]) noexcept {
// ProcessInts(absl::MakeConstSpan(std::vector<int>{ 0, 0, 0 }));
//
template <int&... ExplicitArgumentBarrier, typename T>
-constexpr Span<const T> MakeConstSpan(T* ptr, size_t size) noexcept {
+constexpr Span<const T> MakeConstSpan(absl::Nullable<T*> ptr,
+ size_t size) noexcept {
return Span<const T>(ptr, size);
}
template <int&... ExplicitArgumentBarrier, typename T>
-Span<const T> MakeConstSpan(T* begin, T* end) noexcept {
+Span<const T> MakeConstSpan(absl::Nullable<T*> begin,
+ absl::Nullable<T*> end) noexcept {
return ABSL_HARDENING_ASSERT(begin <= end), Span<const T>(begin, end - begin);
}
diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel
index 061f4c5b..1c01fc12 100644
--- a/absl/utility/BUILD.bazel
+++ b/absl/utility/BUILD.bazel
@@ -21,7 +21,14 @@ load(
"ABSL_TEST_COPTS",
)
-package(default_visibility = ["//visibility:public"])
+package(
+ default_visibility = ["//visibility:public"],
+ features = [
+ "header_modules",
+ "layering_check",
+ "parse_headers",
+ ],
+)
licenses(["notice"])
@@ -49,6 +56,7 @@ cc_test(
"//absl/base:core_headers",
"//absl/memory",
"//absl/strings",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
@@ -72,6 +80,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":if_constexpr",
+ "@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/utility/utility.h b/absl/utility/utility.h
index bf923220..fc0d1f65 100644
--- a/absl/utility/utility.h
+++ b/absl/utility/utility.h
@@ -12,17 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
-// This header file contains C++11 versions of standard <utility> header
-// abstractions available within C++14 and C++17, and are designed to be drop-in
+// This header file contains C++14 versions of standard <utility> header
+// abstractions available within C++17, and are designed to be drop-in
// replacement for code compliant with C++14 and C++17.
//
// The following abstractions are defined:
//
-// * integer_sequence<T, Ints...> == std::integer_sequence<T, Ints...>
-// * index_sequence<Ints...> == std::index_sequence<Ints...>
-// * make_integer_sequence<T, N> == std::make_integer_sequence<T, N>
-// * make_index_sequence<N> == std::make_index_sequence<N>
-// * index_sequence_for<Ts...> == std::index_sequence_for<Ts...>
// * apply<Functor, Tuple> == std::apply<Functor, Tuple>
// * exchange<T> == std::exchange<T>
// * make_from_tuple<T> == std::make_from_tuple<T>
@@ -33,7 +28,6 @@
//
// References:
//
-// https://en.cppreference.com/w/cpp/utility/integer_sequence
// https://en.cppreference.com/w/cpp/utility/apply
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
@@ -53,68 +47,18 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
-// integer_sequence
-//
-// Class template representing a compile-time integer sequence. An instantiation
-// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
-// type through its template arguments (which is a common need when
-// working with C++11 variadic templates). `absl::integer_sequence` is designed
-// to be a drop-in replacement for C++14's `std::integer_sequence`.
-//
-// Example:
-//
-// template< class T, T... Ints >
-// void user_function(integer_sequence<T, Ints...>);
-//
-// int main()
-// {
-// // user_function's `T` will be deduced to `int` and `Ints...`
-// // will be deduced to `0, 1, 2, 3, 4`.
-// user_function(make_integer_sequence<int, 5>());
-// }
-template <typename T, T... Ints>
-struct integer_sequence {
- using value_type = T;
- static constexpr size_t size() noexcept { return sizeof...(Ints); }
-};
-
-// index_sequence
-//
-// A helper template for an `integer_sequence` of `size_t`,
-// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
-// `std::index_sequence`.
-template <size_t... Ints>
-using index_sequence = integer_sequence<size_t, Ints...>;
+// Historical note: Abseil once provided implementations of these
+// abstractions for platforms that had not yet provided them. Those
+// platforms are no longer supported. New code should simply use the
+// the ones from std directly.
+using std::index_sequence;
+using std::index_sequence_for;
+using std::integer_sequence;
+using std::make_index_sequence;
+using std::make_integer_sequence;
namespace utility_internal {
-template <typename Seq, size_t SeqSize, size_t Rem>
-struct Extend;
-
-// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
-template <typename T, T... Ints, size_t SeqSize>
-struct Extend<integer_sequence<T, Ints...>, SeqSize, 0> {
- using type = integer_sequence<T, Ints..., (Ints + SeqSize)...>;
-};
-
-template <typename T, T... Ints, size_t SeqSize>
-struct Extend<integer_sequence<T, Ints...>, SeqSize, 1> {
- using type = integer_sequence<T, Ints..., (Ints + SeqSize)..., 2 * SeqSize>;
-};
-
-// Recursion helper for 'make_integer_sequence<T, N>'.
-// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
-template <typename T, size_t N>
-struct Gen {
- using type =
- typename Extend<typename Gen<T, N / 2>::type, N / 2, N % 2>::type;
-};
-
-template <typename T>
-struct Gen<T, 0> {
- using type = integer_sequence<T>;
-};
-
template <typename T>
struct InPlaceTypeTag {
explicit InPlaceTypeTag() = delete;
@@ -131,32 +75,6 @@ struct InPlaceIndexTag {
} // namespace utility_internal
-// Compile-time sequences of integers
-
-// make_integer_sequence
-//
-// This template alias is equivalent to
-// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
-// replacement for C++14's `std::make_integer_sequence`.
-template <typename T, T N>
-using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
-
-// make_index_sequence
-//
-// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
-// and is designed to be a drop-in replacement for C++14's
-// `std::make_index_sequence`.
-template <size_t N>
-using make_index_sequence = make_integer_sequence<size_t, N>;
-
-// index_sequence_for
-//
-// Converts a typename pack into an index sequence of the same length, and
-// is designed to be a drop-in replacement for C++14's
-// `std::index_sequence_for()`
-template <typename... Ts>
-using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
-
// Tag types
#ifdef ABSL_USES_STD_OPTIONAL
diff --git a/absl/utility/utility_test.cc b/absl/utility/utility_test.cc
index 2f0509aa..1af68136 100644
--- a/absl/utility/utility_test.cc
+++ b/absl/utility/utility_test.cc
@@ -30,139 +30,10 @@
namespace {
-#ifdef _MSC_VER
-// Warnings for unused variables in this test are false positives. On other
-// platforms, they are suppressed by ABSL_ATTRIBUTE_UNUSED, but that doesn't
-// work on MSVC.
-// Both the unused variables and the name length warnings are due to calls
-// to absl::make_index_sequence with very large values, creating very long type
-// names. The resulting warnings are so long they make build output unreadable.
-#pragma warning(push)
-#pragma warning(disable : 4503) // decorated name length exceeded
-#pragma warning(disable : 4101) // unreferenced local variable
-#endif // _MSC_VER
-
using ::testing::ElementsAre;
using ::testing::Pointee;
using ::testing::StaticAssertTypeEq;
-TEST(IntegerSequenceTest, ValueType) {
- StaticAssertTypeEq<int, absl::integer_sequence<int>::value_type>();
- StaticAssertTypeEq<char, absl::integer_sequence<char>::value_type>();
-}
-
-TEST(IntegerSequenceTest, Size) {
- EXPECT_EQ(0, (absl::integer_sequence<int>::size()));
- EXPECT_EQ(1, (absl::integer_sequence<int, 0>::size()));
- EXPECT_EQ(1, (absl::integer_sequence<int, 1>::size()));
- EXPECT_EQ(2, (absl::integer_sequence<int, 1, 2>::size()));
- EXPECT_EQ(3, (absl::integer_sequence<int, 0, 1, 2>::size()));
- EXPECT_EQ(3, (absl::integer_sequence<int, -123, 123, 456>::size()));
- constexpr size_t sz = absl::integer_sequence<int, 0, 1>::size();
- EXPECT_EQ(2, sz);
-}
-
-TEST(IntegerSequenceTest, MakeIndexSequence) {
- StaticAssertTypeEq<absl::index_sequence<>, absl::make_index_sequence<0>>();
- StaticAssertTypeEq<absl::index_sequence<0>, absl::make_index_sequence<1>>();
- StaticAssertTypeEq<absl::index_sequence<0, 1>,
- absl::make_index_sequence<2>>();
- StaticAssertTypeEq<absl::index_sequence<0, 1, 2>,
- absl::make_index_sequence<3>>();
-}
-
-TEST(IntegerSequenceTest, MakeIntegerSequence) {
- StaticAssertTypeEq<absl::integer_sequence<int>,
- absl::make_integer_sequence<int, 0>>();
- StaticAssertTypeEq<absl::integer_sequence<int, 0>,
- absl::make_integer_sequence<int, 1>>();
- StaticAssertTypeEq<absl::integer_sequence<int, 0, 1>,
- absl::make_integer_sequence<int, 2>>();
- StaticAssertTypeEq<absl::integer_sequence<int, 0, 1, 2>,
- absl::make_integer_sequence<int, 3>>();
-}
-
-template <typename... Ts>
-class Counter {};
-
-template <size_t... Is>
-void CountAll(absl::index_sequence<Is...>) {
- // We only need an alias here, but instantiate a variable to silence warnings
- // for unused typedefs in some compilers.
- ABSL_ATTRIBUTE_UNUSED Counter<absl::make_index_sequence<Is>...> seq;
-}
-
-// This test verifies that absl::make_index_sequence can handle large arguments
-// without blowing up template instantiation stack, going OOM or taking forever
-// to compile (there is hard 15 minutes limit imposed by forge).
-TEST(IntegerSequenceTest, MakeIndexSequencePerformance) {
- // O(log N) template instantiations.
- // We only need an alias here, but instantiate a variable to silence warnings
- // for unused typedefs in some compilers.
- ABSL_ATTRIBUTE_UNUSED absl::make_index_sequence<(1 << 16) - 1> seq;
- // O(N) template instantiations.
- CountAll(absl::make_index_sequence<(1 << 8) - 1>());
-}
-
-template <typename F, typename Tup, size_t... Is>
-auto ApplyFromTupleImpl(F f, const Tup& tup, absl::index_sequence<Is...>)
- -> decltype(f(std::get<Is>(tup)...)) {
- return f(std::get<Is>(tup)...);
-}
-
-template <typename Tup>
-using TupIdxSeq = absl::make_index_sequence<std::tuple_size<Tup>::value>;
-
-template <typename F, typename Tup>
-auto ApplyFromTuple(F f, const Tup& tup)
- -> decltype(ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{})) {
- return ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{});
-}
-
-template <typename T>
-std::string Fmt(const T& x) {
- std::ostringstream os;
- os << x;
- return os.str();
-}
-
-struct PoorStrCat {
- template <typename... Args>
- std::string operator()(const Args&... args) const {
- std::string r;
- for (const auto& e : {Fmt(args)...}) r += e;
- return r;
- }
-};
-
-template <typename Tup, size_t... Is>
-std::vector<std::string> TupStringVecImpl(const Tup& tup,
- absl::index_sequence<Is...>) {
- return {Fmt(std::get<Is>(tup))...};
-}
-
-template <typename... Ts>
-std::vector<std::string> TupStringVec(const std::tuple<Ts...>& tup) {
- return TupStringVecImpl(tup, absl::index_sequence_for<Ts...>());
-}
-
-TEST(MakeIndexSequenceTest, ApplyFromTupleExample) {
- PoorStrCat f{};
- EXPECT_EQ("12abc3.14", f(12, "abc", 3.14));
- EXPECT_EQ("12abc3.14", ApplyFromTuple(f, std::make_tuple(12, "abc", 3.14)));
-}
-
-TEST(IndexSequenceForTest, Basic) {
- StaticAssertTypeEq<absl::index_sequence<>, absl::index_sequence_for<>>();
- StaticAssertTypeEq<absl::index_sequence<0>, absl::index_sequence_for<int>>();
- StaticAssertTypeEq<absl::index_sequence<0, 1, 2, 3>,
- absl::index_sequence_for<int, void, char, int>>();
-}
-
-TEST(IndexSequenceForTest, Example) {
- EXPECT_THAT(TupStringVec(std::make_tuple(12, "abc", 3.14)),
- ElementsAre("12", "abc", "3.14"));
-}
int Function(int a, int b) { return a - b; }
diff --git a/ci/absl_alternate_options.h b/ci/absl_alternate_options.h
index 82d2ecf8..a5638591 100644
--- a/ci/absl_alternate_options.h
+++ b/ci/absl_alternate_options.h
@@ -22,6 +22,7 @@
#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_STD_ORDERING 0
#define ABSL_OPTION_USE_INLINE_NAMESPACE 1
#define ABSL_OPTION_INLINE_NAMESPACE_NAME ns
#define ABSL_OPTION_HARDENED 1
diff --git a/ci/cmake_common.sh b/ci/cmake_common.sh
index 051b70dc..784b3815 100644
--- a/ci/cmake_common.sh
+++ b/ci/cmake_common.sh
@@ -14,7 +14,11 @@
# 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="f8d7d77c06936315286eb55f8de22cd23c188571" # v1.14.0
+# TODO(dmauro): After the next GoogleTest release, use the stable file required
+# by Bzlmod. This means downloading a copy of the file and reuploading it to
+# avoid changing checksums if the compression is changed by GitHub. It also
+# means stop referring to it as a commit and instead use the uploaded filename.
+readonly ABSL_GOOGLETEST_COMMIT="f8d7d77c06936315286eb55f8de22cd23c188571"
# 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
diff --git a/ci/linux_arm_clang-latest_libcxx_bazel.sh b/ci/linux_arm_clang-latest_libcxx_bazel.sh
new file mode 100755
index 00000000..13f4ad13
--- /dev/null
+++ b/ci/linux_arm_clang-latest_libcxx_bazel.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+#
+# 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.
+
+# This script that can be invoked to test abseil-cpp in a hermetic environment
+# using a Docker image on Linux. You must have Docker installed to use this
+# script.
+
+set -euox pipefail
+
+if [[ -z ${ABSEIL_ROOT:-} ]]; then
+ ABSEIL_ROOT="$(realpath $(dirname ${0})/..)"
+fi
+
+if [[ -z ${STD:-} ]]; then
+ STD="c++14 c++17 c++20"
+fi
+
+if [[ -z ${COMPILATION_MODE:-} ]]; then
+ COMPILATION_MODE="fastbuild opt"
+fi
+
+if [[ -z ${EXCEPTIONS_MODE:-} ]]; then
+ EXCEPTIONS_MODE="-fno-exceptions -fexceptions"
+fi
+
+source "${ABSEIL_ROOT}/ci/linux_docker_containers.sh"
+readonly DOCKER_CONTAINER=${LINUX_ARM_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="--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
+ # the cache key. Hashing the key is to make it shorter and url-safe.
+ container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
+ BAZEL_EXTRA_ARGS="--remote_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 \
+ --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_CXXOPTS="-std=${std}:-nostdinc++" \
+ -e BAZEL_LINKOPTS="-L/opt/llvm/clang/lib/aarch64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/clang/lib/aarch64-unknown-linux-gnu" \
+ -e CPLUS_INCLUDE_PATH="/opt/llvm/clang/include/aarch64-unknown-linux-gnu/c++/v1:/opt/llvm/clang/include/c++/v1" \
+ ${DOCKER_EXTRA_ARGS:-} \
+ ${DOCKER_CONTAINER} \
+ /bin/sh -c "
+ cp -r /abseil-cpp-ro/* /abseil-cpp/
+ if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
+ cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
+ fi
+ /usr/local/bin/bazel test ... \
+ --compilation_mode=\"${compilation_mode}\" \
+ --copt=\"${exceptions_mode}\" \
+ --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
+ --copt=-Werror \
+ --define=\"absl=1\" \
+ --enable_bzlmod=true \
+ --features=external_include_paths \
+ --keep_going \
+ --show_timestamps \
+ --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
+ --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
+ --test_output=errors \
+ --test_tag_filters=-benchmark \
+ ${BAZEL_EXTRA_ARGS:-}"
+ done
+ done
+done
diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh
index f9c146b0..3153fae4 100755
--- a/ci/linux_clang-latest_libcxx_asan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh
@@ -48,7 +48,7 @@ if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
# remote_http_cache url, we make changes to the container part of
# the cache key. Hashing the key is to make it shorter and url-safe.
container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
- 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:-}"
+ BAZEL_EXTRA_ARGS="--remote_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
@@ -70,8 +70,8 @@ for std in ${STD}; do
--rm \
-e CC="/opt/llvm/clang/bin/clang" \
-e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
- -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu" \
- -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx/include/c++/v1" \
+ -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" \
${DOCKER_EXTRA_ARGS:-} \
${DOCKER_CONTAINER} \
/usr/local/bin/bazel test ... \
@@ -84,7 +84,7 @@ for std in ${STD}; do
--copt="-fsanitize=undefined" \
--copt="-fno-sanitize-blacklist" \
--copt=-Werror \
- --distdir="/bazel-distdir" \
+ --enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--linkopt="-fsanitize=address" \
diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh
index 38b2d744..4f3eba49 100755
--- a/ci/linux_clang-latest_libcxx_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_bazel.sh
@@ -48,7 +48,7 @@ if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
# remote_http_cache url, we make changes to the container part of
# the cache key. Hashing the key is to make it shorter and url-safe.
container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
- 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:-}"
+ BAZEL_EXTRA_ARGS="--remote_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
@@ -71,8 +71,8 @@ for std in ${STD}; do
--rm \
-e CC="/opt/llvm/clang/bin/clang" \
-e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
- -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib/x86_64-unknown-linux-gnu" \
- -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx/include/c++/v1" \
+ -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" \
${DOCKER_EXTRA_ARGS:-} \
${DOCKER_CONTAINER} \
/bin/sh -c "
@@ -86,7 +86,7 @@ for std in ${STD}; do
--copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
--copt=-Werror \
--define=\"absl=1\" \
- --distdir=\"/bazel-distdir\" \
+ --enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--show_timestamps \
diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
index 34d7940e..06f4c2ec 100755
--- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh
+++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh
@@ -48,7 +48,7 @@ if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
# remote_http_cache url, we make changes to the container part of
# the cache key. Hashing the key is to make it shorter and url-safe.
container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
- 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:-}"
+ BAZEL_EXTRA_ARGS="--remote_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
@@ -70,8 +70,8 @@ for std in ${STD}; do
--rm \
-e CC="/opt/llvm/clang/bin/clang" \
-e BAZEL_CXXOPTS="-std=${std}:-nostdinc++" \
- -e BAZEL_LINKOPTS="-L/opt/llvm/libcxx-tsan/lib/x86_64-unknown-linux-gnu:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib/x86_64-unknown-linux-gnu" \
- -e CPLUS_INCLUDE_PATH="/opt/llvm/libcxx-tsan/include/x86_64-unknown-linux-gnu/c++/v1:/opt/llvm/libcxx-tsan/include/c++/v1" \
+ -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" \
${DOCKER_EXTRA_ARGS:-} \
${DOCKER_CONTAINER} \
/usr/local/bin/bazel test ... \
@@ -82,7 +82,7 @@ for std in ${STD}; do
--copt="-fsanitize=thread" \
--copt="-fno-sanitize-blacklist" \
--copt=-Werror \
- --distdir="/bazel-distdir" \
+ --enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--linkopt="-fsanitize=thread" \
diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh
index 13d56fc9..d499e13c 100755
--- a/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ b/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -48,7 +48,7 @@ if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
# remote_http_cache url, we make changes to the container part of
# the cache key. Hashing the key is to make it shorter and url-safe.
container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
- 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:-}"
+ BAZEL_EXTRA_ARGS="--remote_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
@@ -80,7 +80,7 @@ for std in ${STD}; do
--copt="-march=haswell" \
--copt=-Werror \
--define="absl=1" \
- --distdir="/bazel-distdir" \
+ --enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--linkopt="--gcc-toolchain=/usr/local" \
diff --git a/ci/linux_docker_containers.sh b/ci/linux_docker_containers.sh
index a07c64ce..232233d1 100644
--- a/ci/linux_docker_containers.sh
+++ b/ci/linux_docker_containers.sh
@@ -16,6 +16,7 @@
# Test scripts should source this file to get the identifiers.
readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20230612"
-readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20230217"
-readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20230517"
+readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20231218"
+readonly LINUX_ARM_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_arm_hybrid-latest:20231219"
+readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20231218"
readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20230120"
diff --git a/ci/linux_gcc-floor_libstdcxx_bazel.sh b/ci/linux_gcc-floor_libstdcxx_bazel.sh
index 68b39994..5bd1dbf8 100755
--- a/ci/linux_gcc-floor_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-floor_libstdcxx_bazel.sh
@@ -59,6 +59,9 @@ if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
fi
+# TODO(absl-team): This currently uses Bazel 5. When upgrading to a version
+# of Bazel that supports Bzlmod, add --enable_bzlmod=false to keep test
+# coverage for the old WORKSPACE dependency management.
for std in ${STD}; do
for compilation_mode in ${COMPILATION_MODE}; do
for exceptions_mode in ${EXCEPTIONS_MODE}; do
diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh
index 091acb33..8f773466 100755
--- a/ci/linux_gcc-latest_libstdcxx_bazel.sh
+++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh
@@ -48,7 +48,7 @@ if [[ ${USE_BAZEL_CACHE:-0} -ne 0 ]]; then
# remote_http_cache url, we make changes to the container part of
# the cache key. Hashing the key is to make it shorter and url-safe.
container_key=$(echo ${DOCKER_CONTAINER} | sha256sum | head -c 16)
- 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:-}"
+ BAZEL_EXTRA_ARGS="--remote_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
@@ -84,7 +84,7 @@ for std in ${STD}; do
--copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
--copt=-Werror \
--define=\"absl=1\" \
- --distdir=\"/bazel-distdir\" \
+ --enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--show_timestamps \
diff --git a/ci/macos_xcode_bazel.sh b/ci/macos_xcode_bazel.sh
index 04c9a1a2..bb8fb4bd 100755
--- a/ci/macos_xcode_bazel.sh
+++ b/ci/macos_xcode_bazel.sh
@@ -24,7 +24,7 @@ if [[ -z ${ABSEIL_ROOT:-} ]]; then
fi
# If we are running on Kokoro, check for a versioned Bazel binary.
-KOKORO_GFILE_BAZEL_BIN="bazel-5.1.1-darwin-x86_64"
+KOKORO_GFILE_BAZEL_BIN="bazel-7.0.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}
@@ -56,6 +56,7 @@ ${BAZEL_BIN} test ... \
--copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \
--copt="-Werror" \
--cxxopt="-std=c++14" \
+ --enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--show_timestamps \
diff --git a/ci/windows_clangcl_bazel.bat b/ci/windows_clangcl_bazel.bat
index 21230e1f..5162628b 100755
--- a/ci/windows_clangcl_bazel.bat
+++ b/ci/windows_clangcl_bazel.bat
@@ -39,7 +39,7 @@ IF NOT "%ALTERNATE_OPTIONS%"=="" copy %ALTERNATE_OPTIONS% absl\base\options.h
:: /google/data/rw/teams/absl/kokoro/windows.
::
:: TODO(absl-team): Remove -Wno-microsoft-cast
-%KOKORO_GFILE_DIR%\bazel-5.1.1-windows-x86_64.exe ^
+%KOKORO_GFILE_DIR%\bazel-7.0.0-windows-x86_64.exe ^
test ... ^
--compilation_mode=%COMPILATION_MODE% ^
--compiler=clang-cl ^
@@ -48,7 +48,9 @@ IF NOT "%ALTERNATE_OPTIONS%"=="" copy %ALTERNATE_OPTIONS% absl\base\options.h
--copt=-Wno-microsoft-cast ^
--define=absl=1 ^
--distdir=%KOKORO_GFILE_DIR%\distdir ^
- --features=external_include_paths ^
+ --enable_bzlmod=true ^
+ --extra_execution_platforms=//absl:x64_windows-clang-cl ^
+ --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl ^
--keep_going ^
--test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
--test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
diff --git a/ci/windows_msvc_bazel.bat b/ci/windows_msvc_bazel.bat
index 11d9f350..e2acf5fd 100755
--- a/ci/windows_msvc_bazel.bat
+++ b/ci/windows_msvc_bazel.bat
@@ -34,14 +34,14 @@ IF NOT "%ALTERNATE_OPTIONS%"=="" copy %ALTERNATE_OPTIONS% absl\base\options.h
:: To upgrade Bazel, first download a new binary from
:: https://github.com/bazelbuild/bazel/releases and copy it to
:: /google/data/rw/teams/absl/kokoro/windows.
-%KOKORO_GFILE_DIR%\bazel-5.1.1-windows-x86_64.exe ^
+%KOKORO_GFILE_DIR%\bazel-7.0.0-windows-x86_64.exe ^
test ... ^
--compilation_mode=%COMPILATION_MODE% ^
--copt=/WX ^
--copt=/std:%STD% ^
--define=absl=1 ^
--distdir=%KOKORO_GFILE_DIR%\distdir ^
- --features=external_include_paths ^
+ --enable_bzlmod=true ^
--keep_going ^
--test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
--test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
diff --git a/ci/windows_msvc_cmake.bat b/ci/windows_msvc_cmake.bat
index 743b6f79..c0f1ac94 100755
--- a/ci/windows_msvc_cmake.bat
+++ b/ci/windows_msvc_cmake.bat
@@ -14,7 +14,12 @@
SETLOCAL ENABLEDELAYEDEXPANSION
-:: Use GoogleTest v1.14.0
+:: 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.
+:: TODO(dmauro): After the next GoogleTest release, use the stable file required
+:: by Bzlmod. This means downloading a copy of the file and reuploading it to
+:: avoid changing checksums if the compression is changed by GitHub. It also
+:: means stop referring to it as a commit and instead use the uploaded filename.
SET ABSL_GOOGLETEST_COMMIT=f8d7d77c06936315286eb55f8de22cd23c188571
IF EXIST %KOKORO_GFILE_DIR%\distdir\%ABSL_GOOGLETEST_COMMIT%.zip (
diff --git a/create_lts.py b/create_lts.py
index 642b8847..7e5368e1 100755
--- a/create_lts.py
+++ b/create_lts.py
@@ -96,6 +96,11 @@ def main(argv):
# Replacement directives go here.
ReplaceStringsInFile(
+ 'MODULE.bazel', {
+ 'version = "head"':
+ 'version = "{}.0"'.format(datestamp)
+ })
+ ReplaceStringsInFile(
'absl/base/config.h', {
'#undef ABSL_LTS_RELEASE_VERSION':
'#define ABSL_LTS_RELEASE_VERSION {}'.format(datestamp),